-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCJRAutoBank.lua
executable file
·585 lines (507 loc) · 16.2 KB
/
CJRAutoBank.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
-- boilerplate for each source file to encapsulate everything in CJRAB:
if CJRAB == nil then CJRAB = {} end
local CJRAB = CJRAB
--=============================================================================
-- Globals
CJRAB.AddonName = 'CJRAutoBank'
-- TransferQueue
CJRAB.TransferQueue = {}
CJRAB.TransferBags = {}
CJRAB.TransferQueueIndex = 1
CJRAB.TransferDelay = 250 -- Delay between bag transfers in ms
--=============================================================================
-- Output, debugging and console I/O
--=====================================
function CJRAB.Msg(fmt, ...)
-- printf to the active chat window
--CHAT_SYSTEM:AddMessage(string.format(fmt, ...))
if CJRAB.DryRun then
d("CJRAB(DryRun): " .. string.format(fmt, ...))
else
d("CJRAB: " .. string.format(fmt, ...))
end
end
function CJRAB.Err(fmt, ...)
d("CJRAB: ERROR: " .. string.format(fmt, ...))
end
function CJRAB.Dbg(fmt, ...)
if CJRAB.Debug then
d("DEBUG> " .. string.format(fmt, ...))
end
end
local Msg = CJRAB.Msg
local Err = CJRAB.Err
local Dbg = CJRAB.Dbg
--=============================================================================
-- GetString with CUSTOM SI_ types
-- CRAFTING_TYPE_*
-- XXX: use GetSkillLineInfo to fill this in...?
CJRAB_SI_CRAFTINGTYPE = {
[0]="NO_CRAFT",
"Blacksmithing", "Clothing", "Enchanting", "Alchemy",
"Provisioning", "Woodworking", "Jewelcrafting" }
CJRAB_SI_CURRENCY = {
[0]="NO_CURRENCY",
"Gold", -- 1
"Alliance Points", -- 2
"Tel Var Stones", -- 3
"Writ Vouchers", -- 4
"Chaotic Creatia", -- 5
"Crown Gems", -- 6
"Crowns", -- 7
"Style Stones", -- 8
"Event Tickets", -- 9
"Undaunted Keys", -- 10
}
-- bag name
CJRAB_SI_BAGNAME = {
[0] = "EQUIP_BAG",
"Backpack", -- 1
"Bank", -- 2
"GuildBank", -- 3 500
"BuyBackBag", -- 4 0
"CraftBag", -- 5 - VirtualBag
"SubscriberBank", -- 6 170+
"Storage Coffer, Fortified", -- 7 30 HouseBank1
"Storage Chest, <1>", -- 8 60 HouseBank2
"Storage Coffer, <2>", -- 9 30 HouseBank3
"Storage Coffer, <3>", -- 10 30 HouseBank4
"Storage Coffer, <4>", -- 11 30 HouseBank5
"Storage Chest, Oaken", -- 12 60 HouseBank6
"Storage Chest, Secure", -- 13 60 HouseBank7
"Storage Chest, <4>", -- 14 60 HouseBank8
"UNKNOWN1", -- 15 0
"UNKNOWN2", -- 16 0
"DeleteBag", -- 17 23
}
--=====================================
function CJRAB.GetString(stype, n)
-- like GetString but with custom CJRAB_SI tables
local s = nil
if stype then
if stype:find("^CJRAB_SI_") then
-- custom table
local table = _G[stype]
s = table[n]
else
-- elseif stype:find("^SI_") then
s = GetString(stype, n)
end
end
return s
end
--=============================================================================
-- Character
CJRAB.Chars = nil
function CJRAB.InitChars()
-- initialize character table
CJRAB.Chars = {}
for i = 1, GetNumCharacters() do
local name, gender, level, classId, raceId,
allianceId, id, locId = GetCharacterInfo(i)
local enabled = false
if CJRAB.CharsEnabled then
enabled = CJRAB.CharsEnabled[i]
end
CJRAB.Chars[i] = {
name = zo_strformat("<<1>>", name),
gender = gender,
level = level,
classId = classId,
raceId = raceId,
allianceId = allianceId,
locationId = locId,
enabled = enabled,
}
end
end
--=====================================
function CJRAB.CharName(char)
-- return the character name for id 'char'
return CJRAB.Chars[char].name
end
--=====================================
function CJRAB.GetChar(str)
-- Return the char index for character called str.
for i = 1, #CJRAB.Chars do
if CJRAB.Chars[i].name == str then
return i
end
end
return nil
end
--=============================================================================
-- MISC UTILITY FUNCTIONS
--=====================================
function CJRAB.ItemName(bag, slot)
-- Return the item's name (as it appears in the tooltip).
local link = GetItemLink(bag, slot, LINK_STYLE_BRACKETS)
return CJRAB.ItemLinkName(link)
end
--=====================================
function CJRAB.ItemLinkName(link)
-- Return the item's name (as it appears in the tooltip).
return zo_strformat(SI_TOOLTIP_ITEM_NAME, link)
end
--=====================================
function CJRAB.BagName(bag)
-- Return the bag's name
return CJRAB.GetString( 'CJRAB_SI_BAGNAME', bag)
end
--=====================================
function CJRAB.BagItems(bag)
-- return an iterator for all slots containing items in bag
local size = GetBagSize(bag)
local slot = -1 -- slots are offset 0
return function()
slot = slot + 1
while slot < size do
if HasItemInSlot(bag, slot) then
return slot
end
slot = slot + 1
end
end
end
--=====================================
function CJRAB.IsCharBound(bag, slot)
return IsItemBound(bag, slot) and
GetItemBindType(bag, slot) == BIND_TYPE_ON_PICKUP_BACKPACK
end
--=====================================
function CJRAB.FetchLeoData()
-- Set up a global table CJRAB.LeoData indexed by char id
-- with data fetched from LeoAltholic
if not LeoAltholic then return end
if not CJRAB.LeoData then CJRAB.LeoData = {} end
for char = 1, #CJRAB.Chars do
CJRAB.LeoData[char] = LeoAltholic.GetCharByName(CJRAB.CharName(char))
end
end
--=====================================
function CJRAB.GetLeoCraftID(ctype)
-- Return LeoAltholic's craft ID (index) for CRAFTING_TYPE ctype.
for leoId, cId in ipairs(LeoAltholic.allCrafts) do
if cId == ctype then
return leoId
end
end
return nil
end
--=============================================================================
-- Logging
--=====================================
function CJRAB.LogSlotUpdate(bag, slot, isNew, change)
-- Log a slot change message
local msg, stack
if change < 0 then
-- no info available for items removed :(
--[[ -- don't bother logging
change = -change
if change > 1 then
msg = string.format("%d items removed", change)
else
msg = "item removed"
end
--]]
return
else
if change > 0 then
if isNew then
msg = "added"
else
msg = "transferred in"
end
if change > 1 then
msg = msg .. " " .. tostring(change)
end
msg = msg .. " " .. CJRAB.ItemName(bag, slot)
stack = GetSlotStackSize(bag, slot)
if stack > change then
msg = msg .. string.format(" (stack: %d)", stack)
end
else
msg = "changed " .. CJRAB.ItemName(bag, slot)
end
end
d(string.format("%s: %s", CJRAB.BagName(bag), msg))
end
--=====================================
function CJRAB.LogTradeHousePurchase(event, index)
-- idx is the pendingPurchaseIndex... XXX: of what?
local countstr = ""
local unitstr = ""
local icon, name, quality, count, seller, timeRemaining,
price, currency, id, pricePerUnit =
GetTradingHouseSearchResultItemInfo(index)
local link = GetTradingHouseSearchResultItemLink(index, 1)
if count > 1 then
countstr = count .. " "
unitstr = "(" .. pricePerUnit .. " ea)"
-- unitstr = string.format("(%.2f ea)", price/count)
end
Msg("Buying %s%s from %s for %d G %s",
countstr, CJRAB.ItemLinkName(link), seller, price, unitstr)
end
--=============================================================================
-- TRANSFERS (move from one bag to another)
-- All transfers use a queue to do the actual transfers
-- which is processed asynchronously by an Update event loop
-- every CJRAB.TransferDelay milliseconds.
-- Because of this, we need to plan the transfers in advance
-- (which slots fill up, which ones are free, etc) on a
-- "clone" of the two bags which we update stack sizes on.
--=====================================
function CJRAB.ResetTransfer()
CJRAB.TransferQueue = {}
CJRAB.TransferQueueIndex = 1
end
--=====================================
function CJRAB.InitTransfer(bankBag)
-- reset any pending transfer Queue and prepare for a new
-- transfer between backpack and bankBag
-- for a new transfer
CJRAB.ResetTransfer()
-- set up the working plan CloneBags
for i = 1, BAG_MAX_VALUE do
if i == BAG_VIRTUAL then
-- virtual bags are weird: all slotId == itemId
CJRAB.TransferBags[i] = nil
else
CJRAB.TransferBags[i] = {}
end
end
CJRAB.TransferBags[bankBag] = CJRAB.CloneBag(bankBag)
CJRAB.TransferBags[BAG_BACKPACK] = CJRAB.CloneBag(BAG_BACKPACK)
end
--=====================================
function CJRAB.ProcessTransfer()
-- Process the TransferQueue in an asynchronous update loop.
-- Every CJRAB.TransferDelay ms, dequeue the next function and execute it.
-- disable Logging during this (extraneous messages)
local saved_logging = CJRAB.Logging
CJRAB.Logging = false
-- re-enable it after a delay (just add to the TxQueue)
-- XXX: not enough delay though; get a logged msg at the end
table.insert(CJRAB.TransferQueue, function()
CJRAB.Logging = saved_logging end)
local delay = CJRAB.TransferDelay
if not delay then delay = 1000 end
if delay < 250 then delay = 250 end -- don't attract ZMax attention
EVENT_MANAGER:RegisterForUpdate(CJRAB.AddonName, delay,
function()
if CJRAB.TransferQueueIndex <= #CJRAB.TransferQueue then
local func = CJRAB.TransferQueue[CJRAB.TransferQueueIndex]
CJRAB.TransferQueueIndex = CJRAB.TransferQueueIndex + 1
func()
else
CJRAB.ResetTransfer()
EVENT_MANAGER:UnregisterForUpdate(CJRAB.AddonName)
end
end)
end
--=====================================
local function do_transfer(bag, slot, dstBag, dstSlot, count, msg)
-- Transfer count items from bag/slot to dstBag/dstSlot.
-- NB: This is executed asychronously according to a timer schedule.
-- msg is a message to display when the transfer is done
local ret, str
Dbg( "RequestMoveItem %s[%s] --> %s[%s] %d %s",
CJRAB.BagName(bag), slot,
CJRAB.BagName(dstBag), dstSlot,
count,
-- NB: this is the actual item name at the time of transfer:
CJRAB.ItemName(bag, slot)
)
if not CJRAB.DryRun then
ret, str = CallSecureProtected('RequestMoveItem',
bag, slot, dstBag, dstSlot, count)
if not ret then
Msg(". RequestMoveItem FAILED: %s", str)
end
end
-- output message
if msg then
Msg(msg)
end
end
--=====================================
local function makeTxMessage(bag, slot, dstBag, tx_count, reason)
local msg
local sbag = CJRAB.TransferBags[bag]
if dstBag == BAG_BACKPACK then
msg = CJRAB.BagName(bag) .. ": withdrew"
else -- bag == BAG_BACKPACK
msg = CJRAB.BagName(dstBag) .. ": deposited"
end
if tx_count > 1 then msg = msg .. ' ' .. tx_count end
msg = msg .. " " .. sbag:ItemName(slot)
if reason and reason ~= "" then
msg = msg .. " " .. reason
end
return msg
end
--=====================================
function CJRAB.TransferFill(bag, slot, dstBag, reason)
-- Transfer item from bag, slot to dstBag but only to an existing stack.
-- No new stacks are created.
-- Returns the count of the number of items transferred.
-- NB: only dstBag can be BAG_VIRTUAL
local count, max, id, dst_slot, avail, msg
local tx_count = 0
local sbag = CJRAB.TransferBags[bag]
local dbag = CJRAB.TransferBags[dstBag]
local item = sbag:GetItem(slot)
count, max = sbag:GetSlotStackSize(slot)
if dstBag == BAG_VIRTUAL then
-- slots are the item ID
-- NB: dbag == nil in this case
dst_slot = item.id
avail = 2^31 -- XXX; this is arbitrary
else
if count == max then return 0 end-- a full stack, forget it
dst_slot, avail = dbag:GetStackableSlot(item)
end
if dst_slot then
if count <= avail then
tx_count = count
else
tx_count = avail
end
msg = makeTxMessage(bag, slot, dstBag, tx_count, reason)
-- schedule the actual transfer
if not CJRAB.NoQueue then
table.insert(CJRAB.TransferQueue, function()
do_transfer(bag, slot, dstBag, dst_slot, tx_count, msg) end)
end
-- do the clone transfer to update quantities
sbag:Transfer(slot, dbag, dst_slot, tx_count, msg)
end
return tx_count
end
--=====================================
function CJRAB.Transfer(bag, slot, dstBag, reason)
-- Transfer item from bag,slot to dstBag
-- creates another slot if required and available.
-- Returns the count of the number of items transferred.
local count, max, dst_slot, avail, msg
local tx_count = 0
local sbag = CJRAB.TransferBags[bag]
local dbag = CJRAB.TransferBags[dstBag]
-- count, max = GetSlotStackSize(bag, slot)
count, max = sbag:GetSlotStackSize(slot)
-- Fill any existing slot
tx_count = CJRAB.TransferFill(bag, slot, dstBag, reason)
-- transfer any remainder
count = count - tx_count
if count > 0 then
-- NB: should never get here if dstBag == BAG_VIRTUAL; count = 0
tx_count = count
-- find an empty slot
dst_slot = dbag:FindFirstEmptySlotInBag()
if not dst_slot then
Msg("No space in %s for %s", dbag:BagName(), sbag:ItemName(slot))
return 0
end
msg = makeTxMessage(bag, slot, dstBag, tx_count, reason)
-- schedule the actual transfer
if not CJRAB.NoQueue then
table.insert(CJRAB.TransferQueue, function()
do_transfer(bag, slot, dstBag, dst_slot, tx_count, msg) end)
end
-- do the clone transfer to update quantities
sbag:Transfer(slot, dbag, dst_slot, tx_count, msg)
end
return tx_count
end
--=============================================================================
-- HANDLER: EVENT_INVENTORY_SINGLE_SLOT_UPDATE
local function handle_slot(event, bag, slot, isNewItem,
soundCat, reason, stackCountChange)
if CJRAB.Logging then
CJRAB.LogSlotUpdate(bag, slot, isNewItem, stackCountChange)
end
if stackCountChange > 0 then
if CJRAB.Inventory then
CJRAB.Inventory(bag, slot, reason)
end
end
end
--=============================================================================
-- HANDLER: EVENT_OPEN_BANK, EVENT_CLOSE_BANK
local function handle_open_bank(event, bankBag)
if CJRAB.OpenBanking then
CJRAB.OpenBanking(bankBag)
end
end
local function handle_close_bank(event, bankBag)
if CJRAB.CloseBanking then
CJRAB.CloseBanking(bankBag)
end
end
--=============================================================================
-- HANDLER: EVENT_TRADING_HOUSE_CONFIRM_ITEM_PURCHASE
local function handle_tradehouse_purchase(event, idx)
CJRAB.LogTradeHousePurchase(event, idx)
end
--=============================================================================
-- HANDLER: EVENT_ADD_ON_LOADED
local function handle_addon_loaded(event, addonName)
if addonName ~= CJRAB.AddonName then return end
EVENT_MANAGER:UnregisterForEvent(CJRAB.AddonName, EVENT_ADD_ON_LOADED)
CJRAB:Initialize()
end
--=============================================================================
-- INITIALIZE
function CJRAB:Initialize()
-- initialize characters
CJRAB.InitChars()
-- handle_slot
EVENT_MANAGER:RegisterForEvent( self.AddonName,
EVENT_INVENTORY_SINGLE_SLOT_UPDATE,
handle_slot)
EVENT_MANAGER:AddFilterForEvent( self.AddonName,
EVENT_INVENTORY_SINGLE_SLOT_UPDATE,
REGISTER_FILTER_INVENTORY_UPDATE_REASON,
INVENTORY_UPDATE_REASON_DEFAULT)
-- handle_bank
EVENT_MANAGER:RegisterForEvent( self.AddonName, EVENT_OPEN_BANK,
handle_open_bank)
EVENT_MANAGER:RegisterForEvent( self.AddonName, EVENT_CLOSE_BANK,
handle_close_bank)
-- Guild Traders
--[[ XXX: use AwesomeGuildStore instead (this doesn't work with it
EVENT_MANAGER:RegisterForEvent( self.AddonName,
EVENT_TRADING_HOUSE_CONFIRM_ITEM_PURCHASE,
handle_tradehouse_purchase)
--]]
-- Slash commands
SLASH_COMMANDS["/abdryrun"] = function(arg)
CJRAB.DryRun = not CJRAB.DryRun
d("CJRAB.DryRun = " .. tostring(CJRAB.DryRun))
end
SLASH_COMMANDS["/abdebug"] = function(arg)
CJRAB.Debug = not CJRAB.Debug
d("CJRAB.Debug = " .. tostring(CJRAB.Debug))
end
SLASH_COMMANDS["/ablogging"] = function(arg)
CJRAB.Logging = not CJRAB.Logging
d("CJRAB.Logging = " .. tostring(CJRAB.Logging))
end
SLASH_COMMANDS["/dumppack"] = function(pat) CJRAB.DumpBag(BAG_BACKPACK, pat) end
SLASH_COMMANDS["/dumpbag"] = function(bag,pat) CJRAB.DumpBag(bag, pat) end
SLASH_COMMANDS["/dumpbank"] = function(pat) CJRAB.DumpBag(BAG_BANK, pat) end
SLASH_COMMANDS["/dumpchar"] = function(name) CJRAB.DumpChar(name) end
SLASH_COMMANDS["/dumpcharraw"] = function(name) CJRAB.DumpCharRaw(name) end
SLASH_COMMANDS["/listbags"] = function(arg)
for i, name in pairs(CJRAB_SI_BAGNAME) do
Msg("%d: %s [%d]\n", i, name, GetBagSize(i))
end
end
-- User slash commands
if CJRAB.SlashCommands then
CJRAB.SlashCommands()
end
end
EVENT_MANAGER:RegisterForEvent( CJRAB.AddonName, EVENT_ADD_ON_LOADED,
handle_addon_loaded)