vrtc / flowerpicker (public) (License: GPLv3) (since 2019-11-07) (hash sha1)
Flowerpicker is a GUI extension, that is an add-on, for World of Warcraft game client. It is under development and not yet ready for usage by players.
List of commits:
Subject Hash Author Date (UTC)
Add basic money income via looting registration 9cfb08138268bb6985db1729c160b444a5e4e56a Vladyslav Bondarenko 2019-11-06 14:07:53
Update event format 554e63f3490dc48befe37fbfacf5c8c9ad188451 Vladyslav Bondarenko 2019-11-06 09:59:18
Initial commit f014031ddd4b2422ef5d000f33ad6602e64c9b36 Vladyslav Bondarenko 2019-11-05 20:29:14
Commit 9cfb08138268bb6985db1729c160b444a5e4e56a - Add basic money income via looting registration
A new type of event is now registered by the add-on.
Specifically, when a player character collects money from a corpse
of a defeated enemy, then an appropriate event is registered.

The add-on still lacks the ability to register aquisition of money
or items from containers or trade. Neither does it register the loss,
consumption or withdrawal of items or money.

There is a problem with the implementation. Increase in the amount of
flow control instructions is an indication of an error-prone design.
The next step will probably be remaking of some of the API.

Additionally, a README file with add-on description might be useful.
Linting and documentation generation are yet to be introduced.
Author: Vladyslav Bondarenko
Author date (UTC): 2019-11-06 14:07
Committer name: Vladyslav Bondarenko
Committer date (UTC): 2019-11-06 14:07
Parent(s): c55c77418166bac6617ec26b62f7219dbe192c19
Signing key:
Tree: e4c5d2870a66c963725003601a37d9997c29e4bc
File Lines added Lines deleted
Flowerpicker.lua 134 24
File Flowerpicker.lua changed (mode: 100644) (index dbb5427..54d9d6d)
... ... end
265 265
266 266 --[[ Main. ]]-- --[[ Main. ]]--
267 267
268 local function updateLootCache()
269 FlowerpickerLootCache = {}
270 local lootCache = FlowerpickerLootCache
271 assert (lootCache ~= nil)
272 assert ('table' == type(lootCache))
273 local n = GetNumLootItems()
274 if nil == n then
275 n = 0
276 elseif 'number' ~= type(n) then
277 n = 0
278 end
279 local i = 1
280 local j = math.min(math.max(0, math.floor(n)), 1024)
281 while (i <= j) do
282 lootCache[i] = {GetLootSlotInfo(i)}
283 i = i + 1
268 local function clearLootCache(lootCache)
269 assert (lootCache ~= nil)
270 assert ('table' == type(lootCache))
271 for k = 1, #lootCache do
272 lootCache[k] = nil
273 end
274 end
275
276 local function updateLootCache(lootCache)
277 assert (lootCache ~= nil)
278 assert ('table' == type(lootCache))
279 clearLootCache(lootCache)
280 local n = GetNumLootItems()
281 if nil == n then
282 n = 0
283 elseif 'number' ~= type(n) then
284 n = 0
285 end
286 local i = 1
287 local j = math.min(math.max(0, math.floor(n)), 1024)
288 local itemInfo
289 while (i <= j) do
290 itemInfo = {GetLootSlotInfo(i)}
291 table.insert(itemInfo, 1 == LootSlotIsItem(i))
292 table.insert(itemInfo, 1 == LootSlotIsCoin(i))
293 lootCache[i] = itemInfo
294 i = i + 1
295 end
296 end
297
298 local function describeLootCache(lootCache)
299 assert (lootCache ~= nil)
300 assert ('table' == type(lootCache))
301 local d = ''
302 for i = 1, #lootCache do
303 local itemInfo = lootCache[i]
304 if itemInfo ~= nil then
305 d = d .. '{'
306 for j = 1, #itemInfo do
307 d = d .. tostring(itemInfo[j]) .. ', '
308 end
309 d = d .. "}\n"
284 310 end end
311 end
312 return d
313 end
314
315 local function getCachedItemInfo(lootCache, slotId)
316 assert (lootCache ~= nil)
317 assert ('table' == type(lootCache))
318 assert (validatePositiveAndNonZero(slotId))
319
320 local itemInfo = lootCache[slotId]
321 assert (itemInfo ~= nil)
322 assert ('table' == type(itemInfo))
323 return itemInfo
324 end
325
326 local function isCachedItemCoin(lootCache, slotId)
327 local itemInfo = getCachedItemInfo(lootCache, slotId)
328 assert (itemInfo ~= nil)
329 assert ('table' == type(itemInfo))
330 return true == itemInfo[6]
285 331 end end
286 332
287 local function processLoot(slotId, lastSpellCastName, lootedCorpseGuessedName)
333 local function processLoot(lootCache, slotId, lastSpellCastName, lootedCorpseGuessedName, moneyDiff)
288 334 local spellName = sanitiseAnyName(lastSpellCastName) local spellName = sanitiseAnyName(lastSpellCastName)
289 335 local d local d
290 336 local s local s
 
