vanilla-wow-addons – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 --[[
2 WeaponQuickSwap - by CapnBry
3 A script for moving weapons by name between hands, taking slot locking into account.
4  
5 Public Domain. Feel free to use any of this code or ideas in your own mods
6 --]]
7  
8 -- Unit variable to hold the stack of weapon swaps
9 local wswap = nil;
10 local WeaponSwap_IsSwapping = nil;
11  
12 --
13 -- "Exported" functions for the user
14 --
15 function MageWeaponSwap(...)
16 -- insert them backwards! off, main like a stack
17 table.insert(arg, 1, 18);
18 table.insert(arg, 1, 16);
19 return WeaponQuickSwap_WeaponSwapCommon(arg);
20 end
21  
22 function WeaponSwap(...)
23 -- insert them backwards! off, main like a stack
24 table.insert(arg, 1, 17);
25 table.insert(arg, 1, 16);
26 return WeaponQuickSwap_WeaponSwapCommon(arg);
27 end
28  
29 --
30 -- Internal functions and callbacks
31 --
32 function WeaponQuickSwap_OnLoad()
33 if not Print then Print = function (x)
34 ChatFrame1:AddMessage(x, 1.0, 1.0, 1.0);
35 end
36 end
37  
38 if not WeaponSetsExchange then WeaponSetsExchange = WeaponSwap; end;
39  
40 this:RegisterEvent("ITEM_LOCK_CHANGED");
41 --this:RegisterEvent("UPDATE_BONUS_ACTIONBAR");
42 --this:RegisterEvent("BAG_UPDATE");
43 --this:RegisterEvent("UNIT_INVENTORY_CHANGED");
44 --this:RegisterEvent("UNIT_MODEL_CHANGED");
45 end
46  
47 function WeaponQuickSwap_OnEvent(...)
48 if (arg[1] == "ITEM_LOCK_CHANGED") and not arg[2] then
49 return WeaponQuickSwap_ExecuteSwapIteration();
50 end
51 end
52  
53 local function swaplist_push(list, sb, si, db, di)
54 list = { next = list, sb = sb, si = si, db = db, di = di };
55 return list;
56 end
57  
58 local function swaplist_popfirst(list)
59 if not list then return; end
60  
61 list = list.next;
62 return list;
63 end
64  
65 function WeaponQuickSwap_ItemIsLocked(bag,slot)
66 if bag == -1 and slot == -1 then return false; end
67  
68 if bag == -1 then
69 return IsInventoryItemLocked(slot);
70 else
71 local _,_,retval = GetContainerItemInfo(bag,slot);
72 return retval;
73 end
74 end
75  
76 function WeaponQuickSwap_AnyItemLocked()
77 -- Checks all the bags and the 3 equip slots to see if any slot is still locked
78 for i=0,NUM_BAG_FRAMES do
79 for j=1,GetContainerNumSlots(i) do
80 local _,_,retval = GetContainerItemInfo(i,j);
81 if retVal then
82 return retVal;
83 end
84 end
85 end
86  
87 return IsInventoryItemLocked(16) or IsInventoryItemLocked(17) or IsInventoryItemLocked(18);
88 end
89  
90 function WeaponQuickSwap_ExecuteSwapIteration()
91 if not wswap then
92 if WeaponSwap_IsSwapping and not WeaponQuickSwap_AnyItemLocked() then
93 return WeaponQuickSwap_OnSwapComplete();
94 end
95  
96 return;
97 end
98  
99 if WeaponQuickSwap_ItemIsLocked(wswap.sb, wswap.si) or
100 WeaponQuickSwap_ItemIsLocked(wswap.db, wswap.di) then
101 return;
102 end
103  
104 if wswap.sb == -1 then
105 PickupInventoryItem(wswap.si);
106 else
107 PickupContainerItem(wswap.sb, wswap.si);
108 end
109  
110 if wswap.db == -1 then
111 if wswap.di == -1 then
112 PutItemInBackpack();
113 else
114 PickupInventoryItem(wswap.di);
115 end
116 else
117 PickupContainerItem(wswap.db, wswap.di);
118 end
119  
120 wswap = swaplist_popfirst(wswap);
121 if wswap and not WeaponQuickSwap_PerformSlowerSwap then
122 return WeaponQuickSwap_ExecuteSwapIteration();
123 end
124 end
125  
126 function WeaponQuickSwap_OnSwapComplete()
127 WeaponSwap_IsSwapping = nil;
128 -- this is just here to allow people to hook the completion event
129 end
130  
131 function WeaponQuickSwap_OnSwapError(error)
132 WeaponSwap_IsSwapping = nil;
133  
134 -- this is just here to allow people to hook the completion event
135 return Print(error);
136 end
137  
138 function WeaponQuickSwap_GetItemName(bag, slot)
139 local linktext = nil;
140  
141 if (bag == -1) then
142 linktext = GetInventoryItemLink("player", slot);
143 else
144 linktext = GetContainerItemLink(bag, slot);
145 end
146  
147 if linktext then
148 -- local _,_,name = string.find(linktext, "(%b[])");
149 local _,_,name = string.find(linktext, "^.*%[(.*)%].*$");
150 return name;
151 else
152 return "";
153 end
154 end
155  
156 function WeaponQuickSwap_FindItem(name, skipcount)
157 skipcount = skipcount or 0;
158  
159 -- First check the inventory slots 16, 17 and 18
160 for i= 16,18,1 do
161 if (WeaponQuickSwap_GetItemName(-1,i) == name) then
162 if skipcount == 0 then return -1,i; end
163 skipcount = skipcount - 1;
164 end
165 end
166  
167 -- not found check bags
168 for i=NUM_BAG_FRAMES,0,-1 do
169 for j=GetContainerNumSlots(i),1,-1 do
170 if (WeaponQuickSwap_GetItemName(i,j) == name) then
171 if skipcount == 0 then return i,j; end
172 skipcount = skipcount - 1;
173 end
174 end
175 end
176  
177 -- not found return nil,nil implicitly
178 end
179  
180 function WeaponQuickSwap_FindLastEmptyBagSlot(skipcount, bag_affinity, slot_affinity)
181 skipcount = skipcount or 0;
182  
183 -- try to put the item in the requested affinity, if possible
184 if bag_affinity and slot_affinity and
185 not GetContainerItemInfo(bag_affinity, slot_affinity) then
186 return bag_affinity, slot_affinity;
187 end
188  
189 -- if we couldn't get the bag and slot we wanted, just try the same bag
190 if bag_affinity then
191 for j=GetContainerNumSlots(bag_affinity),1,-1 do
192 if not GetContainerItemInfo(bag_affinity,j) then
193 if skipcount == 0 then return bag_affinity,j; end
194 skipcount = skipcount - 1;
195 end
196 end
197 end
198  
199 -- no affinity, chek all bags
200 for i=NUM_BAG_FRAMES,0,-1 do
201 -- but skip any bag we already have affinity for (because it might have
202 -- already modified skipcount
203 if bag_affinity ~= i then
204 -- Make sure this isn't a quiver, those won't hold shit
205 local bagName = GetBagName(i);
206 if bagName and not (
207 string.find(bagName, "Quiver") or
208 string.find(bagName, "Ammo Pouch") or
209 string.find(bagName, "Shot Pouch")
210 ) then
211 for j=GetContainerNumSlots(i),1,-1 do
212 if not GetContainerItemInfo(i,j) then
213 if skipcount == 0 then return i,j; end
214 skipcount = skipcount - 1;
215 end -- if empty
216 end -- for slots
217 end -- if normal bag
218 end -- if not affinity bag
219 end -- for bags
220  
221 -- not found return nil,nil implicitly
222 end
223  
224 function WeaponQuickSwap_FindCurrentSetIndex(mainhandslot, offhandslot, startIndex, setsList)
225 -- loop through the paramters and find what we have in our hands
226 local main, off;
227 local argidx = startIndex;
228 local retVal;
229 while setsList[argidx] and setsList[argidx+1] do
230 main, off = setsList[argidx], setsList[argidx+1];
231  
232 if not main then break; end
233  
234 -- don't need to specify the offhand if this is just a single deal
235 if not off then off = ""; end
236  
237 --Print("Checking:"..main..","..off);
238  
239 if (main == "*" or WeaponQuickSwap_GetItemName(-1, mainhandslot) == main) and
240 (off == "*" or WeaponQuickSwap_GetItemName(-1, offhandslot) == off) then
241 --Print("Found set at idx:"..argidx);
242 retVal = argidx;
243 break;
244 end
245  
246 argidx = argidx + 2;
247 end
248  
249 return retVal;
250 end
251  
252 function WeaponQuickSwap_WeaponSwapCommon(arg)
253 -- I explicitly use arg as a parameter instead of ... to prevent
254 -- having to call unpack on the arg list from the caller
255  
256 if WeaponSwap_IsSwapping then
257 return
258 end
259  
260 WeaponSwap_IsSwapping = 1;
261 wswap = nil;
262 WeaponQuickSwap_PerformSlowerSwap = nil;
263 --if CursorHasItem() then ResetCursor() end
264  
265 local mainhandslot, offhandslot = arg[1], arg[2];
266 local main, off;
267  
268 --Print("Hands are:"..mainhandslot..","..offhandslot);
269 local matchingsetidx = WeaponQuickSwap_FindCurrentSetIndex(mainhandslot, offhandslot, 3, arg);
270  
271 -- if we found a match, and there is at least one weapon speficied in the next
272 -- set, then use that set. Else use the first 2
273 if matchingsetidx and arg[matchingsetidx+2] then
274 main, off = arg[matchingsetidx+2], arg[matchingsetidx+3];
275 else
276 main, off = arg[3], arg[4];
277 end
278  
279 --Print("Picking:"..main..","..off);
280 -- don't need to specify the offhand if this is just a single deal
281 if not off then off = ""; end
282  
283 if not main then
284 return WeaponQuickSwap_OnSwapError("No weapons set found to switch. Not enough arguments?");
285 end
286  
287 -- see what's already set up
288 local m_ok = main == "*" or WeaponQuickSwap_GetItemName(-1, mainhandslot) == main;
289 local o_ok = off == "*" or WeaponQuickSwap_GetItemName(-1, offhandslot) == off;
290  
291 local m_sb, m_si = -1, mainhandslot;
292 if not m_ok then
293 if main == "" then
294 m_sb, m_si = -1, -1;
295 else
296 m_sb, m_si = WeaponQuickSwap_FindItem(main);
297 -- if FindItem returned the offhand weapon and it is already ok
298 -- don't remove it. Look harder
299 if o_ok and m_sb == -1 and m_si == offhandslot then
300 m_sb, m_si = WeaponQuickSwap_FindItem(main, 1);
301 end
302 end
303 if not (m_sb and m_si) then
304 return WeaponQuickSwap_OnSwapError("Can not find mainhand item: "..main);
305 end
306 end -- if not m_ok
307  
308 local multiinst;
309 -- if you're using 2 of the same weapon FindItem needs to
310 -- know not to just return the first
311 if main == off then multiinst = 1; else multiinst = 0; end
312  
313 local o_sb, o_si = -1, offhandslot;
314 if not o_ok then
315 if off == "" then
316 o_sb, o_si = -1, -1;
317 else
318 o_sb, o_si = WeaponQuickSwap_FindItem(off, multiinst);
319 -- note that here we don't have to "look harder" because
320 -- that would mean that both weapons are the same so multinst
321 -- would already be set to 1
322 end
323 if not (o_sb and o_si) then
324 return WeaponQuickSwap_OnSwapError("Can not find offhand item: "..off);
325 end
326 end -- if not o_ok
327  
328 -- Moving both hands from bags, that's easy
329 if m_sb ~= -1 and o_sb ~= -1 then
330 -- Load main first because if it is a 2h and we try to load offhand
331 -- we get a "Cannot Equip that with a Two-handed weapon" error
332 PickupContainerItem(m_sb, m_si);
333 PickupInventoryItem(mainhandslot);
334 PickupContainerItem(o_sb, o_si);
335 PickupInventoryItem(offhandslot);
336 WeaponQuickSwap_LastOffSource = { bag = o_sb, slot = o_si };
337 return;
338 end
339  
340 -- Simple hand swap
341 if m_sb == -1 and m_si == offhandslot and o_sb == -1 and o_si == mainhandslot then
342 PickupInventoryItem(mainhandslot);
343 PickupInventoryItem(offhandslot);
344 return;
345 end
346  
347 -- Push the list. We want to:
348 -- Take the mainhand weapon out if it isn't going to offhand
349 -- Move from wherever to the offhand. If offhand is supposed to be empty, empty it.
350 -- Install the main hand weapon. No blank main hand is supported unless are going to be.
351 --
352 -- Do it backward, the swaplist is a stack
353  
354 local skipcount = 0;
355  
356 -- Install main hand
357 if not m_ok then
358 -- if nothing going to the main hand
359 if (m_sb == -1 and m_si == -1) then
360 -- and the main is not going to the off: put it in a bag
361 if not (o_sb == -1 and o_si == mainhandslot) then
362 local bb, bi = WeaponQuickSwap_FindLastEmptyBagSlot(skipcount);
363 if not (bb and bi) then
364 return WeaponQuickSwap_OnSwapError("Not enough empty bag slots to perform swap.");
365 end
366 skipcount = skipcount + 1;
367 wswap = swaplist_push(wswap, -1, mainhandslot, bb, bi);
368  
369 -- when moving A,"" -> "",B where A is a 2h, the offhand doesn't lock properly
370 -- so work around it by swapping slowly (only one swap per lock notify)
371 WeaponQuickSwap_PerformSlowerSwap = true;
372 end
373 else
374 wswap = swaplist_push(wswap, m_sb, m_si, -1, mainhandslot);
375 end
376 end
377  
378 -- Load offhand if not already there
379 if not o_ok then
380 if (o_sb == -1 and o_si == -1) then
381 if not (m_sb == -1 and m_si == offhandslot) then
382  
383 local bb, bi;
384 if WeaponQuickSwap_LastOffSource then
385 bb, bi = WeaponQuickSwap_FindLastEmptyBagSlot(skipcount,
386 WeaponQuickSwap_LastOffSource.bag, WeaponQuickSwap_LastOffSource.slot);
387 else
388 bb, bi = WeaponQuickSwap_FindLastEmptyBagSlot(skipcount);
389 end
390  
391 if not (bb and bi) then
392 return WeaponQuickSwap_OnSwapError("Not enough empty bag slots to perform swap.");
393 end
394 skipcount = skipcount + 1;
395 wswap = swaplist_push(wswap, -1, offhandslot, bb, bi);
396 end
397 else
398 -- if the main hand weapon is coming from the offhand slot
399 -- we need to fix up its source to be where the offhand is
400 -- GOING to be after the bag->off swap
401 if wswap and (m_sb == -1 and m_si == offhandslot) then
402 wswap.sb = o_sb;
403 wswap.si = o_si;
404 -- don't set o_sb, o_si they're tested later
405 end
406  
407 wswap = swaplist_push(wswap, o_sb, o_si, -1, offhandslot);
408 end
409 end
410  
411 -- Special Case: Moving off to main, and not main to off
412 -- This is because maybe the main hand weapon is main only
413 if (m_sb == -1 and m_si == offhandslot) and not (o_sb == -1 and o_si == mainhandslot) then
414 local bb, bi = WeaponQuickSwap_FindLastEmptyBagSlot(skipcount);
415 if not (bb and bi) then
416 return WeaponQuickSwap_OnSwapError("Not enough empty bag slots to perform swap.");
417 end
418 skipcount = skipcount + 1;
419 wswap = swaplist_push(wswap, -1, mainhandslot, bb, bi);
420 end
421  
422 -- Same thing for off hand
423 if (o_sb == -1 and o_si == mainhandslot) and not (m_sb == -1 and m_si == offhandslot) then
424 local bb, bi = WeaponQuickSwap_FindLastEmptyBagSlot(skipcount);
425 if not (bb and bi) then
426 return WeaponQuickSwap_OnSwapError("Not enough empty bag slots to perform swap.");
427 end
428 skipcount = skipcount + 1;
429 wswap = swaplist_push(wswap, -1, 17, bb, bi);
430 end
431  
432 if o_sb ~= -1 then
433 WeaponQuickSwap_LastOffSource = { bag = o_sb, slot = o_si };
434 end
435  
436 -- Start moving
437 return WeaponQuickSwap_ExecuteSwapIteration();
438 end