vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 OUTFITDISPLAYFRAME_VERSION = "0.5.6";
2 OUTFITDISPLAYFRAME_NAME = "Outfit Display Frame";
3 OUTFITDISPLAYFRAME_TITLE = OUTFITDISPLAYFRAME_NAME.." v"..OUTFITDISPLAYFRAME_VERSION;
4 OUTFITDISPLAYFRAME_BANKDELAY = 0.5;
5  
6 local PlayerSlotNames = {
7 [1] = { name = "HeadSlot", tooltip = HEADSLOT };
8 [2] = { name = "NeckSlot", tooltip = NECKSLOT },
9 [3] = { name = "ShoulderSlot", tooltip = SHOULDERSLOT },
10 [4] = { name = "BackSlot", tooltip = BACKSLOT },
11 [5] = { name = "ChestSlot", tooltip = CHESTSLOT },
12 [6] = { name = "ShirtSlot", tooltip = SHIRTSLOT },
13 [7] = { name = "TabardSlot", tooltip = TABARDSLOT },
14 [8] = { name = "WristSlot", tooltip = WRISTSLOT },
15 [9] = { name = "HandsSlot", tooltip = HANDSSLOT },
16 [10] = { name = "WaistSlot", tooltip = WAISTSLOT },
17 [11] = { name = "LegsSlot", tooltip = LEGSSLOT },
18 [12] = { name = "FeetSlot", tooltip = FEETSLOT },
19 [13] = { name = "Finger0Slot", tooltip = FINGER0SLOT },
20 [14] = { name = "Finger1Slot", tooltip = FINGER1SLOT },
21 [15] = { name = "Trinket0Slot", tooltip = TRINKET0SLOT },
22 [16] = { name = "Trinket1Slot", tooltip = TRINKET1SLOT },
23 [17] = { name = "MainHandSlot", tooltip = MAINHANDSLOT },
24 [18] = { name = "SecondaryHandSlot", tooltip = SECONDARYHANDSLOT },
25 [19] = { name = "RangedSlot", tooltip = RANGEDSLOT },
26 }
27  
28 local SavedPickupContainerItem = nil;
29 local SavedPickupInventoryItem = nil;
30  
31 local LastOffSource = nil;
32 local PerformSlowerSwap = false;
33 local BankIsOpen = false;
34  
35 local function tonil(val)
36 if ( not val ) then
37 return "nil";
38 else
39 return val;
40 end
41 end
42  
43 local function Print(msg, r, g, b)
44 if ( DEFAULT_CHAT_FRAME ) then
45 if ( not r ) then
46 DEFAULT_CHAT_FRAME:AddMessage(msg);
47 else
48 DEFAULT_CHAT_FRAME:AddMessage(msg, r, g, b);
49 end
50 end
51 end
52  
53 -- Based on code in QuickMountEquip
54 local function SafeHookFunction(func, newfunc)
55 local oldValue = getglobal(func);
56 if ( oldValue ~= getglobal(newfunc) ) then
57 setglobal(func, getglobal(newfunc));
58 return true;
59 end
60 return false;
61 end
62  
63 -- this really ought to be in a library somewhere
64 local function SplitLink(link)
65 local _,_, color, item, name = string.find(link, "|c(%x+)|Hitem:(%d+:%d+:%d+:%d+)|h%[(.-)%]|h|r");
66 return color, item, name;
67 end
68  
69 function OutfitDisplayFrame_OnLoad()
70 local temp = PickupContainerItem;
71 if ( SafeHookFunction("PickupContainerItem", "OutfitDisplayFrame_PickupContainerItem") ) then
72 SavedPickupContainerItem = temp;
73 end
74  
75 temp = PickupInventoryItem;
76 if ( SafeHookFunction("PickupInventoryItem", "OutfitDisplayFrame_PickupInventoryItem") ) then
77 SavedPickupInventoryItem = temp;
78 end
79  
80 local parent = this:GetName();
81 for i=1,19,1 do
82 local slotName = PlayerSlotNames[i].name;
83 local button = getglobal(parent..slotName);
84 button.tooltip = PlayerSlotNames[i].tooltip;
85 OutfitDisplayItemButton_Draw(button);
86 PlayerSlotNames[i].id = GetInventorySlotInfo(slotName);
87 end
88  
89 -- ideas borrowed from "WeaponQuickSwap - by CapnBry"
90 this:RegisterEvent("ITEM_LOCK_CHANGED");
91 this:RegisterEvent("BANKFRAME_OPENED");
92 this:RegisterEvent("BANKFRAME_CLOSED");
93  
94 Print(OUTFITDISPLAYFRAME_TITLE.." loaded.");
95 end
96  
97 local function DressUpItem(model, thing)
98 if ( not model or not thing or thing == "" ) then
99 return;
100 end
101 local item = gsub(thing, "(%d+).*", "%1", 1);
102 model:TryOn(item);
103 end
104  
105 local BankFrameClosedTime = 0;
106  
107 function OutfitDisplayFrame_OnEvent(...)
108 if ( arg[1] == "BANKFRAME_OPENED" or arg[1] == "BANKFRAME_CLOSED" ) then
109 BankFrameIsOpen = ( arg[1] == "BANKFRAME_OPENED" );
110 local frame = getglobal(this:GetName().."MessageUpdateFrame");
111 if ( frame ) then
112 BankFrameClosedTime = OUTFITDISPLAYFRAME_BANKDELAY;
113 frame:Show();
114 end
115 elseif (arg[1] == "ITEM_LOCK_CHANGED") and not arg[2] then
116 return OutfitDisplayFrame_ExecuteSwapIteration();
117 end
118 end
119  
120 function OutfitDisplayFrame_MessageUpdate(elapsed)
121 BankFrameClosedTime = BankFrameClosedTime - elapsed;
122 if ( BankFrameClosedTime <= 0 ) then
123 this:Hide();
124 OutfitDisplayFrame_UpdateMessage(this:GetParent());
125 BankFrameClosedTime = 0;
126 end
127 end
128  
129 local function GetSlotButton(button, slotName)
130 local parent = button:GetParent():GetName();
131 return getglobal(parent..slotName);
132 end
133  
134 local function IsItemOneHanded(item)
135 if ( item ) then
136 local _,_,_,_,_,_,_,bodyslot = GetItemInfo("item:"..item);
137 if ( bodyslot == "INVTYPE_2HWEAPON" or bodyslot == INVTYPE_2HWEAPON ) then
138 return false;
139 end
140 end
141 return true;
142 end
143  
144 function OutfitDisplayFrame_CursorCanGoInSlot(button)
145 if ( button.forced ) then
146 return false;
147 end
148 local secondary = GetSlotButton(button, "SecondaryHandSlot");
149 local mainbutton = GetSlotButton(button, "MainHandSlot");
150 if ( button == secondary and mainbutton and mainbutton.item ) then
151 return IsItemOneHanded(mainbutton.item);
152 end
153 return CursorCanGoInSlot(button:GetID())
154 end
155  
156 -- much hackery to know what actually is in the cursor when somebody drops it on us
157 local OD_Track_Bag = nil;
158 local OD_Track_Slot = nil;
159  
160 -- whatever we think the user picked up, apply it to our stuff
161 local function AcceptCursorItem(button)
162 local parent = button:GetParent();
163 local pname = parent:GetName();
164 if ( not OutfitDisplayFrame_CursorCanGoInSlot(button) ) then
165 button = nil;
166 for i=1,19,1 do
167 local temp = getglobal(pname..PlayerSlotNames[i].name);
168 if ( temp and OutfitDisplayFrame_CursorCanGoInSlot(temp) ) then
169 button = temp;
170 end
171 end
172 end
173  
174 if ( button ) then
175 local link, texture;
176 if ( OD_Track_Bag ) then
177 link = GetContainerItemLink(OD_Track_Bag, OD_Track_Slot);
178 texture = GetContainerItemInfo(OD_Track_Bag, OD_Track_Slot);
179 else
180 link = GetInventoryItemLink("player", OD_Track_Slot);
181 texture = GetInventoryItemTexture("player", OD_Track_Slot);
182 end
183 button.color, button.item, button.name = SplitLink(link);
184 button.texture = texture;
185 button.used = true;
186 button.empty = nil;
187 button.missing = false;
188 if ( OD_Track_Bag and (OD_Track_Bag == BANK_CONTAINER or
189 OD_Track_Bag > NUM_BAG_SLOTS) ) then
190 button.banked = true;
191 end
192 OutfitDisplayItemButton_Change(button);
193 end
194  
195 -- clear the cursor item
196 if ( OD_Track_Bag ) then
197 SavedPickupContainerItem(OD_Track_Bag, OD_Track_Slot);
198 elseif ( OD_Track_Slot ) then
199 SavedPickupInventoryItem(OD_Track_Slot);
200 end
201 OD_Track_Bag = nil;
202 OD_Track_Slot = nil;
203 end
204  
205 -- figure out what the user picked up, so that if they drop it on us we can deal with it
206 function OutfitDisplayFrame_TrackItemPickup(bag, slot)
207 OD_Track_Bag = bag;
208 OD_Track_Slot = slot;
209 end
210  
211 function OutfitDisplayFrame_PickupContainerItem(bag, slot)
212 if ( SavedPickupContainerItem ) then
213 SavedPickupContainerItem(bag, slot);
214 end
215 OutfitDisplayFrame_TrackItemPickup(bag, slot);
216 end
217  
218 function OutfitDisplayFrame_PickupInventoryItem(slot)
219 if ( SavedPickupInventoryItem ) then
220 SavedPickupInventoryItem(slot);
221 end
222 OutfitDisplayFrame_TrackItemPickup(nil, slot);
223 end
224  
225 -- handle making the items in the outfit pane do nice things
226 function OutfitDisplayItemButton_OnEnter()
227 if ( GameTooltip.finished ) then
228 return;
229 end
230 GameTooltip.finished = 1;
231 GameTooltip:SetOwner(this, "ANCHOR_RIGHT");
232 if (this.item) then
233 local link = "item:"..this.item;
234 if ( GetItemInfo(link) ) then
235 GameTooltip:SetHyperlink(link);
236 elseif ( this.name ) then
237 GameTooltip:SetText(this.name);
238 elseif ( this.tooltip ) then
239 GameTooltip:SetText(this.tooltip);
240 else
241 GameTooltip:SetText("<unlinkable item>", 1, 0, 0);
242 end
243 elseif ( this.tooltip ) then
244 GameTooltip:AddLine(this.tooltip);
245 else
246 local parentlen = string.len(this:GetParent():GetName())+1;
247 local slotName = strsub(this:GetName(), parentlen);
248 slotName = strsub(slotName, 1, string.len(slotName) - 4);
249 this.tooltip = slotName;
250 GameTooltip:AddLine(this.tooltip);
251 end
252 GameTooltip:AddLine(OUTFITDISPLAYFRAME_ALTCLICK,.8,.8,.8,1);
253 GameTooltip:Show();
254 end
255  
256 function OutfitDisplayItemButton_OnEvent(event)
257 if ( event == "CURSOR_UPDATE" ) then
258 if ( not this.forced ) then
259 if ((this.CursorCanGoInSlot and this.CursorCanGoInSlot(this)) or
260 OutfitDisplayFrame_CursorCanGoInSlot(this)) then
261 this:LockHighlight();
262 else
263 this:UnlockHighlight();
264 end
265 end
266 end
267 end
268  
269 function OutfitDisplayItemButton_OnClick(clicked, ignoreModifiers)
270 if ( clicked == "LeftButton" ) then
271 if( not ignoreModifiers or ignoreModifiers == 0 ) then
272 if ( IsShiftKeyDown() ) then
273 if ( this.item and ChatFrameEditBox:IsVisible() ) then
274 local color = this.color;
275 if ( not color ) then
276 color = "ffffffff";
277 end
278 local link = "|c"..color.."|Hitem"..this.item.."|h["..this.name.."]|h|r";
279 ChatFrameEditBox:Insert(link);
280 return;
281 end
282 elseif ( IsAltKeyDown() ) then
283 OutfitDisplayItemButton_Clear(this, false, false);
284 OutfitDisplayItemButton_Change(this);
285 return;
286 end
287 end
288 -- fall through for drags and non-modified clicks
289 if ( CursorHasItem() ) then
290 AcceptCursorItem(this);
291 end
292 end
293 end
294  
295 function OutfitDisplayItemButton_OnLoad()
296 local parentlen = string.len(this:GetParent():GetName())+1;
297 local slotName = strsub(this:GetName(), parentlen);
298 local id;
299 local textureName;
300 id, textureName = GetInventorySlotInfo(slotName);
301 this:SetID(id);
302 this.backgroundTextureName = textureName;
303 SetItemButtonTexture(this, this.backgroundTextureName);
304 this:RegisterForDrag("LeftButton");
305 this:RegisterForClicks("LeftButtonUp", "RightButtonUp");
306 this:RegisterEvent("CURSOR_UPDATE");
307 end
308  
309 function OutfitDisplayItemButton_Draw(button)
310 if ( button.texture ) then
311 SetItemButtonTexture(button, button.texture);
312 else
313 SetItemButtonTexture(button, button.backgroundTextureName);
314 end
315 if ( button.missing ) then
316 SetItemButtonTextureVertexColor(button, 1.0, 0.1, 0.1);
317 elseif ( button.banked ) then
318 SetItemButtonTextureVertexColor(button, 0.1, 0.1, 1.0);
319 else
320 SetItemButtonTextureVertexColor(button, 1.0, 1.0, 1.0);
321 end
322 local checkbox = getglobal(button:GetName().."CheckBox");
323 if (checkbox) then
324 local pname = button:GetParent():GetName();
325 local showhelm = getglobal(pname.."ShowHelm");
326 local showcloak = getglobal(pname.."ShowCloak");
327 if ( button.forced ) then
328 checkbox:SetChecked(true);
329 checkbox:Disable();
330 checkbox:Show();
331 elseif ( button.empty or not button.used ) then
332 if ( checkbox.slotName == "HeadSlot" ) then
333 showhelm:Hide();
334 showhelm:SetChecked(false);
335 elseif ( checkbox.slotName == "BackSlot" ) then
336 showcloak:Hide();
337 showcloak:SetChecked(false);
338 end
339 checkbox:Enable();
340 checkbox:SetChecked(button.empty);
341 checkbox:Show();
342 elseif ( button.used ) then
343 checkbox:Hide();
344 if ( checkbox.slotName == "HeadSlot" ) then
345 showhelm:Show();
346 elseif ( checkbox.slotName == "BackSlot" ) then
347 showcloak:Show();
348 end
349 end
350 end
351 end
352  
353 function OutfitDisplayItemButton_Change(button)
354 OutfitDisplayItemButton_Draw(button);
355 -- handle two handed weapons
356 local parent = button:GetParent();
357 local pname = parent:GetName();
358 local parentlen = string.len(pname)+1;
359 local slotName = strsub(button:GetName(), parentlen);
360 if ( slotName == "MainHandSlot" ) then
361 local secondary = getglobal(pname.."SecondaryHandSlot");
362 if ( not button.used and secondary.forced ) then
363 OutfitDisplayItemButton_Clear(secondary, false, false);
364 elseif ( button.used and not IsItemOneHanded(button.item) ) then
365 OutfitDisplayItemButton_Clear(secondary, true);
366 secondary.forced = true;
367 end
368 OutfitDisplayItemButton_Draw(secondary);
369 end
370 OutfitDisplayFrame_UpdateMessage(parent);
371 OutfitDisplayFrame_UpdateModel(parent, button);
372 if( parent.OutfitChanged ) then
373 parent.OutfitChanged( button );
374 end
375 end
376  
377 function OutfitDisplayItemButton_Clear(button, empty, used)
378 button.name = nil;
379 button.item = nil;
380 button.texture = nil;
381 button.color = nil;
382 button.missing = nil;
383 button.banked = nil;
384 button.forced = nil;
385 button.empty = empty;
386 button.used = used or empty;
387 end
388  
389 -- override ShowHelm and ShowCloak
390 function OutfitDisplayOverrideBox_OnLoad()
391 local parentlen = string.len(this:GetParent():GetName())+1;
392 this.slotName = strsub(this:GetName(), parentlen);
393 end
394  
395 function OutfitDisplayOverrideBox_OnEnter()
396 GameTooltip:SetOwner(this, "ANCHOR_RIGHT");
397 local text;
398 if ( this.slotName == "ShowHelm" ) then
399 text = OUTFITDISPLAYFRAME_OVERRIDEHELM;
400 else
401 text = OUTFITDISPLAYFRAME_OVERRIDECLOAK;
402 end
403 GameTooltip:SetText(text, nil, nil, nil, nil, 1);
404 GameTooltip:Show();
405 end
406  
407 -- check box handling
408 function OutfitDisplayCheckBox_OnEnter()
409 GameTooltip:SetOwner(this, "ANCHOR_RIGHT");
410 GameTooltip:SetText(OUTFITDISPLAYFRAME_USECHECKBOX, nil, nil, nil, nil, 1);
411 GameTooltip:Show();
412 end
413  
414 function OutfitDisplayCheckBox_OnLoad()
415 local parentlen = string.len(this:GetParent():GetName())+1;
416 local name = strsub(this:GetName(), parentlen);
417 this.slotName = strsub(name, 1, -9);
418 end
419  
420 function OutfitDisplayCheckBox_OnClick()
421 local pname = this:GetParent():GetName();
422 local button = getglobal(pname..this.slotName);
423 OutfitDisplayItemButton_Clear(button, this:GetChecked());
424 OutfitDisplayItemButton_Change(button);
425 end
426  
427 local function UpdateModel_ThisSlot(pname, model, idx)
428 local slotName = PlayerSlotNames[idx].name;
429 local what = getglobal(pname..slotName);
430 local item = nil;
431 if ( what and what.used ) then
432 if ( not what.empty ) then
433 item = what.item;
434 end
435 else
436 local slot = PlayerSlotNames[idx].id;
437 local link = GetInventoryItemLink("player", slot);
438 if ( link ) then
439 _, item, _ = SplitLink(link);
440 end
441 end
442 if ( item ) then
443 DressUpItem(model, item);
444 end
445 end
446  
447 function OutfitDisplayFrame_UpdateModel(frame, button)
448 local pname = frame:GetName();
449 local model = getglobal(pname.."Model");
450 local empty = false;
451 if ( not model ) then
452 return;
453 end
454 if ( button and button.used ) then
455 if ( button.empty and not button.forced ) then
456 empty = true;
457 end
458 else
459 for i=1,19,1 do
460 local what = getglobal(pname..PlayerSlotNames[i].name);
461 if ( what and what.empty ) then
462 empty = true;
463 end
464 end
465 end
466 if ( empty ) then
467 model:Undress();
468 end
469 for i=19,1,-1 do
470 UpdateModel_ThisSlot(pname, model, i);
471 end
472 end
473  
474 function OutfitDisplayFrame_SetOutfit(frame, outfit)
475 if not outfit then
476 return;
477 end
478 local pname = frame:GetName();
479 for i=1,19,1 do
480 local slotName = PlayerSlotNames[i].name;
481 local button = getglobal(pname..slotName);
482 if ( button and outfit[slotName] and outfit[slotName].used ) then
483 OutfitDisplayItemButton_Clear(button);
484 if ( outfit[slotName].used ) then
485 button.name = outfit[slotName].name;
486 button.item = outfit[slotName].item;
487 button.texture = outfit[slotName].texture;
488 button.color = outfit[slotName].color;
489 button.missing = outfit[slotName].missing;
490 button.banked = outfit[slotName].banked;
491 button.forced = outfit[slotName].forced;
492 button.empty = outfit[slotName].empty;
493 button.used = true;
494 else
495 OutfitDisplayItemButton_Clear(button, true, true);
496 end
497 OutfitDisplayItemButton_Draw(button);
498 end
499 end
500 if ( outfit["Options"] ) then
501 local showhelm = getglobal(pname.."ShowHelm");
502 local showcloak = getglobal(pname.."ShowCloak");
503 showhelm:SetChecked(outfit["Options"].helm);
504 showcloak:SetChecked(outfit["Options"].cloak);
505 end
506 OutfitDisplayFrame_UpdateModel(frame);
507 OutfitDisplayFrame_UpdateMessage(frame, outfit);
508 end
509  
510 function OutfitDisplayFrame_GetOutfit(frame, puthere)
511 local pname = frame:GetName();
512 local outfit = puthere or nil;
513 local showhelm = getglobal(pname.."ShowHelm");
514 local showcloak = getglobal(pname.."ShowCloak");
515 local setOptions = false;
516 if not outfit then
517 outfit = {};
518 end
519 outfit["Options"] = {};
520 for i=1,19,1 do
521 local slotName = PlayerSlotNames[i].name;
522 local button = getglobal(pname..slotName);
523  
524 if ( button and button.used ) then
525 outfit[slotName] = {};
526 if ( not button.empty ) then
527 outfit[slotName].name = button.name;
528 outfit[slotName].item = button.item;
529 outfit[slotName].texture = button.texture;
530 outfit[slotName].color = button.color;
531 outfit[slotName].missing = button.missing;
532 outfit[slotName].banked = button.banked;
533 outfit[slotName].forced = button.forced;
534 end
535 outfit[slotName].empty = button.empty;
536 outfit[slotName].used = true;
537 if ( not button.empty ) then
538 if ( slotName == "HeadSlot" ) then
539 if ( showhelm and showhelm:GetChecked() ) then
540 outfit["Options"].helm = true;
541 setOptions = true;
542 end
543 elseif ( slotName == "BackSlot" ) then
544 if ( showcloak and showcloak:GetChecked() ) then
545 outfit["Options"].cloak = true;
546 setOptions = true;
547 end
548 end
549 end
550 elseif ( outfit and outfit.used ) then
551 outfit[slotName] = nil;
552 end
553 end
554 if ( not setOptions ) then
555 outfit["Options"] = nil;
556 end
557 if ( outfit ) then
558 -- check for two-handed weapon
559 local mainhand = outfit["MainHandSlot"];
560 if ( mainhand and not IsItemOneHanded(mainhand.item) ) then
561 outfit["SecondaryHandSlot"] = {};
562 outfit["SecondaryHandSlot"].forced = true;
563 outfit["SecondaryHandSlot"].empty = true;
564 outfit["SecondaryHandSlot"].used = true;
565 end
566 end
567 return outfit;
568 end
569  
570 function OutfitDisplayFrame_GetItemInfo(bag, slot)
571 local link;
572 local c, i, n;
573 if ( bag ) then
574 link = GetContainerItemLink (bag,slot);
575 else
576 link = GetInventoryItemLink("player", slot);
577 end
578 if (link) then
579 c, i, n = SplitLink(link);
580 end
581 return c, i, n;
582 end
583  
584 -- get a snapshot of the current state of the player
585 function OutfitDisplayFrame_GetBagInfo()
586 local contents = {};
587 local empties = {};
588  
589 local numSlots = 0;
590 -- check each of the bags on the player
591 for bag=0, NUM_BAG_FRAMES do
592 -- get the number of slots in the bag (0 if no bag)
593 numSlots = GetContainerNumSlots(bag);
594 if (numSlots > 0) then
595 -- check each slot in the bag
596 for slot=1, numSlots do
597 local c, i, n = OutfitDisplayFrame_GetItemInfo(bag, slot);
598 if ( i ) then
599 contents[i] = {};
600 contents[i].bag = bag;
601 contents[i].slot = slot;
602 contents[i].color = c;
603 contents[i].name = n;
604 else
605 local what = {};
606 what.bag = bag;
607 what.slot = slot;
608 tinsert(empties, what);
609 end
610 end
611 end
612 end
613  
614 for idx=1,19,1 do
615 local slot = PlayerSlotNames[idx].id;
616 local c, i, n = OutfitDisplayFrame_GetItemInfo(nil, slot);
617 if ( i ) then
618 contents[i] = {};
619 contents[i].slot = slot;
620 contents[i].color = c;
621 contents[i].name = n;
622 end
623 end
624  
625 return contents, empties;
626 end
627  
628 -- look in a particular bag
629 local function CheckThisBag(bag, id, skipcount)
630 -- get the number of slots in the bag (0 if no bag)
631 local numSlots = GetContainerNumSlots(bag);
632 if (numSlots > 0) then
633 -- check each slot in the bag
634 for slot=1, numSlots do
635 local c, i, n = OutfitDisplayFrame_GetItemInfo(bag, slot);
636 if ( i and id == i ) then
637 if ( skipcount == 0 ) then
638 return slot, skipcount;
639 end
640 skipcount = skipcount - 1;
641 end
642 end
643 end
644 return nil, skipcount;
645 end
646  
647 -- look for the outfit anywhere we can find it
648 local function FindThisItem(id, skipcount)
649 if ( not id ) then
650 return nil,nil,nil;
651 end
652 local skipcount = skipcount or 0;
653 -- check each of the bags on the player
654 for bag=0, NUM_BAG_FRAMES do
655 local slot;
656 slot, skipcount = CheckThisBag(bag, id, skipcount);
657 if ( slot ) then
658 return bag, slot, false;
659 end
660 end
661  
662 for idx=1,19,1 do
663 local slot = PlayerSlotNames[idx].id;
664 local c, i, n = OutfitDisplayFrame_GetItemInfo(nil, slot);
665 if ( (id and i and id == i) or (name and n and name == n) ) then
666 if ( skipcount == 0 ) then
667 return nil, slot, false;
668 end
669 skipcount = skipcount - 1;
670 end
671 end
672  
673 -- look in the bank
674 -- Geez, this is ugly
675 local bankbags = { BANK_CONTAINER, 5, 6, 7, 8, 9, 10 };
676 for b=1,7 do
677 local slot;
678 slot, skipcount = CheckThisBag(bankbags[b], id, skipcount);
679 if ( slot ) then
680 return bankbags[b], slot, true;
681 end
682 end
683 -- return nil, nil, nil;
684 end
685  
686 -- look for the outfit anywhere we can find it
687 -- we should look in the bank someday
688 function OutfitDisplayFrame_FindOutfit(outfit)
689 -- look for every item, if we find everything return a table of locations
690 -- otherwise don't return anything
691 if ( not outfit ) then
692 return nil;
693 end
694 local spots = { };
695 for slotName in outfit do
696 if ( outfit[slotName].use and not outfit[slotName].empty ) then
697 local bag, slot = FindThisItem( outfit[slotName].item );
698 if ( not bag and not slot ) then
699 spots = nil;
700 return nil;
701 else
702 spots[slotName] = { };
703 spots[slotName].bag = bag;
704 spots[slotName].slot = slot;
705 end
706 end
707 end
708 return spots;
709 end
710  
711 function OutfitDisplayFrame_CanFindOutfit(outfit)
712 if ( not outfit ) then
713 return false;
714 end
715 for slotName in outfit do
716 if ( outfit[slotName].used and not outfit[slotName].empty ) then
717 local bag, slot = FindThisItem( outfit[slotName].item );
718 if ( not bag and not slot ) then
719 return false;
720 end
721 end
722 end
723 return true;
724 end
725  
726 local function CheckSwitchWillFail(frame, outfit)
727 local contents, empties = OutfitDisplayFrame_GetBagInfo();
728 local numfree = table.getn(empties);
729 local needfree = 0;
730 local missing = false;
731 local banked = false;
732 for i=1,19,1 do
733 local slotName = PlayerSlotNames[i].name;
734 local check;
735 if ( outfit ) then
736 check = outfit[slotName];
737 else
738 check = getglobal(frame:GetName()..slotName);
739 end
740 if ( check and check.used ) then
741 if ( not check.empty ) then
742 local bag, slot, inbank = FindThisItem( check.item );
743 if ( not bag and not slot ) then
744 -- pretend it's still in the bank and we just can't get it
745 if ( check.banked ) then
746 check.banked = true;
747 banked = true;
748 else
749 missing = true;
750 end
751 check.missing = not check.banked;
752 else
753 check.missing = nil;
754 check.banked = inbank;
755 end
756 else
757 local slot = PlayerSlotNames[i].id;
758 if ( OutfitDisplayFrame_GetItemInfo(nil, slot) ) then
759 needfree = needfree + 1;
760 end
761 end
762 end
763 end
764 if ( missing ) then
765 return OUTFITDISPLAYFRAME_ITEMSNOTFOUND;
766 end
767 if ( banked and not BankIsOpen ) then
768 return OUTFITDISPLAYFRAME_ITEMSINBANK;
769 end
770 if ( needfree > numfree ) then
771 return OUTFITDISPLAYFRAME_NOTENOUGHFREESPACE;
772 end
773 return nil;
774 end
775  
776 function OutfitDisplayFrame_SwitchWillFail(frame, outfit)
777 if ( not outfit ) then
778 return OUTFITDISPLAYFRAME_INVALIDOUTFIT;
779 end
780 -- if we're swapping, fail early
781 if ( OutfitDisplayFrame_IsSwapping() ) then
782 return OUTFITDISPLAYFRAME_TOOFASTMSG;
783 end
784 return CheckSwitchWillFail(frame, outfit);
785 end
786  
787 function OutfitDisplayFrame_IsWearing(outfit)
788 if ( not outfit ) then
789 return false;
790 end
791 for slotName in outfit do
792 if ( outfit[slotName].used and not outfit[slotName].empty ) then
793 local slot = GetInventorySlotInfo(slotName);
794 local link = GetInventoryItemLink("player", slot);
795 local foundit = false;
796 if ( link ) then
797 local color, item, name = SplitLink(link);
798 if ( item == outfit[slotName].item ) then
799 foundit = true;
800 end
801 end
802 if ( not foundit ) then
803 return false;
804 end
805 end
806 end
807 return true;
808 end
809  
810 function OutfitDisplayFrame_UpdateMessage(frame, outfit)
811 if ( frame and outfit ) then
812 local msg = CheckSwitchWillFail(frame, outfit);
813 local messages = getglobal(frame:GetName().."Message");
814 if ( msg ) then
815 messages:Show();
816 messages:SetText(msg);
817 messages:SetTextColor(1, 1, 1);
818 else
819 messages:SetText("");
820 messages:Hide();
821 end
822 end
823 end
824  
825 function OutfitDisplayFrame_GetSlotContents(slotName, returnEmpty)
826 if ( slotName ~= "Options" ) then
827 local slot = GetInventorySlotInfo(slotName);
828 local link = GetInventoryItemLink("player", slot);
829 if ( link ) then
830 local contents = {};
831 local color, item, name = SplitLink(link);
832 contents.item = item;
833 contents.name = name;
834 contents.color = color;
835 contents.texture = texture;
836 contents.used = true;
837 local bag, slot = FindThisItem( item );
838 contents.bag = bag;
839 contents.slot = slot;
840 return contents;
841 end
842 end
843 -- return nil;
844 end
845  
846 function OutfitDisplayFrame_GetWearing(check)
847 local wearing = {};
848 if ( check ) then
849 for slotName in check do
850 wearing[slotName] = OutfitDisplayFrame_GetSlotContents(slotName);
851 end
852 -- make sure we pick up the off hand if we're going to put something
853 -- two-handed in the main hand slot
854 local mainhand = check["MainHandSlot"];
855 local offhand = wearing["SecondaryHandSlot"];
856 if ( not offhand and mainhand and not IsItemOneHanded(mainhand.item) ) then
857 wearing["SecondaryHandSlot"] = OutfitDisplayFrame_GetSlotContents("SecondaryHandSlot");
858 end
859 else
860 for i=1,19,1 do
861 local slotName = PlayerSlotNames[i].name;
862 wearing[slotName] = OutfitDisplayFrame_GetSlotContents(slotName);
863 end
864 end
865 if ( not ShowingCloak() or not ShowingHelm() ) then
866 wearing["Options"] = {}
867 wearing["Options"].cloak = not(not ShowingCloak());
868 wearing["Options"].helm = not(not ShowingHelm());
869 end
870 return wearing;
871 end
872  
873 function OutfitDisplayFrame_FindLastEmptyBagSlot(skipcount, bag_affinity, slot_affinity)
874 skipcount = skipcount or 0;
875  
876 -- try to put the item in the requested affinity, if possible
877 if bag_affinity and slot_affinity and
878 not GetContainerItemInfo(bag_affinity, slot_affinity) then
879 return bag_affinity, slot_affinity;
880 end
881  
882 -- if we couldn't get the bag and slot we wanted, just try the same bag
883 if bag_affinity then
884 for j=GetContainerNumSlots(bag_affinity),1,-1 do
885 if not GetContainerItemInfo(bag_affinity,j) then
886 if skipcount == 0 then return bag_affinity,j; end
887 skipcount = skipcount - 1;
888 end
889 end
890 end
891  
892 -- no affinity, check all bags
893 for i=NUM_BAG_FRAMES,0,-1 do
894 -- but skip any bag we already have affinity for (because it might have
895 -- already modified skipcount)
896 if bag_affinity ~= i then
897 -- Make sure this isn't a quiver, those won't hold shit
898 local bagName = GetBagName(i);
899 if ( bagName ) then
900 local texture = GetInventoryItemTexture("player",
901 ContainerIDToInventoryID(i));
902  
903 if ( string.find(texture, "INV_Misc_Bag_%d") and not string.find(bagName, AMMOSLOT) ) then
904 for j=GetContainerNumSlots(i),1,-1 do
905 if not GetContainerItemInfo(i,j) then
906 if skipcount == 0 then return i,j; end
907 skipcount = skipcount - 1;
908 end -- if empty
909 end -- for slots
910 end -- if normal bag
911 end -- if there is a bag
912 end -- if not affinity bag
913 end -- for bags
914  
915 -- not found return nil,nil implicitly
916 end
917  
918 -- okay, let's switch to the specified outfit
919 -- code liberally borrowed from "WeaponQuickSwap - by CapnBry"
920  
921 -- list functions
922 local function swapentry_print(entry)
923 if ( DEFAULT_CHAT_FRAME ) then
924 local msg = "Entry "..tonil(entry.sb)..","..tonil(entry.si)..","..tonil(entry.db)..","..tonil(entry.di);
925 DEFAULT_CHAT_FRAME:AddMessage(msg, 1.0,0,0);
926 end
927 end
928  
929 local function swaplist_print(list)
930 while ( list ) do
931 swapentry_print(list);
932 list = list.next;
933 end
934 end
935  
936 local function swaplist_push(list, sb, si, db, di, helm, cloak)
937 list = { next = list, sb = sb, si = si, db = db, di = di,
938 helm = helm, cloak = cloak };
939 return list;
940 end
941  
942 local function swaplist_popfirst(list)
943 if not list then return; end
944 list = list.next;
945 return list;
946 end
947  
948 -- Unit variable to hold the stack of swaps
949 local outfitswap = nil;
950  
951 function OutfitDisplayFrame_IsSwapping()
952 if ( outfitswap or OutfitDisplayFrame_AnyItemLocked() ) then
953 return true;
954 else
955 return false;
956 end
957 end
958  
959 -- First we do everything except the main hand and secondary hand
960 -- Then we do the hard stuff
961  
962 function OutfitDisplayFrame_ItemIsLocked(bag, slot)
963 if not bag and not slot then
964 return false;
965 end
966  
967 if not bag then
968 return IsInventoryItemLocked(slot);
969 else
970 local _,_,locked = GetContainerItemInfo(bag,slot);
971 return locked;
972 end
973 end
974  
975 function OutfitDisplayFrame_AnyItemLocked()
976 -- Checks all the bags and the equipped slots to see if any are still locked
977 for i=0,NUM_BAG_FRAMES do
978 for j=1,GetContainerNumSlots(i) do
979 local _,_,locked = GetContainerItemInfo(i,j);
980 if ( locked ) then
981 return true;
982 end
983 end
984 end
985 for i=1,19,1 do
986 if ( IsInventoryItemLocked(PlayerSlotNames[i].id) ) then
987 return true;
988 end
989 end
990 return false;
991 end
992  
993 function OutfitDisplayFrame_ExecuteSwapIteration()
994 if not outfitswap then
995 PerformSlowerSwap = false;
996 return;
997 end
998  
999 if OutfitDisplayFrame_ItemIsLocked(outfitswap.sb, outfitswap.si) or
1000 OutfitDisplayFrame_ItemIsLocked(outfitswap.db, outfitswap.di) then
1001 return;
1002 end
1003  
1004 if not outfitswap.sb then
1005 SavedPickupInventoryItem(outfitswap.si);
1006 else
1007 SavedPickupContainerItem(outfitswap.sb, outfitswap.si);
1008 end
1009  
1010 if not outfitswap.db then
1011 if not outfitswap.di then
1012 PutItemInBackpack();
1013 else
1014 SavedPickupInventoryItem(outfitswap.di);
1015 end
1016 else
1017 SavedPickupContainerItem(outfitswap.db, outfitswap.di);
1018 end
1019  
1020 if ( outfitswap.cloak ~= nil ) then
1021 ShowCloak(outfitswap.cloak);
1022 elseif ( outfitswap.helm ~= nil ) then
1023 ShowHelm(outfitswap.helm);
1024 end
1025  
1026 outfitswap = swaplist_popfirst(outfitswap);
1027 if ( outfitswap and not PerformSlowerSwap ) then
1028 return OutfitDisplayFrame_ExecuteSwapIteration();
1029 end
1030 end
1031  
1032 function OutfitDisplayFrame_SwitchOne(outfit, index, skipcount, showhelm, showcloak)
1033 local slotName = PlayerSlotNames[index].name;
1034 if ( outfit and outfit[slotName] ) then
1035 local invslot = GetInventorySlotInfo(slotName);
1036 if ( outfit[slotName].empty ) then
1037 local bag, slot = OutfitDisplayFrame_FindLastEmptyBagSlot(skipcount);
1038 outfitswap = swaplist_push(outfitswap, nil, invslot, bag, slot);
1039 skipcount = skipcount + 1;
1040 else
1041 local helmflag, cloakflag;
1042 if ( slotName == "HeadSlot" ) then
1043 helmflag = showhelm;
1044 elseif ( slotName == "BackSlot" ) then
1045 cloakflag = showcloak;
1046 end
1047 local bag, slot = FindThisItem( outfit[slotName].item );
1048 -- either we couldn't find it, or it's where it should be
1049 if ( bag ~= nil or slot ~= invslot ) then
1050 outfitswap = swaplist_push(outfitswap, bag, slot, nil, invslot, helmflag, cloakflag);
1051 end
1052 end
1053 end
1054 return skipcount;
1055 end
1056  
1057 local function OnSwapError(error)
1058 outfitswap = nil;
1059 if ( UIErrorsFrame ) then
1060 UIErrorsFrame:AddMessage(error, 1.0, 0.1, 0.1, 1.0, UIERRORS_HOLD_TIME);
1061 end
1062 return;
1063 end
1064  
1065 function OutfitDisplayFrame_SwitchOutfit(outfit)
1066 if ( CursorHasItem() or OutfitDisplayFrame_IsSwapping() ) then
1067 return OnSwapError(OUTFITDISPLAYFRAME_TOOFASTMSG);
1068 end
1069 if ( not outfit ) then
1070 return OnSwapError(OUTFITDISPLAYFRAME_INVALIDOUTFIT);
1071 end
1072  
1073 -- might not need to swap slowly
1074 PerformSlowerSwap = false;
1075  
1076 -- need to check to see that we have enough room
1077 local old = OutfitDisplayFrame_GetWearing(outfit);
1078  
1079 local showcloak;
1080 local showhelm;
1081 if ( outfit["Options"] ) then
1082 showhelm = outfit["Options"].helm;
1083 showcloak = outfit["Options"].cloak;
1084 end
1085  
1086 -- do everything except weapon slots
1087 local skipcount = 0;
1088 for i=1,16,1 do
1089 skipcount = OutfitDisplayFrame_SwitchOne(outfit, i, skipcount, showhelm, showcloak);
1090 end
1091 OutfitDisplayFrame_SwitchOne(outfit, 19, skipcount);
1092  
1093 if ( not outfit["MainHandSlot"] and not outfit["SecondaryHandSlot"] ) then
1094 OutfitDisplayFrame_ExecuteSwapIteration();
1095 return old;
1096 end
1097  
1098 local mainhandslot = GetInventorySlotInfo("MainHandSlot");
1099 local secondaryslot = GetInventorySlotInfo("SecondaryHandSlot");
1100 local mainhand = outfit["MainHandSlot"];
1101 local offhand = outfit["SecondaryHandSlot"];
1102 -- now do hands
1103 local m_sb;
1104 local m_si;
1105 local o_sb;
1106 local o_si;
1107 local m_ok = not mainhand or not mainhand.item;
1108 local o_ok = not offhand or not offhand.item;
1109  
1110 if ( mainhand and mainhand.item ) then
1111 m_sb, m_si = FindThisItem(mainhand.item);
1112 m_ok = ( not m_sb and m_si == mainhandslot );
1113 end
1114 if ( offhand and offhand.item ) then
1115 local multiples = 0;
1116 if ( mainhand and mainhand.item == offhand.item ) then
1117 multiples = 1;
1118 end
1119 o_sb, o_si = FindThisItem( offhand.item, multiples);
1120 o_ok = ( not o_sb and o_si == secondaryslot );
1121 end
1122  
1123 if ( not m_ok ) then
1124 -- do we need two of these?
1125 if ( o_ok and not m_sb and m_si == secondaryslot ) then
1126 m_sb, m_si = FindThisItem( mainhand.item, 1);
1127 end
1128 end
1129  
1130 -- moving from bags
1131 if ( m_sb and o_sb ) then
1132 -- insert them backwards, since they get popped off
1133 -- main hand has to get done first in case it's currently
1134 -- a two hander
1135 outfitswap = swaplist_push(outfitswap, o_sb, o_si, nil, secondaryslot);
1136 outfitswap = swaplist_push(outfitswap, m_sb, m_si, nil, mainhandslot);
1137 elseif ( not m_sb and m_si == secondaryslot and not o_sb and o_si == mainhandslot ) then
1138 outfitswap = swaplist_push(outfitswap, nil, mainhandslot, nil, secondaryslot);
1139 else
1140 -- Install main hand
1141 if not m_ok then
1142 -- if nothing going to the main hand
1143 if ( not m_sb and not m_si ) then
1144 -- and the main is not going to the off: put it in a bag
1145 if not ( not o_sb and o_si == mainhandslot) then
1146 local bb, bi = OutfitDisplayFrame_FindLastEmptyBagSlot(skipcount);
1147 if not (bb and bi) then
1148 return OnSwapError(OUTFITDISPLAYFRAME_NOTENOUGHFREESPACE);
1149 end
1150 skipcount = skipcount + 1;
1151 outfitswap = swaplist_push(outfitswap, nil, mainhandslot, bb, bi);
1152 -- when moving A,"" -> "",B where A is a 2h, the offhand
1153 -- doesn't lock properly, so work around it by swapping
1154 -- slowly (only one swap per lock notify)
1155 PerformSlowerSwap = not IsItemOneHanded(mainhand.item);
1156 end
1157 else
1158 outfitswap = swaplist_push(outfitswap, m_sb, m_si, nil, mainhandslot);
1159 end
1160 end
1161  
1162 -- Load offhand if not already there
1163 if not o_ok then
1164 if ( not o_sb and not o_si ) then
1165 if not (not m_sb and m_si == secondaryslot) then
1166 local bb, bi;
1167 if LastOffSource then
1168 bb, bi = OutfitDisplayFrame_FindLastEmptyBagSlot(skipcount,
1169 LastOffSource.bag, LastOffSource.slot);
1170 else
1171 bb, bi = OutfitDisplayFrame_FindLastEmptyBagSlot(skipcount);
1172 end
1173  
1174 if not (bb and bi) then
1175 return OnSwapError(OUTFITDISPLAYFRAME_NOTENOUGHFREESPACE);
1176 end
1177 skipcount = skipcount + 1;
1178 outfitswap = swaplist_push(outfitswap, nil, secondaryslot, bb, bi);
1179 end
1180 else
1181 -- if the main hand weapon is coming from the offhand slot
1182 -- we need to fix up its source to be where the offhand is
1183 -- GOING to be after the bag->off swap
1184 if outfitswap and ( not m_sb and m_si == secondaryslot) then
1185 outfitswap.sb = o_sb;
1186 outfitswap.si = o_si;
1187 -- don't set o_sb, o_si they're tested later
1188 end
1189  
1190 outfitswap = swaplist_push(outfitswap, o_sb, o_si, nil, secondaryslot);
1191 end
1192 end
1193  
1194 -- Special Case: Moving off to main, and not main to off
1195 -- This is because maybe the main hand weapon is main only
1196 if (not m_sb and m_si == secondaryslot) and not ( not o_sb and o_si == mainhandslot) then
1197 local bb, bi = OutfitDisplayFrame_FindLastEmptyBagSlot(skipcount);
1198 if not (bb and bi) then
1199 return OnSwapError(OUTFITDISPLAYFRAME_NOTENOUGHFREESPACE);
1200 end
1201 skipcount = skipcount + 1;
1202 outfitswap = swaplist_push(outfitswap, nil, mainhandslot, bb, bi);
1203 end
1204  
1205 -- Same thing for off hand
1206 if (not o_sb and o_si == mainhandslot) and not (not m_sb and m_si == secondaryslot) then
1207 local bb, bi = OutfitDisplayFrame_FindLastEmptyBagSlot(skipcount);
1208 if not (bb and bi) then
1209 return OnSwapError(OUTFITDISPLAYFRAME_NOTENOUGHFREESPACE);
1210 end
1211 skipcount = skipcount + 1;
1212 outfitswap = swaplist_push(outfitswap, nil, mainhandslot, bb, bi);
1213 end
1214  
1215 if o_sb then
1216 LastOffSource = { bag = o_sb, slot = o_si };
1217 end
1218 end
1219 -- Start moving
1220 OutfitDisplayFrame_ExecuteSwapIteration();
1221 return old;
1222 end