... ... local function processLoot(slotId, lastSpellCastName, lootedCorpseGuessedName)
302 348 local p = sanitiseAnyName(UnitName('player')) local p = sanitiseAnyName(UnitName('player'))
303 349 assert (validateAnyName(p)) assert (validateAnyName(p))
304 350 local t = date('%Y-%m-%d %H:%M:%S', time()) .. get_tzoffset(get_timezone()) local t = date('%Y-%m-%d %H:%M:%S', time()) .. get_tzoffset(get_timezone())
305 local lootCache = FlowerpickerLootCache
351
306 352 assert (lootCache ~= nil) assert (lootCache ~= nil)
307 353 assert ('table' == type(lootCache)) assert ('table' == type(lootCache))
308 354 local lootedItem = lootCache[slotId] local lootedItem = lootCache[slotId]
309 355 assert (lootedItem ~= nil) assert (lootedItem ~= nil)
310 356 assert ('table' == type(lootedItem)) assert ('table' == type(lootedItem))
311 local i = sanitiseAnyName(lootedItem[2])
357 local i
358 if 1 == LootSlotIsCoin(slotId) then
359 i = 'Money'
360 else
361 i = sanitiseAnyName(lootedItem[2])
362 end
312 363 assert (validateAnyName(i)) assert (validateAnyName(i))
313 local q = sanitiseShort(lootedItem[3])
364 local q
365 if isCachedItemCoin(lootCache, slotId) then
366 --[[ Potentially error prone! ]]--
367 q = sanitiseShort(moneyDiff)
368 else
369 q = sanitiseShort(lootedItem[3])
370 end
314 371 assert (validatePositiveAndNonZero(q)) assert (validatePositiveAndNonZero(q))
315 372 local z = sanitiseAnyName(GetZoneText()) local z = sanitiseAnyName(GetZoneText())
316 373 if nil == z or string.len(z) <= 0 then if nil == z or string.len(z) <= 0 then
 
