vanilla-wow-addons – Blame information for rev 1
?pathlinks?
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 |