vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 gOutfitter_Settings = nil;
2  
3 local Outfitter_cInitializationEvent = "PLAYER_ENTERING_WORLD";
4  
5 local BANKED_FONT_COLOR = {r = 0.25, g = 0.2, b = 1.0};
6 local BANKED_FONT_COLOR_CODE = "|cff4033ff";
7 local OUTFIT_MESSAGE_COLOR = {r = 0.2, g = 0.75, b = 0.3};
8  
9 local Outfitter_cSlotNames =
10 {
11 -- First priority goes to armor
12  
13 "HeadSlot",
14 "ShoulderSlot",
15 "ChestSlot",
16 "WristSlot",
17 "HandsSlot",
18 "WaistSlot",
19 "LegsSlot",
20 "FeetSlot",
21  
22 -- Second priority goes to weapons
23  
24 "MainHandSlot",
25 "SecondaryHandSlot",
26 "RangedSlot",
27 "AmmoSlot",
28  
29 -- Last priority goes to items with no durability
30  
31 "BackSlot",
32 "NeckSlot",
33 "ShirtSlot",
34 "TabardSlot",
35 "Finger0Slot",
36 "Finger1Slot",
37 "Trinket0Slot",
38 "Trinket1Slot",
39 };
40  
41 local Outfitter_cSlotDisplayNames =
42 {
43 HeadSlot = HEADSLOT,
44 NeckSlot = NECKSLOT,
45 ShoulderSlot = SHOULDERSLOT,
46 BackSlot = BACKSLOT,
47 ChestSlot = CHESTSLOT,
48 ShirtSlot = SHIRTSLOT,
49 TabardSlot = TABARDSLOT,
50 WristSlot = WRISTSLOT,
51 HandsSlot = HANDSSLOT,
52 WaistSlot = WAISTSLOT,
53 LegsSlot = LEGSSLOT,
54 FeetSlot = FEETSLOT,
55 Finger0Slot = Outfitter_cFinger0SlotName,
56 Finger1Slot = Outfitter_cFinger1SlotName,
57 Trinket0Slot = Outfitter_cTrinket0SlotName,
58 Trinket1Slot = Outfitter_cTrinket1SlotName,
59 MainHandSlot = MAINHANDSLOT,
60 SecondaryHandSlot = SECONDARYHANDSLOT,
61 RangedSlot = RANGEDSLOT,
62 AmmoSlot = AMMOSLOT,
63 };
64  
65 local Outfitter_cInvTypeToSlotName =
66 {
67 INVTYPE_2HWEAPON = {SlotName = "MainHandSlot", MetaSlotName = "TwoHandSlot"},
68 INVTYPE_BAG = {SlotName = "Bag"},
69 INVTYPE_BODY = {SlotName = "ShirtSlot"},
70 INVTYPE_CHEST = {SlotName = "ChestSlot"},
71 INVTYPE_CLOAK = {SlotName = "BackSlot"},
72 INVTYPE_FEET = {SlotName = "FeetSlot"},
73 INVTYPE_FINGER = {SlotName = "Finger0Slot"},
74 INVTYPE_HAND = {SlotName = "HandsSlot"},
75 INVTYPE_HEAD = {SlotName = "HeadSlot"},
76 INVTYPE_HOLDABLE = {SlotName = "SecondaryHandSlot"},
77 INVTYPE_LEGS = {SlotName = "LegsSlot"},
78 INVTYPE_NECK = {SlotName = "NeckSlot"},
79 INVTYPE_RANGED = {SlotName = "RangedSlot"},
80 INVTYPE_ROBE = {SlotName = "ChestSlot"},
81 INVTYPE_SHIELD = {SlotName = "SecondaryHandSlot"},
82 INVTYPE_SHOULDER = {SlotName = "ShoulderSlot"},
83 INVTYPE_TABARD = {SlotName = "TabardSlot"},
84 INVTYPE_TRINKET = {SlotName = "Trinket0Slot"},
85 INVTYPE_WAIST = {SlotName = "WaistSlot"},
86 INVTYPE_WEAPON = {SlotName = "MainHandSlot", MetaSlotName = "Weapon0Slot"},
87 INVTYPE_WEAPONMAINHAND = {SlotName = "MainHandSlot"},
88 INVTYPE_WEAPONOFFHAND = {SlotName = "SecondaryHandSlot"},
89 INVTYPE_WRIST = {SlotName = "WristSlot"},
90 INVTYPE_RANGEDRIGHT = {SlotName = "RangedSlot"},
91 INVTYPE_AMMO = {SlotName = "AmmoSlot"},
92 INVTYPE_THROWN = {SlotName = "RangedSlot"},
93 INVTYPE_RELIC = {SlotName = "RangedSlot"},
94 };
95  
96 local Outfitter_cHalfAlternateStatSlot =
97 {
98 Trinket0Slot = "Trinket1Slot",
99 Finger0Slot = "Finger1Slot",
100 Weapon0Slot = "Weapon1Slot",
101 };
102  
103 local Outfitter_cFullAlternateStatSlot =
104 {
105 Trinket0Slot = "Trinket1Slot",
106 Trinket1Slot = "Trinket0Slot",
107 Finger0Slot = "Finger1Slot",
108 Finger1Slot = "Finger0Slot",
109 Weapon0Slot = "Weapon1Slot",
110 Weapon1Slot = "Weapon0Slot",
111 };
112  
113 local gOutfitter_cCategoryOrder =
114 {
115 "Complete",
116 "Partial",
117 "Accessory",
118 "Special"
119 };
120  
121 local gOutfitter_Collapsed = {};
122 local gOutfitter_BankFrameOpened = false;
123  
124 local Outfitter_cItemAliases =
125 {
126 [18608] = 18609, -- Benediction -> Anathema
127 [18609] = 18608, -- Anathema -> Benediction
128 [17223] = 17074, -- Thunderstrike -> Shadowstrike
129 [17074] = 17223, -- Shadowstrike -> Thunderstrike
130 };
131  
132 local Outfitter_cSpecialtyBags =
133 {
134 [21340] = {Name = "Soul Pouch", Type = "ShardBag"},
135 [21341] = {Name = "Felcloth Bag", Type = "ShardBag"},
136 [21342] = {Name = "Core Felcloth Bag", Type = "ShardBag"},
137 [22243] = {Name = "Small Soul Pouch", Type = "ShardBag"},
138 [22244] = {Name = "Box of Souls", Type = "ShardBag"},
139  
140 [2102] = {Name = "Small Ammo Pouch", Type = "AmmoPouch"},
141 [7279] = {Name = "Small Leather Ammo Pouch", Type = "AmmoPouch"},
142 [8218] = {Name = "Thick Leather Ammo Pouch", Type = "AmmoPouch"},
143 [7372] = {Name = "Heavy Leather Ammo Pouch", Type = "AmmoPouch"},
144 [3574] = {Name = "Hunting Ammo Sack", Type = "AmmoPouch"},
145 [3604] = {Name = "Bandolier of the Night Watch", Type = "AmmoPouch"},
146 [5441] = {Name = "Small Shot Pouch", Type = "AmmoPouch"},
147 [2663] = {Name = "Ribbly's Bandolier", Type = "AmmoPouch"},
148 [19320] = {Name = "Gnoll Skin Bandolier", Type = "AmmoPouch"},
149  
150 [19319] = {Name = "Harpy Hide Quiver", Type = "Quiver"},
151 [7371] = {Name = "Heavy Quiver", Type = "Quiver"},
152 [3573] = {Name = "Hunting Quiver", Type = "Quiver"},
153 [7278] = {Name = "Light Leather Quiver", Type = "Quiver"},
154 [2101] = {Name = "Light Quiver", Type = "Quiver"},
155 [11362] = {Name = "Medium Quiver", Type = "Quiver"},
156 [8217] = {Name = "Quickdraw Quiver", Type = "Quiver"},
157 [3605] = {Name = "Quiver of the Night Watch", Type = "Quiver"},
158 [2662] = {Name = "Ribbly's Quiver", Type = "Quiver"},
159 [5439] = {Name = "Small Quiver", Type = "Quiver"},
160 [18714] = {Name = "Ancient Sinew Wrapped Lamina", Type = "Quiver"},
161  
162 [22246] = {Name = "Enchanted Mageweave Pouch", Type = "Enchant"},
163 [22248] = {Name = "Enchanted Runecloth Bag", Type = "Enchant"},
164 [22249] = {Name = "Big Bag of Enchantment", Type = "Enchant"},
165  
166 [22250] = {Name = "Herb Pouch", Type = "Herb"},
167 [22251] = {Name = "Cenarian Herb Bag", Type = "Herb"},
168 [22252] = {Name = "Satchel of Cenarious", Type = "Herb"},
169 };
170  
171 local Outfitter_cFishingPoles =
172 {
173 {Code = 19970, SubCode = 0}, -- Outfitter_cArcaniteFishingPole
174 {Code = 19022, SubCode = 0}, -- Outfitter_cNatPaglesFishingPole
175 {Code = 12224, SubCode = 0}, -- Outfitter_cBlumpFishingPole
176 {Code = 6367, SubCode = 0}, -- Outfitter_cBigIronFishingPole
177 {Code = 6365, SubCode = 0}, -- Outfitter_cStrongFishingPole
178 {Code = 6256, SubCode = 0}, -- Outfitter_cFishingPole
179 };
180  
181 local Outfitter_cRidingItems =
182 {
183 {Code = 11122, SubCode = 0}, -- Outfitter_cCarrotOnAStick
184 };
185  
186 local Outfitter_cArgentDawnTrinkets =
187 {
188 {Code = 13209, SubCode = 0}, -- Outfitter_cSealOfTheDawn
189 {Code = 19812, SubCode = 0}, -- Outfitter_cRuneOfTheDawn
190 {Code = 12846, SubCode = 0}, -- Outfitter_cArgentDawnCommission
191 };
192  
193 local Outfitter_cStatIDItems =
194 {
195 Fishing = Outfitter_cFishingPoles,
196 Riding = Outfitter_cRidingItems,
197 ArgentDawn = Outfitter_cArgentDawnTrinkets,
198 };
199  
200 local Outfitter_cIgnoredUnusedItems =
201 {
202 [2901] = "Mining Pick",
203 [5956] = "Blacksmith hammer",
204 [6219] = "Arclight Spanner",
205 [7005] = "Skinning Knife",
206 [7297] = "Morbent's Bane",
207 [10696] = "Enchanted Azsharite Felbane Sword",
208 [10697] = "Enchanted Azsharite Felbane Dagger",
209 [10698] = "Enchanted Azsharite Felbane Staff",
210 [20406] = "Twilight Cultist Mantle",
211 [20407] = "Twilight Cultist Robe",
212 [20408] = "Twilight Cultist Cowl",
213 };
214  
215 local Outfitter_cSmartOutfits =
216 {
217 {Name = Outfitter_cFishingOutfit, StatID = "Fishing", IsAccessory = true},
218 {Name = Outfitter_cHerbalismOutfit, StatID = "Herbalism", IsAccessory = true},
219 {Name = Outfitter_cMiningOutfit, StatID = "Mining", IsAccessory = true},
220 {Name = Outfitter_cSkinningOutfit, StatID = "Skinning", IsAccessory = true},
221 {Name = Outfitter_cFireResistOutfit, StatID = "FireResist"},
222 {Name = Outfitter_cNatureResistOutfit, StatID = "NatureResist"},
223 {Name = Outfitter_cShadowResistOutfit, StatID = "ShadowResist"},
224 {Name = Outfitter_cArcaneResistOutfit, StatID = "ArcaneResist"},
225 {Name = Outfitter_cFrostResistOutfit, StatID = "FrostResist"},
226 };
227  
228 local Outfitter_cStatCategoryInfo =
229 {
230 {Category = "Stat", Name = Outfitter_cStatsCategory},
231 {Category = "Melee", Name = Outfitter_cMeleeCategory},
232 {Category = "Spell", Name = Outfitter_cSpellsCategory},
233 {Category = "Regen", Name = Outfitter_cRegenCategory},
234 {Category = "Resist", Name = Outfitter_cResistCategory},
235 {Category = "Trade", Name = Outfitter_cTradeCategory},
236 };
237  
238 local Outfitter_cItemStatInfo =
239 {
240 {ID = "Agility", Name = Outfitter_cAgilityStatName, Category = "Stat"},
241 {ID = "Armor", Name = Outfitter_cArmorStatName, Category = "Stat"},
242 {ID = "Defense", Name = Outfitter_cDefenseStatName, Category = "Stat"},
243 {ID = "Intellect", Name = Outfitter_cIntellectStatName, Category = "Stat"},
244 {ID = "Spirit", Name = Outfitter_cSpiritStatName, Category = "Stat"},
245 {ID = "Stamina", Name = Outfitter_cStaminaStatName, Category = "Stat"},
246 {ID = "Strength", Name = Outfitter_cStrengthStatName, Category = "Stat"},
247  
248 {ID = "ManaRegen", Name = Outfitter_cManaRegenStatName, Category = "Regen"},
249 {ID = "HealthRegen", Name = Outfitter_cHealthRegenStatName, Category = "Regen"},
250  
251 {ID = "SpellCrit", Name = Outfitter_cSpellCritStatName, Category = "Spell"},
252 {ID = "SpellHit", Name = Outfitter_cSpellHitStatName, Category = "Spell"},
253 {ID = "SpellDmg", Name = Outfitter_cSpellDmgStatName, Category = "Spell"},
254 {ID = "FrostDmg", Name = Outfitter_cFrostDmgStatName, Category = "Spell"},
255 {ID = "FireDmg", Name = Outfitter_cFireDmgStatName, Category = "Spell"},
256 {ID = "ArcaneDmg", Name = Outfitter_cArcaneDmgStatName, Category = "Spell"},
257 {ID = "ShadowDmg", Name = Outfitter_cShadowDmgStatName, Category = "Spell"},
258 {ID = "NatureDmg", Name = Outfitter_cNatureDmgStatName, Category = "Spell"},
259 {ID = "Healing", Name = Outfitter_cHealingStatName, Category = "Spell"},
260  
261 {ID = "MeleeCrit", Name = Outfitter_cMeleeCritStatName, Category = "Melee"},
262 {ID = "MeleeHit", Name = Outfitter_cMeleeHitStatName, Category = "Melee"},
263 {ID = "MeleeDmg", Name = Outfitter_cMeleeDmgStatName, Category = "Melee"},
264 {ID = "Dodge", Name = Outfitter_cDodgeStatName, Category = "Melee"},
265 {ID = "Attack", Name = Outfitter_cAttackStatName, Category = "Melee"},
266  
267 {ID = "ArcaneResist", Name = Outfitter_cArcaneResistStatName, Category = "Resist"},
268 {ID = "FireResist", Name = Outfitter_cFireResistStatName, Category = "Resist"},
269 {ID = "FrostResist", Name = Outfitter_cFrostResistStatName, Category = "Resist"},
270 {ID = "NatureResist", Name = Outfitter_cNatureResistStatName, Category = "Resist"},
271 {ID = "ShadowResist", Name = Outfitter_cShadowResistStatName, Category = "Resist"},
272  
273 {ID = "Fishing", Name = Outfitter_cFishingStatName, Category = "Trade"},
274 {ID = "Herbalism", Name = Outfitter_cHerbalismStatName, Category = "Trade"},
275 {ID = "Mining", Name = Outfitter_cMiningStatName, Category = "Trade"},
276 {ID = "Skinning", Name = Outfitter_cSkinningStatName, Category = "Trade"},
277 };
278  
279 local Outfitter_cNormalizedClassName =
280 {
281 [Outfitter_cDruidClassName] = "Druid",
282 [Outfitter_cHunterClassName] = "Hunter",
283 [Outfitter_cMageClassName] = "Mage",
284 [Outfitter_cPaladinClassName] = "Paladin",
285 [Outfitter_cPriestClassName] = "Priest",
286 [Outfitter_cRogueClassName] = "Rogue",
287 [Outfitter_cShamanClassName] = "Shaman",
288 [Outfitter_cWarlockClassName] = "Warlock",
289 [Outfitter_cWarriorClassName] = "Warrior",
290 };
291  
292 local Outfitter_cClassSpecialOutfits =
293 {
294 Warrior =
295 {
296 {Name = Outfitter_cWarriorBattleStance, SpecialID = "Battle"},
297 {Name = Outfitter_cWarriorDefensiveStance, SpecialID = "Defensive"},
298 {Name = Outfitter_cWarriorBerserkerStance, SpecialID = "Berserker"},
299 },
300  
301 Druid =
302 {
303 {Name = Outfitter_cDruidBearForm, SpecialID = "Bear"},
304 {Name = Outfitter_cDruidCatForm, SpecialID = "Cat"},
305 {Name = Outfitter_cDruidAquaticForm, SpecialID = "Aquatic"},
306 {Name = Outfitter_cDruidTravelForm, SpecialID = "Travel"},
307 {Name = Outfitter_cDruidMoonkinForm, SpecialID = "Moonkin"},
308 },
309  
310 Priest =
311 {
312 {Name = Outfitter_cPriestShadowform, SpecialID = "Shadowform"},
313 },
314  
315 Rogue =
316 {
317 {Name = Outfitter_cRogueStealth, SpecialID = "Stealth"},
318 },
319  
320 Shaman =
321 {
322 {Name = Outfitter_cShamanGhostWolf, SpecialID = "GhostWolf"},
323 },
324  
325 Hunter =
326 {
327 {Name = Outfitter_cHunterMonkey, SpecialID = "Monkey"},
328 {Name = Outfitter_cHunterHawk, SpecialID = "Hawk"},
329 {Name = Outfitter_cHunterCheetah, SpecialID = "Cheetah"},
330 {Name = Outfitter_cHunterPack, SpecialID = "Pack"},
331 {Name = Outfitter_cHunterBeast, SpecialID = "Beast"},
332 {Name = Outfitter_cHunterWild, SpecialID = "Wild"},
333 },
334  
335 Mage =
336 {
337 {Name = Outfitter_cMageEvocate, SpecialID = "Evocate"},
338 },
339 };
340  
341 local gOutfitter_SpellNameSpecialID =
342 {
343 [Outfitter_cAspectOfTheCheetah] = "Cheetah",
344 [Outfitter_cAspectOfThePack] = "Pack",
345 [Outfitter_cAspectOfTheBeast] = "Beast",
346 [Outfitter_cAspectOfTheWild] = "Wild",
347 [Outfitter_cEvocate] = "Evocate",
348 };
349  
350 local gOutfitter_AuraIconSpecialID =
351 {
352 ["INV_Misc_Fork&Knife"] = "Dining",
353 ["Spell_Shadow_Shadowform"] = "Shadowform",
354 ["Spell_Nature_SpiritWolf"] = "GhostWolf",
355 ["Ability_Rogue_FeignDeath"] = "Feigning",
356 ["Ability_Hunter_AspectOfTheMonkey"] = "Monkey",
357 ["Spell_Nature_RavenForm"] = "Hawk",
358 };
359  
360 local Outfitter_cSpecialOutfitDescriptions =
361 {
362 ArgentDawn = Outfitter_cArgentDawnOutfitDescription,
363 Riding = Outfitter_cRidingOutfitDescription,
364 Dining = Outfitter_cDiningOutfitDescription,
365 Battleground = Outfitter_cBattlegroundOutfitDescription,
366 AB = Outfitter_cArathiBasinOutfitDescription,
367 AV = Outfitter_cAlteracValleyOutfitDescription,
368 WSG = Outfitter_cWarsongGulchOutfitDescription,
369 City = Outfitter_cCityOutfitDescription,
370 };
371  
372 -- Note that zone special outfits will be worn in the order
373 -- the are listed here, with later outfits being worn over
374 -- earlier outfits (when they're being applied at the same time)
375 -- This allows BG-specific outfits to take priority of the generic
376 -- BG outfit
377  
378 local Outfitter_cZoneSpecialIDs =
379 {
380 "ArgentDawn",
381 "City",
382 "Battleground",
383 "AV",
384 "AB",
385 "WSG",
386 };
387  
388 local Outfitter_cZoneSpecialIDMap =
389 {
390 [Outfitter_cWesternPlaguelands] = {"ArgentDawn"},
391 [Outfitter_cEasternPlaguelands] = {"ArgentDawn"},
392 [Outfitter_cStratholme] = {"ArgentDawn"},
393 [Outfitter_cScholomance] = {"ArgentDawn"},
394 [Outfitter_cNaxxramas] = {"ArgentDawn"},
395 [Outfitter_cAlteracValley] = {"Battleground", "AV"},
396 [Outfitter_cArathiBasin] = {"Battleground", "AB"},
397 [Outfitter_cWarsongGulch] = {"Battleground", "WSG"},
398 [Outfitter_cIronforge] = {"City"},
399 [Outfitter_cCityOfIronforge] = {"City"},
400 [Outfitter_cDarnassus] = {"City"},
401 [Outfitter_cStormwind] = {"City"},
402 [Outfitter_cOrgrimmar] = {"City"},
403 [Outfitter_cThunderBluff] = {"City"},
404 [Outfitter_cUndercity] = {"City"},
405 };
406  
407 local gOutfitter_StatDistribution =
408 {
409 DRUID =
410 {
411 Agility = {Armor = {Coeff = 2}, Dodge = {Coeff = 1 / 20}},
412 Stamina = {Health = {Coeff = 10}},
413 Intellect = {Mana = {Coeff = 15}, SpellCrit = {Coeff = 1 / 30}},
414 Spirit = {ManaRegen = {Coeff = 0.25 * 2.5}}, -- * 2.5 to convert from ticks to per-five-seconds
415 Strength = {BlockAmount = {Coeff = 1 / 22}},
416 },
417  
418 HUNTER =
419 {
420 Agility = {Armor = {Coeff = 2}, Dodge = {Coeff = 1 / 26.5}, MeleeCrit = {Coeff = 1 / 53}},
421 Stamina = {Health = {Coeff = 10}},
422 Intellect = {Mana = {Coeff = 15}},
423 Spirit = {ManaRegen = {Coeff = 0.25 * 2.5}}, -- * 2.5 to convert from ticks to per-five-seconds
424 Strength = {BlockAmount = {Coeff = 1 / 22}},
425 },
426  
427 MAGE =
428 {
429 Agility = {Armor = {Coeff = 2}, Dodge = {Coeff = 1 / 20}, MeleeCrit = {Coeff = 1 / 20}},
430 Stamina = {Health = {Coeff = 10}},
431 Intellect = {Mana = {Coeff = 15}, SpellCrit = {Coeff = 1.0 / 59.5}},
432 Spirit = {ManaRegen = {Coeff = 0.25 * 2.5}}, -- * 2.5 to convert from ticks to per-five-seconds
433 Strength = {BlockAmount = {Coeff = 1 / 22}},
434 },
435  
436 PALADIN =
437 {
438 Agility = {Armor = {Coeff = 2}, Dodge = {Coeff = 1 / 20}, MeleeCrit = {Coeff = 1 / 20}},
439 Stamina = {Health = {Coeff = 10}},
440 Intellect = {Mana = {Coeff = 15}, SpellCrit = {Coeff = 1.0 / 20}},
441 Spirit = {ManaRegen = {Coeff = 0.25 * 2.5}}, -- * 2.5 to convert from ticks to per-five-seconds
442 Strength = {BlockAmount = {Coeff = 1 / 22}},
443 },
444  
445 PRIEST =
446 {
447 Agility = {Armor = {Coeff = 2}, Dodge = {Coeff = 1 / 20}, MeleeCrit = {Coeff = 1 / 20}},
448 Stamina = {Health = {Coeff = 10}},
449 Intellect = {Mana = {Coeff = 15}, SpellCrit = {Coeff = 1.0 / 30}},
450 Spirit = {ManaRegen = {Coeff = 0.25 * 2.5}}, -- * 2.5 to convert from ticks to per-five-seconds
451 Strength = {BlockAmount = {Coeff = 1 / 22}},
452 },
453  
454 ROGUE =
455 {
456 Agility = {Armor = {Coeff = 2}, Dodge = {Coeff = 1 / 14.5}, MeleeCrit = {Coeff = 1 / 29}},
457 Stamina = {Health = {Coeff = 10}},
458 Intellect = {Mana = {Coeff = 15}},
459 Spirit = {ManaRegen = {Coeff = 0.25 * 2.5}}, -- * 2.5 to convert from ticks to per-five-seconds
460 Strength = {BlockAmount = {Coeff = 1 / 22}},
461 },
462  
463 SHAMAN =
464 {
465 Agility = {Armor = {Coeff = 2}, Dodge = {Coeff = 1 / 20}, MeleeCrit = {Coeff = 1 / 20}},
466 Stamina = {Health = {Coeff = 10}},
467 Intellect = {Mana = {Coeff = 15}, SpellCrit = {Coeff = 1.0 / 20}},
468 Spirit = {ManaRegen = {Coeff = 0.25 * 2.5}}, -- * 2.5 to convert from ticks to per-five-seconds
469 Strength = {BlockAmount = {Coeff = 1 / 22}},
470 },
471  
472 WARLOCK =
473 {
474 Agility = {Armor = {Coeff = 2}, Dodge = {Coeff = 1 / 20}, MeleeCrit = {Coeff = 1 / 20}},
475 Stamina = {Health = {Coeff = 10}},
476 Intellect = {Mana = {Coeff = 15}, SpellCrit = {Coeff = 1.0 / 30}},
477 Spirit = {ManaRegen = {Coeff = 0.25 * 2.5}}, -- * 2.5 to convert from ticks to per-five-seconds
478 Strength = {BlockAmount = {Coeff = 1 / 22}},
479 },
480  
481 WARRIOR =
482 {
483 Agility = {Armor = {Coeff = 2}, Dodge = {Coeff = 1 / 20}, MeleeCrit = {Coeff = 1 / 20}},
484 Stamina = {Health = {Coeff = 10}},
485 Intellect = {Mana = {Coeff = 15}},
486 Spirit = {ManaRegen = {Coeff = 0.25 * 2.5}}, -- * 2.5 to convert from ticks to per-five-seconds
487 Strength = {BlockAmount = {Coeff = 1 / 22}},
488 },
489 };
490  
491 local Outfitter_cCombatEquipmentSlots =
492 {
493 MainHandSlot = true,
494 SecondaryHandSlot = true,
495 RangedSlot = true,
496 AmmoSlot = true,
497 };
498  
499 local gOutfitter_OutfitStack = {};
500  
501 local gOutfitter_SelectedOutfit = nil;
502 local gOutfitter_DisplayIsDirty = true;
503  
504 local gOutfitter_CurrentZone = nil;
505 local gOutfitter_InCombat = false;
506 local gOutfitter_IsDead = false;
507 local gOutfitter_IsFeigning = false;
508  
509 local gOutfitter_EquippedNeedsUpdate = false;
510 local gOutfitter_WeaponsNeedUpdate = false;
511 local gOutfitter_LastEquipmentUpdateTime = 0;
512 local Outfitter_cMinEquipmentUpdateInterval = 1.5;
513  
514 local gOutfitter_CurrentOutfit = nil;
515 local gOutfitter_ExpectedOutfit = nil;
516 local gOutfitter_CurrentInventoryOutfit = nil;
517 local gOutfitter_EquippableItems = nil;
518  
519 local gOutfitter_Initialized = false;
520 local gOutfitter_Suspended = false;
521  
522 local Outfitter_cMaxDisplayedItems = 14;
523  
524 local gOutfitter_PanelFrames =
525 {
526 "OutfitterMainFrame",
527 "OutfitterOptionsFrame",
528 "OutfitterAboutFrame",
529 };
530  
531 local gOutfitter_CurrentPanel = 0;
532  
533 local Outfitter_cShapeshiftSpecialIDs =
534 {
535 -- Warriors
536  
537 [Outfitter_cBattleStance] = {ID = "Battle"},
538 [Outfitter_cDefensiveStance] = {ID = "Defensive"},
539 [Outfitter_cBerserkerStance] = {ID = "Berserker"},
540  
541 -- Druids
542  
543 [Outfitter_cBearForm] = {ID = "Bear"},
544 [Outfitter_cCatForm] = {ID = "Cat"},
545 [Outfitter_cAquaticForm] = {ID = "Aquatic"},
546 [Outfitter_cTravelForm] = {ID = "Travel"},
547 [Outfitter_cDireBearForm] = {ID = "Bear"},
548 [Outfitter_cMoonkinForm] = {ID = "Moonkin"},
549  
550 -- Rogues
551  
552 [Outfitter_cStealth] = {ID = "Stealth"},
553 };
554  
555 local gOutfitter_SpecialState = {};
556  
557 StaticPopupDialogs["OUTFITTER_CONFIRM_DELETE"] =
558 {
559 text = TEXT(Outfitter_cConfirmDeleteMsg),
560 button1 = TEXT(DELETE),
561 button2 = TEXT(CANCEL),
562 OnAccept = function() Outfitter_DeleteSelectedOutfit(); end,
563 timeout = 0,
564 whileDead = 1,
565 hideOnEscape = 1
566 };
567  
568 StaticPopupDialogs["OUTFITTER_CONFIRM_REBUILD"] =
569 {
570 text = TEXT(Outfitter_cConfirmRebuildMsg),
571 button1 = TEXT(Outfitter_cRebuild),
572 button2 = TEXT(CANCEL),
573 OnAccept = function() Outfitter_RebuildSelectedOutfit(); end,
574 timeout = 0,
575 whileDead = 1,
576 hideOnEscape = 1,
577 };
578  
579 function Outfitter_ToggleOutfitterFrame()
580 if Outfitter_IsOpen() then
581 OutfitterFrame:Hide();
582 else
583 OutfitterFrame:Show();
584 end
585 end
586  
587 function Outfitter_IsOpen()
588 return OutfitterFrame:IsVisible();
589 end
590  
591 function Outfitter_OnLoad()
592 Outfitter_RegisterEvent(this, "PLAYER_ENTERING_WORLD", Outfitter_PlayerEnteringWorld);
593 Outfitter_RegisterEvent(this, "PLAYER_LEAVING_WORLD", Outfitter_PlayerLeavingWorld);
594 Outfitter_RegisterEvent(this, "VARIABLES_LOADED", Outfitter_VariablesLoaded);
595  
596 -- For monitoring mounted, dining and shadowform states
597  
598 Outfitter_RegisterEvent(this, "PLAYER_AURAS_CHANGED", Outfitter_UpdateAuraStates);
599  
600 -- For monitoring plaguelands and battlegrounds
601  
602 Outfitter_RegisterEvent(this, "ZONE_CHANGED_NEW_AREA", Outfitter_UpdateZone);
603  
604 -- For monitoring player combat state
605  
606 Outfitter_RegisterEvent(this, "PLAYER_REGEN_ENABLED", Outfitter_RegenEnabled);
607 Outfitter_RegisterEvent(this, "PLAYER_REGEN_DISABLED", Outfitter_RegenDisabled);
608  
609 -- For monitoring player dead/alive stat
610  
611 Outfitter_RegisterEvent(this, "PLAYER_DEAD", Outfitter_PlayerDead);
612 Outfitter_RegisterEvent(this, "PLAYER_ALIVE", Outfitter_PlayerAlive);
613 Outfitter_RegisterEvent(this, "PLAYER_UNGHOST", Outfitter_PlayerAlive);
614  
615 Outfitter_RegisterEvent(this, "UNIT_INVENTORY_CHANGED", Outfitter_InventoryChanged);
616  
617 -- For indicating which outfits are missing items
618  
619 Outfitter_RegisterEvent(this, "BAG_UPDATE", Outfitter_BagUpdate);
620 Outfitter_RegisterEvent(this, "PLAYERBANKSLOTS_CHANGED", Outfitter_BankSlotsChanged);
621  
622 -- For monitoring bank bags
623  
624 Outfitter_RegisterEvent(this, "BANKFRAME_OPENED", Outfitter_BankFrameOpened);
625 Outfitter_RegisterEvent(this, "BANKFRAME_CLOSED", Outfitter_BankFrameClosed);
626  
627 -- For unequipping the dining outfit
628  
629 Outfitter_RegisterEvent(this, "UNIT_HEALTH", Outfitter_UnitHealthOrManaChanged);
630 Outfitter_RegisterEvent(this, "UNIT_MANA", Outfitter_UnitHealthOrManaChanged);
631  
632 Outfitter_SuspendEvent(this, "UNIT_HEALTH"); -- Don't actually care until the dining outfit equips
633 Outfitter_SuspendEvent(this, "UNIT_MANA");
634  
635 -- Tabs
636  
637 PanelTemplates_SetNumTabs(this, table.getn(gOutfitter_PanelFrames));
638 OutfitterFrame.selectedTab = gOutfitter_CurrentPanel;
639 PanelTemplates_UpdateTabs(this);
640  
641 -- Install the /outfit command handler
642  
643 SlashCmdList["OUTFITTER"] = Outfitter_ExecuteCommand;
644  
645 SLASH_OUTFITTER1 = "/outfitter";
646  
647 -- Fake a leaving world event to suspend inventory/bag
648 -- updating until loading is completed
649  
650 Outfitter_PlayerLeavingWorld();
651 end
652  
653 function Outfitter_OnShow()
654 Outfitter_ShowPanel(1); -- Always switch to the main view when showing the window
655 end
656  
657 function Outfitter_OnHide()
658 Outfitter_ClearSelection();
659 OutfitterQuickSlots_Close();
660 OutfitterFrame:Hide(); -- This seems redundant, but the OnHide handler gets called
661 -- in response to the parent being hidden (the character window)
662 -- so calling Hide() on the frame here ensures that when the
663 -- character window is hidden then Outfitter won't be displayed
664 -- next time it's opened
665 end
666  
667 function Outfitter_OnEvent(pEvent)
668 -- Ignore all events except for entering world until initialization is
669 -- completed
670  
671 if not gOutfitter_Initialized
672 and pEvent ~= "VARIABLES_LOADED" then
673 if pEvent ~= Outfitter_cInitializationEvent then
674 return;
675 end
676  
677 Outfitter_Initialize();
678 end
679  
680 --
681  
682 Outfitter_DispatchEvent(this, pEvent);
683 Outfitter_Update(false);
684 end
685  
686 function Outfitter_PlayerLeavingWorld()
687 -- To improve load screen performance, suspend events which are
688 -- fired repeatedly and rapidly during zoning
689  
690 gOutfitter_Suspended = true;
691  
692 Outfitter_SuspendEvent(OutfitterFrame, "BAG_UPDATE");
693 Outfitter_SuspendEvent(OutfitterFrame, "UNIT_INVENTORY_CHANGED");
694 Outfitter_SuspendEvent(OutfitterFrame, "UPDATE_INVENTORY_ALERTS");
695 Outfitter_SuspendEvent(OutfitterFrame, "SPELLS_CHANGED");
696 Outfitter_SuspendEvent(OutfitterFrame, "PLAYER_AURAS_CHANGED");
697 Outfitter_SuspendEvent(OutfitterFrame, "PLAYERBANKSLOTS_CHANGED");
698 end
699  
700 function Outfitter_PlayerEnteringWorld()
701 OutfitterItemList_FlushEquippableItems();
702  
703 Outfitter_RegenEnabled();
704 Outfitter_UpdateAuraStates();
705 Outfitter_SetSpecialOutfitEnabled("Riding", false);
706  
707 Outfitter_ResumeLoadScreenEvents();
708 end
709  
710 function Outfitter_ResumeLoadScreenEvents()
711 if gOutfitter_Suspended then
712 -- To improve load screen performance, suspend events which are
713 -- fired repeatedly and rapidly during zoning
714  
715 gOutfitter_Suspended = false;
716  
717 Outfitter_ResumeEvent(OutfitterFrame, "BAG_UPDATE");
718 Outfitter_ResumeEvent(OutfitterFrame, "UNIT_INVENTORY_CHANGED");
719 Outfitter_ResumeEvent(OutfitterFrame, "UPDATE_INVENTORY_ALERTS");
720 Outfitter_ResumeEvent(OutfitterFrame, "SPELLS_CHANGED");
721 Outfitter_ResumeEvent(OutfitterFrame, "PLAYER_AURAS_CHANGED");
722 Outfitter_ResumeEvent(OutfitterFrame, "PLAYERBANKSLOTS_CHANGED");
723  
724 Outfitter_InventoryChanged2();
725 end
726 end
727  
728 function Outfitter_VariablesLoaded()
729 -- Change the initialization event to PLAYER_ALIVE if this is the first use
730 -- This will ensure that the bags and inventory info has been loaded before
731 -- trying to generate the automatic outfits
732  
733 if not gOutfitter_Settings
734 or not gOutfitter_Settings.Outfits then
735 Outfitter_cInitializationEvent = "PLAYER_ALIVE";
736 end
737 end
738  
739 function Outfitter_BankSlotsChanged()
740 OutfitterItemList_FlushBagFromEquippableItems(-1);
741  
742 for vBagIndex = NUM_BAG_SLOTS + 1, NUM_BAG_SLOTS + NUM_BANKBAGSLOTS do
743 OutfitterItemList_FlushBagFromEquippableItems(vBagIndex);
744 end
745  
746 -- Force the bank bags to update since they now exist
747  
748 if gOutfitter_EquippableItems then
749 gOutfitter_EquippableItems.NeedsUpdate = true;
750 end
751  
752 gOutfitter_DisplayIsDirty = true;
753 Outfitter_Update(false);
754 end
755  
756 function Outfitter_BagUpdate()
757 local vBagIndex = arg1;
758  
759 OutfitterItemList_FlushBagFromEquippableItems(vBagIndex);
760  
761 -- This is a messy hack to ensure the database gets updated properly
762 -- after an upgrade. WoW doesn't always have the players items
763 -- loaded on PLAYER_ENTERING_WORLD so once the bag update fires
764 -- we check the databases again if necessary
765  
766 if gOutfitter_NeedItemCodesUpdated then
767 gOutfitter_NeedItemCodesUpdated = gOutfitter_NeedItemCodesUpdated - 1;
768  
769 if gOutfitter_NeedItemCodesUpdated == 0 then
770 gOutfitter_NeedItemCodesUpdated = nil;
771 end
772  
773 if Outfitter_UpdateDatabaseItemCodes() then
774 gOutfitter_NeedItemCodesUpdated = nil;
775 end
776 end
777  
778 --
779  
780 gOutfitter_DisplayIsDirty = true;
781 Outfitter_Update(false);
782 end
783  
784 local gOutfitter_OutfitEvents = {};
785  
786 function Outfitter_RegisterOutfitEvent(pEvent, pFunction)
787 local vHandlers = gOutfitter_OutfitEvents[pEvent];
788  
789 if not vHandlers then
790 vHandlers = {};
791 gOutfitter_OutfitEvents[pEvent] = vHandlers;
792 end
793  
794 table.insert(vHandlers, pFunction);
795 end
796  
797 function Outfitter_UnregisterOutfitEvent(pEvent, pFunction)
798 local vHandlers = gOutfitter_OutfitEvents[pEvent];
799  
800 if not vHandlers then
801 return;
802 end
803  
804 for vIndex, vFunction in vHandlers do
805 if vFunction == pFunction then
806 table.remove(vHandlers, vIndex);
807 return;
808 end
809 end
810 end
811  
812 function Outfitter_DispatchOutfitEvent(pEvent, pParameter1, pParameter2)
813 -- Don't send out events until we're initialized
814  
815 if not gOutfitter_Initialized then
816 return;
817 end
818  
819 --
820  
821 OutfitterMinimapDropDown_OutfitEvent(pEvent, pParameter1, pParameter2);
822  
823 local vHandlers = gOutfitter_OutfitEvents[pEvent];
824  
825 if not vHandlers then
826 return;
827 end
828  
829 for _, vFunction in vHandlers do
830 -- Call in protected mode so that if they fail it doesn't
831 -- screw up Outfitter or other addons wishing to be notified
832  
833 pcall(vFunction, pEvent, pParameter1, pParameter2);
834 end
835 end
836  
837 function Outfitter_BankFrameOpened()
838 gOutfitter_BankFrameOpened = true;
839 gOutfitter_DisplayIsDirty = true;
840  
841 Outfitter_BankSlotsChanged();
842  
843 Outfitter_Update(false);
844 end
845  
846 function Outfitter_BankFrameClosed()
847 gOutfitter_BankFrameOpened = false;
848  
849 Outfitter_BankSlotsChanged();
850  
851 gOutfitter_DisplayIsDirty = true;
852 Outfitter_Update(false);
853 end
854  
855 function Outfitter_RegenEnabled(pEvent)
856 gOutfitter_InCombat = false;
857 end
858  
859 function Outfitter_RegenDisabled(pEvent)
860 gOutfitter_InCombat = true;
861 end
862  
863 function Outfitter_PlayerDead(pEvent)
864 gOutfitter_IsDead = true;
865 end
866  
867 function Outfitter_PlayerAlive(pEvent)
868 if not UnitIsDeadOrGhost("player") then
869 gOutfitter_IsDead = false;
870 end
871 end
872  
873 function Outfitter_UnitHealthOrManaChanged()
874 if arg1 ~= "player" then
875 return;
876 end
877  
878 local vHealth = UnitHealth("player");
879 local vMaxHealth = UnitHealthMax("player");
880 local vFullHealth = false;
881 local vFullMana = false;
882  
883 if vHealth > (vMaxHealth * 0.99) then
884 vFullHealth = true;
885 end
886  
887 if UnitPowerType("player") == 0 then
888 local vMana = UnitMana("player");
889 local vMaxMana = UnitManaMax("player");
890  
891 if vMana > (vMaxMana * 0.99) then
892 vFullMana = true;
893 end
894 else
895 vFullMana = true;
896 end
897  
898 if vFullHealth and vFullMana then
899 Outfitter_SetSpecialOutfitEnabled("Dining", false);
900 end
901 end
902  
903 function Outfitter_InventoryChanged(pEvent)
904 if arg1 ~= "player" then
905 return;
906 end
907  
908 Outfitter_InventoryChanged2();
909 end
910  
911 function Outfitter_InventoryChanged2()
912 OutfitterItemList_FlushEquippableItems(); -- Flush everything because the game sends bag update
913 -- events for an item after it's already appeared in the inventory. This
914 -- creates a brief situation in which the item appears to be both in the
915 -- bank and in the inventory which causes various problems for Outfitter
916 -- when checking for items.
917  
918 gOutfitter_DisplayIsDirty = true; -- Update the list so the checkboxes reflect the current state
919  
920 local vNewItemsOutfit, vCurrentOutfit = Outfitter_GetNewItemsOutfit(gOutfitter_CurrentOutfit);
921  
922 if vNewItemsOutfit then
923 if not gOutfitter_Settings.Options.DisableAutoVisibility then
924 -- If the cloak is changing, remember the visibility for the old one and set
925 -- it for the new one
926  
927 if vNewItemsOutfit.Items.BackSlot then
928 if gOutfitter_CurrentOutfit.Items.BackSlot.Code then
929 if ShowingCloak() then
930 gOutfitter_Settings.HideCloak[gOutfitter_CurrentOutfit.Items.BackSlot.Code] = false;
931 else
932 gOutfitter_Settings.HideCloak[gOutfitter_CurrentOutfit.Items.BackSlot.Code] = true;
933 end
934 end
935  
936 if gOutfitter_Settings.HideCloak[vNewItemsOutfit.Items.BackSlot.Code] ~= nil then
937 if gOutfitter_Settings.HideCloak[vNewItemsOutfit.Items.BackSlot.Code] then
938 ShowCloak(0);
939 else
940 ShowCloak(1);
941 end
942 end
943 end
944  
945 -- If the helm is changing, remember the visibility for the old one and set
946 -- it for the new one
947  
948 if vNewItemsOutfit.Items.HeadSlot then
949 if gOutfitter_CurrentOutfit.Items.HeadSlot.Code then
950 if ShowingHelm() then
951 gOutfitter_Settings.HideHelm[gOutfitter_CurrentOutfit.Items.HeadSlot.Code] = false;
952 else
953 gOutfitter_Settings.HideHelm[gOutfitter_CurrentOutfit.Items.HeadSlot.Code] = true;
954 end
955 end
956  
957 if gOutfitter_Settings.HideHelm[vNewItemsOutfit.Items.HeadSlot.Code] ~= nil then
958 if gOutfitter_Settings.HideHelm[vNewItemsOutfit.Items.HeadSlot.Code] then
959 ShowHelm(0);
960 else
961 ShowHelm(1);
962 end
963 end
964 end
965 end
966  
967 -- Save the new outfit
968  
969 gOutfitter_CurrentOutfit = vCurrentOutfit;
970  
971 -- Close QuickSlots if there's an inventory change (except if the only
972 -- change is with the ammo slots)
973  
974 if OutfitterQuickSlots.SlotName ~= "AmmoSlot"
975 or not Outfitter_OutfitIsAmmoOnly(vNewItemsOutfit) then
976 OutfitterQuickSlots_Close();
977 end
978  
979 -- Update the selected outfit or temporary outfit
980  
981 Outfitter_SubtractOutfit(vNewItemsOutfit, gOutfitter_ExpectedOutfit);
982  
983 if gOutfitter_SelectedOutfit then
984 Outfitter_UpdateOutfitFromInventory(gOutfitter_SelectedOutfit, vNewItemsOutfit);
985 else
986 Outfitter_UpdateTemporaryOutfit(vNewItemsOutfit);
987 end
988 end
989  
990 Outfitter_Update(true);
991 end
992  
993 function Outfitter_OutfitIsAmmoOnly(pOutfit)
994 local vHasAmmoItem = false;
995  
996 for vInventorySlot, vItem in pOutfit.Items do
997 if vInventorySlot ~= "AmmoSlot" then
998 return false;
999 else
1000 vHasAmmoItem = true;
1001 end
1002 end
1003  
1004 return vHasAmmoItem;
1005 end
1006  
1007 function Outfitter_ExecuteCommand(pCommand)
1008 vCommands =
1009 {
1010 wear = {useOutfit = true, func = Outfitter_WearOutfit},
1011 unwear = {useOutfit = true, func = Outfitter_RemoveOutfit},
1012 toggle = {useOutfit = true, func = Outfitter_ToggleOutfit},
1013 summary = {useOutfit = false, func = Outfitter_OutfitSummary},
1014 }
1015  
1016 local vStartIndex, vEndIndex, vCommand, vParameter = string.find(pCommand, "(%w+) ?(.*)");
1017  
1018 if not vCommand then
1019 Outfitter_NoteMessage(HIGHLIGHT_FONT_COLOR_CODE.."/outfitter wear <outfit name>"..NORMAL_FONT_COLOR_CODE..": Wear an outfit");
1020 Outfitter_NoteMessage(HIGHLIGHT_FONT_COLOR_CODE.."/outfitter unwear <outfit name>"..NORMAL_FONT_COLOR_CODE..": Remove an outfit");
1021 Outfitter_NoteMessage(HIGHLIGHT_FONT_COLOR_CODE.."/outfitter toggle <outfit name>"..NORMAL_FONT_COLOR_CODE..": Wears or removes an outfit");
1022 return;
1023 end
1024  
1025 vCommand = strlower(vCommand);
1026  
1027 local vCommandInfo = vCommands[vCommand];
1028  
1029 if not vCommandInfo then
1030 Outfitter_ErrorMessage("Outfitter: Expected command");
1031 return;
1032 end
1033  
1034 local vOutfit = nil;
1035 local vCategoryID = nil;
1036  
1037 if vCommandInfo.useOutfit then
1038 if not vParameter then
1039 Outfitter_ErrorMessage("Outfitter: Expected outfit name for "..vCommand.." command");
1040 return;
1041 end
1042  
1043 vOutfit, vCategoryID = Outfitter_FindOutfitByName(vParameter);
1044  
1045 if not vOutfit then
1046 Outfitter_ErrorMessage("Outfitter: Couldn't find outfit named "..vParameter);
1047 return;
1048 end
1049  
1050 vCommandInfo.func(vOutfit, vCategoryID);
1051 else
1052 vCommandInfo.func();
1053 end
1054 end
1055  
1056 function Outfitter_AskRebuildOutfit(pOutfit, pCategoryID)
1057 gOutfitter_OutfitToRebuild = pOutfit;
1058 gOutfitter_OutfitCategoryToRebuild = pCategoryID;
1059  
1060 StaticPopup_Show("OUTFITTER_CONFIRM_REBUILD", gOutfitter_OutfitToRebuild.Name);
1061 end
1062  
1063 function Outfitter_RebuildSelectedOutfit()
1064 if not gOutfitter_OutfitToRebuild then
1065 return;
1066 end
1067  
1068 local vOutfit = Outfitter_GenerateSmartOutfit("temp", gOutfitter_OutfitToRebuild.StatID, OutfitterItemList_GetEquippableItems(true));
1069  
1070 if vOutfit then
1071 gOutfitter_OutfitToRebuild.Items = vOutfit.Items;
1072 Outfitter_UpdateOutfitCategory(gOutfitter_OutfitToRebuild);
1073 Outfitter_WearOutfit(gOutfitter_OutfitToRebuild, gOutfitter_OutfitCategoryToRebuild);
1074 Outfitter_Update(true);
1075 end
1076  
1077 gOutfitter_OutfitToRebuild = nil;
1078 gOutfitter_OutfitCategoryToRebuild = nil;
1079 end
1080  
1081 function Outfitter_AskDeleteOutfit(pOutfit)
1082 gOutfitter_OutfitToDelete = pOutfit;
1083 StaticPopup_Show("OUTFITTER_CONFIRM_DELETE", gOutfitter_OutfitToDelete.Name);
1084 end
1085  
1086 function Outfitter_DeleteSelectedOutfit()
1087 if not gOutfitter_OutfitToDelete then
1088 return;
1089 end
1090  
1091 Outfitter_DeleteOutfit(gOutfitter_OutfitToDelete);
1092  
1093 Outfitter_Update(true);
1094 end
1095  
1096 function Outfitter_ShowPanel(pPanelIndex)
1097 Outfitter_CancelDialogs(); -- Force any dialogs to close if they're open
1098  
1099 if gOutfitter_CurrentPanel > 0
1100 and gOutfitter_CurrentPanel ~= pPanelIndex then
1101 Outfitter_HidePanel(gOutfitter_CurrentPanel);
1102 end
1103  
1104 -- NOTE: Don't check for redundant calls since this function
1105 -- will be called to reset the field values as well as to
1106 -- actually show the panel when it's hidden
1107  
1108 gOutfitter_CurrentPanel = pPanelIndex;
1109  
1110 getglobal(gOutfitter_PanelFrames[pPanelIndex]):Show();
1111  
1112 PanelTemplates_SetTab(OutfitterFrame, pPanelIndex);
1113  
1114 -- Update the control values
1115  
1116 if pPanelIndex == 1 then
1117 -- Main panel
1118  
1119 elseif pPanelIndex == 2 then
1120 -- Options panel
1121  
1122 elseif pPanelIndex == 3 then
1123 -- About panel
1124  
1125 else
1126 Outfitter_ErrorMessage("Outfitter: Unknown index ("..pPanelIndex..") in ShowPanel()");
1127 end
1128  
1129 Outfitter_Update(false);
1130 end
1131  
1132 function Outfitter_HidePanel(pPanelIndex)
1133 if gOutfitter_CurrentPanel ~= pPanelIndex then
1134 return;
1135 end
1136  
1137 getglobal(gOutfitter_PanelFrames[pPanelIndex]):Hide();
1138 gOutfitter_CurrentPanel = 0;
1139 end
1140  
1141 function Outfitter_CancelDialogs()
1142 end
1143  
1144 function OutfitterItemDropDown_OnLoad()
1145 UIDropDownMenu_SetAnchor(0, 0, this, "TOPLEFT", this:GetName(), "CENTER");
1146 UIDropDownMenu_Initialize(this, OutfitterItemDropDown_Initialize);
1147 --UIDropDownMenu_Refresh(this); -- Don't refresh on menus which don't have a text portion
1148  
1149 this:SetHeight(this.SavedHeight);
1150 end
1151  
1152 function Outfitter_AddDividerMenuItem()
1153 UIDropDownMenu_AddButton({text = " ", notCheckable = true, notClickable = true});
1154 end
1155  
1156 function Outfitter_AddCategoryMenuItem(pName)
1157 UIDropDownMenu_AddButton({text = pName, notCheckable = true, notClickable = true});
1158 end
1159  
1160 function Outfitter_AddMenuItem(pFrame, pName, pValue, pChecked, pLevel, pColor, pDisabled)
1161 if not pColor then
1162 pColor = NORMAL_FONT_COLOR;
1163 end
1164  
1165 UIDropDownMenu_AddButton({text = pName, value = pValue, owner = pFrame, checked = pChecked, func = OutfitterDropDown_OnClick2, textR = pColor.r, textG = pColor.g, textB = pColor.b, disabled = pDisabled}, pLevel);
1166 end
1167  
1168 function Outfitter_AddSubmenuItem(pFrame, pName, pValue)
1169 UIDropDownMenu_AddButton({text = pName, owner = pFrame, hasArrow = 1, value = pValue, textR = NORMAL_FONT_COLOR.r, textG = NORMAL_FONT_COLOR.g, textB = NORMAL_FONT_COLOR.b});
1170 end
1171  
1172 function OutfitterItemDropDown_Initialize()
1173 local vFrame = getglobal(UIDROPDOWNMENU_INIT_MENU);
1174 local vItem = vFrame:GetParent():GetParent();
1175 local vOutfit, vCategoryID = Outfitter_GetOutfitFromListItem(vItem);
1176  
1177 if not vOutfit then
1178 return;
1179 end
1180  
1181 if UIDROPDOWNMENU_MENU_LEVEL == 1 then
1182 local vIsSpecialOutfit = vCategoryID == "Special";
1183  
1184 Outfitter_AddCategoryMenuItem(vOutfit.Name);
1185  
1186 if vIsSpecialOutfit then
1187 Outfitter_AddMenuItem(vFrame, Outfitter_cDisableOutfit, "DISABLE", vOutfit.Disabled);
1188 Outfitter_AddMenuItem(vFrame, Outfitter_cDisableOutfitInBG, "BGDISABLE", vOutfit.BGDisabled);
1189 else
1190 Outfitter_AddMenuItem(vFrame, PET_RENAME, "RENAME");
1191 end
1192  
1193 if not vIsSpecialOutfit
1194 and vOutfit.StatID then
1195 local vStatName = Outfitter_GetStatIDName(vOutfit.StatID);
1196  
1197 if vStatName then
1198 Outfitter_AddMenuItem(vFrame, format(Outfitter_cRebuildOutfitFormat, vStatName), "REBUILD");
1199 end
1200 end
1201  
1202 Outfitter_AddSubmenuItem(vFrame, Outfitter_cKeyBinding, "BINDING");
1203  
1204 if not vIsSpecialOutfit then
1205 Outfitter_AddMenuItem(vFrame, DELETE, "DELETE");
1206 end
1207  
1208 Outfitter_AddCategoryMenuItem(Outfitter_cBankCategoryTitle);
1209 Outfitter_AddMenuItem(vFrame, Outfitter_cDepositToBank, "DEPOSIT", nil, nil, nil, not gOutfitter_BankFrameOpened);
1210 Outfitter_AddMenuItem(vFrame, Outfitter_cDepositUniqueToBank, "DEPOSITUNIQUE", nil, nil, nil, not gOutfitter_BankFrameOpened);
1211 Outfitter_AddMenuItem(vFrame, Outfitter_cWithdrawFromBank, "WITHDRAW", nil, nil, nil, not gOutfitter_BankFrameOpened);
1212  
1213 if not vIsSpecialOutfit
1214 and vCategoryID ~= "Complete" then
1215 Outfitter_AddCategoryMenuItem(Outfitter_cOutfitCategoryTitle);
1216 Outfitter_AddMenuItem(vFrame, Outfitter_cPartialOutfits, "PARTIAL", vCategoryID == "Partial");
1217 Outfitter_AddMenuItem(vFrame, Outfitter_cAccessoryOutfits, "ACCESSORY", vCategoryID == "Accessory");
1218 end
1219  
1220 elseif UIDROPDOWNMENU_MENU_LEVEL == 2 then
1221 if UIDROPDOWNMENU_MENU_VALUE == "BINDING" then
1222 for vIndex = 1, 10 do
1223 Outfitter_AddMenuItem(vFrame, getglobal("BINDING_NAME_OUTFITTER_OUTFIT"..vIndex), "BINDING"..vIndex, vOutfit.BindingIndex == vIndex, UIDROPDOWNMENU_MENU_LEVEL);
1224 end
1225 end
1226 end
1227  
1228 vFrame:SetHeight(vFrame.SavedHeight);
1229 end
1230  
1231 function Outfitter_SetShowMinimapButton(pShowButton)
1232 gOutfitter_Settings.Options.HideMinimapButton = not pShowButton;
1233  
1234 if gOutfitter_Settings.Options.HideMinimapButton then
1235 OutfitterMinimapButton:Hide();
1236 else
1237 OutfitterMinimapButton:Show();
1238 end
1239  
1240 Outfitter_Update(false);
1241 end
1242  
1243 function Outfitter_SetRememberVisibility(pRememberVisibility)
1244 gOutfitter_Settings.Options.DisableAutoVisibility = not pRememberVisibility;
1245  
1246 Outfitter_Update(false);
1247 end
1248  
1249 function Outfitter_SetShowHotkeyMessages(pShowHotkeyMessages)
1250 gOutfitter_Settings.Options.DisableHotkeyMessages = not pShowHotkeyMessages;
1251  
1252 Outfitter_Update(false);
1253 end
1254  
1255 function OutfitterMinimapDropDown_OnLoad()
1256 UIDropDownMenu_SetAnchor(3, -7, this, "TOPRIGHT", this:GetName(), "TOPLEFT");
1257 UIDropDownMenu_Initialize(this, OutfitterMinimapDropDown_Initialize);
1258 --UIDropDownMenu_Refresh(this); -- Don't refresh on menus which don't have a text portion
1259  
1260 Outfitter_RegisterOutfitEvent("WEAR_OUTFIT", OutfitterMinimapDropDown_OutfitEvent);
1261 Outfitter_RegisterOutfitEvent("UNWEAR_OUTFIT", OutfitterMinimapDropDown_OutfitEvent);
1262 end
1263  
1264 function OutfitterMinimapDropDown_OutfitEvent(pEvent, pParameter1, pParameter2)
1265 if UIDROPDOWNMENU_OPEN_MENU ~= "OutfitterMinimapButton" then
1266 return;
1267 end
1268  
1269 UIDropDownMenu_Initialize(OutfitterMinimapButton, OutfitterMinimapDropDown_Initialize);
1270 end
1271  
1272 function OutfitterMinimapDropDown_AdjustScreenPosition(pMenu)
1273 local vListFrame = getglobal("DropDownList1");
1274  
1275 if not vListFrame:IsVisible() then
1276 return;
1277 end
1278  
1279 local vCenterX, vCenterY = pMenu:GetCenter();
1280 local vScreenWidth, vScreenHeight = GetScreenWidth(), GetScreenHeight();
1281  
1282 local vAnchor;
1283 local vOffsetX, vOffsetY;
1284  
1285 if vCenterY < vScreenHeight / 2 then
1286 vAnchor = "BOTTOM";
1287 vOffsetY = -8;
1288 else
1289 vAnchor = "TOP";
1290 vOffsetY = -17;
1291 end
1292  
1293 if vCenterX < vScreenWidth / 2 then
1294 vAnchor = vAnchor.."LEFT";
1295 vOffsetX = 21;
1296 else
1297 vAnchor = vAnchor.."RIGHT";
1298 vOffsetX = 3;
1299 end
1300  
1301 vListFrame:ClearAllPoints();
1302 vListFrame:SetPoint(vAnchor, pMenu.relativeTo, pMenu.relativePoint, vOffsetX, vOffsetY);
1303 end
1304  
1305 function Outfitter_OutfitIsVisible(pOutfit)
1306 return not pOutfit.Disabled
1307 and not Outfitter_IsEmptyOutfit(pOutfit);
1308 end
1309  
1310 function Outfitter_HasVisibleOutfits(pOutfits)
1311 if not pOutfits then
1312 return false;
1313 end
1314  
1315 for vIndex, vOutfit in pOutfits do
1316 if Outfitter_OutfitIsVisible(vOutfit) then
1317 return true;
1318 end
1319 end
1320  
1321 return false;
1322 end
1323  
1324 function OutfitterMinimapDropDown_Initialize()
1325 -- Just return if not initialized yet
1326  
1327 if not gOutfitter_Initialized then
1328 return;
1329 end
1330  
1331 --
1332  
1333 local vFrame = getglobal(UIDROPDOWNMENU_INIT_MENU);
1334  
1335 Outfitter_AddCategoryMenuItem(Outfitter_cTitleVersion);
1336 Outfitter_AddMenuItem(vFrame, Outfitter_cOpenOutfitter, 0);
1337  
1338 OutfitterMinimapDropDown_InitializeOutfitList();
1339 end
1340  
1341 function Outfitter_GetCategoryOrder()
1342 return gOutfitter_cCategoryOrder;
1343 end
1344  
1345 function Outfitter_GetOutfitsByCategoryID(pCategoryID)
1346 return gOutfitter_Settings.Outfits[pCategoryID];
1347 end
1348  
1349 function OutfitterMinimapDropDown_InitializeOutfitList()
1350 -- Just return if not initialized yet
1351  
1352 if not gOutfitter_Initialized then
1353 return;
1354 end
1355  
1356 --
1357  
1358 local vFrame = getglobal(UIDROPDOWNMENU_INIT_MENU);
1359 local vEquippableItems = OutfitterItemList_GetEquippableItems();
1360 local vCategoryOrder = Outfitter_GetCategoryOrder();
1361  
1362 for vCategoryIndex, vCategoryID in vCategoryOrder do
1363 local vCategoryName = getglobal("Outfitter_c"..vCategoryID.."Outfits");
1364 local vOutfits = Outfitter_GetOutfitsByCategoryID(vCategoryID);
1365  
1366 if Outfitter_HasVisibleOutfits(vOutfits) then
1367 Outfitter_AddCategoryMenuItem(vCategoryName);
1368  
1369 for vIndex, vOutfit in vOutfits do
1370 if Outfitter_OutfitIsVisible(vOutfit) then
1371 local vWearingOutfit = Outfitter_WearingOutfit(vOutfit);
1372 local vMissingItems, vBankedItems = OutfitterItemList_GetMissingItems(vEquippableItems, vOutfit);
1373 local vItemColor = NORMAL_FONT_COLOR;
1374  
1375 if vMissingItems then
1376 vItemColor = RED_FONT_COLOR;
1377 elseif vBankedItems then
1378 vItemColor = BANKED_FONT_COLOR;
1379 end
1380  
1381 Outfitter_AddMenuItem(vFrame, vOutfit.Name, {CategoryID = vCategoryID, Index = vIndex}, vWearingOutfit, nil, vItemColor);
1382 end
1383 end
1384 end
1385 end
1386 end
1387  
1388 function OutfitterDropDown_OnClick()
1389 UIDropDownMenu_SetSelectedValue(this.owner, this.value);
1390 OutfitterDropDown_OnClick2();
1391 end
1392  
1393 function OutfitterDropDown_OnClick2()
1394 if this.owner.ChangedValueFunc then
1395 this.owner.ChangedValueFunc(this.owner, this.value);
1396 end
1397  
1398 CloseDropDownMenus();
1399 end
1400  
1401 function OutfitterItem_SetTextColor(pItem, pRed, pGreen, pBlue)
1402 local vItemNameField;
1403  
1404 if pItem.isCategory then
1405 vItemNameField = getglobal(pItem:GetName().."CategoryName");
1406 else
1407 vItemNameField = getglobal(pItem:GetName().."OutfitName");
1408 end
1409  
1410 vItemNameField:SetTextColor(pRed, pGreen, pBlue);
1411 end
1412  
1413 Outfitter_cCategoryDescriptions =
1414 {
1415 Complete = Outfitter_cCompleteCategoryDescripton,
1416 Partial = Outfitter_cPartialCategoryDescription,
1417 Accessory = Outfitter_cAccessoryCategoryDescription,
1418 Special = Outfitter_cSpecialCategoryDescription,
1419 OddsNEnds = Outfitter_cOddsNEndsCategoryDescription,
1420 };
1421  
1422 Outfitter_cMissingItemsSeparator = ", ";
1423  
1424 function Outfitter_GenerateItemListString(pLabel, pListColorCode, pItems)
1425 local vItemList = nil;
1426  
1427 for vIndex, vOutfitItem in pItems do
1428 if not vItemList then
1429 vItemList = HIGHLIGHT_FONT_COLOR_CODE..pLabel..pListColorCode..vOutfitItem.Name;
1430 else
1431 vItemList = vItemList..Outfitter_cMissingItemsSeparator..vOutfitItem.Name;
1432 end
1433 end
1434  
1435 return vItemList;
1436 end
1437  
1438 function OutfitterItem_OnEnter(pItem)
1439 OutfitterItem_SetTextColor(pItem, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b);
1440  
1441 if pItem.isCategory then
1442 local vDescription = Outfitter_cCategoryDescriptions[pItem.categoryID];
1443  
1444 if vDescription then
1445 local vCategoryName = getglobal("Outfitter_c"..pItem.categoryID.."Outfits");
1446  
1447 GameTooltip_AddNewbieTip(vCategoryName, 1.0, 1.0, 1.0, vDescription, 1);
1448 end
1449  
1450 ResetCursor();
1451 elseif pItem.isOutfitItem then
1452 local vHasCooldown, vRepairCost;
1453  
1454 GameTooltip:SetOwner(pItem, "ANCHOR_TOP");
1455  
1456 if pItem.outfitItem.Location.SlotName then
1457 if not pItem.outfitItem.Location.SlotID then
1458 local vSlotID, vEmptySlotTexture = GetInventorySlotInfo(pItem.outfitItem.Location.SlotName);
1459  
1460 pItem.outfitItem.Location.SlotID = vSlotID;
1461 end
1462  
1463 GameTooltip:SetInventoryItem("player", pItem.outfitItem.Location.SlotID);
1464 else
1465 vHasCooldown, vRepairCost = GameTooltip:SetBagItem(pItem.outfitItem.Location.BagIndex, pItem.outfitItem.Location.BagSlotIndex);
1466 end
1467  
1468 GameTooltip:Show();
1469  
1470 if InRepairMode() and (vRepairCost and vRepairCost > 0) then
1471 GameTooltip:AddLine(TEXT(REPAIR_COST), "", 1, 1, 1);
1472 SetTooltipMoney(GameTooltip, vRepairCost);
1473 GameTooltip:Show();
1474 elseif MerchantFrame:IsShown() and MerchantFrame.selectedTab == 1 then
1475 if pItem.outfitItem.Location.BagIndex then
1476 ShowContainerSellCursor(pItem.outfitItem.Location.BagIndex, pItem.outfitItem.Location.BagSlotIndex);
1477 end
1478 else
1479 ResetCursor();
1480 end
1481 else
1482 local vOutfit = Outfitter_GetOutfitFromListItem(pItem);
1483  
1484 if pItem.MissingItems
1485 or pItem.BankedItems then
1486 GameTooltip:SetOwner(pItem, "ANCHOR_LEFT");
1487  
1488 GameTooltip:AddLine(vOutfit.Name);
1489  
1490 if pItem.MissingItems then
1491 local vItemList = Outfitter_GenerateItemListString(Outfitter_cMissingItemsLabel, RED_FONT_COLOR_CODE, pItem.MissingItems);
1492 GameTooltip:AddLine(vItemList, RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b, true);
1493 end
1494  
1495 if pItem.BankedItems then
1496 local vItemList = Outfitter_GenerateItemListString(Outfitter_cBankedItemsLabel, BANKED_FONT_COLOR_CODE, pItem.BankedItems);
1497 GameTooltip:AddLine(vItemList, RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b, true);
1498 end
1499  
1500 GameTooltip:Show();
1501 elseif vOutfit.SpecialID then
1502 local vDescription = Outfitter_cSpecialOutfitDescriptions[vOutfit.SpecialID];
1503  
1504 if vDescription then
1505 GameTooltip_AddNewbieTip(vOutfit.Name, 1.0, 1.0, 1.0, vDescription, 1);
1506 end
1507 end
1508  
1509 ResetCursor();
1510 end
1511 end
1512  
1513 function OutfitterItem_OnLeave(pItem)
1514 if pItem.isCategory then
1515 OutfitterItem_SetTextColor(pItem, 1, 1, 1);
1516 else
1517 OutfitterItem_SetTextColor(pItem, pItem.DefaultColor.r, pItem.DefaultColor.g, pItem.DefaultColor.b);
1518 end
1519  
1520 GameTooltip:Hide();
1521 end
1522  
1523 function OutfitterItem_OnClick(pItem, pButton, pIgnoreModifiers)
1524 if pItem.isCategory then
1525 local vCategoryOutfits = gOutfitter_Settings.Outfits[pItem.categoryID];
1526  
1527 gOutfitter_Collapsed[pItem.categoryID] = not gOutfitter_Collapsed[pItem.categoryID];
1528 gOutfitter_DisplayIsDirty = true;
1529 elseif pItem.isOutfitItem then
1530 if pButton == "LeftButton" then
1531 Outfitter_PickupItemLocation(pItem.outfitItem.Location);
1532 StackSplitFrame:Hide();
1533 else
1534 if MerchantFrame:IsShown() and MerchantFrame.selectedTab == 2 then
1535 -- Don't sell the item if the buyback tab is selected
1536 return;
1537 else
1538 if pItem.outfitItem.Location.BagIndex then
1539 UseContainerItem(pItem.outfitItem.Location.BagIndex, pItem.outfitItem.Location.BagSlotIndex);
1540 StackSplitFrame:Hide();
1541 end
1542 end
1543 end
1544 else
1545 local vOutfit = Outfitter_GetOutfitFromListItem(pItem);
1546  
1547 if not vOutfit then
1548 -- Error: outfit not found
1549 return;
1550 end
1551  
1552 vOutfit.Disabled = nil;
1553 Outfitter_WearOutfit(vOutfit, pItem.categoryID);
1554 end
1555  
1556 Outfitter_Update(true);
1557 end
1558  
1559 function OutfitterItem_CheckboxClicked(pItem)
1560 if pItem.isCategory then
1561 return;
1562 end
1563  
1564 local vOutfits = gOutfitter_Settings.Outfits[pItem.categoryID];
1565  
1566 if not vOutfits then
1567 -- Error: outfit category not found
1568 return;
1569 end
1570  
1571 local vOutfit = vOutfits[pItem.outfitIndex];
1572  
1573 if not vOutfit then
1574 -- Error: outfit not found
1575 return;
1576 end
1577  
1578 local vCheckbox = getglobal(pItem:GetName().."OutfitSelected");
1579  
1580 if vCheckbox:GetChecked() then
1581 vOutfit.Disabled = nil;
1582 Outfitter_WearOutfit(vOutfit, pItem.categoryID);
1583 else
1584 Outfitter_RemoveOutfit(vOutfit);
1585 end
1586  
1587 Outfitter_Update(true);
1588 end
1589  
1590 function OutfitterItem_SetToOutfit(pItemIndex, pOutfit, pCategoryID, pOutfitIndex, pEquippableItems)
1591 local vItemName = "OutfitterItem"..pItemIndex;
1592 local vItem = getglobal(vItemName);
1593 local vOutfitFrameName = vItemName.."Outfit";
1594 local vOutfitFrame = getglobal(vOutfitFrameName);
1595 local vItemFrame = getglobal(vItemName.."Item");
1596 local vCategoryFrame = getglobal(vItemName.."Category");
1597 local vMissingItems, vBankedItems = OutfitterItemList_GetMissingItems(pEquippableItems, pOutfit);
1598  
1599 vOutfitFrame:Show();
1600 vCategoryFrame:Hide();
1601 vItemFrame:Hide();
1602  
1603 local vItemSelectedCheckmark = getglobal(vOutfitFrameName.."Selected");
1604 local vItemNameField = getglobal(vOutfitFrameName.."Name");
1605 local vItemMenu = getglobal(vOutfitFrameName.."Menu");
1606  
1607 vItemSelectedCheckmark:Show();
1608  
1609 if Outfitter_WearingOutfit(pOutfit) then
1610 vItemSelectedCheckmark:SetChecked(true);
1611 else
1612 vItemSelectedCheckmark:SetChecked(nil);
1613 end
1614  
1615 vItem.MissingItems = vMissingItems;
1616 vItem.BankedItems = vBankedItems;
1617  
1618 if pOutfit.Disabled then
1619 vItemNameField:SetText(format(Outfitter_cDisabledOutfitName, pOutfit.Name));
1620 vItem.DefaultColor = GRAY_FONT_COLOR;
1621 else
1622 vItemNameField:SetText(pOutfit.Name);
1623 if vMissingItems then
1624 vItem.DefaultColor = RED_FONT_COLOR;
1625 elseif vBankedItems then
1626 vItem.DefaultColor = BANKED_FONT_COLOR;
1627 else
1628 vItem.DefaultColor = NORMAL_FONT_COLOR;
1629 end
1630 end
1631  
1632 vItemNameField:SetTextColor(vItem.DefaultColor.r, vItem.DefaultColor.g, vItem.DefaultColor.b);
1633  
1634 vItemMenu:Show();
1635  
1636 vItem.isCategory = false;
1637 vItem.isOutfitItem = false;
1638 vItem.outfitItem = nil;
1639 vItem.categoryID = pCategoryID;
1640 vItem.outfitIndex = pOutfitIndex;
1641  
1642 vItem:Show();
1643  
1644 -- Update the highlighting
1645  
1646 if gOutfitter_SelectedOutfit == pOutfit then
1647 OutfitterMainFrameHighlight:SetPoint("TOPLEFT", vItem, "TOPLEFT", 0, 0);
1648 OutfitterMainFrameHighlight:Show();
1649 end
1650 end
1651  
1652 function OutfitterItem_SetToItem(pItemIndex, pOutfitItem)
1653 local vItemName = "OutfitterItem"..pItemIndex;
1654 local vItem = getglobal(vItemName);
1655 local vCategoryFrameName = vItemName.."Category";
1656 local vItemFrameName = vItemName.."Item";
1657 local vItemFrame = getglobal(vItemFrameName);
1658 local vOutfitFrame = getglobal(vItemName.."Outfit");
1659 local vCategoryFrame = getglobal(vCategoryFrameName);
1660  
1661 vItem.isOutfitItem = true;
1662 vItem.isCategory = false;
1663 vItem.outfitItem = pOutfitItem;
1664  
1665 vItemFrame:Show();
1666 vOutfitFrame:Hide();
1667 vCategoryFrame:Hide();
1668  
1669 local vItemNameField = getglobal(vItemFrameName.."Name");
1670 local vItemIcon = getglobal(vItemFrameName.."Icon");
1671  
1672 vItemNameField:SetText(pOutfitItem.Name);
1673  
1674 if pOutfitItem.Quality then
1675 vItem.DefaultColor = ITEM_QUALITY_COLORS[pOutfitItem.Quality];
1676 else
1677 vItem.DefaultColor = GRAY_FONT_COLOR;
1678 end
1679  
1680 if pOutfitItem.Texture then
1681 vItemIcon:SetTexture(pOutfitItem.Texture);
1682 vItemIcon:Show();
1683 else
1684 vItemIcon:Hide();
1685 end
1686  
1687 vItemNameField:SetTextColor(vItem.DefaultColor.r, vItem.DefaultColor.g, vItem.DefaultColor.b);
1688  
1689 vItem:Show();
1690 end
1691  
1692 function OutfitterItem_SetToCategory(pItemIndex, pCategoryID)
1693 local vCategoryName = getglobal("Outfitter_c"..pCategoryID.."Outfits");
1694 local vItemName = "OutfitterItem"..pItemIndex;
1695 local vItem = getglobal(vItemName);
1696 local vCategoryFrameName = vItemName.."Category";
1697 local vOutfitFrame = getglobal(vItemName.."Outfit");
1698 local vItemFrame = getglobal(vItemName.."Item");
1699 local vCategoryFrame = getglobal(vCategoryFrameName);
1700  
1701 vOutfitFrame:Hide();
1702 vCategoryFrame:Show();
1703 vItemFrame:Hide();
1704  
1705 local vItemNameField = getglobal(vCategoryFrameName.."Name");
1706 local vExpandButton = getglobal(vCategoryFrameName.."Expand");
1707  
1708 vItem.MissingItems = nil;
1709 vItem.BankedItems = nil;
1710  
1711 if gOutfitter_Collapsed[pCategoryID] then
1712 vExpandButton:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up");
1713 else
1714 vExpandButton:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up");
1715 end
1716  
1717 vItemNameField:SetText(vCategoryName);
1718  
1719 vItem.isCategory = true;
1720 vItem.isOutfitItem = false;
1721 vItem.outfitItem = nil;
1722 vItem.categoryID = pCategoryID;
1723  
1724 vItem:Show();
1725 end
1726  
1727 function Outfitter_AddOutfitsToList(pOutfits, pCategoryID, pItemIndex, pFirstItemIndex, pEquippableItems)
1728 local vOutfits = pOutfits[pCategoryID];
1729 local vItemIndex = pItemIndex;
1730 local vFirstItemIndex = pFirstItemIndex;
1731  
1732 if vFirstItemIndex == 0 then
1733 OutfitterItem_SetToCategory(vItemIndex, pCategoryID, false);
1734 vItemIndex = vItemIndex + 1;
1735 else
1736 vFirstItemIndex = vFirstItemIndex - 1;
1737 end
1738  
1739 if vItemIndex >= Outfitter_cMaxDisplayedItems then
1740 return vItemIndex, vFirstItemIndex;
1741 end
1742  
1743 if not gOutfitter_Collapsed[pCategoryID]
1744 and vOutfits then
1745 for vIndex, vOutfit in vOutfits do
1746 if vFirstItemIndex == 0 then
1747 OutfitterItem_SetToOutfit(vItemIndex, vOutfit, pCategoryID, vIndex, pEquippableItems);
1748 vItemIndex = vItemIndex + 1;
1749  
1750 if vItemIndex >= Outfitter_cMaxDisplayedItems then
1751 return vItemIndex, vFirstItemIndex;
1752 end
1753 else
1754 vFirstItemIndex = vFirstItemIndex - 1;
1755 end
1756 end
1757 end
1758  
1759 return vItemIndex, vFirstItemIndex;
1760 end
1761  
1762 function Outfitter_AddOutfitItemsToList(pOutfitItems, pCategoryID, pItemIndex, pFirstItemIndex)
1763 local vItemIndex = pItemIndex;
1764 local vFirstItemIndex = pFirstItemIndex;
1765  
1766 if vFirstItemIndex == 0 then
1767 OutfitterItem_SetToCategory(vItemIndex, pCategoryID, false);
1768 vItemIndex = vItemIndex + 1;
1769 else
1770 vFirstItemIndex = vFirstItemIndex - 1;
1771 end
1772  
1773 if vItemIndex >= Outfitter_cMaxDisplayedItems then
1774 return vItemIndex, vFirstItemIndex;
1775 end
1776  
1777 if not gOutfitter_Collapsed[pCategoryID] then
1778 for vIndex, vOutfitItem in pOutfitItems do
1779 if vFirstItemIndex == 0 then
1780 OutfitterItem_SetToItem(vItemIndex, vOutfitItem);
1781 vItemIndex = vItemIndex + 1;
1782  
1783 if vItemIndex >= Outfitter_cMaxDisplayedItems then
1784 return vItemIndex, vFirstItemIndex;
1785 end
1786 else
1787 vFirstItemIndex = vFirstItemIndex - 1;
1788 end
1789 end
1790 end
1791  
1792 return vItemIndex, vFirstItemIndex;
1793 end
1794  
1795 function Outfitter_SortOutfits()
1796 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
1797 table.sort(vOutfits, Outfiter_CompareOutfitNames);
1798 end
1799 end
1800  
1801 function Outfiter_CompareOutfitNames(pOutfit1, pOutfit2)
1802 return pOutfit1.Name < pOutfit2.Name;
1803 end
1804  
1805 function Outfitter_Update(pUpdateSlotEnables)
1806 if not OutfitterFrame:IsVisible() then
1807 return;
1808 end
1809  
1810 if gOutfitter_CurrentPanel == 1 then
1811 -- Main panel
1812  
1813 if not gOutfitter_DisplayIsDirty then
1814 return;
1815 end
1816  
1817 gOutfitter_DisplayIsDirty = false;
1818  
1819 -- Sort the outfits
1820  
1821 Outfitter_SortOutfits();
1822  
1823 -- Get the equippable items so outfits can be marked if they're missing anything
1824  
1825 local vEquippableItems = OutfitterItemList_GetEquippableItems();
1826  
1827 -- Update the slot enables if they're shown
1828  
1829 if pUpdateSlotEnables
1830 and OutfitterSlotEnables:IsVisible() then
1831 Outfitter_UpdateSlotEnables(gOutfitter_SelectedOutfit, vEquippableItems);
1832 end
1833  
1834 OutfitterItemList_CompiledUnusedItemsList(vEquippableItems);
1835  
1836 -- Update the list
1837  
1838 OutfitterMainFrameHighlight:Hide();
1839  
1840 local vFirstItemIndex = FauxScrollFrame_GetOffset(OutfitterMainFrameScrollFrame);
1841 local vItemIndex = 0;
1842  
1843 OutfitterItemList_ResetIgnoreItemFlags(vEquippableItems);
1844  
1845 for vCategoryIndex, vCategoryID in gOutfitter_cCategoryOrder do
1846 vItemIndex, vFirstItemIndex = Outfitter_AddOutfitsToList(gOutfitter_Settings.Outfits, vCategoryID, vItemIndex, vFirstItemIndex, vEquippableItems);
1847  
1848 if vItemIndex >= Outfitter_cMaxDisplayedItems then
1849 break;
1850 end
1851 end
1852  
1853 if vItemIndex < Outfitter_cMaxDisplayedItems
1854 and vEquippableItems.UnusedItems then
1855 vItemIndex, vFirstItemIndex = Outfitter_AddOutfitItemsToList(vEquippableItems.UnusedItems, "OddsNEnds", vItemIndex, vFirstItemIndex);
1856 end
1857  
1858 -- Hide any unused items
1859  
1860 for vItemIndex2 = vItemIndex, (Outfitter_cMaxDisplayedItems - 1) do
1861 local vItemName = "OutfitterItem"..vItemIndex2;
1862 local vItem = getglobal(vItemName);
1863  
1864 vItem:Hide();
1865 end
1866  
1867 local vTotalNumItems = 0;
1868  
1869 for vCategoryIndex, vCategoryID in gOutfitter_cCategoryOrder do
1870 vTotalNumItems = vTotalNumItems + 1;
1871  
1872 local vOutfits = gOutfitter_Settings.Outfits[vCategoryID];
1873  
1874 if not gOutfitter_Collapsed[vCategoryID]
1875 and vOutfits then
1876 vTotalNumItems = vTotalNumItems + table.getn(vOutfits);
1877 end
1878 end
1879  
1880 if vEquippableItems.UnusedItems then
1881 vTotalNumItems = vTotalNumItems + 1;
1882  
1883 if not gOutfitter_Collapsed["OddsNEnds"] then
1884 vTotalNumItems = vTotalNumItems + table.getn(vEquippableItems.UnusedItems);
1885 end
1886 end
1887  
1888 FauxScrollFrame_Update(
1889 OutfitterMainFrameScrollFrame,
1890 vTotalNumItems, -- numItems
1891 Outfitter_cMaxDisplayedItems, -- numToDisplay
1892 18, -- valueStep
1893 nil, nil, nil, -- button, smallWidth, bigWidth
1894 nil, -- highlightFrame
1895 0, 0); -- smallHighlightWidth, bigHighlightWidth
1896 elseif gOutfitter_CurrentPanel == 2 then -- Options panel
1897 OutfitterShowMinimapButton:SetChecked(not gOutfitter_Settings.Options.HideMinimapButton);
1898 OutfitterRememberVisibility:SetChecked(not gOutfitter_Settings.Options.DisableAutoVisibility);
1899 OutfitterShowHotkeyMessages:SetChecked(not gOutfitter_Settings.Options.DisableHotkeyMessages);
1900 end
1901 end
1902  
1903 function Outfitter_OnVerticalScroll()
1904 gOutfitter_DisplayIsDirty = true;
1905 Outfitter_Update(false);
1906 end
1907  
1908 function Outfitter_SelectOutfit(pOutfit, pCategoryID)
1909 if not Outfitter_IsOpen() then
1910 return;
1911 end
1912  
1913 gOutfitter_SelectedOutfit = pOutfit;
1914  
1915 -- Get the equippable items so outfits can be marked if they're missing anything
1916  
1917 local vEquippableItems = OutfitterItemList_GetEquippableItems();
1918  
1919 -- Update the slot enables
1920  
1921 Outfitter_UpdateSlotEnables(pOutfit, vEquippableItems);
1922 OutfitterSlotEnables:Show();
1923  
1924 -- Done, rebuild the list
1925  
1926 gOutfitter_DisplayIsDirty = true;
1927 end
1928  
1929 function Outfitter_UpdateSlotEnables(pOutfit, pEquippableItems)
1930 if UnitHasRelicSlot("player") then
1931 OutfitterEnableAmmoSlot:Hide();
1932 else
1933 OutfitterEnableAmmoSlot:Show();
1934 end
1935  
1936 for _, vInventorySlot in Outfitter_cSlotNames do
1937 local vOutfitItem = pOutfit.Items[vInventorySlot];
1938 local vCheckbox = getglobal("OutfitterEnable"..vInventorySlot);
1939  
1940 if not vOutfitItem then
1941 vCheckbox:SetChecked(false);
1942 else
1943 if OutfitterItemList_InventorySlotContainsItem(pEquippableItems, vInventorySlot, vOutfitItem) then
1944 vCheckbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check");
1945 vCheckbox.IsUnknown = false;
1946 else
1947 vCheckbox:SetCheckedTexture("Interface\\Addons\\Outfitter\\Textures\\CheckboxUnknown");
1948 vCheckbox.IsUnknown = true;
1949 end
1950  
1951 vCheckbox:SetChecked(true);
1952 end
1953 end
1954 end
1955  
1956 function Outfitter_ClearSelection()
1957 gOutfitter_SelectedOutfit = nil;
1958 gOutfitter_DisplayIsDirty = true;
1959 OutfitterSlotEnables:Hide();
1960 end
1961  
1962 function Outfitter_FindOutfitItemIndex(pOutfit)
1963 local vOutfitCategoryID, vOutfitIndex = Outfitter_FindOutfit(pOutfit);
1964  
1965 if not vOutfitCategoryID then
1966 return nil;
1967 end
1968  
1969 local vItemIndex = 0;
1970  
1971 for vCategoryIndex, vCategoryID in gOutfitter_cCategoryOrder do
1972 vItemIndex = vItemIndex + 1;
1973  
1974 if not gOutfitter_Collapsed[vCategoryID] then
1975 if vOutfitCategoryID == vCategoryID then
1976 return vItemIndex + vOutfitIndex - 1;
1977 else
1978 vItemIndex = vItemIndex + table.getn(gOutfitter_Settings.Outfits[vCategoryID]);
1979 end
1980 end
1981 end
1982  
1983 return nil;
1984 end
1985  
1986 function OutfitterStack_FindOutfit(pOutfit)
1987 for vIndex, vOutfit in gOutfitter_OutfitStack do
1988 if vOutfit == pOutfit then
1989 return true, vIndex;
1990 end
1991 end
1992  
1993 return false, nil;
1994 end
1995  
1996 function OutfitterStack_FindOutfitByCategory(pCategoryID)
1997 for vIndex, vOutfit in gOutfitter_OutfitStack do
1998 if vOutfit.CategoryID == pCategoryID then
1999 return true, vIndex;
2000 end
2001 end
2002  
2003 return false, nil;
2004 end
2005  
2006 function OutfitterStack_Clear()
2007 for vIndex, vOutfit in gOutfitter_OutfitStack do
2008 Outfitter_DispatchOutfitEvent("UNWEAR_OUTFIT", vOutfit.Name, vOutfit)
2009 end
2010  
2011 gOutfitter_OutfitStack = {};
2012 gOutfitter_Settings.LastOutfitStack = {};
2013 gOutfitter_DisplayIsDirty = true;
2014  
2015 if gOutfitter_Settings.Options.ShowStackContents then
2016 Outfitter_DebugMessage("Outfitter stack cleared");
2017 end
2018 end
2019  
2020 function OutfitterStack_ClearCategory(pCategoryID)
2021 local vIndex = 1;
2022 local vStackLength = table.getn(gOutfitter_OutfitStack);
2023 local vChanged = false;
2024  
2025 while vIndex <= vStackLength do
2026 local vOutfit = gOutfitter_OutfitStack[vIndex];
2027  
2028 if vOutfit
2029 and vOutfit.CategoryID == pCategoryID then
2030 Outfitter_DispatchOutfitEvent("UNWEAR_OUTFIT", vOutfit.Name, vOutfit)
2031  
2032 table.remove(gOutfitter_OutfitStack, vIndex);
2033 table.remove(gOutfitter_Settings.LastOutfitStack, vIndex);
2034  
2035 vStackLength = vStackLength - 1;
2036 vChanged = true;
2037 else
2038 vIndex = vIndex + 1;
2039 end
2040 end
2041  
2042 OutfitterStack_CollapseTemporaryOutfits();
2043  
2044 if vChanged then
2045 if gOutfitter_Settings.Options.ShowStackContents then
2046 OutfitterStack_DumpStackContents("Clear category "..pCategoryID);
2047 end
2048  
2049 gOutfitter_DisplayIsDirty = true;
2050 end
2051 end
2052  
2053 function OutfitterStack_GetTemporaryOutfit()
2054 local vStackSize = table.getn(gOutfitter_OutfitStack);
2055  
2056 if vStackSize == 0 then
2057 return nil;
2058 end
2059  
2060 local vOutfit = gOutfitter_OutfitStack[vStackSize];
2061  
2062 if vOutfit.Name then
2063 return nil;
2064 end
2065  
2066 return vOutfit;
2067 end
2068  
2069 function OutfitterStack_CollapseTemporaryOutfits()
2070 local vIndex = 1;
2071 local vStackLength = table.getn(gOutfitter_OutfitStack);
2072 local vTemporaryOutfit1 = nil;
2073  
2074 while vIndex <= vStackLength do
2075 local vOutfit = gOutfitter_OutfitStack[vIndex];
2076  
2077 if vOutfit
2078 and vOutfit.Name == nil then
2079 if vTemporaryOutfit1 then
2080 -- Copy the items up
2081  
2082 for vInventorySlot, vItem in vTemporaryOutfit1.Items do
2083 if not vOutfit.Items[vInventorySlot] then
2084 vOutfit.Items[vInventorySlot] = vItem;
2085 end
2086 end
2087  
2088 -- Remove the lower temp outfit
2089  
2090 table.remove(gOutfitter_OutfitStack, vIndex - 1);
2091 vStackLength = vStackLength - 1;
2092 else
2093 vIndex = vIndex + 1;
2094 end
2095  
2096 vTemporaryOutfit1 = vOutfit;
2097 else
2098 vTemporaryOutfit1 = nil;
2099 vIndex = vIndex + 1;
2100 end
2101 end
2102 end
2103  
2104 function OutfitterStack_IsTopmostOutfit(pOutfit)
2105 local vStackLength = table.getn(gOutfitter_OutfitStack);
2106  
2107 if vStackLength == 0 then
2108 return false;
2109 end
2110  
2111 return gOutfitter_OutfitStack[vStackLength] == pOutfit;
2112 end
2113  
2114 function OutfitterStack_AddOutfit(pOutfit, pBelowOutfit)
2115 local vFound, vIndex = OutfitterStack_FindOutfit(pOutfit);
2116  
2117 -- If it's already on then remove it from the stack
2118 -- so it can be added to the end
2119  
2120 if vFound then
2121 table.remove(gOutfitter_OutfitStack, vIndex);
2122 table.remove(gOutfitter_Settings.LastOutfitStack, vIndex);
2123 Outfitter_DispatchOutfitEvent("UNWEAR_OUTFIT", pOutfit.Name, pOutfit);
2124 end
2125  
2126 -- Figure out the position to insert at
2127  
2128 local vStackLength = table.getn(gOutfitter_OutfitStack);
2129 local vInsertIndex = vStackLength + 1;
2130  
2131 if pBelowOutfit then
2132 local vFound2, vIndex = OutfitterStack_FindOutfit(pBelowOutfit);
2133  
2134 if vFound2 then
2135 vInsertIndex = vIndex;
2136 end
2137 end
2138  
2139 --[[ Always insert below the temporary outfit
2140  
2141 local vTemporaryOutfit;
2142  
2143 if vStackLength > 0 then
2144 vTemporaryOutfit = gOutfitter_OutfitStack[vStackLength];
2145 end
2146  
2147 if vTemporaryOutfit and vTemporaryOutfit.Name == nil then
2148 -- Knock out any slots used by the new outfit if it's being inserted at the top
2149  
2150 if vInsertIndex >= vStackLength then
2151 for vInventorySlot, vItem in pOutfit.Items do
2152 vTemporaryOutfit.Items[vInventorySlot] = nil;
2153 end
2154  
2155 -- Remove the temporary outfit if it's empty now
2156  
2157 if Outfitter_IsEmptyOutfit(vTemporaryOutfit) then
2158 table.remove(gOutfitter_OutfitStack, vStackLength);
2159 table.remove(gOutfitter_Settings.LastOutfitStack, vStackLength);
2160  
2161 vInsertIndex = vStackLength;
2162 vStackLength = vStackLength - 1;
2163 else
2164 vInsertIndex = vStackLength;
2165 end
2166 end
2167 end
2168  
2169 ]]--
2170  
2171 -- Add the outfit
2172  
2173 table.insert(gOutfitter_OutfitStack, vInsertIndex, pOutfit);
2174  
2175 if pOutfit.Name then
2176 table.insert(gOutfitter_Settings.LastOutfitStack, vInsertIndex, {Name = pOutfit.Name});
2177 else
2178 table.insert(gOutfitter_Settings.LastOutfitStack, vInsertIndex, pOutfit);
2179 end
2180  
2181 gOutfitter_DisplayIsDirty = true;
2182  
2183 if gOutfitter_Settings.Options.ShowStackContents then
2184 OutfitterStack_DumpStackContents("Add outfit");
2185 end
2186  
2187 if vFound then
2188 OutfitterStack_CollapseTemporaryOutfits();
2189 end
2190  
2191 Outfitter_DispatchOutfitEvent("WEAR_OUTFIT", pOutfit.Name, pOutfit);
2192 end
2193  
2194 function OutfitterStack_RemoveOutfit(pOutfit)
2195 local vFound, vIndex = OutfitterStack_FindOutfit(pOutfit);
2196  
2197 if not vFound then
2198 return false;
2199 end
2200  
2201 -- Remove the outfit
2202  
2203 table.remove(gOutfitter_OutfitStack, vIndex);
2204 table.remove(gOutfitter_Settings.LastOutfitStack, vIndex);
2205  
2206 OutfitterStack_CollapseTemporaryOutfits();
2207  
2208 gOutfitter_DisplayIsDirty = true;
2209  
2210 if gOutfitter_Settings.Options.ShowStackContents then
2211 OutfitterStack_DumpStackContents("Remove outfit");
2212 end
2213  
2214 return true;
2215 end
2216  
2217 function OutfitterStack_RestoreSavedStack()
2218 if not gOutfitter_Settings.LastOutfitStack then
2219 gOutfitter_Settings.LastOutfitStack = {};
2220 end
2221  
2222 for vIndex, vOutfit in gOutfitter_Settings.LastOutfitStack do
2223 if vOutfit.Name then
2224 vOutfit = Outfitter_FindOutfitByName(vOutfit.Name);
2225 end
2226  
2227 if vOutfit then
2228 table.insert(gOutfitter_OutfitStack, vOutfit);
2229 end
2230 end
2231  
2232 gOutfitter_ExpectedOutfit = Outfitter_GetCompiledOutfit();
2233  
2234 Outfitter_UpdateTemporaryOutfit(Outfitter_GetNewItemsOutfit(gOutfitter_ExpectedOutfit));
2235  
2236 if gOutfitter_Settings.Options.ShowStackContents then
2237 OutfitterStack_DumpStackContents("Restore saved stack");
2238 end
2239 end
2240  
2241 function OutfitterStack_DumpStackContents(pOperation)
2242 Outfitter_DebugMessage("Outfitter Stack Contents: "..pOperation);
2243  
2244 for vIndex, vOutfit in gOutfitter_OutfitStack do
2245 if vOutfit.Name then
2246 Outfitter_DebugMessage("Slot "..vIndex..": "..vOutfit.Name);
2247 else
2248 Outfitter_DebugMessage("Slot "..vIndex..": Temporaray outfit");
2249 end
2250 end
2251 end
2252  
2253 function Outfitter_WearOutfit(pOutfit, pCategoryID, pWearBelowOutfit)
2254 if pOutfit.Disabled then
2255 return;
2256 end
2257  
2258 --
2259  
2260 Outfitter_BeginEquipmentUpdate();
2261  
2262 if pCategoryID == "Complete" then
2263 OutfitterStack_Clear();
2264 elseif pCategoryID == "Partial" then
2265 OutfitterStack_ClearCategory(pCategoryID);
2266 OutfitterStack_ClearCategory("Accessory");
2267 end
2268  
2269 OutfitterStack_AddOutfit(pOutfit, pWearBelowOutfit);
2270  
2271 -- If outfitter is open then also select the outfit
2272  
2273 if Outfitter_IsOpen() then
2274 if OutfitterStack_IsTopmostOutfit(pOutfit) then
2275 Outfitter_SelectOutfit(pOutfit, pCategoryID);
2276 else
2277 Outfitter_ClearSelection();
2278 end
2279 end
2280  
2281 -- Update the equipment
2282  
2283 gOutfitter_EquippedNeedsUpdate = true;
2284 gOutfitter_WeaponsNeedUpdate = true;
2285  
2286 Outfitter_EndEquipmentUpdate("Outfitter_WearOutfit");
2287 end
2288  
2289 function Outfitter_SetOutfitBindingIndex(pOutfit, pBindingIndex)
2290 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
2291 for vOutfitIndex, vOutfit in vOutfits do
2292 if vOutfit.BindingIndex == pBindingIndex then
2293 vOutfit.BindingIndex = nil;
2294 end
2295 end
2296 end
2297  
2298 pOutfit.BindingIndex = pBindingIndex;
2299 end
2300  
2301 local gOutfitter_LastBindingIndex = nil;
2302 local gOutfitter_LastBindingTime = nil;
2303 local Outfitter_cMinBindingTime = 0.75;
2304  
2305 function Outfitter_WearBoundOutfit(pBindingIndex)
2306 -- Check for the user spamming the button so prevent the outfit from
2307 -- toggling if they're panicking
2308  
2309 local vTime = GetTime();
2310  
2311 if gOutfitter_LastBindingIndex == pBindingIndex then
2312 local vElapsed = vTime - gOutfitter_LastBindingTime;
2313  
2314 if vElapsed < Outfitter_cMinBindingTime then
2315 gOutfitter_LastBindingTime = vTime;
2316 return;
2317 end
2318 end
2319  
2320 --
2321  
2322 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
2323 for vOutfitIndex, vOutfit in vOutfits do
2324 if vOutfit.BindingIndex == pBindingIndex then
2325 vOutfit.Disabled = nil;
2326 if vCategoryID == "Complete" then
2327 Outfitter_WearOutfit(vOutfit, vCategoryID);
2328 if not gOutfitter_Settings.Options.DisableHotkeyMessages then
2329 UIErrorsFrame:AddMessage(format(Outfitter_cEquipOutfitMessageFormat, vOutfit.Name), OUTFIT_MESSAGE_COLOR.r, OUTFIT_MESSAGE_COLOR.g, OUTFIT_MESSAGE_COLOR.b);
2330 end
2331 else
2332 local vEquipped = Outfitter_ToggleOutfit(vOutfit, vCategoryID);
2333  
2334 if not gOutfitter_Settings.Options.DisableHotkeyMessages then
2335 if vEquipped then
2336 UIErrorsFrame:AddMessage(format(Outfitter_cEquipOutfitMessageFormat, vOutfit.Name), OUTFIT_MESSAGE_COLOR.r, OUTFIT_MESSAGE_COLOR.g, OUTFIT_MESSAGE_COLOR.b);
2337 else
2338 UIErrorsFrame:AddMessage(format(Outfitter_cUnequipOutfitMessageFormat, vOutfit.Name), OUTFIT_MESSAGE_COLOR.r, OUTFIT_MESSAGE_COLOR.g, OUTFIT_MESSAGE_COLOR.b);
2339 end
2340 end
2341  
2342 end
2343  
2344 -- Remember the binding used to filter for button spam
2345  
2346 gOutfitter_LastBindingIndex = pBindingIndex;
2347 gOutfitter_LastBindingTime = vTime;
2348  
2349 return;
2350 end
2351 end
2352 end
2353 end
2354  
2355 function Outfitter_FindOutfit(pOutfit)
2356 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
2357 for vOutfitIndex, vOutfit in vOutfits do
2358 if vOutfit == pOutfit then
2359 return vCategoryID, vOutfitIndex;
2360 end
2361 end
2362 end
2363  
2364 return nil, nil;
2365 end
2366  
2367 function Outfitter_FindOutfitByName(pName)
2368 if not pName
2369 or pName == "" then
2370 return nil;
2371 end
2372  
2373 local vLowerName = strlower(pName);
2374  
2375 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
2376 for vOutfitIndex, vOutfit in vOutfits do
2377 if strlower(vOutfit.Name) == vLowerName then
2378 return vOutfit, vCategoryID, vOutfitIndex;
2379 end
2380 end
2381 end
2382  
2383 return nil, nil;
2384 end
2385  
2386 -- Outfitter doesn't use this function, but other addons such as
2387 -- Fishing Buddy might use it to locate specific generated outfits
2388  
2389 function Outfitter_FindOutfitByStatID(pStatID)
2390 if not pStatID or pStatID == "" then
2391 return nil;
2392 end
2393  
2394 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
2395 for vOutfitIndex, vOutfit in vOutfits do
2396 if vOutfit.StatID and vOutfit.StatID == pStatID then
2397 return vOutfit, vCategoryID, vOutfitIndex;
2398 end
2399 end
2400 end
2401  
2402 return nil;
2403 end
2404  
2405 function Outfitter_RemoveOutfit(pOutfit)
2406 if not OutfitterStack_RemoveOutfit(pOutfit) then
2407 return;
2408 end
2409  
2410 -- Stop monitoring health and mana if it's the dining outfit
2411  
2412 if pOutfit.SpecialID == "Dining" then
2413 Outfitter_SuspendEvent(OutfitterFrame, "UNIT_HEALTH");
2414 Outfitter_SuspendEvent(OutfitterFrame, "UNIT_MANA");
2415 end
2416  
2417 --
2418  
2419 Outfitter_BeginEquipmentUpdate();
2420  
2421 -- Clear the selection if the outfit being removed
2422 -- is selected too
2423  
2424 if gOutfitter_SelectedOutfit == pOutfit then
2425 Outfitter_ClearSelection();
2426 end
2427  
2428 -- Update the list
2429  
2430 gOutfitter_EquippedNeedsUpdate = true;
2431 gOutfitter_WeaponsNeedUpdate = true;
2432  
2433 Outfitter_EndEquipmentUpdate("Outfitter_RemoveOutfit");
2434  
2435 Outfitter_DispatchOutfitEvent("UNWEAR_OUTFIT", pOutfit.Name, pOutfit);
2436 end
2437  
2438 function Outfitter_ToggleOutfit(pOutfit, pCategoryID)
2439 if Outfitter_WearingOutfit(pOutfit) then
2440 Outfitter_RemoveOutfit(pOutfit);
2441 return false;
2442 else
2443 Outfitter_WearOutfit(pOutfit, pCategoryID);
2444 return true;
2445 end
2446 end
2447  
2448 function Outfitter_OutfitSummary()
2449 local _, vPlayerClass = UnitClass("player");
2450 local vStatDistribution = gOutfitter_StatDistribution[vPlayerClass];
2451 local vCurrentOutfitStats = OutfitterTankPoints_GetCurrentOutfitStats(vStatDistribution);
2452  
2453 Outfitter_DumpArray("Current Stats", vCurrentOutfitStats);
2454 end
2455  
2456 function Outfitter_GetCompiledOutfit()
2457 local vCompiledOutfit = Outfitter_NewEmptyOutfit();
2458  
2459 vCompiledOutfit.SourceOutfit = {};
2460  
2461 for vStackIndex, vOutfit in gOutfitter_OutfitStack do
2462 for vInventorySlot, vOutfitItem in vOutfit.Items do
2463 vCompiledOutfit.Items[vInventorySlot] = vOutfitItem;
2464 vCompiledOutfit.SourceOutfit[vInventorySlot] = vOutfit.Name;
2465 end
2466 end
2467  
2468 return vCompiledOutfit;
2469 end
2470  
2471 function Outfitter_GetExpectedOutfit(pExcludeOutfit)
2472 local vCompiledOutfit = Outfitter_NewEmptyOutfit();
2473  
2474 vCompiledOutfit.SourceOutfit = {};
2475  
2476 for vStackIndex, vOutfit in gOutfitter_OutfitStack do
2477 if vOutfit ~= pExcludeOutfit then
2478 for vInventorySlot, vOutfitItem in vOutfit.Items do
2479 vCompiledOutfit.Items[vInventorySlot] = vOutfitItem;
2480 vCompiledOutfit.SourceOutfit[vInventorySlot] = vOutfit.Name;
2481 end
2482 end
2483 end
2484  
2485 return vCompiledOutfit;
2486 end
2487  
2488 function Outfitter_GetEmptyBagSlot(pStartBagIndex, pStartBagSlotIndex, pIncludeBank)
2489 local vStartBagIndex = pStartBagIndex;
2490 local vStartBagSlotIndex = pStartBagSlotIndex;
2491  
2492 if not vStartBagIndex then
2493 vStartBagIndex = NUM_BAG_SLOTS;
2494 end
2495  
2496 if not vStartBagSlotIndex then
2497 vStartBagSlotIndex = 1;
2498 end
2499  
2500 local vEndBagIndex = 0;
2501  
2502 if pIncludeBank then
2503 vEndBagIndex = -1;
2504 end
2505  
2506 for vBagIndex = vStartBagIndex, vEndBagIndex, -1 do
2507 -- Skip the bag if it's a specialty bag (ammo pouch, quiver, shard bag)
2508  
2509 local vSkipBag = false;
2510  
2511 if vBagIndex > 0 then -- Don't worry about the backpack
2512 local vItemLink = GetInventoryItemLink("player", ContainerIDToInventoryID(vBagIndex));
2513 local vItemInfo = Outfitter_GetItemInfoFromLink(vItemLink);
2514  
2515 if vItemInfo
2516 and Outfitter_cSpecialtyBags[vItemInfo.Code] ~= nil then
2517 vSkipBag = true;
2518 end
2519 end
2520  
2521 -- Search the bag for empty slots
2522  
2523 if not vSkipBag then
2524 local vNumBagSlots = GetContainerNumSlots(vBagIndex);
2525  
2526 if vNumBagSlots > 0 then
2527 for vSlotIndex = vStartBagSlotIndex, vNumBagSlots do
2528 local vItemInfo = Outfitter_GetBagItemInfo(vBagIndex, vSlotIndex);
2529  
2530 if not vItemInfo then
2531 return {BagIndex = vBagIndex, BagSlotIndex = vSlotIndex};
2532 end
2533 end
2534 end
2535 end
2536  
2537 vStartBagSlotIndex = 1;
2538 end
2539  
2540 return nil;
2541 end
2542  
2543 function Outfitter_GetEmptyBagSlotList()
2544 local vEmptyBagSlots = {};
2545  
2546 local vBagIndex = NUM_BAG_SLOTS;
2547 local vBagSlotIndex = 1;
2548  
2549 while true do
2550 local vBagSlotInfo = Outfitter_GetEmptyBagSlot(vBagIndex, vBagSlotIndex);
2551  
2552 if not vBagSlotInfo then
2553 return vEmptyBagSlots;
2554 end
2555  
2556 table.insert(vEmptyBagSlots, vBagSlotInfo);
2557  
2558 vBagIndex = vBagSlotInfo.BagIndex;
2559 vBagSlotIndex = vBagSlotInfo.BagSlotIndex + 1;
2560 end
2561 end
2562  
2563 function Outfitter_GetEmptyBankSlotList()
2564 local vEmptyBagSlots = {};
2565  
2566 local vBagIndex = NUM_BAG_SLOTS + NUM_BANKBAGSLOTS;
2567 local vBagSlotIndex = 1;
2568  
2569 while true do
2570 local vBagSlotInfo = Outfitter_GetEmptyBagSlot(vBagIndex, vBagSlotIndex, true);
2571  
2572 if not vBagSlotInfo then
2573 return vEmptyBagSlots;
2574  
2575 elseif vBagSlotInfo.BagIndex > NUM_BAG_SLOTS
2576 or vBagSlotInfo.BagIndex < 0 then
2577 table.insert(vEmptyBagSlots, vBagSlotInfo);
2578 end
2579  
2580 vBagIndex = vBagSlotInfo.BagIndex;
2581 vBagSlotIndex = vBagSlotInfo.BagSlotIndex + 1;
2582 end
2583 end
2584  
2585 function Outfitter_FindItemsInBagsForSlot(pSlotName)
2586 local vInventorySlot = pSlotName;
2587  
2588 -- Alias the slot names down for finger and trinket
2589  
2590 if vInventorySlot == "Finger1Slot" then
2591 vInventorySlot = "Finger0Slot";
2592 elseif vInventorySlot == "Trinket1Slot" then
2593 vInventorySlot = "Trinket0Slot";
2594 end
2595  
2596 --
2597  
2598 local vItems = {};
2599 local vNumBags, vFirstBagIndex = Outfitter_GetNumBags();
2600  
2601 for vBagIndex = vFirstBagIndex, vNumBags do
2602 local vNumBagSlots = GetContainerNumSlots(vBagIndex);
2603  
2604 if vNumBagSlots > 0 then
2605 for vSlotIndex = 1, vNumBagSlots do
2606 local vItemInfo = Outfitter_GetBagItemInfo(vBagIndex, vSlotIndex);
2607  
2608 if vItemInfo then
2609 local vItemSlotName = vItemInfo.ItemSlotName;
2610  
2611 if vItemInfo.MetaSlotName then
2612 vItemSlotName = vItemInfo.MetaSlotName;
2613 end
2614  
2615 if vItemSlotName == "TwoHandSlot" then
2616 vItemSlotName = "MainHandSlot";
2617 elseif vItemSlotName == "Weapon0Slot" then
2618 if vInventorySlot == "MainHandSlot"
2619 or vInventorySlot == "SecondaryHandSlot" then
2620 vItemSlotName = vInventorySlot;
2621 end
2622 end
2623  
2624 if vItemSlotName == vInventorySlot then
2625 table.insert(vItems, {BagIndex = vBagIndex, BagSlotIndex = vSlotIndex, Code = vItemInfo.Code, Name = vItemInfo.Name});
2626 end
2627 end
2628 end
2629 end
2630 end
2631  
2632 if table.getn(vItems) == 0 then
2633 return nil;
2634 end
2635  
2636 return vItems;
2637 end
2638  
2639 function Outfitter_PickupItemLocation(pItemLocation)
2640 if pItemLocation == nil then
2641 Outfitter_ErrorMessage("Outfitter: nil location in PickupItemLocation");
2642 return;
2643 end
2644  
2645 if pItemLocation.BagIndex then
2646 PickupContainerItem(pItemLocation.BagIndex, pItemLocation.BagSlotIndex);
2647 elseif pItemLocation.SlotName then
2648 local vSlotID, vEmptySlotTexture = GetInventorySlotInfo(pItemLocation.SlotName);
2649  
2650 PickupInventoryItem(vSlotID);
2651 else
2652 Outfitter_ErrorMessage("Outfitter: Unknown location in PickupItemLocation");
2653 return;
2654 end
2655 end
2656  
2657 function Outfitter_BuildUnequipChangeList(pOutfit, pEquippableItems)
2658 local vEquipmentChangeList = {};
2659  
2660 for vInventorySlot, vOutfitItem in pOutfit.Items do
2661 local vItem, vIgnoredItem = OutfitterItemList_FindItemOrAlt(pEquippableItems, vOutfitItem, true);
2662  
2663 if vItem then
2664 table.insert(vEquipmentChangeList, {FromLocation = vItem.Location, Item = vItem, ToLocation = nil});
2665 end
2666 end -- for
2667  
2668 return vEquipmentChangeList;
2669 end
2670  
2671 function Outfitter_BuildEquipmentChangeList(pOutfit, pEquippableItems)
2672 local vEquipmentChangeList = {};
2673  
2674 OutfitterItemList_ResetIgnoreItemFlags(pEquippableItems);
2675  
2676 -- Remove items which are already in the correct slot from the outfit and from the
2677 -- equippable items list
2678  
2679 for vInventorySlot, vOutfitItem in pOutfit.Items do
2680 local vContainsItem, vItem = OutfitterItemList_InventorySlotContainsItem(pEquippableItems, vInventorySlot, vOutfitItem);
2681  
2682 if vContainsItem then
2683 pOutfit.Items[vInventorySlot] = nil;
2684  
2685 if vItem then
2686 vItem.IgnoreItem = true;
2687 end
2688 end
2689 end
2690  
2691 -- Scan the outfit using the Outfitter_cSlotNames array as an index so that changes
2692 -- are executed in the specified order
2693  
2694 for _, vInventorySlot in Outfitter_cSlotNames do
2695 local vOutfitItem = pOutfit.Items[vInventorySlot];
2696  
2697 if vOutfitItem then
2698 local vSlotID, vEmptySlotTexture = GetInventorySlotInfo(vInventorySlot);
2699 local vCurrentItemInfo = Outfitter_GetInventoryItemInfo(vInventorySlot);
2700  
2701 -- Empty the slot if it's supposed to be blank
2702  
2703 if vOutfitItem.Code == 0 then
2704 if vCurrentItemInfo then
2705 table.insert(vEquipmentChangeList, {SlotName = vInventorySlot, SlotID = vSlotID, ItemName = vOutfitItem.Name, ItemLocation = nil});
2706 end
2707  
2708 else
2709 -- Find the item
2710  
2711 local vItem, vIgnoredItem = OutfitterItemList_FindItemOrAlt(pEquippableItems, vOutfitItem, true);
2712  
2713 -- If the item wasn't found then show an appropriate error message
2714  
2715 if not vItem then
2716 if vOutfitItem.Name then
2717 if vIgnoredItem then
2718 local vSlotDisplayName = Outfitter_cSlotDisplayNames[vInventorySlot];
2719  
2720 if not vSlotDisplayName then
2721 vSlotDisplayName = vInventorySlot;
2722 end
2723  
2724 Outfitter_ErrorMessage(format(Outfitter_cItemAlreadyUsedError, vOutfitItem.Name, vSlotDisplayName));
2725 else
2726 Outfitter_ErrorMessage(format(Outfitter_cItemNotFoundError, vOutfitItem.Name));
2727 end
2728 else
2729 Outfitter_ErrorMessage(format(Outfitter_cItemNotFoundError, "unknown"));
2730 end
2731  
2732 -- Generate a change to move the item from its present location to the correct slot
2733  
2734 else
2735 pOutfit.Items[vInventorySlot].MetaSlotName = vItem.MetaSlotName;
2736 table.insert(vEquipmentChangeList, {SlotName = vInventorySlot, SlotID = vSlotID, ItemName = vOutfitItem.Name, ItemMetaSlotName = vItem.MetaSlotName, ItemLocation = vItem});
2737 end
2738 end
2739 end -- if
2740 end -- for
2741  
2742 if table.getn(vEquipmentChangeList) == 0 then
2743 return nil;
2744 end
2745  
2746 Outfitter_OptimizeEquipmentChangeList(vEquipmentChangeList);
2747  
2748 return vEquipmentChangeList;
2749 end
2750  
2751 function Outfitter_FindEquipmentChangeForSlot(pEquipmentChangeList, pSlotName)
2752 for vChangeIndex, vEquipmentChange in pEquipmentChangeList do
2753 if vEquipmentChange.SlotName == pSlotName then
2754 return vChangeIndex, vEquipmentChange;
2755 end
2756 end
2757  
2758 return nil, nil;
2759 end
2760  
2761 function Outfitter_FixSlotSwapChange(pEquipmentList, pChangeIndex1, pEquipmentChange1, pSlotName1, pChangeIndex2, pEquipmentChange2, pSlotName2)
2762 -- No problem if both slots will be emptied
2763  
2764 if not pEquipmentChange1.ItemLocation
2765 and not pEquipmentChange2.ItemLocation then
2766 return;
2767 end
2768  
2769 -- No problem if neither slot is being moved to the other one
2770  
2771 local vSlot2ToSlot1 = pEquipmentChange1.ItemLocation ~= nil
2772 and pEquipmentChange1.ItemLocation.SlotName == pSlotName2;
2773  
2774 local vSlot1ToSlot2 = pEquipmentChange2.ItemLocation ~= nil
2775 and pEquipmentChange2.ItemLocation.SlotName == pSlotName1;
2776  
2777 -- No problem if the slots are swapping with each other
2778 -- or not moving between each other at all
2779  
2780 if vSlot2ToSlot1 == vSlot1ToSlot2 then
2781 return;
2782 end
2783  
2784 -- Slot 1 is moving to slot 2
2785  
2786 if vSlot1ToSlot2 then
2787  
2788 if pEquipmentChange1.ItemLocation then
2789 -- Swap change 1 and change 2 around
2790  
2791 pEquipmentList[pChangeIndex1] = pEquipmentChange2;
2792 pEquipmentList[pChangeIndex2] = pEquipmentChange1;
2793  
2794 -- Insert a change to empty slot 2
2795  
2796 table.insert(pEquipmentList, pChangeIndex1, {SlotName = pEquipmentChange2.SlotName, SlotID = pEquipmentChange2.SlotID, ItemLocation = nil});
2797 else
2798 -- Slot 1 is going to be empty, so empty slot 2 instead
2799 -- and then when slot 1 is moved it'll swap the empty space
2800  
2801 pEquipmentChange1.SlotName = pSlotName2;
2802 pEquipmentChange1.SlotID = pEquipmentChange2.SlotID;
2803 pEquipmentChange1.ItemLocation = nil;
2804 end
2805  
2806 -- Slot 2 is moving to slot 1
2807  
2808 else
2809 if pEquipmentChange2.ItemLocation then
2810 -- Insert a change to empty slot 1 first
2811  
2812 table.insert(pEquipmentList, pChangeIndex1, {SlotName = pEquipmentChange1.SlotName, SlotID = pEquipmentChange1.SlotID, ItemLocation = nil});
2813 else
2814 -- Slot 2 is going to be empty, so empty slot 1 instead
2815 -- and then when slot 2 is moved it'll swap the empty space
2816  
2817 pEquipmentChange2.SlotName = pSlotName1;
2818 pEquipmentChange2.SlotID = pEquipmentChange1.SlotID;
2819 pEquipmentChange2.ItemLocation = nil;
2820  
2821 -- Change the order so that slot 1 gets emptied before the move
2822  
2823 pEquipmentList[pChangeIndex1] = pEquipmentChange2;
2824 pEquipmentList[pChangeIndex2] = pEquipmentChange1;
2825 end
2826 end
2827 end
2828  
2829 function Outfitter_OptimizeEquipmentChangeList(pEquipmentChangeList)
2830 local vSwapList =
2831 {
2832 {Slot1 = "Finger0Slot", Slot2 = "Finger1Slot"},
2833 {Slot1 = "Trinket0Slot", Slot2 = "Trinket1Slot"},
2834 {Slot1 = "MainHandSlot", Slot2 = "SecondaryHandSlot"},
2835 };
2836  
2837 local vDidSlot = {};
2838  
2839 local vChangeIndex = 1;
2840 local vNumChanges = table.getn(pEquipmentChangeList);
2841  
2842 while vChangeIndex <= vNumChanges do
2843 local vEquipmentChange = pEquipmentChangeList[vChangeIndex];
2844  
2845 -- If a two-hand weapon is being equipped, remove the change event
2846 -- for removing the offhand slot
2847  
2848 if vEquipmentChange.ItemMetaSlotName == "TwoHandSlot" then
2849 local vChangeIndex2, vEquipmentChange2 = Outfitter_FindEquipmentChangeForSlot(pEquipmentChangeList, "SecondaryHandSlot");
2850  
2851 -- If there's a change for the offhand slot, remove it
2852  
2853 if vChangeIndex2 then
2854 table.remove(pEquipmentChangeList, vChangeIndex2);
2855  
2856 if vChangeIndex2 < vChangeIndex then
2857 vChangeIndex = vChangeIndex - 1;
2858 end
2859  
2860 vNumChanges = vNumChanges - 1;
2861 end
2862  
2863 -- Insert a new change for the offhand slot to empty it ahead
2864 -- of equipping the two-hand item
2865  
2866 local vSlotID, vEmptySlotTexture = GetInventorySlotInfo("SecondaryHandSlot");
2867  
2868 table.insert(pEquipmentChangeList, vChangeIndex, {SlotName = "SecondaryHandSlot", SlotID = vSlotID, ItemLocation = nil});
2869  
2870 -- Otherwise see if the change needs to be re-arranged so that slot
2871 -- swapping works correctly
2872  
2873 else
2874 for vSwapListIndex, vSwapSlotInfo in vSwapList do
2875 if vEquipmentChange.SlotName == vSwapSlotInfo.Slot1
2876 and not vDidSlot[vEquipmentChange.SlotName] then
2877 local vChangeIndex2, vEquipmentChange2 = Outfitter_FindEquipmentChangeForSlot(pEquipmentChangeList, vSwapSlotInfo.Slot2);
2878  
2879 if vChangeIndex2 then
2880 Outfitter_FixSlotSwapChange(pEquipmentChangeList, vChangeIndex, vEquipmentChange, vSwapSlotInfo.Slot1, vChangeIndex2, vEquipmentChange2, vSwapSlotInfo.Slot2);
2881 end
2882  
2883 vDidSlot[vEquipmentChange.SlotName] = true;
2884  
2885 vNumChanges = table.getn(pEquipmentChangeList);
2886 end
2887 end
2888 end
2889  
2890 vChangeIndex = vChangeIndex + 1;
2891 end
2892 end
2893  
2894 function Outfitter_ExecuteEquipmentChangeList(pEquipmentChangeList, pEmptyBagSlots, pExpectedEquippableItems)
2895 for vChangeIndex, vEquipmentChange in pEquipmentChangeList do
2896 if vEquipmentChange.ItemLocation then
2897 Outfitter_PickupItemLocation(vEquipmentChange.ItemLocation);
2898 EquipCursorItem(vEquipmentChange.SlotID);
2899  
2900 if pExpectedEquippableItems then
2901 OutfitterItemList_SwapLocationWithInventorySlot(pExpectedEquippableItems, vEquipmentChange.ItemLocation, vEquipmentChange.SlotName);
2902 end
2903 else
2904 -- Remove the item
2905  
2906 if not pEmptyBagSlots
2907 or table.getn(pEmptyBagSlots) == 0 then
2908 local vItemInfo = Outfitter_GetInventoryItemInfo(vEquipmentChange.SlotName);
2909  
2910 if not vItemInfo then
2911 Outfitter_ErrorMessage("Outfitter internal error: Can't empty slot "..vEquipmentChange.SlotName.." because bags are full but slot is empty");
2912 else
2913 Outfitter_ErrorMessage(format(Outfitter_cBagsFullError, vItemInfo.Name));
2914 end
2915 else
2916 local vBagIndex = pEmptyBagSlots[1].BagIndex;
2917 local vBagSlotIndex = pEmptyBagSlots[1].BagSlotIndex;
2918  
2919 table.remove(pEmptyBagSlots, 1);
2920  
2921 PickupInventoryItem(vEquipmentChange.SlotID);
2922 PickupContainerItem(vBagIndex, vBagSlotIndex);
2923  
2924 if pExpectedEquippableItems then
2925 OutfitterItemList_SwapBagSlotWithInventorySlot(pExpectedEquippableItems, vBagIndex, vBagSlotIndex, vEquipmentChange.SlotName);
2926 end
2927 end
2928 end
2929 end
2930 end
2931  
2932 function Outfitter_ExecuteEquipmentChangeList2(pEquipmentChangeList, pEmptySlots, pBagsFullErrorFormat, pExpectedEquippableItems)
2933 for vChangeIndex, vEquipmentChange in pEquipmentChangeList do
2934 if vEquipmentChange.ToLocation then
2935 Outfitter_PickupItemLocation(vEquipmentChange.FromLocation);
2936 EquipCursorItem(vEquipmentChange.SlotID);
2937  
2938 if pExpectedEquippableItems then
2939 OutfitterItemList_SwapLocationWithInventorySlot(pExpectedEquippableItems, vEquipmentChange.ToLocation, vEquipmentChange.SlotName);
2940 end
2941 else
2942 -- Remove the item
2943  
2944 if not pEmptySlots
2945 or table.getn(pEmptySlots) == 0 then
2946 Outfitter_ErrorMessage(format(pBagsFullErrorFormat, vEquipmentChange.Item.Name));
2947 else
2948 local vToLocation = {BagIndex = pEmptySlots[1].BagIndex, BagSlotIndex = pEmptySlots[1].BagSlotIndex};
2949  
2950 table.remove(pEmptySlots, 1);
2951  
2952 Outfitter_PickupItemLocation(vEquipmentChange.FromLocation);
2953 Outfitter_PickupItemLocation(vToLocation);
2954  
2955 if pExpectedEquippableItems then
2956 OutfitterItemList_SwapLocations(pExpectedEquippableItems, vEquipmentChange.FromLocation, vToLocation);
2957 end
2958 end
2959 end
2960 end
2961 end
2962  
2963 function Outfitter_OutfitHasCombatEquipmentSlots(pOutfit)
2964 for vEquipmentSlot, _ in Outfitter_cCombatEquipmentSlots do
2965 if pOutfit.Items[vEquipmentSlot] then
2966 return true;
2967 end
2968 end
2969  
2970 return false;
2971 end
2972  
2973 function Outfitter_OutfitOnlyHasCombatEquipmentSlots(pOutfit)
2974 for vEquipmentSlot, _ in pOutfit.Items do
2975 if not Outfitter_cCombatEquipmentSlots[vEquipmentSlot] then
2976 return false;
2977 end
2978 end
2979  
2980 return true;
2981 end
2982  
2983 local gOutfitter_EquipmentUpdateCount = 0;
2984  
2985 function Outfitter_BeginEquipmentUpdate()
2986 gOutfitter_EquipmentUpdateCount = gOutfitter_EquipmentUpdateCount + 1;
2987 end
2988  
2989 function Outfitter_EndEquipmentUpdate(pCallerName)
2990 gOutfitter_EquipmentUpdateCount = gOutfitter_EquipmentUpdateCount - 1;
2991  
2992 if gOutfitter_EquipmentUpdateCount == 0 then
2993 Outfitter_UpdateEquippedItems();
2994 Outfitter_Update(false);
2995 end
2996 end
2997  
2998 function Outfitter_UpdateEquippedItems()
2999 if not gOutfitter_EquippedNeedsUpdate
3000 and not gOutfitter_WeaponsNeedUpdate then
3001 return;
3002 end
3003  
3004 -- Delay all changes until they're alive
3005  
3006 if gOutfitter_IsDead then
3007 -- or gOutfitter_IsFeigning then -- no longer disabling outfit changes during FD
3008 return;
3009 end
3010  
3011 local vCurrentTime = GetTime();
3012  
3013 if vCurrentTime - gOutfitter_LastEquipmentUpdateTime < Outfitter_cMinEquipmentUpdateInterval then
3014 OutfitterTimer_AdjustTimer();
3015 return;
3016 end
3017  
3018 gOutfitter_LastEquipmentUpdateTime = vCurrentTime;
3019  
3020 local vWeaponsNeedUpdate = gOutfitter_WeaponsNeedUpdate;
3021  
3022 gOutfitter_EquippedNeedsUpdate = false;
3023 gOutfitter_WeaponsNeedUpdate = false;
3024  
3025 -- Compile the outfit
3026  
3027 local vEquippableItems = OutfitterItemList_GetEquippableItems();
3028 local vCompiledOutfit = Outfitter_GetCompiledOutfit();
3029  
3030 -- If the outfit contains non-weapon changes then
3031 -- delay the change until they're out of combat but go
3032 -- ahead and swap the weapon slots if there are any
3033  
3034 if gOutfitter_InCombat then
3035 if vWeaponsNeedUpdate
3036 and Outfitter_OutfitHasCombatEquipmentSlots(vCompiledOutfit) then
3037  
3038 -- Allow the weapon change to proceed but defer the rest
3039 -- until they're out of combat
3040  
3041 local vWeaponOutfit = Outfitter_NewEmptyOutfit();
3042  
3043 for vEquipmentSlot, _ in Outfitter_cCombatEquipmentSlots do
3044 vWeaponOutfit.Items[vEquipmentSlot] = vCompiledOutfit.Items[vEquipmentSlot];
3045 end
3046  
3047 -- Still need to update the rest once they exit combat
3048 -- if there are non-equipment slot items
3049  
3050 if not Outfitter_OutfitOnlyHasCombatEquipmentSlots(vCompiledOutfit) then
3051 gOutfitter_EquippedNeedsUpdate = true;
3052 end
3053  
3054 -- Switch to the weapons-only part
3055  
3056 vCompiledOutfit = vWeaponOutfit;
3057 else
3058 -- No weapon changes, just defer the whole outfit change
3059  
3060 gOutfitter_EquippedNeedsUpdate = true;
3061 return;
3062 end
3063 end
3064  
3065 -- Equip it
3066  
3067 local vEquipmentChangeList = Outfitter_BuildEquipmentChangeList(vCompiledOutfit, vEquippableItems);
3068  
3069 if vEquipmentChangeList then
3070 -- local vExpectedEquippableItems = OutfitterItemList_New();
3071  
3072 Outfitter_ExecuteEquipmentChangeList(vEquipmentChangeList, Outfitter_GetEmptyBagSlotList(), vExpectedEquippableItems);
3073  
3074 -- Outfitter_DumpArray("ExpectedEquippableItems", vExpectedEquippableItems);
3075 end
3076  
3077 -- Update the outfit we're expecting to see on the player
3078  
3079 for vInventorySlot, vItem in vCompiledOutfit.Items do
3080 gOutfitter_ExpectedOutfit.Items[vInventorySlot] = vCompiledOutfit.Items[vInventorySlot];
3081 end
3082 end
3083  
3084 function Outfitter_InitDebugging()
3085 if gOutfitter_InitializedDebug then
3086 return;
3087 end
3088  
3089 gOutfitter_InitializedDebug = true;
3090  
3091 -- Find the debug frame if there is one
3092  
3093 for vChatIndex = 1, NUM_CHAT_WINDOWS do
3094 local vChatFrame = getglobal("ChatFrame"..vChatIndex);
3095  
3096 if vChatFrame
3097 and (vChatFrame:IsVisible() or vChatFrame.isDocked) then
3098 local vTab = getglobal("ChatFrame"..vChatIndex.."Tab");
3099 local vName = vTab:GetText();
3100  
3101 if vName == "Debug" then
3102 gOutfitter_DebugFrame = vChatFrame;
3103 if gOutfitter_DebugFrame:GetMaxLines() < 1000 then
3104 gOutfitter_DebugFrame:SetMaxLines(1000);
3105 end
3106 _ERRORMESSAGE = function(message) Outfitter_DebugMessage(message); end;
3107 end
3108 end
3109 end
3110  
3111 if gOutfitter_DebugFrame then
3112 Outfitter_DebugMessage("Found debugging chat frame");
3113 end
3114 end
3115  
3116 function Outfitter_DebugMessage(pMessage)
3117 if gOutfitter_DebugFrame then
3118 gOutfitter_DebugFrame:AddMessage("DEBUG: "..pMessage, 0.7, 0.3, 1.0);
3119  
3120 local vTabFlash = getglobal(gOutfitter_DebugFrame:GetName().."TabFlash");
3121  
3122 vTabFlash:Show();
3123 UIFrameFlash(vTabFlash, 0.25, 0.25, 60, nil, 0.5, 0.5);
3124 else
3125 DEFAULT_CHAT_FRAME:AddMessage("DEBUG: "..pMessage, 0.7, 0.3, 1.0);
3126 end
3127 end
3128  
3129 function Outfitter_ErrorMessage(pMessage)
3130 DEFAULT_CHAT_FRAME:AddMessage(pMessage, 0.8, 0.3, 0.5);
3131 end
3132  
3133 function Outfitter_TestMessage(pMessage)
3134 if gOutfitter_DebugFrame then
3135 gOutfitter_DebugFrame:AddMessage("TEST: "..pMessage, 0.7, 0.3, 1.0);
3136 else
3137 DEFAULT_CHAT_FRAME:AddMessage("TEST: "..pMessage, 0.7, 0.3, 1.0);
3138 end
3139 end
3140  
3141 function Outfitter_NoteMessage(pMessage)
3142 DEFAULT_CHAT_FRAME:AddMessage(pMessage, 0.6, 1.0, 0.3);
3143 end
3144  
3145 function Outfitter_RenameLink(pLink, pName)
3146 local vMessage = string.gsub(pLink, "%[.*%]", "["..pName.."]");
3147 -- local vMessage = string.gsub(pMessage, "||", "|");
3148  
3149 DEFAULT_CHAT_FRAME:AddMessage(vMessage);
3150 end
3151  
3152 function Outfitter_DumpArray(pPrefixString, pArray)
3153 if not pArray then
3154 Outfitter_DebugMessage(pPrefixString.." is nil");
3155 return;
3156 end
3157  
3158 local vFoundElement = false;
3159  
3160 for vIndex, vElement in pArray do
3161 vFoundElement = true;
3162  
3163 local vType = type(vElement);
3164 local vPrefix;
3165  
3166 if type(vIndex) == "number" then
3167 vPrefix = pPrefixString.."["..vIndex.."]";
3168 else
3169 vPrefix = pPrefixString.."."..vIndex;
3170 end
3171  
3172 if vType == "number" then
3173 Outfitter_DebugMessage(vPrefix.." = "..vElement);
3174 elseif vType == "string" then
3175 Outfitter_DebugMessage(vPrefix.." = \""..vElement.."\"");
3176 elseif vType == "boolean" then
3177 if vElement then
3178 Outfitter_DebugMessage(vPrefix.." = true");
3179 else
3180 Outfitter_DebugMessage(vPrefix.." = false");
3181 end
3182 elseif vType == "table" then
3183 Outfitter_DumpArray(vPrefix, vElement);
3184 else
3185 Outfitter_DebugMessage(vPrefix.." "..vType);
3186 end
3187 end
3188  
3189 if not vFoundElement then
3190 Outfitter_DebugMessage(pPrefixString.." is empty");
3191 end
3192 end
3193  
3194 function Outfitter_InventorySlotIsEmpty(pInventorySlot)
3195 return Outfitter_GetInventoryItemInfo(pInventorySlot) == nil;
3196 end
3197  
3198 function Outfitter_GetBagItemInfo(pBagIndex, pSlotIndex)
3199 local vItemLink = GetContainerItemLink(pBagIndex, pSlotIndex);
3200 local vItemInfo = Outfitter_GetItemInfoFromLink(vItemLink);
3201  
3202 if not vItemInfo then
3203 return nil;
3204 end
3205  
3206 vItemInfo.Texture, _, _, vItemInfo.Quality, _ = GetContainerItemInfo(pBagIndex, pSlotIndex);
3207  
3208 return vItemInfo;
3209 end
3210  
3211 local gOutfitter_AmmoSlotInfoCache = nil;
3212  
3213 function Outfitter_FindAmmoSlotItem(pName, pTexture)
3214 if gOutfitter_AmmoSlotInfoCache
3215 and gOutfitter_AmmoSlotInfoCache.Name == pName
3216 and gOutfitter_AmmoSlotInfoCache.Texture == pTexture then
3217 return gOutfitter_AmmoSlotInfoCache.ItemInfo;
3218 end
3219  
3220 for vBagIndex = 0, NUM_BAG_SLOTS do
3221 local vNumBagSlots = GetContainerNumSlots(vBagIndex);
3222  
3223 if vNumBagSlots > 0 then
3224 for vBagSlotIndex = 1, vNumBagSlots do
3225 local vTexture = GetContainerItemInfo(vBagIndex, vBagSlotIndex);
3226  
3227 if vTexture == pTexture then
3228 local vItemInfo = Outfitter_GetBagItemInfo(vBagIndex, vBagSlotIndex);
3229  
3230 if vItemInfo.Name == pName then
3231 if not gOutfitter_AmmoSlotInfoCache then
3232 gOutfitter_AmmoSlotInfoCache = {};
3233 end
3234  
3235 gOutfitter_AmmoSlotInfoCache.Name = pName;
3236 gOutfitter_AmmoSlotInfoCache.Texture = pTexture;
3237 gOutfitter_AmmoSlotInfoCache.ItemInfo = vItemInfo;
3238  
3239 return vItemInfo;
3240 end
3241 end
3242 end -- for vBagSlotIndex
3243 end -- if vNumBagSlots
3244 end -- for vBagIndex
3245  
3246 return nil;
3247 end
3248  
3249 function Outfitter_GetInventoryItemInfo(pInventorySlot)
3250 local vSlotID = GetInventorySlotInfo(pInventorySlot);
3251 local vItemLink = GetInventoryItemLink("player", vSlotID);
3252  
3253 -- GetInventoryItemLink doesn't work for the ammo slot, so instead get the icon
3254 -- for the slot and then search for a matching icon in the bags
3255  
3256 if vItemLink == nil
3257 and pInventorySlot == "AmmoSlot" then
3258 OutfitterTooltip:SetOwner(OutfitterFrame, "ANCHOR_BOTTOMRIGHT", 0, 0);
3259 OutfitterTooltip:SetInventoryItem("player", vSlotID);
3260  
3261 if not OutfitterTooltipTextLeft1:IsShown() then
3262 OutfitterTooltip:Hide();
3263 return nil;
3264 end
3265  
3266 local vAmmoItemName = OutfitterTooltipTextLeft1:GetText();
3267  
3268 OutfitterTooltip:Hide();
3269  
3270 local vAmmoItemTexture = GetInventoryItemTexture("player", vSlotID);
3271  
3272 return Outfitter_FindAmmoSlotItem(vAmmoItemName, vAmmoItemTexture);
3273 end
3274  
3275 local vItemInfo = Outfitter_GetItemInfoFromLink(vItemLink);
3276  
3277 if not vItemInfo then
3278 return nil;
3279 end
3280  
3281 vItemInfo.Quality = GetInventoryItemQuality("player", vSlotID);
3282 vItemInfo.Texture = GetInventoryItemTexture("player", vSlotID);
3283  
3284 return vItemInfo;
3285 end
3286  
3287 function Outfitter_GetItemInfoFromLink(pItemLink)
3288 if not pItemLink then
3289 return nil;
3290 end
3291 -- |cff1eff00|Hitem:1465:803:0:0|h[Tigerbane]|h|r
3292 -- |cff1eff00|Hitem:1465:803:0:0|h[Tigerbane]|h|r
3293 -- |(hex code for item color)|Hitem:(item ID code):(enchant code):(added stats code):0|h[(item name)]|h|r
3294  
3295 local vStartIndex, vEndIndex, vLinkColor, vItemCode, vItemEnchantCode, vItemSubCode, vUnknownCode, vItemName = strfind(pItemLink, "|(%x+)|Hitem:(%d+):(%d+):(%d+):(%d+)|h%[([^%]]+)%]|h|r");
3296  
3297 if not vStartIndex then
3298 return nil;
3299 end
3300  
3301 vItemCode = tonumber(vItemCode);
3302 vItemSubCode = tonumber(vItemSubCode);
3303 vItemEnchantCode = tonumber(vItemEnchantCode);
3304  
3305 local vItemFamilyName,
3306 vItemLink,
3307 vItemQuality,
3308 vItemLevel,
3309 vItemType,
3310 vItemSubType,
3311 vItemCount,
3312 vItemInvType = GetItemInfo(vItemCode);
3313  
3314 local vItemInfo =
3315 {
3316 Code = vItemCode,
3317 SubCode = vItemSubCode,
3318 Name = vItemName,
3319 EnchantCode = vItemEnchantCode,
3320 Level = vItemLevel,
3321 };
3322  
3323 -- Just return if there's no inventory type
3324  
3325 if not vItemInvType
3326 or vItemInvType == "" then
3327 return vItemInfo;
3328 end
3329  
3330 -- Just return if we don't know anything about the inventory type
3331  
3332 local vInvTypeInfo = Outfitter_cInvTypeToSlotName[vItemInvType];
3333  
3334 if not vInvTypeInfo then
3335 Outfitter_ErrorMessage("Outfitter error: Unknown slot type "..vItemInvType.." for item "..vItemName);
3336 return vItemInfo;
3337 end
3338  
3339 -- Get the slot name
3340  
3341 if not vInvTypeInfo.SlotName then
3342 Outfitter_ErrorMessage("Unknown slot name for inventory type "..vItemInvType);
3343 return vItemInfo;
3344 end
3345  
3346 vItemInfo.ItemSlotName = vInvTypeInfo.SlotName;
3347 vItemInfo.MetaSlotName = vInvTypeInfo.MetaSlotName;
3348  
3349 -- Return the info
3350  
3351 return vItemInfo;
3352 end
3353  
3354 function Outfitter_CreateNewOutfit()
3355 OutfitterNameOutfit_Open(nil);
3356 end
3357  
3358 function Outfitter_NewEmptyOutfit(pName)
3359 return {Name = pName, Items = {}};
3360 end
3361  
3362 function Outfitter_IsEmptyOutfit(pOutfit)
3363 return Outfitter_ArrayIsEmpty(pOutfit.Items);
3364 end
3365  
3366 function Outfitter_NewNakedOutfit(pName)
3367 local vOutfit = Outfitter_NewEmptyOutfit(pName);
3368  
3369 for _, vInventorySlot in Outfitter_cSlotNames do
3370 Outfitter_AddOutfitItem(vOutfit, vInventorySlot, 0, 0, "", 0);
3371 end
3372  
3373 return vOutfit;
3374 end
3375  
3376 function Outfitter_AddOutfitItem(pOutfit, pSlotName, pItemCode, pItemSubCode, pItemName, pItemEnchantCode)
3377 pOutfit.Items[pSlotName] = {Code = pItemCode, SubCode = pItemSubCode, Name = pItemName, EnchantCode = pItemEnchantCode};
3378 end
3379  
3380 function Outfitter_AddOutfitStatItem(pOutfit, pSlotName, pItemCode, pItemSubCode, pItemName, pItemEnchantCode, pStatID, pStatValue)
3381 if not pSlotName then
3382 Outfitter_ErrorMessage("AddOutfitStatItem: SlotName is nil for "..pItemName);
3383 return;
3384 end
3385  
3386 if not pStatID then
3387 Outfitter_ErrorMessage("AddOutfitStatItem: StatID is nil for "..pItemName);
3388 return;
3389 end
3390  
3391 Outfitter_AddOutfitItem(pOutfit, pSlotName, pItemCode, pItemSubCode, pItemName, pItemEnchantCode);
3392 pOutfit.Items[pSlotName][pStatID] = pStatValue;
3393 end
3394  
3395 function Outfitter_AddOutfitStatItemIfBetter(pOutfit, pSlotName, pItemCode, pItemSubCode, pItemName, pItemEnchantCode, pStatID, pStatValue)
3396 local vCurrentItem = pOutfit.Items[pSlotName];
3397 local vAlternateSlotName = Outfitter_cHalfAlternateStatSlot[pSlotName];
3398  
3399 if not vCurrentItem
3400 or not vCurrentItem[pStatID]
3401 or vCurrentItem[pStatID] < pStatValue then
3402 -- If we're bumping the current item, see if it should be moved to the alternate slot
3403  
3404 if vCurrentItem
3405 and vCurrentItem[pStatID]
3406 and vAlternateSlotName then
3407 Outfitter_AddOutfitStatItemIfBetter(pOutfit, vAlternateSlotName, vCurrentItem.Code, vCurrentItem.SubCode, vCurrentItem.Name, vCurrentItem.EnchantCode, pStatID, vCurrentItem[pStatID])
3408 end
3409  
3410 Outfitter_AddOutfitStatItem(pOutfit, pSlotName, pItemCode, pItemSubCode, pItemName, pItemEnchantCode, pStatID, pStatValue);
3411 else
3412 if not vAlternateSlotName then
3413 return;
3414 end
3415  
3416 return Outfitter_AddOutfitStatItemIfBetter(pOutfit, vAlternateSlotName, pItemCode, pItemSubCode, pItemName, pItemEnchantCode, pStatID, pStatValue);
3417 end
3418 end
3419  
3420 function Outfitter_AddStats(pItem1, pItem2, pStatID)
3421 local vStat = 0;
3422  
3423 if pItem1
3424 and pItem1[pStatID] then
3425 vStat = pItem1[pStatID];
3426 end
3427  
3428 if pItem2
3429 and pItem2[pStatID] then
3430 vStat = vStat + pItem2[pStatID];
3431 end
3432  
3433 return vStat;
3434 end
3435  
3436 function Outfitter_CollapseMetaSlotsIfBetter(pOutfit, pStatID)
3437 -- Compare the weapon slot with the 1H/OH slots
3438  
3439 local vWeapon0Item = pOutfit.Items.Weapon0Slot;
3440 local vWeapon1Item = pOutfit.Items.Weapon1Slot;
3441  
3442 if vWeapon0Item or vWeapon1Item then
3443 -- Try the various combinations of MH/OH/W0/W1
3444  
3445 local v1HItem = pOutfit.Items.MainHandSlot;
3446 local vOHItem = pOutfit.Items.SecondaryHandSlot;
3447  
3448 local vCombinations =
3449 {
3450 {MainHand = v1HItem, SecondaryHand = vOHItem, AllowEmptyMainHand = true},
3451 {MainHand = v1HItem, SecondaryHand = vWeapon0Item, AllowEmptyMainHand = false},
3452 {MainHand = v1HItem, SecondaryHand = vWeapon1Item, AllowEmptyMainHand = false},
3453 {MainHand = vWeapon0Item, SecondaryHand = vOHItem, AllowEmptyMainHand = true},
3454 {MainHand = vWeapon1Item, SecondaryHand = vOHItem, AllowEmptyMainHand = true},
3455 {MainHand = vWeapon0Item, SecondaryHand = vWeapon1Item, AllowEmptyMainHand = false},
3456 };
3457  
3458 local vBestCombinationIndex = nil;
3459 local vBestCombinationValue = nil;
3460  
3461 for vIndex = 1, 6 do
3462 local vCombination = vCombinations[vIndex];
3463  
3464 -- Ignore combinations where the main hand is empty if
3465 -- that's not allowed in this combinations
3466  
3467 if vCombination.AllowEmptyMainHand
3468 or vCombination.MainHand then
3469 local vCombinationValue = Outfitter_AddStats(vCombination.MainHand, vCombination.SecondaryHand, pStatID);
3470  
3471 if not vBestCombinationIndex
3472 or vCombinationValue > vBestCombinationValue then
3473 vBestCombinationIndex = vIndex;
3474 vBestCombinationValue = vCombinationValue;
3475 end
3476 end
3477 end
3478  
3479 if vBestCombinationIndex then
3480 local vCombination = vCombinations[vBestCombinationIndex];
3481  
3482 pOutfit.Items.MainHandSlot = vCombination.MainHand;
3483 pOutfit.Items.SecondaryHandSlot = vCombination.SecondaryHand;
3484 end
3485  
3486 pOutfit.Items.Weapon0Slot = nil;
3487 pOutfit.Items.Weapon1Slot = nil;
3488 end
3489  
3490 -- Compare the 2H slot with the 1H/OH slots
3491  
3492 local v2HItem = pOutfit.Items.TwoHandSlot;
3493  
3494 if v2HItem then
3495 local v1HItem = pOutfit.Items.MainHandSlot;
3496 local vOHItem = pOutfit.Items.SecondaryHandSlot;
3497 local v1HOHTotalStat = Outfitter_AddStats(v1HItem, vOHItem, pStatID);
3498  
3499 if v2HItem[pStatID]
3500 and v2HItem[pStatID] > v1HOHTotalStat then
3501 pOutfit.Items.MainHandSlot = v2HItem;
3502 pOutfit.Items.SecondaryHandSlot = nil;
3503 end
3504  
3505 pOutfit.Items.TwoHandSlot = nil;
3506 end
3507 end
3508  
3509 function Outfitter_RemoveOutfitItem(pOutfit, pSlotName)
3510 pOutfit.Items[pSlotName] = nil;
3511 end
3512  
3513 function Outfitter_GetInventoryOutfit(pName, pOutfit)
3514 local vOutfit;
3515  
3516 if pOutfit then
3517 vOutfit = pOutfit;
3518 else
3519 vOutfit = Outfitter_NewEmptyOutfit(pName);
3520 end
3521  
3522 for _, vInventorySlot in Outfitter_cSlotNames do
3523 local vItemInfo = Outfitter_GetInventoryItemInfo(vInventorySlot);
3524  
3525 -- To avoid extra memory operations, only update the item if it's different
3526  
3527 local vExistingItem = vOutfit.Items[vInventorySlot];
3528  
3529 if not vItemInfo then
3530 if not vExistingItem
3531 or vExistingItem.Code ~= 0 then
3532 Outfitter_AddOutfitItem(vOutfit, vInventorySlot, 0, 0, "", 0);
3533 end
3534 else
3535 if not vExistingItem
3536 or vExistingItem.Code ~= vItemInfo.Code
3537 or vExistingItem.SubCode ~= vItemInfo.SubCode
3538 or vExistingItem.EnchantCode ~= vItemInfo.EnchantCode then
3539 Outfitter_AddOutfitItem(vOutfit, vInventorySlot, vItemInfo.Code, vItemInfo.SubCode, vItemInfo.Name, vItemInfo.EnchantCode);
3540 end
3541 end
3542 end
3543  
3544 return vOutfit;
3545 end
3546  
3547 function Outfitter_UpdateOutfitFromInventory(pOutfit, pNewItemsOutfit)
3548 if not pNewItemsOutfit then
3549 return;
3550 end
3551  
3552 for vInventorySlot, vItem in pNewItemsOutfit.Items do
3553 -- Only update slots which aren't in an unknown state
3554  
3555 local vCheckbox = getglobal("OutfitterEnable"..vInventorySlot);
3556  
3557 if not vCheckbox:GetChecked()
3558 or not vCheckbox.IsUnknown then
3559 pOutfit.Items[vInventorySlot] = vItem;
3560 Outfitter_NoteMessage(format(Outfitter_cAddingItem, vItem.Name, pOutfit.Name));
3561 Outfitter_UpdateOutfitCategory(pOutfit);
3562 end
3563 end
3564  
3565 -- Add the new items to the current compiled outfit
3566  
3567 for vInventorySlot, vItem in pNewItemsOutfit.Items do
3568 gOutfitter_ExpectedOutfit.Items[vInventorySlot] = pNewItemsOutfit.Items[vInventorySlot];
3569 end
3570  
3571 gOutfitter_DisplayIsDirty = true;
3572 end
3573  
3574 function Outfitter_SubtractOutfit(pOutfit1, pOutfit2, pCheckAlternateSlots)
3575 local vEquippableItems = OutfitterItemList_GetEquippableItems();
3576  
3577 -- Remove items from pOutfit1 if they match the item in pOutfit2
3578  
3579 for _, vInventorySlot in Outfitter_cSlotNames do
3580 local vItem1 = pOutfit1.Items[vInventorySlot];
3581 local vItem2 = pOutfit2.Items[vInventorySlot];
3582  
3583 if OutfitterItemList_ItemsAreSame(vEquippableItems, vItem1, vItem2) then
3584 pOutfit1.Items[vInventorySlot] = nil;
3585 elseif pCheckAlternateSlots then
3586 local vAlternateSlotName = Outfitter_cFullAlternateStatSlot[vInventorySlot];
3587  
3588 vItem2 = pOutfit2.Items[vAlternateSlotName];
3589  
3590 if OutfitterItemList_ItemsAreSame(vEquippableItems, vItem1, vItem2) then
3591 pOutfit1.Items[vInventorySlot] = nil;
3592 end
3593 end
3594 end
3595 end
3596  
3597 function Outfitter_GetNewItemsOutfit(pPreviousOutfit)
3598 -- Get the current outfit and the list
3599 -- of equippable items
3600  
3601 gOutfitter_CurrentInventoryOutfit = Outfitter_GetInventoryOutfit(gOutfitter_CurrentInventoryOutfit);
3602  
3603 local vEquippableItems = OutfitterItemList_GetEquippableItems();
3604  
3605 -- Create a temporary outfit from the differences
3606  
3607 local vNewItemsOutfit = Outfitter_NewEmptyOutfit();
3608 local vOutfitHasItems = false;
3609  
3610 for _, vInventorySlot in Outfitter_cSlotNames do
3611 local vCurrentItem = gOutfitter_CurrentInventoryOutfit.Items[vInventorySlot];
3612 local vPreviousItem = pPreviousOutfit.Items[vInventorySlot];
3613 local vSkipSlot = false;
3614  
3615 if vInventorySlot == "SecondaryHandSlot" then
3616 local vMainHandItem = pPreviousOutfit.Items["MainHandSlot"];
3617  
3618 if vMainHandItem
3619 and vMainHandItem.MetaSlotName == "TwoHandSlot" then
3620 vSkipSlot = true;
3621 end
3622 elseif vInventorySlot == "AmmoSlot"
3623 and (not vCurrentItem or vCurrentItem.Code == 0) then
3624 vSkipSlot = true;
3625 end
3626  
3627 if not vSkipSlot
3628 and not OutfitterItemList_InventorySlotContainsItem(vEquippableItems, vInventorySlot, vPreviousItem) then
3629 vNewItemsOutfit.Items[vInventorySlot] = vCurrentItem;
3630 vOutfitHasItems = true;
3631 end
3632 end
3633  
3634 if not vOutfitHasItems then
3635 return nil;
3636 end
3637  
3638 return vNewItemsOutfit, gOutfitter_CurrentInventoryOutfit;
3639 end
3640  
3641 function Outfitter_UpdateTemporaryOutfit(pNewItemsOutfit)
3642 -- Just return if nothing has changed
3643  
3644 if not pNewItemsOutfit then
3645 return;
3646 end
3647  
3648 -- Merge the new items with an existing temporary outfit
3649  
3650 local vTemporaryOutfit = OutfitterStack_GetTemporaryOutfit();
3651 local vUsingExistingTempOutfit = false;
3652  
3653 if vTemporaryOutfit then
3654  
3655 for vInventorySlot, vItem in pNewItemsOutfit.Items do
3656 vTemporaryOutfit.Items[vInventorySlot] = vItem;
3657 end
3658  
3659 vUsingExistingTempOutfit = true;
3660  
3661 -- Otherwise add the new items as the temporary outfit
3662  
3663 else
3664 vTemporaryOutfit = pNewItemsOutfit;
3665 end
3666  
3667 -- Subtract out items which are expected to be in the outfit
3668  
3669 local vExpectedOutfit = Outfitter_GetExpectedOutfit(vTemporaryOutfit);
3670  
3671 Outfitter_SubtractOutfit(vTemporaryOutfit, vExpectedOutfit);
3672  
3673 if Outfitter_IsEmptyOutfit(vTemporaryOutfit) then
3674 if vUsingExistingTempOutfit then
3675 Outfitter_RemoveOutfit(vTemporaryOutfit);
3676 end
3677 else
3678 if not vUsingExistingTempOutfit then
3679 OutfitterStack_AddOutfit(vTemporaryOutfit);
3680 end
3681 end
3682  
3683 -- Add the new items to the current compiled outfit
3684  
3685 for vInventorySlot, vItem in pNewItemsOutfit.Items do
3686 gOutfitter_ExpectedOutfit.Items[vInventorySlot] = vItem;
3687 end
3688 end
3689  
3690 function Outfitter_SetSlotEnable(pSlotName, pEnable)
3691 if not gOutfitter_SelectedOutfit then
3692 return;
3693 end
3694  
3695 if pEnable then
3696 local vItemInfo = Outfitter_GetInventoryItemInfo(pSlotName);
3697  
3698 if vItemInfo then
3699 gOutfitter_SelectedOutfit.Items[pSlotName] = {Code = vItemInfo.Code, SubCode = vItemInfo.SubCode, Name = vItemInfo.Name, EnchantCode = vItemInfo.EnchantCode};
3700 else
3701 gOutfitter_SelectedOutfit.Items[pSlotName] = {Code = 0, SubCode = 0, Name = "", EnchantCode = 0};
3702 end
3703 else
3704 gOutfitter_SelectedOutfit.Items[pSlotName] = nil;
3705 end
3706  
3707 gOutfitter_DisplayIsDirty = true;
3708 end
3709  
3710 function Outfitter_GetSpecialOutfit(pSpecialID)
3711 for vOutfitIndex, vOutfit in gOutfitter_Settings.Outfits.Special do
3712 if vOutfit.SpecialID == pSpecialID then
3713 return vOutfit;
3714 end
3715 end
3716  
3717 return nil;
3718 end
3719  
3720 function Outfitter_GetPlayerAuraStates()
3721 local vAuraStates =
3722 {
3723 Dining = false,
3724 Shadowform = false,
3725 Riding = false,
3726 GhostWolf = false,
3727 Feigning = false,
3728 Evocate = false,
3729 Monkey = false,
3730 Hawk = false,
3731 Cheetah = false,
3732 Pack = false,
3733 Beast = false,
3734 Wild = false
3735 };
3736  
3737 local vBuffIndex = 1;
3738  
3739 while true do
3740 vTexture = UnitBuff("player", vBuffIndex);
3741  
3742 if not vTexture then
3743 return vAuraStates;
3744 end
3745  
3746 local vStartIndex, vEndIndex, vTextureName = string.find(vTexture, "([^%\\]*)$");
3747  
3748 --
3749  
3750 local vSpecialID = gOutfitter_AuraIconSpecialID[vTextureName];
3751  
3752 if vSpecialID then
3753 vAuraStates[vSpecialID] = true;
3754  
3755 --
3756  
3757 elseif not vAuraStates.Dining
3758 and string.find(vTextureName, "INV_Drink") then
3759 vAuraStates.Dining = true;
3760  
3761 --
3762  
3763 else
3764 local vTextLine1, vTextLine2 = Outfitter_GetBuffTooltipText(vBuffIndex);
3765  
3766 if vTextLine1 then
3767 local vSpecialID = gOutfitter_SpellNameSpecialID[vTextLine1];
3768  
3769 if vSpecialID then
3770 vAuraStates[vSpecialID] = true;
3771  
3772 elseif vTextLine2
3773 and string.find(vTextLine2, Outfitter_cMountSpeedFormat) then
3774 vAuraStates.Riding = true;
3775 end
3776 end
3777 end
3778  
3779 vBuffIndex = vBuffIndex + 1;
3780 end
3781 end
3782  
3783 function Outfitter_GetBuffTooltipText(pBuffIndex)
3784 OutfitterTooltip:SetOwner(OutfitterFrame, "ANCHOR_BOTTOMRIGHT", 0, 0);
3785 OutfitterTooltip:SetUnitBuff("player", pBuffIndex);
3786  
3787 local vText1, vText2;
3788  
3789 if OutfitterTooltipTextLeft1:IsShown() then
3790 vText1 = OutfitterTooltipTextLeft1:GetText();
3791 end -- if IsShown
3792  
3793 if OutfitterTooltipTextLeft2:IsShown() then
3794 vText2 = OutfitterTooltipTextLeft2:GetText();
3795 end -- if IsShown
3796  
3797 OutfitterTooltip:Hide();
3798  
3799 return vText1, vText2;
3800 end
3801  
3802 function Outfitter_UpdateAuraStates()
3803 -- Check for special aura outfits
3804  
3805 local vAuraStates = Outfitter_GetPlayerAuraStates();
3806  
3807 for vSpecialID, vIsActive in vAuraStates do
3808 if vSpecialID == "Feigning" then
3809 gOutfitter_IsFeigning = vIsActive;
3810 else
3811 if not gOutfitter_SpecialState[vSpecialID] then
3812 gOutfitter_SpecialState[vSpecialID] = false;
3813 end
3814  
3815 if gOutfitter_SpecialState[vSpecialID] ~= vIsActive then
3816 gOutfitter_SpecialState[vSpecialID] = vIsActive;
3817 Outfitter_SetSpecialOutfitEnabled(vSpecialID, vIsActive);
3818 end
3819 end
3820 end
3821  
3822 -- As of 1.12 aura changes are the only way to detect shapeshifts, so update those too
3823  
3824 Outfitter_UpdateShapeshiftState();
3825 end
3826  
3827 function Outfitter_UpdateShapeshiftState()
3828 local vNumForms = GetNumShapeshiftForms();
3829  
3830 for vIndex = 1, vNumForms do
3831 local vTexture, vName, vIsActive, vIsCastable = GetShapeshiftFormInfo(vIndex);
3832 local vSpecialID = Outfitter_cShapeshiftSpecialIDs[vName];
3833  
3834 if vSpecialID then
3835 if not vIsActive then
3836 vIsActive = false;
3837 end
3838  
3839 if gOutfitter_SpecialState[vSpecialID.ID] == nil then
3840 gOutfitter_SpecialState[vSpecialID.ID] = Outfitter_WearingSpecialOutfit(vSpecialID.ID);
3841 end
3842  
3843 if gOutfitter_SpecialState[vSpecialID.ID] ~= vIsActive then
3844 gOutfitter_SpecialState[vSpecialID.ID] = vIsActive;
3845 Outfitter_SetSpecialOutfitEnabled(vSpecialID.ID, vIsActive);
3846 end
3847 end
3848 end
3849 end
3850  
3851 function Outfitter_SetSpecialOutfitEnabled(pSpecialID, pEnable)
3852 local vOutfit = Outfitter_GetSpecialOutfit(pSpecialID);
3853  
3854 if not vOutfit
3855 or vOutfit.Disabled
3856 or (pEnable and vOutfit.BGDisabled and Outfitter_InBattlegroundZone()) then
3857 return;
3858 end
3859  
3860 if pEnable then
3861 -- Start monitoring health and mana if it's the dining outfit
3862  
3863 if pSpecialID == "Dining" then
3864 Outfitter_ResumeEvent(OutfitterFrame, "UNIT_HEALTH");
3865 Outfitter_ResumeEvent(OutfitterFrame, "UNIT_MANA");
3866 end
3867  
3868 --
3869  
3870 local vWearBelowOutfit = nil;
3871  
3872 -- If it's the ArgentDawn outfit, wear it below the
3873 -- riding outfit. Once the player dismounts then
3874 -- overlapping items from the ArgentDawn outfit will equip.
3875 -- This will prevent the Argent Dawn trinket from interfering
3876 -- with the carrot trinket when riding into the plaguelands
3877  
3878 if pSpecialID == "ArgentDawn" then
3879 vWearBelowOutfit = Outfitter_GetSpecialOutfit("Riding");
3880 end
3881  
3882 --
3883  
3884 Outfitter_WearOutfit(vOutfit, "Special", vWearBelowOutfit);
3885 else
3886 Outfitter_RemoveOutfit(vOutfit);
3887 end
3888 end
3889  
3890 function Outfitter_WearingSpecialOutfit(pSpecialID)
3891 for vIndex, vOutfit in gOutfitter_OutfitStack do
3892 if vOutfit.SpecialID == pSpecialID then
3893 return true, vIndex;
3894 end
3895 end
3896 end
3897  
3898 function Outfitter_UpdateZone()
3899 local vCurrentZone = GetZoneText();
3900 local vPVPType, vFactionName, vIsArena = GetZonePVPInfo();
3901  
3902 if vCurrentZone == gOutfitter_CurrentZone then
3903 return;
3904 end
3905  
3906 gOutfitter_CurrentZone = vCurrentZone;
3907  
3908 local vZoneSpecialIDMap = Outfitter_cZoneSpecialIDMap[vCurrentZone];
3909 local vSpecialZoneStates = {};
3910  
3911 if vZoneSpecialIDMap then
3912 for _, vZoneSpecialID in vZoneSpecialIDMap do
3913 if vZoneSpecialID ~= "City" or vPVPType ~= "hostile" then
3914 vSpecialZoneStates[vZoneSpecialID] = true;
3915 end
3916 end
3917 end
3918  
3919 for _, vSpecialID in Outfitter_cZoneSpecialIDs do
3920 local vIsActive = vSpecialZoneStates[vSpecialID];
3921  
3922 if vIsActive == nil then
3923 vIsActive = false;
3924 end
3925  
3926 local vCurrentIsActive = gOutfitter_SpecialState[vSpecialID];
3927  
3928 if vCurrentIsActive == nil then
3929 vCurrentIsActive = Outfitter_WearingSpecialOutfit(vSpecialID);
3930 gOutfitter_SpecialState[vSpecialID] = vCurrentIsActive;
3931 end
3932  
3933 if vCurrentIsActive ~= vIsActive then
3934 gOutfitter_SpecialState[vSpecialID] = vIsActive;
3935 Outfitter_SetSpecialOutfitEnabled(vSpecialID, vIsActive);
3936 end
3937 end
3938 end
3939  
3940 function Outfitter_InBattlegroundZone()
3941 local vZoneSpecialIDMap = Outfitter_cZoneSpecialIDMap[gOutfitter_CurrentZone];
3942  
3943 return vZoneSpecialIDMap and vZoneSpecialIDMap[1] == "Battleground";
3944 end
3945  
3946 function Outfitter_SetAllSlotEnables(pEnable)
3947 for _, vInventorySlot in Outfitter_cSlotNames do
3948 Outfitter_SetSlotEnable(vInventorySlot, pEnable);
3949 end
3950  
3951 Outfitter_UpdateOutfitCategory(gOutfitter_SelectedOutfit);
3952 Outfitter_Update(true);
3953 end
3954  
3955 function Outfitter_OutfitIsComplete(pOutfit, pIgnoreAmmoSlot)
3956 for _, vInventorySlot in Outfitter_cSlotNames do
3957 if not pOutfit.Items[vInventorySlot]
3958 and (not pIgnoreAmmoSlot or vInventorySlot ~= "AmmoSlot") then
3959 return false;
3960 end
3961 end
3962  
3963 return true;
3964 end
3965  
3966 function Outfitter_CalculateOutfitCategory(pOutfit)
3967 local vIgnoreAmmoSlot = UnitHasRelicSlot("player");
3968  
3969 if Outfitter_OutfitIsComplete(pOutfit, vIgnoreAmmoSlot) then
3970 return "Complete";
3971 elseif pOutfit.IsAccessory then
3972 return "Accessory";
3973 else
3974 return "Partial";
3975 end
3976 end
3977  
3978 function Outfitter_UpdateOutfitCategory(pOutfit)
3979 if not pOutfit then
3980 return;
3981 end
3982  
3983 local vTargetCategoryID = Outfitter_CalculateOutfitCategory(pOutfit);
3984 local vOutfitCategoryID, vOutfitIndex = Outfitter_FindOutfit(pOutfit);
3985  
3986 -- Don't move special outfits around
3987  
3988 if vOutfitCategoryID == "Special" then
3989 return;
3990 end
3991  
3992 -- Move the outfit if necessary
3993  
3994 if vTargetCategoryID ~= vOutfitCategoryID then
3995 table.remove(gOutfitter_Settings.Outfits[vOutfitCategoryID], vOutfitIndex);
3996 Outfitter_AddOutfit(pOutfit);
3997 end
3998 end
3999  
4000 function Outfitter_DeleteOutfit(pOutfit)
4001 local vWearingOutfit = Outfitter_WearingOutfit(pOutfit);
4002 local vOutfitCategoryID, vOutfitIndex = Outfitter_FindOutfit(pOutfit);
4003  
4004 if not vOutfitCategoryID then
4005 return;
4006 end
4007  
4008 -- Delete the outfit
4009  
4010 table.remove(gOutfitter_Settings.Outfits[vOutfitCategoryID], vOutfitIndex);
4011  
4012 -- Deselect the outfit
4013  
4014 if pOutfit == gOutfitter_SelectedOutfit then
4015 Outfitter_ClearSelection();
4016 end
4017  
4018 -- Remove the outfit if it's being worn
4019  
4020 Outfitter_RemoveOutfit(pOutfit);
4021  
4022 --
4023  
4024 gOutfitter_DisplayIsDirty = true;
4025 end
4026  
4027 function Outfitter_AddOutfit(pOutfit)
4028 local vCategoryID;
4029  
4030 if pOutfit.SpecialID then
4031 vCategoryID = "Special"
4032 else
4033 vCategoryID = Outfitter_CalculateOutfitCategory(pOutfit);
4034 end
4035  
4036 if not gOutfitter_Settings.Outfits then
4037 gOutfitter_Settings.Outfits = {};
4038 end
4039  
4040 if not gOutfitter_Settings.Outfits[vCategoryID] then
4041 gOutfitter_Settings.Outfits[vCategoryID] = {};
4042 end
4043  
4044 table.insert(gOutfitter_Settings.Outfits[vCategoryID], pOutfit);
4045 pOutfit.CategoryID = vCategoryID;
4046  
4047 gOutfitter_DisplayIsDirty = true;
4048  
4049 return vCategoryID;
4050 end
4051  
4052 function Outfitter_SlotEnableClicked(pCheckbox, pButton)
4053 -- If the user is attempting to drop an item put it in the slot for them
4054  
4055 if CursorHasItem() then
4056 local vSlotID, vEmptySlotTexture = GetInventorySlotInfo(pCheckbox.SlotName);
4057 PickupInventoryItem(vSlotID);
4058 return;
4059 end
4060  
4061 --
4062  
4063 local vChecked = pCheckbox:GetChecked();
4064  
4065 if pCheckbox.IsUnknown then
4066 pCheckbox.IsUnknown = false;
4067 pCheckbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check");
4068 vChecked = true;
4069 end
4070  
4071 Outfitter_SetSlotEnable(pCheckbox.SlotName, vChecked);
4072 Outfitter_UpdateOutfitCategory(gOutfitter_SelectedOutfit);
4073 Outfitter_Update(true);
4074 end
4075  
4076 function Outfitter_FindMultipleItemLocation(pItems, pEquippableItems)
4077 for vListIndex, vListItem in pItems do
4078 local vItem = OutfitterItemList_FindItemOrAlt(pEquippableItems, vListItem);
4079  
4080 if vItem then
4081 return vItem, vListItem;
4082 end
4083 end
4084  
4085 return nil, nil;
4086 end
4087  
4088 function Outfitter_FindAndAddItemsToOutfit(pOutfit, pSlotName, pItems, pEquippableItems)
4089 vItemLocation, vItem = Outfitter_FindMultipleItemLocation(pItems, pEquippableItems);
4090  
4091 if vItemLocation then
4092 local vInventorySlot = pSlotName;
4093  
4094 if not vInventorySlot then
4095 vInventorySlot = vItemLocation.ItemSlotName;
4096 end
4097  
4098 Outfitter_AddOutfitItem(pOutfit, vInventorySlot, vItem.Code, vItem.SubCode, vItem.Name, vItem.EnchantCOde);
4099 end
4100 end
4101  
4102 function Outfitter_AddItemsWithStatToOutfit(pOutfit, pStatID, pEquippableItems)
4103 local vItemStats;
4104  
4105 if not pEquippableItems then
4106 return;
4107 end
4108  
4109 for vInventorySlot, vItems in pEquippableItems.ItemsBySlot do
4110 for vIndex, vItem in vItems do
4111 local vStatValue = vItem.Stats[pStatID];
4112  
4113 if vStatValue then
4114 local vSlotName = vItem.MetaSlotName;
4115  
4116 if not vSlotName then
4117 vSlotName = vItem.ItemSlotName;
4118 end
4119  
4120 Outfitter_AddOutfitStatItemIfBetter(pOutfit, vSlotName, vItem.Code, vItem.SubCode, vItem.Name, vItem.EnchantCode, pStatID, vStatValue);
4121 end
4122 end
4123 end
4124  
4125 -- Collapse the meta slots (currently just 2H vs. 1H/OH)
4126  
4127 Outfitter_CollapseMetaSlotsIfBetter(pOutfit, pStatID);
4128 end
4129  
4130 function Outfitter_IsInitialized()
4131 return gOutfitter_Initialized;
4132 end
4133  
4134 function Outfitter_Initialize()
4135 if gOutfitter_Initialized then
4136 return;
4137 end
4138  
4139 --
4140  
4141 if not gOutfitter_Settings then
4142 gOutfitter_Settings = {};
4143 gOutfitter_Settings.Version = 7;
4144 gOutfitter_Settings.Options = {};
4145 gOutfitter_Settings.LastOutfitStack = {};
4146 gOutfitter_Settings.HideHelm = {};
4147 gOutfitter_Settings.HideCloak = {};
4148 end
4149  
4150 if not gOutfitter_Settings.HideHelm then
4151 gOutfitter_Settings.HideHelm = {};
4152 end
4153  
4154 if not gOutfitter_Settings.HideCloak then
4155 gOutfitter_Settings.HideCloak = {};
4156 end
4157  
4158 --
4159  
4160 Outfitter_InitDebugging();
4161  
4162 -- Initialize the outfits and outfit stack
4163  
4164 gOutfitter_CurrentOutfit = Outfitter_GetInventoryOutfit();
4165  
4166 if not gOutfitter_Settings.Outfits then
4167 Outfitter_InitializeOutfits();
4168 end
4169  
4170 Outfitter_CheckDatabase();
4171  
4172 Outfitter_InitializeSpecialOccassionOutfits(); -- Make sure the special occassion outfits are intact
4173 -- since the user has no way of creating them himself
4174 OutfitterStack_RestoreSavedStack();
4175  
4176 -- Set the minimap button
4177  
4178 if gOutfitter_Settings.Options.HideMinimapButton then
4179 OutfitterMinimapButton:Hide();
4180 else
4181 OutfitterMinimapButton:Show();
4182 end
4183  
4184 if not gOutfitter_Settings.Options.MinimapButtonAngle then
4185 gOutfitter_Settings.Options.MinimapButtonAngle = -1.5708;
4186 end
4187  
4188 OutfitterMinimapButton_SetPositionAngle(gOutfitter_Settings.Options.MinimapButtonAngle);
4189  
4190 -- Hook QuickSlots into the paper doll frame
4191  
4192 Outfitter_HookPaperDollFrame();
4193  
4194 -- Done initializing
4195  
4196 gOutfitter_Initialized = true;
4197  
4198 -- Make sure the outfit state is good
4199  
4200 Outfitter_SetSpecialOutfitEnabled("Riding", false);
4201 Outfitter_SetSpecialOutfitEnabled("Spirit", false);
4202 Outfitter_UpdateAuraStates();
4203  
4204 Outfitter_ResumeLoadScreenEvents();
4205  
4206 Outfitter_DispatchOutfitEvent("OUTFITTER_INIT")
4207 end
4208  
4209 function Outfitter_InitializeOutfits()
4210 local vOutfit, vItemLocation, vItem;
4211 local vEquippableItems = OutfitterItemList_GetEquippableItems(true);
4212  
4213 -- Create the outfit categories
4214  
4215 gOutfitter_Settings.Outfits = {};
4216  
4217 for vCategoryIndex, vCategoryID in gOutfitter_cCategoryOrder do
4218 gOutfitter_Settings.Outfits[vCategoryID] = {};
4219 end
4220  
4221 -- Create the normal outfit using the current
4222 -- inventory and set it as the currently equipped outfit
4223  
4224 vOutfit = Outfitter_GetInventoryOutfit(Outfitter_cNormalOutfit);
4225 Outfitter_AddOutfit(vOutfit);
4226 gOutfitter_Settings.LastOutfitStack = {{Name = Outfitter_cNormalOutfit}};
4227 gOutfitter_OutfitStack = {vOutfit};
4228  
4229 -- Create the naked outfit
4230  
4231 vOutfit = Outfitter_NewNakedOutfit(Outfitter_cNakedOutfit);
4232 Outfitter_AddOutfit(vOutfit);
4233  
4234 -- Generate the smart outfits
4235  
4236 for vSmartIndex, vSmartOutfit in Outfitter_cSmartOutfits do
4237 vOutfit = Outfitter_GenerateSmartOutfit(vSmartOutfit.Name, vSmartOutfit.StatID, vEquippableItems);
4238  
4239 if vOutfit then
4240 vOutfit.IsAccessory = vSmartOutfit.IsAccessory;
4241 Outfitter_AddOutfit(vOutfit);
4242 end
4243 end
4244  
4245 Outfitter_InitializeSpecialOccassionOutfits();
4246 end
4247  
4248 function Outfitter_CreateEmptySpecialOccassionOutfit(pSpecialID, pName)
4249 vOutfit = Outfitter_GetSpecialOutfit(pSpecialID);
4250  
4251 if not vOutfit then
4252 vOutfit = Outfitter_NewEmptyOutfit(pName);
4253 vOutfit.SpecialID = pSpecialID;
4254  
4255 Outfitter_AddOutfit(vOutfit);
4256 end
4257 end
4258  
4259 function Outfitter_InitializeSpecialOccassionOutfits()
4260 local vEquippableItems = OutfitterItemList_GetEquippableItems(true);
4261 local vOutfit;
4262  
4263 -- Find an argent dawn trinket and set the argent dawn outfit
4264  
4265 vOutfit = Outfitter_GetSpecialOutfit("ArgentDawn");
4266  
4267 if not vOutfit then
4268 vOutfit = Outfitter_GenerateSmartOutfit(Outfitter_cArgentDawnOutfit, "ArgentDawn", vEquippableItems, true);
4269 vOutfit.SpecialID = "ArgentDawn";
4270 Outfitter_AddOutfit(vOutfit);
4271 end
4272  
4273 -- Find riding items
4274  
4275 vOutfit = Outfitter_GetSpecialOutfit("Riding");
4276  
4277 if not vOutfit then
4278 vOutfit = Outfitter_GenerateSmartOutfit(Outfitter_cRidingOutfit, "Riding", vEquippableItems, true);
4279 vOutfit.SpecialID = "Riding";
4280 vOutfit.BGDisabled = true;
4281 Outfitter_AddOutfit(vOutfit);
4282 end
4283  
4284 -- Create the dining outfit
4285  
4286 Outfitter_CreateEmptySpecialOccassionOutfit("Dining", Outfitter_cDiningOutfit);
4287  
4288 -- Create the Battlegrounds outfits
4289  
4290 Outfitter_CreateEmptySpecialOccassionOutfit("Battleground", Outfitter_cBattlegroundOutfit);
4291 Outfitter_CreateEmptySpecialOccassionOutfit("AB", Outfitter_cABOutfit);
4292 Outfitter_CreateEmptySpecialOccassionOutfit("AV", Outfitter_cAVOutfit);
4293 Outfitter_CreateEmptySpecialOccassionOutfit("WSG", Outfitter_cWSGOutfit);
4294  
4295 -- Create the city outfit
4296  
4297 Outfitter_CreateEmptySpecialOccassionOutfit("City", Outfitter_cCityOutfit);
4298  
4299 -- Create class-specific outfits
4300  
4301 Outfitter_InitializeClassOutfits();
4302 end
4303  
4304 function Outfitter_InitializeClassOutfits()
4305 local vClassName = Outfitter_cNormalizedClassName[UnitClass("player")];
4306 local vOutfits = Outfitter_cClassSpecialOutfits[vClassName];
4307  
4308 if not vOutfits then
4309 return;
4310 end
4311  
4312 for vIndex, vOutfitInfo in vOutfits do
4313 Outfitter_CreateEmptySpecialOccassionOutfit(vOutfitInfo.SpecialID, vOutfitInfo.Name);
4314 end
4315 end
4316  
4317 function Outfitter_IsStatText(pText)
4318 for vStatIndex, vStatInfo in Outfitter_cItemStatFormats do
4319 local vStartIndex, vEndIndex, vValue = string.find(pText, vStatInfo.Format);
4320  
4321 if vStartIndex then
4322 vValue = tonumber(vValue);
4323  
4324 if not vValue then
4325 vValue = vStatInfo.Value;
4326 end
4327  
4328 if not vValue then
4329 vValue = 0;
4330 end
4331  
4332 return vStatInfo.Types, vValue;
4333 end
4334 end
4335  
4336 return nil, nil;
4337 end
4338  
4339 function Outfitter_GetItemStatsFromTooltip(pTooltip, pDistribution)
4340 local vStats = {};
4341 local vTooltipName = pTooltip:GetName();
4342  
4343 for vLineIndex = 1, 30 do
4344 local vLeftText = getglobal(vTooltipName.."TextLeft"..vLineIndex):GetText();
4345 -- local vRightText = getglobal(vTooltipName.."TextRight"..vLineIndex):GetText();
4346  
4347 if vLeftText then
4348 -- Check for the start of the set bonus section
4349  
4350 local vStartIndex, vEndIndex, vValue = string.find(vLeftText, "%(%d/%d%)");
4351  
4352 if vStartIndex then
4353 break;
4354 end
4355  
4356 --
4357  
4358 for vStatString in string.gfind(vLeftText, "([^/]+)") do
4359 local vStatIDs, vValue = Outfitter_IsStatText(vStatString);
4360  
4361 if vStatIDs then
4362 for vStatIDIndex, vStatID in vStatIDs do
4363 OutfitterStats_AddStatValue(vStats, vStatID, vValue, pDistribution);
4364 end
4365 end
4366 end
4367 end
4368 end -- for vLineIndex
4369  
4370 return vStats;
4371 end
4372  
4373 function Outfitter_TooltipContainsText(pTooltip, pText)
4374 local vTooltipName = pTooltip:GetName();
4375  
4376 for vLineIndex = 1, 30 do
4377 local vLeftText = getglobal(vTooltipName.."TextLeft"..vLineIndex):GetText();
4378  
4379 if vLeftText
4380 and string.find(vLeftText, pText) then
4381 return true;
4382 end
4383 end -- for vLineIndex
4384  
4385 return false;
4386 end
4387  
4388 function Outfitter_CanEquipBagItem(pBagIndex, pBagSlotIndex)
4389 local vItemInfo = Outfitter_GetBagItemInfo(pBagIndex, pBagSlotIndex);
4390  
4391 if vItemInfo
4392 and vItemInfo.Level
4393 and UnitLevel("player") < vItemInfo.Level then
4394 return false;
4395 end
4396  
4397 return true;
4398 end
4399  
4400 function Outfitter_BagItemWillBind(pBagIndex, pBagSlotIndex)
4401 local vItemLink = GetContainerItemLink(pBagIndex, pBagSlotIndex);
4402  
4403 if not vItemLink then
4404 return nil;
4405 end
4406  
4407 OutfitterTooltip:SetOwner(OutfitterFrame, "ANCHOR_BOTTOMRIGHT", 0, 0);
4408 OutfitterTooltip:SetBagItem(pBagIndex, pBagSlotIndex);
4409  
4410 local vIsBOE = Outfitter_TooltipContainsText(OutfitterTooltip, ITEM_BIND_ON_EQUIP);
4411  
4412 OutfitterTooltip:Hide();
4413  
4414 return vIsBOE;
4415 end
4416  
4417 function Outfitter_GenerateSmartOutfit(pName, pStatID, pEquippableItems, pAllowEmptyOutfit)
4418 local vOutfit = Outfitter_NewEmptyOutfit(pName);
4419  
4420 if pStatID == "TANKPOINTS" then
4421 return;
4422 end
4423  
4424 local vItems = Outfitter_cStatIDItems[pStatID];
4425  
4426 OutfitterItemList_ResetIgnoreItemFlags(pEquippableItems);
4427  
4428 if vItems then
4429 Outfitter_FindAndAddItemsToOutfit(vOutfit, nil, vItems, pEquippableItems);
4430 end
4431  
4432 Outfitter_AddItemsWithStatToOutfit(vOutfit, pStatID, pEquippableItems);
4433  
4434 if not pAllowEmptyOutfit
4435 and Outfitter_IsEmptyOutfit(vOutfit) then
4436 return nil;
4437 end
4438  
4439 vOutfit.StatID = pStatID;
4440  
4441 return vOutfit;
4442 end
4443  
4444 function Outfitter_ArrayIsEmpty(pArray)
4445 if not pArray then
4446 return true;
4447 end
4448  
4449 for vIndex, vValue in pArray do
4450 return false;
4451 end
4452  
4453 return true;
4454 end
4455  
4456 function OutfitterNameOutfit_Open(pOutfit)
4457 gOutfitter_OutfitToRename = pOutfit;
4458  
4459 if gOutfitter_OutfitToRename then
4460 OutfitterNameOutfitDialogTitle:SetText(Outfitter_cRenameOutfit);
4461 OutfitterNameOutfitDialogName:SetText(gOutfitter_OutfitToRename.Name);
4462 OutfitterNameOutfitDialogCreateUsing:Hide();
4463 OutfitterNameOutfitDialog:SetHeight(OutfitterNameOutfitDialog.baseHeight - 35);
4464 else
4465 OutfitterNameOutfitDialogTitle:SetText(Outfitter_cNewOutfit);
4466 OutfitterNameOutfitDialogName:SetText("");
4467 OutfitterDropDown_SetSelectedValue(OutfitterNameOutfitDialogCreateUsing, 0);
4468 OutfitterNameOutfitDialogCreateUsing:Show();
4469 OutfitterNameOutfitDialog:SetHeight(OutfitterNameOutfitDialog.baseHeight);
4470 OutfitterNameOutfitDialogCreateUsing.ChangedValueFunc = OutfitterNameOutfit_CheckForStatOutfit;
4471 end
4472  
4473 OutfitterNameOutfitDialog:Show();
4474 OutfitterNameOutfitDialogName:SetFocus();
4475 end
4476  
4477 function OutfitterNameOutfit_CheckForStatOutfit(pMenu, pValue)
4478 OutfitterNameOutfit_Update(true);
4479 end
4480  
4481 function OutfitterNameOutfit_Done()
4482 local vName = OutfitterNameOutfitDialogName:GetText();
4483  
4484 if vName
4485 and vName ~= "" then
4486 if gOutfitter_OutfitToRename then
4487 local vWearingOutfit = Outfitter_WearingOutfit(gOutfitter_OutfitToRename);
4488  
4489 if vWearingOutfit then
4490 Outfitter_DispatchOutfitEvent("UNWEAR_OUTFIT", gOutfitter_OutfitToRename.Name, gOutfitter_OutfitToRename)
4491 end
4492  
4493 gOutfitter_OutfitToRename.Name = vName;
4494 gOutfitter_DisplayIsDirty = true;
4495  
4496 if vWearingOutfit then
4497 Outfitter_DispatchOutfitEvent("WEAR_OUTFIT", gOutfitter_OutfitToRename.Name, gOutfitter_OutfitToRename)
4498 end
4499 else
4500 -- New outift
4501  
4502 local vStatID = UIDropDownMenu_GetSelectedValue(OutfitterNameOutfitDialogCreateUsing);
4503 local vOutfit;
4504  
4505 if not vStatID
4506 or vStatID == 0 then
4507 vOutfit = Outfitter_GetInventoryOutfit(vName);
4508 elseif vStatID == "EMPTY" then
4509 vOutfit = Outfitter_NewEmptyOutfit(vName);
4510 else
4511 vOutfit = Outfitter_GenerateSmartOutfit(vName, vStatID, OutfitterItemList_GetEquippableItems(true));
4512 end
4513  
4514 if not vOutfit then
4515 vOutfit = Outfitter_NewEmptyOutfit(vName);
4516 end
4517  
4518 local vCategoryID = Outfitter_AddOutfit(vOutfit);
4519  
4520 Outfitter_WearOutfit(vOutfit, vCategoryID);
4521 end
4522 end
4523  
4524 OutfitterNameOutfitDialog:Hide();
4525  
4526 Outfitter_Update(true);
4527 end
4528  
4529 function OutfitterNameOutfit_Cancel()
4530 OutfitterNameOutfitDialog:Hide();
4531 end
4532  
4533 function OutfitterNameOutfit_Update(pCheckForStatOutfit)
4534 local vEnableDoneButton = true;
4535 local vErrorMessage = nil;
4536  
4537 -- If there's no name entered then disable the okay button
4538  
4539 local vName = OutfitterNameOutfitDialogName:GetText();
4540  
4541 if not vName
4542 or vName == "" then
4543 vEnableDoneButton = false;
4544 else
4545 local vOutfit = Outfitter_FindOutfitByName(vName);
4546  
4547 if vOutfit
4548 and vOutfit ~= gOutfitter_OutfitToRename then
4549 vErrorMessage = Outfitter_cNameAlreadyUsedError;
4550 vEnableDoneButton = false;
4551 end
4552 end
4553  
4554 --
4555  
4556 if not vErrorMessage
4557 and pCheckForStatOutfit then
4558 local vStatID = UIDropDownMenu_GetSelectedValue(OutfitterNameOutfitDialogCreateUsing);
4559  
4560 if vStatID
4561 and vStatID ~= 0
4562 and vStatID ~= "EMPTY" then
4563 local vOutfit = Outfitter_GenerateSmartOutfit("temp outfit", vStatID, OutfitterItemList_GetEquippableItems(true));
4564  
4565 if not vOutfit
4566 or Outfitter_IsEmptyOutfit(vOutfit) then
4567 vErrorMessage = Outfitter_cNoItemsWithStatError;
4568 end
4569 end
4570 end
4571  
4572 if vErrorMessage then
4573 OutfitterNameOutfitDialogError:SetText(vErrorMessage);
4574 OutfitterNameOutfitDialogError:Show();
4575 else
4576 OutfitterNameOutfitDialogError:Hide();
4577 end
4578  
4579 Outfitter_SetButtonEnable(OutfitterNameOutfitDialogDoneButton, vEnableDoneButton);
4580 end
4581  
4582 function Outfitter_SetButtonEnable(pButton, pEnabled)
4583 if pEnabled then
4584 pButton:Enable();
4585 pButton:SetAlpha(1.0);
4586 pButton:EnableMouse(true);
4587 --getglobal(pButton:GetName().."Text"):SetAlpha(1.0);
4588 else
4589 pButton:Disable();
4590 pButton:SetAlpha(0.7);
4591 pButton:EnableMouse(false);
4592 --getglobal(pButton:GetName().."Text"):SetAlpha(0.7);
4593 end
4594 end
4595  
4596 function Outfitter_GetOutfitFromListItem(pItem)
4597 if pItem.isCategory then
4598 return nil;
4599 end
4600  
4601 if not gOutfitter_Settings.Outfits then
4602 return nil;
4603 end
4604  
4605 local vOutfits = gOutfitter_Settings.Outfits[pItem.categoryID];
4606  
4607 if not vOutfits then
4608 -- Error: outfit category not found
4609 return nil;
4610 end
4611  
4612 return vOutfits[pItem.outfitIndex], pItem.categoryID;
4613 end
4614  
4615 function Outfitter_OutfitItemSelected(pMenu, pValue)
4616 local vItem = pMenu:GetParent():GetParent();
4617 local vOutfit, vCategoryID = Outfitter_GetOutfitFromListItem(vItem);
4618  
4619 if not vOutfit then
4620 Outfitter_ErrorMessage("Outfitter Error: Outfit for menu item "..vItem:GetName().." not found");
4621 return;
4622 end
4623  
4624 -- Perform the selected action
4625  
4626 if pValue == "DELETE" then
4627 Outfitter_AskDeleteOutfit(vOutfit);
4628 elseif pValue == "RENAME" then
4629 OutfitterNameOutfit_Open(vOutfit);
4630 elseif pValue == "DISABLE" then
4631 if vOutfit.Disabled then
4632 vOutfit.Disabled = nil;
4633 else
4634 vOutfit.Disabled = true;
4635 end
4636 gOutfitter_DisplayIsDirty = true;
4637 elseif pValue == "BGDISABLE" then
4638 if vOutfit.BGDisabled then
4639 vOutfit.BGDisabled = nil;
4640 else
4641 vOutfit.BGDisabled = true;
4642 end
4643 gOutfitter_DisplayIsDirty = true;
4644 elseif pValue == "ACCESSORY" then
4645 vOutfit.IsAccessory = true;
4646 Outfitter_UpdateOutfitCategory(vOutfit);
4647 elseif pValue == "PARTIAL" then
4648 vOutfit.IsAccessory = nil;
4649 Outfitter_UpdateOutfitCategory(vOutfit);
4650 elseif string.sub(pValue, 1, 7) == "BINDING" then
4651 Outfitter_SetOutfitBindingIndex(vOutfit, tonumber(string.sub(pValue, 8)));
4652 elseif pValue == "REBUILD" then
4653 Outfitter_AskRebuildOutfit(vOutfit, vCategoryID);
4654 elseif pValue == "DEPOSIT" then
4655 Outfitter_DepositOutfit(vOutfit);
4656 elseif pValue == "DEPOSITUNIQUE" then
4657 Outfitter_DepositOutfit(vOutfit, true);
4658 elseif pValue == "WITHDRAW" then
4659 Outfitter_WithdrawOutfit(vOutfit);
4660 end
4661  
4662 Outfitter_Update(true);
4663 end
4664  
4665 function OutfitterStatDropdown_OnLoad()
4666 UIDropDownMenu_Initialize(this, OutfitterStatDropdown_Initialize);
4667 UIDropDownMenu_SetWidth(150);
4668 UIDropDownMenu_Refresh(this);
4669 end
4670  
4671 function Outfitter_GetStatIDName(pStatID)
4672 for vStatIndex, vStatInfo in Outfitter_cItemStatInfo do
4673 if vStatInfo.ID == pStatID then
4674 return vStatInfo.Name;
4675 end
4676 end
4677  
4678 return nil;
4679 end
4680  
4681 function OutfitterStatDropdown_Initialize()
4682 local vFrame = getglobal(UIDROPDOWNMENU_INIT_MENU);
4683  
4684 if UIDROPDOWNMENU_MENU_LEVEL == 2 then
4685 for vStatIndex, vStatInfo in Outfitter_cItemStatInfo do
4686 if vStatInfo.Category == UIDROPDOWNMENU_MENU_VALUE then
4687 UIDropDownMenu_AddButton({text = vStatInfo.Name, value = vStatInfo.ID, owner = vFrame, func = OutfitterDropDown_OnClick}, UIDROPDOWNMENU_MENU_LEVEL);
4688 end
4689 end
4690 else
4691 UIDropDownMenu_AddButton({text = Outfitter_cUseCurrentOutfit, value = 0, owner = vFrame, func = OutfitterDropDown_OnClick});
4692 UIDropDownMenu_AddButton({text = Outfitter_cUseEmptyOutfit, value = "EMPTY", owner = vFrame, func = OutfitterDropDown_OnClick});
4693  
4694 UIDropDownMenu_AddButton({text = " ", notCheckable = true, notClickable = true});
4695  
4696 for vCategoryIndex, vCategoryInfo in Outfitter_cStatCategoryInfo do
4697 UIDropDownMenu_AddButton({text = vCategoryInfo.Name, owner = vFrame, hasArrow = 1, value = vCategoryInfo.Category});
4698 end
4699  
4700 if false and IsAddOnLoaded("TankPoints") then
4701 UIDropDownMenu_AddButton({text = " ", notCheckable = true, notClickable = true});
4702 UIDropDownMenu_AddButton({text = Outfitter_cTankPoints, value="TANKPOINTS", owner = vFrame, func = OutfitterDropDown_OnClick});
4703 end
4704 end
4705 end
4706  
4707 function OutfitterDropDown_SetSelectedValue(pDropDown, pValue)
4708 UIDropDownMenu_SetText("", pDropDown); -- Set to empty in case the selected value isn't there
4709  
4710 UIDropDownMenu_Initialize(pDropDown, pDropDown.initialize);
4711 UIDropDownMenu_SetSelectedValue(pDropDown, pValue);
4712  
4713 -- All done if the item text got set successfully
4714  
4715 local vItemText = UIDropDownMenu_GetText(pDropDown);
4716  
4717 if vItemText and vItemText ~= "" then
4718 return;
4719 end
4720  
4721 -- Scan for submenus
4722  
4723 local vRootListFrameName = "DropDownList1";
4724 local vRootListFrame = getglobal(vRootListFrameName);
4725 local vRootNumItems = vRootListFrame.numButtons;
4726  
4727 for vRootItemIndex = 1, vRootNumItems do
4728 local vItem = getglobal(vRootListFrameName.."Button"..vRootItemIndex);
4729  
4730 if vItem.hasArrow then
4731 local vSubMenuFrame = getglobal("DropDownList2");
4732  
4733 UIDROPDOWNMENU_OPEN_MENU = pDropDown:GetName();
4734 UIDROPDOWNMENU_MENU_VALUE = vItem.value;
4735 UIDROPDOWNMENU_MENU_LEVEL = 2;
4736  
4737 UIDropDownMenu_Initialize(pDropDown, pDropDown.initialize, nil, 2);
4738 UIDropDownMenu_SetSelectedValue(pDropDown, pValue);
4739  
4740 -- All done if the item text got set successfully
4741  
4742 local vItemText = UIDropDownMenu_GetText(pDropDown);
4743  
4744 if vItemText and vItemText ~= "" then
4745 return;
4746 end
4747  
4748 -- Switch back to the root menu
4749  
4750 UIDROPDOWNMENU_OPEN_MENU = nil;
4751 UIDropDownMenu_Initialize(pDropDown, pDropDown.initialize, nil, 1);
4752 end
4753 end
4754 end
4755  
4756 function OutfitterScrollbarTrench_SizeChanged(pScrollbarTrench)
4757 local vScrollbarTrenchName = pScrollbarTrench:GetName();
4758 local vScrollbarTrenchMiddle = getglobal(vScrollbarTrenchName.."Middle");
4759  
4760 local vMiddleHeight= pScrollbarTrench:GetHeight() - 51;
4761 vScrollbarTrenchMiddle:SetHeight(vMiddleHeight);
4762 end
4763  
4764 function OutfitterInputBox_OnLoad(pChildDepth)
4765 if not pChildDepth then
4766 pChildDepth = 0;
4767 end
4768  
4769 local vParent = this:GetParent();
4770  
4771 for vDepthIndex = 1, pChildDepth do
4772 vParent = vParent:GetParent();
4773 end
4774  
4775 if vParent.lastEditBox then
4776 this.prevEditBox = vParent.lastEditBox;
4777 this.nextEditBox = vParent.lastEditBox.nextEditBox;
4778  
4779 this.prevEditBox.nextEditBox = this;
4780 this.nextEditBox.prevEditBox = this;
4781 else
4782 this.prevEditBox = this;
4783 this.nextEditBox = this;
4784 end
4785  
4786 vParent.lastEditBox = this;
4787 end
4788  
4789 function OutfitterInputBox_TabPressed()
4790 local vReverse = IsShiftKeyDown();
4791 local vEditBox = this;
4792  
4793 for vIndex = 1, 50 do
4794 local vNextEditBox;
4795  
4796 if vReverse then
4797 vNextEditBox = vEditBox.prevEditBox;
4798 else
4799 vNextEditBox = vEditBox.nextEditBox;
4800 end
4801  
4802 if vNextEditBox:IsVisible()
4803 and not vNextEditBox.isDisabled then
4804 vNextEditBox:SetFocus();
4805 return;
4806 end
4807  
4808 vEditBox = vNextEditBox;
4809 end
4810 end
4811  
4812 function OutfitterTimer_AdjustTimer()
4813 local vNeedTimer = false;
4814  
4815 if OutfitterMinimapButton.IsDragging then
4816 vNeedTimer = true;
4817 end
4818  
4819 if gOutfitter_EquippedNeedsUpdate
4820 or gOutfitter_WeaponsNeedUpdate then
4821 vNeedTimer = true;
4822 end
4823  
4824 if vNeedTimer then
4825 OutfitterUpdateFrame:Show();
4826 else
4827 OutfitterUpdateFrame:Hide();
4828 OutfitterUpdateFrame.Elapsed = nil;
4829 end
4830 end
4831  
4832 function OutfitterUpdateFrame_OnUpdate(pElapsed)
4833 if OutfitterMinimapButton.IsDragging then
4834 OutfitterMinimapButton_UpdateDragPosition();
4835 end
4836  
4837 if not OutfitterUpdateFrame.Elapsed then
4838 OutfitterUpdateFrame.Elapsed = 0;
4839 else
4840 OutfitterUpdateFrame.Elapsed = OutfitterUpdateFrame.Elapsed + pElapsed;
4841  
4842 if OutfitterUpdateFrame.Elapsed > 0.25 then
4843 Outfitter_UpdateEquippedItems();
4844 OutfitterUpdateFrame.Elapsed = 0;
4845 end
4846 end
4847  
4848 OutfitterTimer_AdjustTimer();
4849 end
4850  
4851 function OutfitterMinimapButton_MouseDown()
4852 -- Remember where the cursor was in case the user drags
4853  
4854 local vCursorX, vCursorY = GetCursorPosition();
4855  
4856 vCursorX = vCursorX / this:GetEffectiveScale();
4857 vCursorY = vCursorY / this:GetEffectiveScale();
4858  
4859 OutfitterMinimapButton.CursorStartX = vCursorX;
4860 OutfitterMinimapButton.CursorStartY = vCursorY;
4861  
4862 local vCenterX, vCenterY = OutfitterMinimapButton:GetCenter();
4863 local vMinimapCenterX, vMinimapCenterY = Minimap:GetCenter();
4864  
4865 OutfitterMinimapButton.CenterStartX = vCenterX - vMinimapCenterX;
4866 OutfitterMinimapButton.CenterStartY = vCenterY - vMinimapCenterY;
4867 end
4868  
4869 function OutfitterMinimapButton_DragStart()
4870 OutfitterMinimapButton.IsDragging = true;
4871 OutfitterTimer_AdjustTimer();
4872 end
4873  
4874 function OutfitterMinimapButton_DragEnd()
4875 OutfitterMinimapButton.IsDragging = false;
4876 OutfitterTimer_AdjustTimer();
4877 end
4878  
4879 function OutfitterMinimapButton_UpdateDragPosition()
4880 -- Remember where the cursor was in case the user drags
4881  
4882 local vCursorX, vCursorY = GetCursorPosition();
4883  
4884 vCursorX = vCursorX / this:GetEffectiveScale();
4885 vCursorY = vCursorY / this:GetEffectiveScale();
4886  
4887 local vCursorDeltaX = vCursorX - OutfitterMinimapButton.CursorStartX;
4888 local vCursorDeltaY = vCursorY - OutfitterMinimapButton.CursorStartY;
4889  
4890 --
4891  
4892 local vCenterX = OutfitterMinimapButton.CenterStartX + vCursorDeltaX;
4893 local vCenterY = OutfitterMinimapButton.CenterStartY + vCursorDeltaY;
4894  
4895 -- Calculate the angle
4896  
4897 local vAngle = math.atan2(vCenterX, vCenterY);
4898  
4899 -- Set the new position
4900  
4901 OutfitterMinimapButton_SetPositionAngle(vAngle);
4902 end
4903  
4904 function Outfitter_RestrictAngle(pAngle, pRestrictStart, pRestrictEnd)
4905 if pAngle <= pRestrictStart
4906 or pAngle >= pRestrictEnd then
4907 return pAngle;
4908 end
4909  
4910 local vDistance = (pAngle - pRestrictStart) / (pRestrictEnd - pRestrictStart);
4911  
4912 if vDistance > 0.5 then
4913 return pRestrictEnd;
4914 else
4915 return pRestrictStart;
4916 end
4917 end
4918  
4919 function OutfitterMinimapButton_SetPositionAngle(pAngle)
4920 local vAngle = pAngle;
4921  
4922 -- Restrict the angle from going over the date/time icon or the zoom in/out icons
4923  
4924 local vRestrictedStartAngle = nil;
4925 local vRestrictedEndAngle = nil;
4926  
4927 if GameTimeFrame:IsVisible() then
4928 if MinimapZoomIn:IsVisible()
4929 or MinimapZoomOut:IsVisible() then
4930 vAngle = Outfitter_RestrictAngle(vAngle, 0.4302272732931596, 2.930420793963121);
4931 else
4932 vAngle = Outfitter_RestrictAngle(vAngle, 0.4302272732931596, 1.720531504573905);
4933 end
4934  
4935 elseif MinimapZoomIn:IsVisible()
4936 or MinimapZoomOut:IsVisible() then
4937 vAngle = Outfitter_RestrictAngle(vAngle, 1.720531504573905, 2.930420793963121);
4938 end
4939  
4940 -- Restrict it from the tracking icon area
4941  
4942 vAngle = Outfitter_RestrictAngle(vAngle, -1.290357134304173, -0.4918423429923585);
4943  
4944 --
4945  
4946 local vRadius = 80;
4947  
4948 vCenterX = math.sin(vAngle) * vRadius;
4949 vCenterY = math.cos(vAngle) * vRadius;
4950  
4951 OutfitterMinimapButton:SetPoint("CENTER", "Minimap", "CENTER", vCenterX - 1, vCenterY - 1);
4952  
4953 gOutfitter_Settings.Options.MinimapButtonAngle = vAngle;
4954 end
4955  
4956 function OutfitterMinimapButton_ItemSelected(pMenu, pValue)
4957 local vType = type(pValue);
4958  
4959 if vType == "table" then
4960 local vCategoryID = pValue.CategoryID;
4961 local vIndex = pValue.Index;
4962 local vOutfit = gOutfitter_Settings.Outfits[vCategoryID][vIndex];
4963 local vDoToggle = vCategoryID ~= "Complete";
4964  
4965 if vDoToggle
4966 and Outfitter_WearingOutfit(vOutfit) then
4967 Outfitter_RemoveOutfit(vOutfit);
4968 else
4969 Outfitter_WearOutfit(vOutfit, vCategoryID);
4970 end
4971  
4972 if vDoToggle then
4973 return true;
4974 end
4975 else
4976 if pValue == 0 then -- Open Outfitter
4977 ShowUIPanel(CharacterFrame);
4978 CharacterFrame_ShowSubFrame("PaperDollFrame");
4979 OutfitterFrame:Show();
4980 end
4981 end
4982  
4983 return false;
4984 end
4985  
4986 function Outfitter_WearingOutfit(pOutfit)
4987 return OutfitterStack_FindOutfit(pOutfit);
4988 end
4989  
4990 function Outfitter_GetCurrentOutfitInfo()
4991 if not gOutfitter_Initialized then
4992 return "", nil;
4993 end
4994  
4995 local vStackLength = table.getn(gOutfitter_OutfitStack);
4996  
4997 if vStackLength == 0 then
4998 return "", nil;
4999 end
5000  
5001 local vOutfit = gOutfitter_OutfitStack[vStackLength];
5002  
5003 if vOutfit and vOutfit.Name then
5004 return vOutfit.Name, vOutfit;
5005 else
5006 return Outfitter_cCustom, vOutfit;
5007 end
5008 end
5009  
5010 function Outfitter_CheckDatabase()
5011 local vOutfit;
5012  
5013 if not gOutfitter_Settings.Version then
5014 local vOutfits = gOutfitter_Settings.Outfits[vCategoryID];
5015  
5016 if gOutfitter_Settings.Outfits then
5017 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
5018 for vIndex, vOutfit in vOutfits do
5019 if Outfitter_OutfitIsComplete(vOutfit, true) then
5020 Outfitter_AddOutfitItem(vOutfit, "AmmoSlot", 0, 0, "", 0);
5021 end
5022 end
5023 end
5024 end
5025  
5026 gOutfitter_Settings.Version = 1;
5027 end
5028  
5029 -- Versions 1 and 2 both simply add class outfits
5030 -- so just reinitialize those
5031  
5032 if gOutfitter_Settings.Version < 3 then
5033 Outfitter_InitializeClassOutfits();
5034 gOutfitter_Settings.Version = 3;
5035 end
5036  
5037 -- Version 4 sets the BGDisabled flag for the mounted outfit
5038  
5039 if gOutfitter_Settings.Version < 4 then
5040 local vRidingOutfit = Outfitter_GetSpecialOutfit("Riding");
5041  
5042 if vRidingOutfit then
5043 vRidingOutfit.BGDisabled = true;
5044 end
5045  
5046 gOutfitter_Settings.Version = 4;
5047 end
5048  
5049 -- Version 5 adds moonkin form, just reinitialize class outfits
5050  
5051 if gOutfitter_Settings.Version < 5 then
5052 Outfitter_InitializeClassOutfits();
5053 gOutfitter_Settings.Version = 5;
5054 end
5055  
5056 -- Make sure all outfits have an associated category ID
5057  
5058 if gOutfitter_Settings.Outfits then
5059 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
5060 for vIndex, vOutfit in vOutfits do
5061 vOutfit.CategoryID = vCategoryID;
5062 end
5063 end
5064 end
5065  
5066 -- Version 6 and 7 adds item sub-code and enchantment codes
5067 -- (7 tries to clean up failed updates from 6)
5068  
5069 if gOutfitter_Settings.Version < 7 then
5070 if not Outfitter_UpdateDatabaseItemCodes() then
5071 gOutfitter_NeedItemCodesUpdated = 5; -- Do up to five attempts at updated the item codes
5072 end
5073  
5074 gOutfitter_Settings.Version = 7;
5075 end
5076  
5077 -- Scan the outfits and make sure everything is in order
5078  
5079 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
5080 for vIndex, vOutfit in vOutfits do
5081 Outfitter_CheckOutfit(vOutfit);
5082 end
5083 end
5084 end
5085  
5086 function Outfitter_CheckOutfit(pOutfit)
5087 if not pOutfit.Name then
5088 pOutfit.Name = "Damaged outfit";
5089 end
5090  
5091 if not pOutfit.Items then
5092 pOutfit.Items = {};
5093 end
5094  
5095 for vInventorySlot, vItem in pOutfit.Items do
5096 if not vItem.Code then
5097 vItem.Code = 0;
5098 end
5099  
5100 if not vItem.SubCode then
5101 vItem.SubCode = 0;
5102 end
5103  
5104 if not vItem.Name then
5105 vItem.Name = "";
5106 end
5107  
5108 if not vItem.EnchantCode then
5109 vItem.EnchantCode = 0;
5110 end
5111 end
5112 end
5113  
5114 function Outfitter_UpdateDatabaseItemCodes()
5115 local vEquippableItems = OutfitterItemList_GetEquippableItems();
5116 local vResult = true;
5117  
5118 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
5119 for vIndex, vOutfit in vOutfits do
5120 for vInventorySlot, vOutfitItem in vOutfit.Items do
5121 if vOutfitItem.Code ~= 0 then
5122 local vItem = OutfitterItemList_FindItemOrAlt(vEquippableItems, vOutfitItem, false, true);
5123  
5124 if vItem then
5125 vOutfitItem.SubCode = vItem.SubCode;
5126 vOutfitItem.Name = vItem.Name;
5127 vOutfitItem.EnchantCode = vItem.EnchantCode;
5128 vOutfitItem.Checksum = nil;
5129 else
5130 vResult = false;
5131 end
5132 end
5133 end
5134 end
5135 end
5136  
5137 return vResult;
5138 end
5139  
5140 local gOutfitter_PaperDollItemSlotButton_OnClick;
5141  
5142 function Outfitter_HookPaperDollFrame()
5143 gOutfitter_PaperDollItemSlotButton_OnClick = PaperDollItemSlotButton_OnClick;
5144 PaperDollItemSlotButton_OnClick = Outfitter_PaperDollItemSlotButton_OnClick
5145 end
5146  
5147 local Outfitter_cMaxNumQuickSlots = 9;
5148 local Outfitter_cSlotIDToInventorySlot = nil;
5149  
5150 function Outfitter_PaperDollItemSlotButton_OnClick(pButton, pIgnoreModifiers)
5151 -- Build the table to convert from slot ID to inventory slot name
5152  
5153 if not Outfitter_cSlotIDToInventorySlot then
5154 Outfitter_cSlotIDToInventorySlot = {};
5155  
5156 for _, vInventorySlot in Outfitter_cSlotNames do
5157 local vSlotID = GetInventorySlotInfo(vInventorySlot);
5158  
5159 Outfitter_cSlotIDToInventorySlot[vSlotID] = vInventorySlot;
5160 end
5161 end
5162  
5163 --
5164  
5165 local vSlotID = this:GetID();
5166 local vInventorySlot = Outfitter_cSlotIDToInventorySlot[vSlotID];
5167 local vItemLink = GetInventoryItemLink("player", vSlotID);
5168 local vSlotIsEmpty = vItemLink == nil;
5169  
5170 -- Call the original function
5171  
5172 gOutfitter_PaperDollItemSlotButton_OnClick(pButton, pIgnoreModifiers);
5173  
5174 -- If there's an item on the cursor then open the slots otherwise
5175 -- make sure they're closed
5176  
5177 if not OutfitterQuickSlots:IsVisible()
5178 and (CursorHasItem() or vSlotIsEmpty) then
5179 -- Hide the tooltip so that it isn't in the way
5180  
5181 GameTooltip:Hide();
5182  
5183 -- Open QuickSlots
5184  
5185 OutfitterQuickSlots_Open(vInventorySlot);
5186 else
5187 OutfitterQuickSlots_Close();
5188 end
5189 end
5190  
5191 function OutfitterItemList_AddItem(pItemList, pItem)
5192 -- Add the item to the code list
5193  
5194 local vItemFamily = pItemList.ItemsByCode[pItem.Code];
5195  
5196 if not vItemFamily then
5197 vItemFamily = {};
5198 pItemList.ItemsByCode[pItem.Code] = vItemFamily;
5199 end
5200  
5201 table.insert(vItemFamily, pItem);
5202  
5203 -- Add the item to the slot list
5204  
5205 local vItemSlot = pItemList.ItemsBySlot[pItem.ItemSlotName];
5206  
5207 if not vItemSlot then
5208 vItemSlot = {};
5209 pItemList.ItemsBySlot[pItem.ItemSlotName] = vItemSlot;
5210 end
5211  
5212 table.insert(vItemSlot, pItem);
5213  
5214 -- Add the item to the bags
5215  
5216 if pItem.Location.BagIndex then
5217 local vBagItems = pItemList.BagItems[pItem.Location.BagIndex];
5218  
5219 if not vBagItems then
5220 vBagItems = {};
5221 pItemList.BagItems[pItem.Location.BagIndex] = vBagItems;
5222 end
5223  
5224 vBagItems[pItem.Location.BagSlotIndex] = pItem;
5225  
5226 -- Add the item to the inventory
5227  
5228 elseif pItem.Location.SlotName then
5229 pItemList.InventoryItems[pItem.Location.SlotName] = pItem;
5230 end
5231 end
5232  
5233 function Outfitter_GetNumBags()
5234 if gOutfitter_BankFrameOpened then
5235 return NUM_BAG_SLOTS + NUM_BANKBAGSLOTS, -1;
5236 else
5237 return NUM_BAG_SLOTS, 0;
5238 end
5239 end
5240  
5241 function OutfitterItemList_FlushEquippableItems()
5242 gOutfitter_EquippableItems = nil;
5243 end
5244  
5245 function OutfitterItemList_FlushBagFromEquippableItems(pBagIndex)
5246 if gOutfitter_EquippableItems
5247 and gOutfitter_EquippableItems.BagItems[pBagIndex] then
5248 for vBagSlotIndex, vItem in gOutfitter_EquippableItems.BagItems[pBagIndex] do
5249 OutfitterItemList_RemoveItem(gOutfitter_EquippableItems, vItem);
5250 end
5251  
5252 gOutfitter_EquippableItems.NeedsUpdate = true;
5253 gOutfitter_EquippableItems.BagItems[pBagIndex] = nil;
5254 end
5255 end
5256  
5257 function OutfitterItemList_FlushInventoryFromEquippableItems()
5258 if gOutfitter_EquippableItems then
5259 for vInventorySlot, vItem in gOutfitter_EquippableItems.InventoryItems do
5260 OutfitterItemList_RemoveItem(gOutfitter_EquippableItems, vItem);
5261 end
5262  
5263 gOutfitter_EquippableItems.NeedsUpdate = true;
5264 gOutfitter_EquippableItems.InventoryItems = nil;
5265 end
5266 end
5267  
5268 function OutfitterItemList_New()
5269 return {ItemsByCode = {}, ItemsBySlot = {}, InventoryItems = nil, BagItems = {}};
5270 end
5271  
5272 function OutfitterItemList_RemoveItem(pItemList, pItem)
5273 -- Remove the item from the code list
5274  
5275 local vItems = pItemList.ItemsByCode[pItem.Code];
5276  
5277 for vIndex, vItem in vItems do
5278 if vItem == pItem then
5279 table.remove(vItems, vIndex);
5280 break;
5281 end
5282 end
5283  
5284 -- Remove the item from the slot list
5285  
5286 local vItemSlot = pItemList.ItemsBySlot[pItem.ItemSlotName];
5287  
5288 if vItemSlot then
5289 for vIndex, vItem in vItemSlot do
5290 if vItem == pItem then
5291 table.remove(vItemSlot, vIndex);
5292 break;
5293 end
5294 end
5295 end
5296  
5297 -- Remove the item from the bags list
5298  
5299 if pItem.Location.BagIndex then
5300 local vBagItems = pItemList.BagItems[pItem.Location.BagIndex];
5301  
5302 if vBagItems then
5303 vBagItems[pItem.Location.BagSlotIndex] = nil;
5304 end
5305  
5306 -- Remove the item from the inventory list
5307  
5308 elseif pItem.Location.SlotName then
5309 pItemList.InventoryItems[pItem.Location.SlotName] = nil;
5310 end
5311 end
5312  
5313 function OutfitterItemList_GetInventoryOutfit(pEquippableItems)
5314 return pEquippableItems.InventoryItems;
5315 end
5316  
5317 function OutfitterItemList_ResetIgnoreItemFlags(pItemList)
5318 for vItemCode, vItemFamily in pItemList.ItemsByCode do
5319 for _, vItem in vItemFamily do
5320 vItem.IgnoreItem = nil;
5321 end
5322 end
5323 end
5324  
5325 function OutfitterItemList_GetEquippableItems(pIncludeItemStats)
5326 -- If there's a cached copy just clear the IgnoreItem flags and return it
5327 -- (never used cached copy if the caller wants stats)
5328  
5329 if gOutfitter_EquippableItems
5330 and not gOutfitter_EquippableItems.NeedsUpdate
5331 and not pIncludeItemStats then
5332 OutfitterItemList_ResetIgnoreItemFlags(gOutfitter_EquippableItems);
5333  
5334 return gOutfitter_EquippableItems;
5335 end
5336  
5337 if not gOutfitter_EquippableItems
5338 or pIncludeItemStats then
5339 gOutfitter_EquippableItems = OutfitterItemList_New();
5340 end
5341  
5342 local _, vPlayerClass = UnitClass("player");
5343 local vStatDistribution = gOutfitter_StatDistribution[vPlayerClass];
5344  
5345 if not gOutfitter_EquippableItems.InventoryItems
5346 or pIncludeItemStats then
5347 gOutfitter_EquippableItems.InventoryItems = {};
5348  
5349 for _, vInventorySlot in Outfitter_cSlotNames do
5350 local vItemInfo = Outfitter_GetInventoryItemInfo(vInventorySlot);
5351  
5352 if vItemInfo
5353 and vItemInfo.ItemSlotName
5354 and vItemInfo.Code ~= 0 then
5355 vItemInfo.SlotName = vInventorySlot;
5356 vItemInfo.Location = {SlotName = vInventorySlot};
5357  
5358 if pIncludeItemStats then
5359 OutfitterItemList_GetItemStats(vItemInfo, vStatDistribution);
5360 end
5361  
5362 OutfitterItemList_AddItem(gOutfitter_EquippableItems, vItemInfo);
5363 end
5364 end
5365 else
5366 for vInventorySlot, vItem in gOutfitter_EquippableItems.InventoryItems do
5367 vItem.IgnoreItem = nil;
5368 end
5369 end
5370  
5371 local vNumBags, vFirstBagIndex = Outfitter_GetNumBags();
5372  
5373 for vBagIndex = vFirstBagIndex, vNumBags do
5374 local vBagItems = gOutfitter_EquippableItems.BagItems[vBagIndex];
5375  
5376 if not vBagItems
5377 or pIncludeItemStats then
5378 gOutfitter_EquippableItems.BagItems[vBagIndex] = {};
5379  
5380 local vNumBagSlots = GetContainerNumSlots(vBagIndex);
5381  
5382 if vNumBagSlots > 0 then
5383 for vBagSlotIndex = 1, vNumBagSlots do
5384 local vItemInfo = Outfitter_GetBagItemInfo(vBagIndex, vBagSlotIndex);
5385  
5386 if vItemInfo
5387 and vItemInfo.Code ~= 0
5388 and vItemInfo.ItemSlotName
5389 and Outfitter_CanEquipBagItem(vBagIndex, vBagSlotIndex)
5390 and not Outfitter_BagItemWillBind(vBagIndex, vBagSlotIndex) then
5391 vItemInfo.BagIndex = vBagIndex;
5392 vItemInfo.BagSlotIndex = vBagSlotIndex;
5393 vItemInfo.Location = {BagIndex = vBagIndex, BagSlotIndex = vBagSlotIndex};
5394  
5395 if pIncludeItemStats then
5396 OutfitterItemList_GetItemStats(vItemInfo, vStatDistribution);
5397 end
5398  
5399 OutfitterItemList_AddItem(gOutfitter_EquippableItems, vItemInfo);
5400 end
5401 end -- for vBagSlotIndex
5402 end -- if vNumBagSlots > 0
5403 else -- if not BagItems
5404 for vBagSlotIndex, vItem in vBagItems do
5405 vItem.IgnoreItem = nil;
5406 end
5407 end -- if not BagItems
5408 end -- for vBagIndex
5409  
5410 gOutfitter_EquippableItems.NeedsUpdate = false;
5411  
5412 return gOutfitter_EquippableItems;
5413 end
5414  
5415 function OutfitterItemList_SwapLocations(pItemList, pLocation1, pLocation2)
5416 -- if pLocation1.BagIndex then
5417 -- Outfitter_TestMessage("OutfitterItemList_SwapLocations: Swapping bag "..pLocation1.BagIndex..", "..pLocation1.BagSlotIndex);
5418 -- elseif pLocation1.SlotName then
5419 -- Outfitter_TestMessage("OutfitterItemList_SwapLocations: Swapping slot "..pLocation1.SlotName);
5420 -- end
5421 -- if pLocation2.BagIndex then
5422 -- Outfitter_TestMessage("OutfitterItemList_SwapLocations: with bag "..pLocation2.BagIndex..", "..pLocation2.BagSlotIndex);
5423 -- elseif pLocation2.SlotName then
5424 -- Outfitter_TestMessage("OutfitterItemList_SwapLocations: with slot "..pLocation2.SlotName);
5425 -- end
5426 end
5427  
5428 function OutfitterItemList_SwapLocationWithInventorySlot(pItemList, pLocation, pSlotName)
5429 -- if pLocation.BagIndex then
5430 -- Outfitter_TestMessage("OutfitterItemList_SwapLocationWithInventorySlot: Swapping bag "..pLocation.BagIndex..", "..pLocation.BagSlotIndex.." with slot "..pSlotName);
5431 -- elseif pLocation.SlotName then
5432 -- Outfitter_TestMessage("OutfitterItemList_SwapLocationWithInventorySlot: Swapping slot "..pLocation.SlotName.." with slot "..pSlotName);
5433 -- end
5434 end
5435  
5436 function OutfitterItemList_SwapBagSlotWithInventorySlot(pItemList, pBagIndex, pBagSlotIndex, pSlotName)
5437 -- Outfitter_TestMessage("OutfitterItemList_SwapBagSlotWithInventorySlot: Swapping bag "..pBagIndex..", "..pBagSlotIndex.." with slot "..pSlotName);
5438 end
5439  
5440 function OutfitterItemList_FindItemOrAlt(pItemList, pOutfitItem, pMarkAsInUse, pAllowSubCodeWildcard)
5441 local vItem, vIgnoredItem = OutfitterItemList_FindItem(pItemList, pOutfitItem, pMarkAsInUse, pAllowSubCodeWildcard);
5442  
5443 if vItem then
5444 return vItem;
5445 end
5446  
5447 -- See if there's an alias for the item if it wasn't found
5448  
5449 local vAltCode = Outfitter_cItemAliases[pOutfitItem.Code];
5450  
5451 if not vAltCode then
5452 return nil, vIgnoredItem;
5453 end
5454  
5455 return OutfitterItemList_FindItem(pItemList, {Code = vAltCode}, pMarkAsInUse, true);
5456 end
5457  
5458 function OutfitterItemList_FindItem(pItemList, pOutfitItem, pMarkAsInUse, pAllowSubCodeWildcard)
5459 local vItem, vIndex, vItemFamily, vIgnoredItem = OutfitterItemList_FindItemIndex(pItemList, pOutfitItem, pAllowSubCodeWildcard);
5460  
5461 if not vItem then
5462 return nil, vIgnoredItem;
5463 end
5464  
5465 if pMarkAsInUse then
5466 vItem.IgnoreItem = true;
5467 end
5468  
5469 return vItem;
5470 end
5471  
5472 function OutfitterItemList_FindAllItemsOrAlt(pItemList, pOutfitItem, pAllowSubCodeWildcard, rItems)
5473 local vNumItems = OutfitterItemList_FindAllItems(pItemList, pOutfitItem, pAllowSubCodeWildcard, rItems);
5474 local vAltCode = Outfitter_cItemAliases[pOutfitItem.Code];
5475  
5476 if vAltCode then
5477 vNumItems = vNumItems + OutfitterItemList_FindAllItems(pItemList, {Code = vAltCode}, true, rItems);
5478 end
5479  
5480 return vNumItems;
5481 end
5482  
5483 function OutfitterItemList_FindAllItems(pItemList, pOutfitItem, pAllowSubCodeWildcard, rItems)
5484 if not pItemList then
5485 return 0;
5486 end
5487  
5488 local vItemFamily = pItemList.ItemsByCode[pOutfitItem.Code];
5489  
5490 if not vItemFamily then
5491 return 0;
5492 end
5493  
5494 local vNumItemsFound = 0;
5495  
5496 for vIndex, vItem in vItemFamily do
5497 if (pAllowSubCodeWildcard and not pOutfitItem.SubCode)
5498 or vItem.SubCode == pOutfitItem.SubCode then
5499 table.insert(rItems, vItem);
5500 vNumItemsFound = vNumItemsFound + 1;
5501 end
5502 end
5503  
5504 return vNumItemsFound;
5505 end
5506  
5507 function OutfitterItemList_FindItemIndex(pItemList, pOutfitItem, pAllowSubCodeWildcard)
5508 if not pItemList then
5509 return nil, nil, nil, nil;
5510 end
5511  
5512 local vItemFamily = pItemList.ItemsByCode[pOutfitItem.Code];
5513  
5514 if not vItemFamily then
5515 return nil, nil, nil, nil;
5516 end
5517  
5518 local vBestMatch = nil;
5519 local vBestMatchIndex = nil;
5520 local vNumItemsFound = 0;
5521 local vFoundIgnoredItem = nil;
5522  
5523 for vIndex, vItem in vItemFamily do
5524 if pAllowSubCodeWildcard
5525 and not pOutfitItem.SubCode then
5526 if vItem.IgnoreItem then
5527 vFoundIgnoredItem = vItem;
5528 else
5529 return vItem, vIndex, vItemFamily, nil;
5530 end
5531  
5532 -- If the subcode matches then check for an enchant match
5533  
5534 elseif vItem.SubCode == pOutfitItem.SubCode then
5535 -- If the enchant matches then we're all done
5536  
5537 if vItem.EnchantCode == pOutfitItem.EnchantCode then
5538 if vItem.IgnoreItem then
5539 vFoundIgnoredItem = vItem;
5540 else
5541 return vItem, vIndex, vItemFamily;
5542 end
5543  
5544 -- Otherwise save the match in case a better one can
5545 -- be found
5546  
5547 else
5548 if vItem.IgnoreItem then
5549 if not vFoundIgnoredItem then
5550 vFoundIgnoredItem = vItem;
5551 end
5552 else
5553 vBestMatch = vItem;
5554 vBestMatchIndex = vIndex;
5555 vNumItemsFound = vNumItemsFound + 1;
5556 end
5557 end
5558 end
5559 end
5560  
5561 -- Return the match if only one item was found
5562  
5563 if vNumItemsFound == 1
5564 and not vBestMatch.IgnoreItem then
5565 return vBestMatch, vBestMatchIndex, vItemFamily, nil;
5566 end
5567  
5568 return nil, nil, nil, vFoundIgnoredItem;
5569 end
5570  
5571 function OutfitterItemList_GetItemStats(pItem, pDistribution)
5572 if pItem.Stats then
5573 return pItem.Stats;
5574 end
5575  
5576 OutfitterTooltip:SetOwner(OutfitterFrame, "ANCHOR_BOTTOMRIGHT", 0, 0);
5577  
5578 if pItem.SlotName then
5579 local vHasItem = OutfitterTooltip:SetInventoryItem("player", GetInventorySlotInfo(pItem.SlotName));
5580  
5581 if not vHasItem then
5582 OutfitterTooltip:Hide();
5583 return nil;
5584 end
5585 elseif pItem.BagIndex == -1 then
5586 OutfitterTooltip:SetInventoryItem("player", BankButtonIDToInvSlotID(pItem.BagSlotIndex));
5587 else
5588 OutfitterTooltip:SetBagItem(pItem.BagIndex, pItem.BagSlotIndex);
5589 end
5590  
5591 local vStats = Outfitter_GetItemStatsFromTooltip(OutfitterTooltip, pDistribution);
5592  
5593 OutfitterTooltip:Hide();
5594  
5595 if not vStats then
5596 return nil;
5597 end
5598  
5599 pItem.Stats = vStats;
5600  
5601 return vStats;
5602 end
5603  
5604 function Outfitter_IsBankBagIndex(pBagIndex)
5605 return pBagIndex and (pBagIndex > NUM_BAG_SLOTS or pBagIndex < 0);
5606 end
5607  
5608 function OutfitterItemList_GetMissingItems(pEquippableItems, pOutfit)
5609 local vMissingItems = nil;
5610 local vBankedItems = nil;
5611  
5612 for vInventorySlot, vOutfitItem in pOutfit.Items do
5613 if vOutfitItem.Code ~= 0 then
5614 local vItem = OutfitterItemList_FindItemOrAlt(pEquippableItems, vOutfitItem);
5615  
5616 if not vItem then
5617 if not vMissingItems then
5618 vMissingItems = {};
5619 end
5620  
5621 table.insert(vMissingItems, vOutfitItem);
5622 elseif Outfitter_IsBankBagIndex(vItem.Location.BagIndex) then
5623 if not vBankedItems then
5624 vBankedItems = {};
5625 end
5626  
5627 table.insert(vBankedItems, vOutfitItem);
5628 end
5629 end
5630 end
5631  
5632 return vMissingItems, vBankedItems;
5633 end
5634  
5635 function OutfitterItemList_CompiledUnusedItemsList(pEquippableItems)
5636 OutfitterItemList_ResetIgnoreItemFlags(pEquippableItems);
5637  
5638 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
5639 for vOutfitIndex, vOutfit in vOutfits do
5640 for vInventorySlot, vOutfitItem in vOutfit.Items do
5641 if vOutfitItem.Code ~= 0 then
5642 local vItem = OutfitterItemList_FindItemOrAlt(pEquippableItems, vOutfitItem, true);
5643  
5644 if vItem then
5645 vItem.UsedInOutfit = true;
5646 end
5647 end
5648 end
5649 end
5650 end
5651  
5652 local vUnusedItems = nil;
5653  
5654 for vCode, vFamilyItems in pEquippableItems.ItemsByCode do
5655 for vIndex, vOutfitItem in vFamilyItems do
5656 if not vOutfitItem.UsedInOutfit
5657 and vOutfitItem.ItemSlotName ~= "AmmoSlot"
5658 and Outfitter_cIgnoredUnusedItems[vOutfitItem.Code] == nil then
5659 if not vUnusedItems then
5660 vUnusedItems = {};
5661 end
5662  
5663 table.insert(vUnusedItems, vOutfitItem);
5664 end
5665 end
5666 end
5667  
5668 pEquippableItems.UnusedItems = vUnusedItems;
5669 end
5670  
5671 function OutfitterItemList_ItemsAreSame(pEquippableItems, pItem1, pItem2)
5672 if not pItem1 then
5673 return pItem2 == nil;
5674 end
5675  
5676 if not pItem2 then
5677 return false;
5678 end
5679  
5680 if pItem1.Code == 0 then
5681 return pItem2.Code == 0;
5682 end
5683  
5684 if pItem1.Code ~= pItem2.Code
5685 or pItem1.SubCode ~= pItem2.SubCode then
5686 return false;
5687 end
5688  
5689 local vItems = {};
5690 local vNumItems = OutfitterItemList_FindAllItemsOrAlt(pEquippableItems, pItem1, nil, vItems);
5691  
5692 if vNumItems == 0 then
5693 -- Shouldn't ever get here
5694  
5695 Outfitter_ErrorMessage("OutfitterItemList_ItemsAreSame: Item not found");
5696 return false;
5697 elseif vNumItems == 1 then
5698 -- If there's only one of that item then the enchant code
5699 -- is disregarded so just make sure it's the same
5700  
5701 return true;
5702 else
5703 return pItem1.EnchantCode == pItem2.EnchantCode;
5704 end
5705 end
5706  
5707 function OutfitterItemList_InventorySlotContainsItem(pEquippableItems, pInventorySlot, pOutfitItem)
5708 -- Nil items are supposed to be ignored, so never claim the slot contains them
5709  
5710 if pOutfitItem == nil then
5711 return false, nil;
5712 end
5713  
5714 -- If the item specifies and empty slot check to see if the slot is actually empty
5715  
5716 if pOutfitItem.Code == 0 then
5717 return pEquippableItems.InventoryItems[pInventorySlot] == nil;
5718 end
5719  
5720 local vItems = {};
5721 local vNumItems = OutfitterItemList_FindAllItemsOrAlt(pEquippableItems, pOutfitItem, nil, vItems);
5722  
5723 if vNumItems == 0 then
5724 return false;
5725 elseif vNumItems == 1 then
5726 -- If there's only one of that item then the enchant code
5727 -- is disregarded so just make sure it's in the slot
5728  
5729 return vItems[1].SlotName == pInventorySlot, vItems[1];
5730 else
5731 -- See if one of the items is in the slot
5732  
5733 for vIndex, vItem in vItems do
5734 if vItem.SlotName == pInventorySlot then
5735 -- Must match the enchant code if there are multiple items
5736 -- in order to be considered a perfect match
5737  
5738 return vItem.EnchantCode == pOutfitItem.EnchantCode, vItem;
5739 end
5740 end
5741  
5742 -- No items in the slot
5743  
5744 return false, nil;
5745 end
5746 end
5747  
5748 function OutfitterQuickSlots_Open(pSlotName)
5749 local vPaperDollSlotName = "Character"..pSlotName;
5750  
5751 -- Hide the tooltip so that it isn't in the way
5752  
5753 GameTooltip:Hide();
5754  
5755 -- Position the window
5756  
5757 if pSlotName == "MainHandSlot"
5758 or pSlotName == "SecondaryHandSlot"
5759 or pSlotName == "RangedSlot"
5760 or pSlotName == "AmmoSlot" then
5761 OutfitterQuickSlots:SetPoint("TOPLEFT", vPaperDollSlotName, "BOTTOMLEFT", 0, 0);
5762 else
5763 OutfitterQuickSlots:SetPoint("TOPLEFT", vPaperDollSlotName, "TOPRIGHT", 5, 6);
5764 end
5765  
5766 OutfitterQuickSlots.SlotName = pSlotName;
5767  
5768 -- Populate the items
5769  
5770 local vItems = Outfitter_FindItemsInBagsForSlot(pSlotName);
5771 local vNumSlots = 0;
5772  
5773 if vItems then
5774 for vItemInfoIndex, vItemInfo in vItems do
5775 if vNumSlots >= Outfitter_cMaxNumQuickSlots then
5776 break;
5777 end
5778  
5779 vNumSlots = vNumSlots + 1;
5780 OutfitterQuickSlots_SetSlotToBag(vNumSlots, vItemInfo.BagIndex, vItemInfo.BagSlotIndex);
5781 end
5782 end
5783  
5784 -- If the slot isn't empty, offer an empty slot to put the item in
5785  
5786 if vNumSlots < Outfitter_cMaxNumQuickSlots
5787 and not Outfitter_InventorySlotIsEmpty(pSlotName) then
5788 local vBagSlotInfo = Outfitter_GetEmptyBagSlot();
5789  
5790 if vBagSlotInfo then
5791 vNumSlots = vNumSlots + 1;
5792 OutfitterQuickSlots_SetSlotToBag(vNumSlots, vBagSlotInfo.BagIndex, vBagSlotInfo.BagSlotIndex);
5793 end
5794 end
5795  
5796 -- Resize the window and show it
5797  
5798 OutfitterQuickSlots_SetNumSlots(vNumSlots);
5799  
5800 if vNumSlots == 0 then
5801 OutfitterQuickSlots:Hide();
5802 else
5803 OutfitterQuickSlots:Show();
5804 end
5805 end
5806  
5807 function OutfitterQuickSlots_Close()
5808 OutfitterQuickSlots:Hide();
5809 end
5810  
5811 function OutfitterQuickSlots_OnLoad()
5812 table.insert(UIMenus, this:GetName());
5813 end
5814  
5815 function OutfitterQuickSlots_OnShow()
5816 end
5817  
5818 function OutfitterQuickSlots_OnHide()
5819 end
5820  
5821 function OutfitterQuickSlots_OnEvent(pEvent)
5822 end
5823  
5824 function OutfitterQuickSlotItem_OnLoad()
5825 this.size = 1; -- one-slot container
5826 end
5827  
5828 function OutfitterQuickSlotItem_OnShow()
5829 this:RegisterEvent("BAG_UPDATE");
5830 this:RegisterEvent("BAG_UPDATE_COOLDOWN");
5831 this:RegisterEvent("ITEM_LOCK_CHANGED");
5832 this:RegisterEvent("UPDATE_INVENTORY_ALERTS");
5833 end
5834  
5835 function OutfitterQuickSlotItem_OnHide()
5836 this:UnregisterEvent("BAG_UPDATE");
5837 this:UnregisterEvent("BAG_UPDATE_COOLDOWN");
5838 this:UnregisterEvent("ITEM_LOCK_CHANGED");
5839 this:UnregisterEvent("UPDATE_INVENTORY_ALERTS");
5840 end
5841  
5842 function OutfitterQuickSlotItemButton_OnEnter(button)
5843 GameTooltip:SetOwner(button, "ANCHOR_RIGHT");
5844  
5845 local vBagIndex = button:GetParent():GetID();
5846 local vBagSlotIndex = button:GetID();
5847  
5848 local hasItem, hasCooldown, repairCost;
5849  
5850 if vBagIndex == -1 then
5851 hasItem, hasCooldown, repairCost = GameTooltip:SetInventoryItem("player", BankButtonIDToInvSlotID(vBagSlotIndex));
5852 else
5853 hasCooldown, repairCost = GameTooltip:SetBagItem(vBagIndex, vBagSlotIndex);
5854 end
5855  
5856 if ( InRepairMode() and (repairCost and repairCost > 0) ) then
5857 GameTooltip:AddLine(TEXT(REPAIR_COST), "", 1, 1, 1);
5858 SetTooltipMoney(GameTooltip, repairCost);
5859 GameTooltip:Show();
5860 elseif ( this.readable or (IsControlKeyDown() and button.hasItem) ) then
5861 ShowInspectCursor();
5862 elseif ( MerchantFrame:IsVisible() and MerchantFrame.selectedTab == 1 ) then
5863 ShowContainerSellCursor(button:GetParent():GetID(),button:GetID());
5864 else
5865 ResetCursor();
5866 end
5867 end
5868  
5869 function OutfitterQuickSlots_SetNumSlots(pNumSlots)
5870 if pNumSlots > Outfitter_cMaxNumQuickSlots then
5871 pNumSlots = Outfitter_cMaxNumQuickSlots;
5872 end
5873  
5874 local vBaseWidth = 11;
5875 local vSlotWidth = 42;
5876  
5877 for vIndex = 1, pNumSlots do
5878 local vSlotItem = getglobal("OutfitterQuickSlotsItem"..vIndex);
5879  
5880 vSlotItem:ClearAllPoints();
5881  
5882 if vIndex == 1 then
5883 vSlotItem:SetPoint("TOPLEFT", "OutfitterQuickSlots", "TOPLEFT", 6, -6);
5884 else
5885 vSlotItem:SetPoint("TOPLEFT", "OutfitterQuickSlotsItem"..(vIndex - 1), "TOPLEFT", vSlotWidth, 0);
5886 end
5887  
5888 vSlotItem:Show();
5889 end
5890  
5891 -- Hide the unused slots
5892  
5893 for vIndex = pNumSlots + 1, Outfitter_cMaxNumQuickSlots do
5894 local vSlotItem = getglobal("OutfitterQuickSlotsItem"..vIndex);
5895  
5896 vSlotItem:Hide();
5897 end
5898  
5899 -- Size the frame
5900  
5901 OutfitterQuickSlots:SetWidth(vBaseWidth + vSlotWidth * pNumSlots);
5902  
5903 -- Fix the background
5904  
5905 if pNumSlots > 0 then
5906 for vIndex = 1, pNumSlots - 1 do
5907 getglobal("OutfitterQuickSlotsBack"..vIndex):Show();
5908 end
5909  
5910 for vIndex = pNumSlots, Outfitter_cMaxNumQuickSlots - 1 do
5911 getglobal("OutfitterQuickSlotsBack"..vIndex):Hide();
5912 end
5913  
5914 OutfitterQuickSlotsBackEnd:SetPoint("LEFT", "OutfitterQuickSlotsBack"..(pNumSlots - 1), "RIGHT", 0, 0);
5915 end
5916 end
5917  
5918 function OutfitterQuickSlots_SetSlotToBag(pQuickSlotIndex, pBagIndex, pBagSlotIndex)
5919 local vQuickSlotItem = getglobal("OutfitterQuickSlotsItem"..pQuickSlotIndex);
5920 local vQuickSlotItemButton = getglobal("OutfitterQuickSlotsItem"..pQuickSlotIndex.."Item1");
5921  
5922 vQuickSlotItem:SetID(pBagIndex);
5923 vQuickSlotItemButton:SetID(pBagSlotIndex);
5924  
5925 ContainerFrame_Update(vQuickSlotItem);
5926 end
5927  
5928 function Outfitter_RegisterEvent(pFrame, pEvent, pHandler)
5929 if not pFrame.EventHandlers then
5930 pFrame.EventHandlers = {};
5931 end
5932  
5933 pFrame.EventHandlers[pEvent] = pHandler;
5934 pFrame:RegisterEvent(pEvent);
5935 end
5936  
5937 function Outfitter_UnregisterEvent(pFrame, pEvent)
5938 if pFrame.EventHandlers then
5939 pFrame.EventHandlers[pEvent] = nil;
5940 end
5941  
5942 pFrame:UnregisterEvent(pEvent);
5943 end
5944  
5945 function Outfitter_SuspendEvent(pFrame, pEvent)
5946 if not pFrame.EventHandlers
5947 or not pFrame.EventHandlers[pEvent] then
5948 return;
5949 end
5950  
5951 pFrame:UnregisterEvent(pEvent);
5952 end
5953  
5954 function Outfitter_ResumeEvent(pFrame, pEvent)
5955 if not pFrame.EventHandlers
5956 or not pFrame.EventHandlers[pEvent] then
5957 return;
5958 end
5959  
5960 pFrame:RegisterEvent(pEvent);
5961 end
5962  
5963 function Outfitter_DispatchEvent(pFrame, pEvent)
5964 if not pFrame.EventHandlers then
5965 return false;
5966 end
5967  
5968 local vEventHandler = pFrame.EventHandlers[pEvent];
5969  
5970 if not vEventHandler then
5971 return false;
5972 end
5973  
5974 Outfitter_BeginEquipmentUpdate();
5975 vEventHandler(pEvent);
5976 Outfitter_EndEquipmentUpdate("Outfitter_DispatchEvent("..pEvent..")");
5977  
5978 return true;
5979 end
5980  
5981 function Outfitter_GetPlayerStat(pStatIndex)
5982 local _, vEffectiveValue, vPosValue, vNegValue = UnitStat("player", pStatIndex);
5983  
5984 return vEffectiveValue - vPosValue - vNegValue, vPosValue + vNegValue;
5985 end
5986  
5987 function Outfitter_DepositOutfit(pOutfit, pUniqueItemsOnly)
5988 -- Deselect any outfits to avoid them from being updated when
5989 -- items get put away
5990  
5991 Outfitter_ClearSelection();
5992  
5993 -- Build a list of items for the outfit
5994  
5995 local vEquippableItems = OutfitterItemList_GetEquippableItems();
5996  
5997 OutfitterItemList_ResetIgnoreItemFlags(vEquippableItems);
5998  
5999 -- Make a copy of the outfit
6000  
6001 local vUnequipOutfit = Outfitter_NewEmptyOutfit();
6002  
6003 for vInventorySlot, vItem in pOutfit.Items do
6004 vUnequipOutfit.Items[vInventorySlot] = vItem;
6005 end
6006  
6007 -- Subtract out items from other outfits if unique is specified
6008  
6009 if pUniqueItemsOnly then
6010 for vCategoryID, vOutfits in gOutfitter_Settings.Outfits do
6011 for vOutfitIndex, vOutfit in vOutfits do
6012 if vOutfit ~= pOutfit then
6013 local vMissingItems, vBankedItems = OutfitterItemList_GetMissingItems(vEquippableItems, vOutfit);
6014  
6015 -- Only subtract out items from outfits which aren't themselves partialy banked
6016  
6017 if vBankedItems == nil then
6018 Outfitter_SubtractOutfit(vUnequipOutfit, vOutfit, true);
6019 end
6020 end -- if vOutfit
6021 end -- for vOutfitIndex
6022 end -- for vCategoryID
6023 end -- if pUniqueItemsOnly
6024  
6025 -- Build the change list
6026  
6027 OutfitterItemList_ResetIgnoreItemFlags(vEquippableItems);
6028  
6029 local vEquipmentChangeList = Outfitter_BuildUnequipChangeList(vUnequipOutfit, vEquippableItems);
6030  
6031 if not vEquipmentChangeList then
6032 return;
6033 end
6034  
6035 -- Eliminate items which are already banked
6036  
6037 local vChangeIndex = 1;
6038 local vNumChanges = table.getn(vEquipmentChangeList);
6039  
6040 while vChangeIndex <= vNumChanges do
6041 vEquipmentChange = vEquipmentChangeList[vChangeIndex];
6042  
6043 if Outfitter_IsBankBagIndex(vEquipmentChange.FromLocation.BagIndex) then
6044 table.remove(vEquipmentChangeList, vChangeIndex);
6045 vNumChanges = vNumChanges - 1;
6046 else
6047 vChangeIndex = vChangeIndex + 1;
6048 end
6049 end
6050  
6051 -- Get the list of empty bank slots
6052  
6053 local vEmptyBankSlots = Outfitter_GetEmptyBankSlotList();
6054  
6055 -- Execute the changes
6056  
6057 Outfitter_ExecuteEquipmentChangeList2(vEquipmentChangeList, vEmptyBankSlots, Outfitter_cDepositBagsFullError, vExpectedEquippableItems);
6058 end
6059  
6060 function Outfitter_WithdrawOutfit(pOutfit)
6061 local vEquippableItems = OutfitterItemList_GetEquippableItems();
6062  
6063 -- Build a list of items for the outfit
6064  
6065 OutfitterItemList_ResetIgnoreItemFlags(vEquippableItems);
6066  
6067 local vEquipmentChangeList = Outfitter_BuildUnequipChangeList(pOutfit, vEquippableItems);
6068  
6069 if not vEquipmentChangeList then
6070 return;
6071 end
6072  
6073 -- Eliminate items which aren't in the bank
6074  
6075 local vChangeIndex = 1;
6076 local vNumChanges = table.getn(vEquipmentChangeList);
6077  
6078 while vChangeIndex <= vNumChanges do
6079 vEquipmentChange = vEquipmentChangeList[vChangeIndex];
6080  
6081 if not Outfitter_IsBankBagIndex(vEquipmentChange.FromLocation.BagIndex) then
6082 table.remove(vEquipmentChangeList, vChangeIndex);
6083 vNumChanges = vNumChanges - 1;
6084 else
6085 vChangeIndex = vChangeIndex + 1;
6086 end
6087 end
6088  
6089 -- Get the list of empty bag slots
6090  
6091 local vEmptyBagSlots = Outfitter_GetEmptyBagSlotList();
6092  
6093 -- Execute the changes
6094  
6095 Outfitter_ExecuteEquipmentChangeList2(vEquipmentChangeList, vEmptyBagSlots, Outfitter_cWithdrawBagsFullError, vExpectedEquippableItems);
6096 end
6097  
6098 function Outfitter_TestOutfitCombinations()
6099 local vEquippableItems = OutfitterItemList_GetEquippableItems(true);
6100 local vFilterStats = {["FireResist"] = true};
6101 local vOutfit = Outfitter_FindOutfitCombination(vEquippableItems, vFilterStats, Outfitter_OutfitTestEval, {});
6102 end
6103  
6104 function Outfitter_OutfitTestEval(pOpcode, pParams, pOutfit1, pOutfit2)
6105 if pOpcode == "INIT" then
6106 Outfitter_TestMessage("Outfitter_OutfitTestEval: INIT");
6107 elseif pOpcode == "COMPARE" then
6108 Outfitter_TestMessage("Outfitter_OutfitTestEval: COMPARE");
6109 end
6110 end
6111  
6112 function Outfitter_FindOutfitCombination(pEquippableItems, pFilterStats, pOutfitEvalFunc, pOutfitEvalParams)
6113 local vSlotIterators = OutfitterSlotIterators_New(pEquippableItems, pFilterStats);
6114  
6115 Outfitter_DumpArray("vSlotIterators", vSlotIterators);
6116  
6117 local vBestOutfit = nil;
6118 local vNumIterations = 0;
6119  
6120 pOutfitEvalFunc("INIT", pOutfitEvalParams);
6121  
6122 while vSlotIterators:Increment() do
6123 local vOutfit = vSlotIterators:GetOutfit();
6124  
6125 if pOutfitEvalFunc("COMPARE", pOutfitEvalParams, vBestOutfit, vOutfit) then
6126 vBestOutfit = vOutfit;
6127 end
6128  
6129 vNumIterations = vNumIterations + 1;
6130  
6131 if vNumIterations > 20 then
6132 return vBestOutfit;
6133 end
6134 end
6135  
6136 return vBestOutfit;
6137 end
6138  
6139 function Outfitter_ItemContainsStats(pItem, pFilterStats)
6140 for vStatID, _ in pFilterStats do
6141 if pItem.Stats[vStatID] then
6142 return true;
6143 end
6144 end
6145  
6146 return false;
6147 end
6148  
6149 function OutfitterSlotIterators_New(pEquippableItems, pFilterStats)
6150 local vSlotIterators = {Slots = {}};
6151 local vNumCombinations = 1;
6152  
6153 for vInventorySlot, vItems in pEquippableItems.ItemsBySlot do
6154 local vNumItems = table.getn(vItems);
6155  
6156 if vInventorySlot ~= "AmmoSlot"
6157 and vNumItems > 0 then
6158 -- Filter the items by stat
6159  
6160 local vFilteredItems = nil;
6161  
6162 if pFilterStats then
6163 vNumItems = 0;
6164  
6165 for vItemIndex, vItem in vItems do
6166 if Outfitter_ItemContainsStats(vItem, pFilterStats) then
6167 if not vFilteredItems then
6168 vFilteredItems = {};
6169 end
6170  
6171 table.insert(vFilteredItems, vItem);
6172 vNumItems = vNumItems + 1;
6173 end
6174 end
6175 else
6176 vFilteredItems = vItems;
6177 end
6178  
6179 -- Add the filtered list
6180  
6181 if vFilteredItems then
6182 table.insert(vSlotIterators.Slots, {ItemSlotName = vInventorySlot, Items = vItems, Index = 0, MaxIndex = vNumItems});
6183  
6184 vNumCombinations = vNumCombinations * (vNumItems + 1);
6185  
6186 Outfitter_TestMessage("OutfitterSlotIterators_New: "..vInventorySlot.." has "..vNumItems.." items. Combinations "..vNumCombinations);
6187 end
6188 end
6189 end
6190  
6191 vSlotIterators.Increment = OutfitterSlotIterators_Increment;
6192 vSlotIterators.GetOutfit = OutfitterSlotIterators_GetOutfit;
6193 vSlotIterators.NumCombinations = vNumCombinations;
6194  
6195 Outfitter_TestMessage("OutfitterSlotIterators_New: Total combinations "..vNumCombinations);
6196  
6197 return vSlotIterators;
6198 end
6199  
6200 function OutfitterSlotIterators_Increment(pSlotIterators)
6201 for vSlotIndex, vSlotIterator in pSlotIterators.Slots do
6202 vSlotIterator.Index = vSlotIterator.Index + 1;
6203  
6204 if vSlotIterator.Index <= vSlotIterator.MaxIndex then
6205 return true;
6206 end
6207  
6208 vSlotIterator.Index = 0;
6209 end
6210  
6211 return false; -- Couldn't increment
6212 end
6213  
6214 function OutfitterSlotIterators_GetOutfit(pSlotIterators)
6215 local vOutfit = Outfitter_NewEmptyOutfit();
6216  
6217 for _, vItems in pSlotIterators.Slots do
6218 -- if vItems.Index > 0 then
6219 -- local vItem = vItems.Items[vItems.Index];
6220 --
6221 -- Outfitter_AddOutfitItem(vOutfit, vItems.ItemSlotName, vItem.Code, vItem.SubCode, vItem.Name, vItem.EnchantCode);
6222 -- end
6223 end
6224  
6225 return vOutfit;
6226 end
6227  
6228 function OutfitterStats_AddStatValue(pStats, pStat, pValue, pDistribution)
6229 if not pStats[pStat] then
6230 pStats[pStat] = pValue;
6231 else
6232 pStats[pStat] = pStats[pStat] + pValue;
6233 end
6234  
6235 if not pDistribution then
6236 return;
6237 end
6238  
6239 local vStatDistribution = pDistribution[pStat];
6240  
6241 if not vStatDistribution then
6242 return;
6243 end
6244  
6245 for vSecondaryStat, vFactors in vStatDistribution do
6246 local vSecondaryValue = pValue * vFactors.Coeff;
6247  
6248 if vFactors.Const then
6249 vSecondaryValue = vSecondaryValue + vFactors.Const;
6250 end
6251  
6252 if pStats[vSecondaryStat] then
6253 pStats[vSecondaryStat] = pStats[vSecondaryStat] + vSecondaryValue;
6254 else
6255 pStats[vSecondaryStat] = vSecondaryValue;
6256 end
6257 end
6258 end
6259  
6260 function OutfitterStats_SubtractStats(pStats, pStats2)
6261 for vStat, vValue in pStats2 do
6262 if pStats[vStat] then
6263 pStats[vStat] = pStats[vStat] - vValue;
6264 end
6265 end
6266 end
6267  
6268 function OutfitterStats_AddStats(pStats, pStats2)
6269 for vStat, vValue in pStats2 do
6270 if pStats[vStat] then
6271 pStats[vStat] = pStats[vStat] + vValue;
6272 else
6273 pStats[vStat] = vValue;
6274 end
6275 end
6276 end
6277  
6278 function OutfitterTankPoints_New()
6279 local vTankPointData = {};
6280 local _, vPlayerClass = UnitClass("player");
6281 local vStatDistribution = gOutfitter_StatDistribution[vPlayerClass];
6282  
6283 if not vStatDistribution then
6284 Outfitter_ErrorMessage("Outfitter: Missing stat distribution data for "..vPlayerClass);
6285 end
6286  
6287 vTankPointData.PlayerLevel = UnitLevel("player");
6288 vTankPointData.StaminaFactor = 1.0; -- Warlocks with demonic embrace = 1.15
6289  
6290 -- Get the base stats
6291  
6292 vTankPointData.BaseStats = {};
6293  
6294 OutfitterStats_AddStatValue(vTankPointData.BaseStats, "Strength", UnitStat("player", 1), vStatDistribution);
6295 OutfitterStats_AddStatValue(vTankPointData.BaseStats, "Agility", UnitStat("player", 2), vStatDistribution);
6296 OutfitterStats_AddStatValue(vTankPointData.BaseStats, "Stamina", UnitStat("player", 3), vStatDistribution);
6297 OutfitterStats_AddStatValue(vTankPointData.BaseStats, "Intellect", UnitStat("player", 4), vStatDistribution);
6298 OutfitterStats_AddStatValue(vTankPointData.BaseStats, "Spirit", UnitStat("player", 5), vStatDistribution);
6299  
6300 OutfitterStats_AddStatValue(vTankPointData.BaseStats, "Health", UnitHealthMax("player"), vStatDistribution);
6301  
6302 vTankPointData.BaseStats.Health = vTankPointData.BaseStats.Health - vTankPointData.BaseStats.Stamina * 10;
6303  
6304 vTankPointData.BaseStats.Dodge = GetDodgeChance();
6305 vTankPointData.BaseStats.Parry = GetParryChance();
6306 vTankPointData.BaseStats.Block = GetBlockChance();
6307  
6308 local vBaseDefense, vBuffDefense = UnitDefense("player");
6309 OutfitterStats_AddStatValue(vTankPointData.BaseStats, "Defense", vBaseDefense + vBuffDefense, vStatDistribution);
6310  
6311 -- Replace the armor with the current value since that already includes various factors
6312  
6313 local vBaseArmor, vEffectiveArmor, vArmor, vArmorPosBuff, vArmorNegBuff = UnitArmor("player");
6314 vTankPointData.BaseStats.Armor = vEffectiveArmor;
6315  
6316 Outfitter_TestMessage("------------------------------------------");
6317 Outfitter_DumpArray("vTankPointData", vTankPointData);
6318  
6319 -- Subtract out the current outfit
6320  
6321 local vCurrentOutfitStats = OutfitterTankPoints_GetCurrentOutfitStats(vStatDistribution);
6322  
6323 Outfitter_TestMessage("------------------------------------------");
6324 Outfitter_DumpArray("vCurrentOutfitStats", vCurrentOutfitStats);
6325  
6326 OutfitterStats_SubtractStats(vTankPointData.BaseStats, vCurrentOutfitStats);
6327  
6328 -- Calculate the buff stats (stuff from auras/spell buffs/whatever)
6329  
6330 vTankPointData.BuffStats = {};
6331  
6332 -- Reset the cumulative values
6333  
6334 OutfitterTankPoints_Reset(vTankPointData);
6335  
6336 Outfitter_TestMessage("------------------------------------------");
6337 Outfitter_DumpArray("vTankPointData", vTankPointData);
6338  
6339 Outfitter_TestMessage("------------------------------------------");
6340 return vTankPointData;
6341 end
6342  
6343 function OutfitterTankPoints_Reset(pTankPointData)
6344 pTankPointData.AdditionalStats = {};
6345 end
6346  
6347 function OutfitterTankPoints_GetTotalStat(pTankPointData, pStat)
6348 local vTotalStat = pTankPointData.BaseStats[pStat];
6349  
6350 if not vTotalStat then
6351 vTotalStat = 0;
6352 end
6353  
6354 local vAdditionalStat = pTankPointData.AdditionalStats[pStat];
6355  
6356 if vAdditionalStat then
6357 vTotalStat = vTotalStat + vAdditionalStat;
6358 end
6359  
6360 local vBuffStat = pTankPointData.BuffStats[pStat];
6361  
6362 if vBuffStat then
6363 vTotalStat = vTotalStat + vBuffStat;
6364 end
6365  
6366 --
6367  
6368 return vTotalStat;
6369 end
6370  
6371 function OutfitterTankPoints_CalcTankPoints(pTankPointData, pStanceModifier)
6372 if not pStanceModifier then
6373 pStanceModifier = 1;
6374 end
6375  
6376 Outfitter_DumpArray("pTankPointData", pTankPointData);
6377  
6378 local vEffectiveArmor = OutfitterTankPoints_GetTotalStat(pTankPointData, "Armor");
6379  
6380 Outfitter_TestMessage("Armor: "..vEffectiveArmor);
6381  
6382 local vArmorReduction = vEffectiveArmor / ((85 * pTankPointData.PlayerLevel) + 400);
6383  
6384 vArmorReduction = vArmorReduction / (vArmorReduction + 1);
6385  
6386 local vEffectiveHealth = OutfitterTankPoints_GetTotalStat(pTankPointData, "Health");
6387  
6388 Outfitter_TestMessage("Health: "..vEffectiveHealth);
6389  
6390 Outfitter_TestMessage("Stamina: "..OutfitterTankPoints_GetTotalStat(pTankPointData, "Stamina"));
6391  
6392 --
6393  
6394 local vEffectiveDodge = OutfitterTankPoints_GetTotalStat(pTankPointData, "Dodge") * 0.01;
6395 local vEffectiveParry = OutfitterTankPoints_GetTotalStat(pTankPointData, "Parry") * 0.01;
6396 local vEffectiveBlock = OutfitterTankPoints_GetTotalStat(pTankPointData, "Block") * 0.01;
6397 local vEffectiveDefense = OutfitterTankPoints_GetTotalStat(pTankPointData, "Defense");
6398  
6399 -- Add agility and defense to dodge
6400  
6401 -- defenseInputBox:GetNumber() * 0.04 + agiInputBox:GetNumber() * 0.05
6402  
6403 Outfitter_TestMessage("Dodge: "..vEffectiveDodge);
6404 Outfitter_TestMessage("Parry: "..vEffectiveParry);
6405 Outfitter_TestMessage("Block: "..vEffectiveBlock);
6406 Outfitter_TestMessage("Defense: "..vEffectiveDefense);
6407  
6408 local vDefenseModifier = (vEffectiveDefense - pTankPointData.PlayerLevel * 5) * 0.04 * 0.01;
6409  
6410 Outfitter_TestMessage("Crit reduction: "..vDefenseModifier);
6411  
6412 local vMobCrit = max(0, 0.05 - vDefenseModifier);
6413 local vMobMiss = 0.05 + vDefenseModifier;
6414 local vMobDPS = 1;
6415  
6416 local vTotalReduction = 1 - (vMobCrit * 2 + (1 - vMobCrit - vMobMiss - vEffectiveDodge - vEffectiveParry)) * (1 - vArmorReduction) * pStanceModifier;
6417  
6418 Outfitter_TestMessage("Total reduction: "..vTotalReduction);
6419  
6420 local vTankPoints = vEffectiveHealth / (vMobDPS * (1 - vTotalReduction));
6421  
6422 return vTankPoints;
6423  
6424 --[[
6425 Stats used in TankPoints calculation:
6426 Health
6427 Dodge
6428 Parry
6429 Block
6430 Defense
6431 Armor
6432 ]]--
6433 end
6434  
6435 function OutfitterTankPoints_GetCurrentOutfitStats(pStatDistribution)
6436 local vTotalStats = {};
6437  
6438 for _, vSlotName in Outfitter_cSlotNames do
6439 local vStats = OutfitterItemList_GetItemStats({SlotName = vSlotName});
6440  
6441 if vStats then
6442 for vStat, vValue in vStats do
6443 OutfitterStats_AddStatValue(vTotalStats, vStat, vValue, pStatDistribution);
6444 end
6445 end
6446 end
6447  
6448 return vTotalStats;
6449 end
6450  
6451 function OutfitterTankPoints_Test()
6452 local _, vPlayerClass = UnitClass("player");
6453 local vStatDistribution = gOutfitter_StatDistribution[vPlayerClass];
6454  
6455 local vTankPointData = OutfitterTankPoints_New();
6456 local vStats = OutfitterTankPoints_GetCurrentOutfitStats(vStatDistribution);
6457  
6458 OutfitterStats_AddStats(vTankPointData.AdditionalStats, vStats);
6459  
6460 local vTankPoints = OutfitterTankPoints_CalcTankPoints(vTankPointData);
6461  
6462 Outfitter_TestMessage("TankPoints = "..vTankPoints);
6463 end
6464  
6465 function Outfitter_TestAmmoSlot()
6466 local vItemInfo = Outfitter_GetInventoryItemInfo("AmmoSlot");
6467 local vSlotID = GetInventorySlotInfo("AmmoSlot");
6468 local vItemLink = GetInventoryItemLink("player", vSlotID);
6469  
6470 Outfitter_TestMessage("SlotID: "..vSlotID);
6471 Outfitter_TestMessage("ItemLink: "..vItemLink);
6472  
6473 Outfitter_DumpArray("vItemInfo", vItemInfo);
6474 end