... ... local function main(self, event, arg1, arg2, arg3, arg4, arg5)
339 396 self.api.lastSpellCastName = spellName self.api.lastSpellCastName = spellName
340 397 end end
341 398 elseif 'LOOT_OPENED' == event then elseif 'LOOT_OPENED' == event then
342 updateLootCache()
399 updateLootCache(FlowerpickerLootCache)
343 400 self.api.lootedCorpseGuessedName = sanitiseAnyName(UnitName('target')) self.api.lootedCorpseGuessedName = sanitiseAnyName(UnitName('target'))
401 self.api.isLooting = true
344 402 elseif 'LOOT_SLOT_CLEARED' == event then elseif 'LOOT_SLOT_CLEARED' == event then
345 403 local slotId = sanitiseShort(arg1) local slotId = sanitiseShort(arg1)
404 local lootCache = FlowerpickerLootCache
405 assert (lootCache ~= nil)
406 assert ('table' == type(lootCache))
407 self.api.lootSlotId = slotId
408 if isCachedItemCoin(lootCache, slotId) then
409 return
410 end
346 411 assert (slotId ~= nil) assert (slotId ~= nil)
347 412 assert ('number' == type(slotId)) assert ('number' == type(slotId))
348 413 local lastSpellCastName = sanitiseAnyName(self.api.lastSpellCastName) local lastSpellCastName = sanitiseAnyName(self.api.lastSpellCastName)
349 414 local corpseName = sanitiseAnyName(self.api.lootedCorpseGuessedName) local corpseName = sanitiseAnyName(self.api.lootedCorpseGuessedName)
350 processLoot(slotId, lastSpellCastName, corpseName)
415 processLoot(FlowerpickerLootCache, slotId, lastSpellCastName, corpseName)
351 416 elseif 'LOOT_CLOSED' == event then elseif 'LOOT_CLOSED' == event then
352 FlowerpickerLootCache = {}
417 clearLootCache(FlowerpickerLootCache)
418 self.api.lootSlotId = nil
419 self.api.lastSpellCastName = nil
353 420 self.api.lootedCorpseGuessedName = nil self.api.lootedCorpseGuessedName = nil
421 self.api.isLooting = false
422 elseif 'PLAYER_MONEY' == event then
423 local updatedMoney = GetMoney()
424 assert (updatedMoney ~= nil)
425 assert ('number' == type(updatedMoney))
426 assert (updatedMoney >= 0)
427 local playerMoney = self.api.playerMoney
428 assert (playerMoney ~= nil)
429 assert ('number' == type(playerMoney))
430 assert (playerMoney >= 0)
431 local moneyDiff = updatedMoney - playerMoney
432 assert (moneyDiff ~= nil)
433 assert ('number' == type(moneyDiff))
434 --[[ Only register money income for now. ]]--
435 if (moneyDiff > 0 and true == self.api.isLooting) then
436 local slotId = self.api.lootSlotId
437 local lastSpellCastName = sanitiseAnyName(self.api.lastSpellCastName)
438 local corpseName = sanitiseAnyName(self.api.lootedCorpseGuessedName)
439 assert (slotId ~= nil and (lastSpellCastName ~= nil or corpseName ~= nil), 'Unknown money source.')
440
441 --[[
442 -- There is a limit to item quantity per registered loot event.
443 -- Therefore, split potentially larger money amount between multiple events.
444 ]]--
445 local i = 0
446 local m = moneyDiff
447 local maxItemQuantityPerEvent = 32767
448 local maxEvents = 8
449 local lootCache = FlowerpickerLootCache
450 assert (lootCache ~= nil)
451 assert ('table' == type(lootCache))
452 while (m > maxItemQuantityPerEvent and i < maxEvents) do
453 processLoot(lootCache, slotId, lastSpellCastName, corpseName, maxItemQuantitiyPerEvent)
454 m = m - maxItemQuantityPerEvent
455 i = i + 1
456 end
457 m = moneyDiff - maxItemQuantityPerEvent*i
458 processLoot(lootCache, slotId, lastSpellCastName, corpseName, m)
459 end
460 self.api.playerMoney = updatedMoney
354 461 else else
355 462 error('Unknown event encountered and ignored "' .. event .. '".') error('Unknown event encountered and ignored "' .. event .. '".')
356 463 end end
 
... ... local function init()
380 487 addonFrame:RegisterEvent('LOOT_OPENED') addonFrame:RegisterEvent('LOOT_OPENED')
381 488 addonFrame:RegisterEvent('LOOT_SLOT_CLEARED') addonFrame:RegisterEvent('LOOT_SLOT_CLEARED')
382 489 addonFrame:RegisterEvent('LOOT_CLOSED') addonFrame:RegisterEvent('LOOT_CLOSED')
490 addonFrame:RegisterEvent('PLAYER_MONEY')
383 491 addonFrame:SetScript('OnEvent', main) addonFrame:SetScript('OnEvent', main)
384 492
385 493 local api = { local api = {
386 494 ['createEventHarvest'] = createEventHarvest, ['createEventHarvest'] = createEventHarvest,
387 495 ['lootCache'] = lootCache, ['lootCache'] = lootCache,
388 496 ['lootedCorpseGuessedName'] = nil, ['lootedCorpseGuessedName'] = nil,
389 ['lastSpellCastName'] = nil
497 ['lastSpellCastName'] = nil,
498 ['isLooting'] = false,
499 ['playerMoney'] = GetMoney()
390 500 } }
391 501 addonFrame.api = api addonFrame.api = api
392 502 end end
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/vrtc/flowerpicker

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/vrtc/flowerpicker

Clone this repository using git:
git clone git://git.rocketgit.com/user/vrtc/flowerpicker

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main