changeset 1:d9375a473042

Handle looting items and money from NPCs. Beginning of handling for looting from objects and gathering (mining, herbalism) from NPCs among other spell-related obtaining methods.
author James D. Callahan III <jcallahan@curse.com>
date Fri, 27 Apr 2012 03:49:03 -0500
parents 2e4d83460542
children d563ea0ec911
files Constants.lua Main.lua WoWDBProfiler.toc
diffstat 3 files changed, 288 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Constants.lua	Fri Apr 27 03:49:03 2012 -0500
@@ -0,0 +1,67 @@
+-----------------------------------------------------------------------
+-- Upvalued Lua API.
+-----------------------------------------------------------------------
+local _G = getfenv(0)
+
+local bit = _G.bit
+
+
+-----------------------------------------------------------------------
+-- AddOn namespace.
+-----------------------------------------------------------------------
+local ADDON_NAME, private = ...
+
+local LibStub = _G.LibStub
+
+
+-----------------------------------------------------------------------
+-- Constants.
+-----------------------------------------------------------------------
+private.UNIT_TYPES = {
+    PLAYER = 0,
+    OBJECT = 1,
+    NPC = 3,
+    PET = 4,
+    VEHICLE = 5,
+}
+
+
+private.ACTION_TYPE_FLAGS = {
+    ITEM = 0x00000001,
+    NPC = 0x00000002,
+    OBJECT = 0x00000004,
+    ZONE = 0x00000008,
+}
+
+
+private.SPELL_LABELS_BY_NAME = {
+    [_G.GetSpellInfo(13262)] = "DISENCHANT",
+    [_G.GetSpellInfo(4036)] = "ENGINEERING",
+    [_G.GetSpellInfo(7620)] = "FISHING",
+    [_G.GetSpellInfo(2366)] = "HERB_GATHERING",
+    [_G.GetSpellInfo(51005)] = "MILLING",
+    [_G.GetSpellInfo(605)] = "MIND_CONTROL",
+    [_G.GetSpellInfo(2575)] = "MINING",
+    [_G.GetSpellInfo(3365)] = "OPENING",
+    [_G.GetSpellInfo(921)] = "PICK_POCKET",
+    [_G.GetSpellInfo(31252)] = "PROSPECTING",
+    [_G.GetSpellInfo(73979)] = "SEARCHING_FOR_ARTIFACTS",
+    [_G.GetSpellInfo(8613)] = "SKINNING",
+}
+
+local AF = private.ACTION_TYPE_FLAGS
+
+private.SPELL_FLAGS_BY_LABEL = {
+    DISENCHANT = AF.ITEM,
+    ENGINEERING = AF.NPC,
+    FISHING =  AF.ZONE,
+    HERB_GATHERING = bit.bxor(AF.NPC, AF.OBJECT),
+    MILLING = AF.ITEM,
+    MIND_CONTROL = AF.NPC,
+    MINING = bit.bxor(AF.NPC, AF.OBJECT),
+    OPENING = AF.OBJECT,
+    PICK_POCKET = AF.NPC,
+    PROSPECTING = AF.ITEM,
+    SEARCHING_FOR_ARTIFACTS = AF.OBJECT,
+    SKINNING = AF.NPC,
+}
--- a/Main.lua	Thu Apr 26 13:03:54 2012 -0500
+++ b/Main.lua	Fri Apr 27 03:49:03 2012 -0500
@@ -4,6 +4,12 @@
 local _G = getfenv(0)
 
 local pairs = _G.pairs
+local tonumber = _G.tonumber
+
+local bit = _G.bit
+local math = _G.math
+local table = _G.table
+
 
 -----------------------------------------------------------------------
 -- AddOn namespace.
@@ -13,11 +19,6 @@
 local LibStub = _G.LibStub
 local WDP = LibStub("AceAddon-3.0"):NewAddon(ADDON_NAME, "AceEvent-3.0", "AceTimer-3.0")
 
------------------------------------------------------------------------
--- Function declarations.
------------------------------------------------------------------------
-local HandleSpellFailure
-local HandleZoneChange
 
 -----------------------------------------------------------------------
 -- Local constants.
@@ -32,7 +33,8 @@
 }
 
 
-local EVENT_MAPPING = {--    ARTIFACT_COMPLETE = true,
+local EVENT_MAPPING = {
+    --    ARTIFACT_COMPLETE = true,
     --    ARTIFACT_HISTORY_READY = true,
     --    AUCTION_HOUSE_SHOW = true,
     --    BANKFRAME_OPENED = true,
@@ -54,7 +56,7 @@
     --    ITEM_TEXT_BEGIN = true,
     --    LOCALPLAYER_PET_RENAMED = true,
     --    LOOT_CLOSED = true,
-    --    LOOT_OPENED = true,
+    LOOT_OPENED = true,
     --    MAIL_SHOW = true,
     --    MERCHANT_SHOW = true,
     --    MERCHANT_UPDATE = true,
@@ -75,22 +77,78 @@
     --    TRADE_SKILL_UPDATE = true,
     --    TRAINER_SHOW = true,
     --    UNIT_QUEST_LOG_CHANGED = true,
-    --    UNIT_SPELLCAST_FAILED = HandleSpellFailure,
-    --    UNIT_SPELLCAST_FAILED_QUIET = HandleSpellFailure,
-    --    UNIT_SPELLCAST_INTERRUPTED = HandleSpellFailure,
-    --    UNIT_SPELLCAST_SENT = true,
-    --    UNIT_SPELLCAST_SUCCEEDED = true,
+    UNIT_SPELLCAST_FAILED = "HandleSpellFailure",
+    UNIT_SPELLCAST_FAILED_QUIET = "HandleSpellFailure",
+    UNIT_SPELLCAST_INTERRUPTED = "HandleSpellFailure",
+    UNIT_SPELLCAST_SENT = true,
+    UNIT_SPELLCAST_SUCCEEDED = true,
     --    ZONE_CHANGED = HandleZoneChange,
     --    ZONE_CHANGED_NEW_AREA = HandleZoneChange,
 }
 
+local AF = private.ACTION_TYPE_FLAGS
 
 -----------------------------------------------------------------------
 -- Local variables.
 -----------------------------------------------------------------------
 local db
 local durability_timer_handle
+local action_data = {}
 
+do
+    local UNIT_TYPE_BITMASK = 0x007
+
+    function WDP:ParseGUID(guid)
+        local types = private.UNIT_TYPES
+        local unit_type = _G.bit.band(tonumber(guid:sub(1, 5)), UNIT_TYPE_BITMASK)
+
+        if unit_type ~= types.PLAYER or unit_type ~= types.OBJECT or unit_type ~= types.PET then
+            return unit_type, tonumber(guid:sub(-12, -9), 16)
+        end
+
+        return unit_type
+    end
+end -- do-block
+
+
+-----------------------------------------------------------------------
+-- Helper Functions.
+-----------------------------------------------------------------------
+local function CurrentLocationData()
+    local map_level = _G.GetCurrentMapDungeonLevel() or 0
+    local x, y = _G.GetPlayerMapPosition("player")
+
+    x = x or 0
+    y = y or 0
+
+    if x == 0 and y == 0 then
+        for level_index = 1, _G.GetNumDungeonMapLevels() do
+            _G.SetDungeonMapLevel(level_index)
+            x, y = _G.GetPlayerMapPosition("player")
+
+            if x and y and (x > 0 or y > 0) then
+                _G.SetDungeonMapLevel(map_level)
+                map_level = level_index
+                break
+            end
+        end
+    end
+
+    if _G.DungeonUsesTerrainMap() then
+        map_level = map_level - 1
+    end
+
+    return _G.GetRealZoneText(), math.floor(x * 1000 + 0.5), math.floor(y * 1000 + 0.5), map_level or 0
+end
+
+
+local function ItemLinkToID(item_link)
+    if not item_link then
+        return
+    end
+    local id = item_link:match("item:(%d+)")
+    return id and tonumber(id) or nil
+end
 
 -----------------------------------------------------------------------
 -- Methods.
@@ -102,7 +160,7 @@
 
 function WDP:OnEnable()
     for event_name, mapping in pairs(EVENT_MAPPING) do
-        self:RegisterEvent(event_name, (type(mapping) ~= "boolean") and mapping or nil)
+        self:RegisterEvent(event_name, (_G.type(mapping) ~= "boolean") and mapping or nil)
     end
     durability_timer_handle = self:ScheduleRepeatingTimer("ProcessDurability", 30)
 end
@@ -122,20 +180,20 @@
 
 function WDP:ProcessDurability()
     for slot_index = 0, _G.INVSLOT_LAST_EQUIPPED do
-        local item_id = _G.GetInventoryItemID("player", slot_index);
+        local item_id = _G.GetInventoryItemID("player", slot_index)
 
         if item_id and item_id > 0 then
-            local _, max_durability = _G.GetInventoryItemDurability(slot_index);
+            local _, max_durability = _G.GetInventoryItemDurability(slot_index)
             RecordDurability(item_id, max_durability)
         end
     end
 
     for bag_index = 0, _G.NUM_BAG_SLOTS do
         for slot_index = 1, _G.GetContainerNumSlots(bag_index) do
-            local item_id = _G.GetContainerItemID(bag_index, slot_index);
+            local item_id = _G.GetContainerItemID(bag_index, slot_index)
 
             if item_id and item_id > 0 then
-                local _, max_durability = _G.GetContainerItemDurability(bag_index, slot_index);
+                local _, max_durability = _G.GetContainerItemDurability(bag_index, slot_index)
                 RecordDurability(item_id, max_durability)
             end
         end
@@ -146,53 +204,167 @@
 -----------------------------------------------------------------------
 -- Event handlers.
 -----------------------------------------------------------------------
-function WDP:AUCTION_HOUSE_SHOW()
+function WDP:CHAT_MSG_SYSTEM(event_name, message, sender_name, language)
 end
 
 
-function WDP:CHAT_MSG_MONSTER_EMOTE()
+local re_gold = _G.GOLD_AMOUNT:gsub("%%d", "(%%d+)")
+local re_silver = _G.SILVER_AMOUNT:gsub("%%d", "(%%d+)")
+local re_copper = _G.COPPER_AMOUNT:gsub("%%d", "(%%d+)")
+
+
+local function _moneyMatch(money, re)
+    return money:match(re) or 0
 end
 
 
-function WDP:CHAT_MSG_MONSTER_SAY()
+local function _toCopper(money)
+    if not money then
+        return 0
+    end
+
+    return _moneyMatch(money, re_gold) * 10000 + _moneyMatch(money, re_silver) * 100 + _moneyMatch(money, re_copper)
 end
 
 
-function WDP:CHAT_MSG_MONSTER_WHISPER()
+local LOOT_VERIFY_FUNCS = {
+    [AF.NPC] = function()
+        local fishing_loot = _G.IsFishingLoot()
+
+        if not fishing_loot and _G.UnitExists("target") and not _G.UnitIsFriend("player", "target") and _G.UnitIsDead("target") then
+            if _G.UnitIsPlayer("target") or _G.UnitPlayerControlled("target") then
+                return false
+            end
+            local unit_type, id_num = WDP:ParseGUID(_G.UnitGUID("target"))
+            action_data.id_num = id_num
+        end
+        return true
+    end,
+}
+
+local LOOT_UPDATE_FUNCS = {
+    [AF.NPC] = function()
+        local npc = db.npcs[action_data.id_num]
+
+        if not npc then
+            db.npcs[action_data.id_num] = {}
+            npc = db.npcs[action_data.id_num]
+        end
+        npc.drops = npc.drops or {}
+
+        for index = 1, #action_data.drops do
+            table.insert(npc.drops, action_data.drops[index])
+        end
+    end,
+}
+
+
+function WDP:LOOT_OPENED()
+    if not action_data.type then
+        action_data.type = AF.NPC
+    end
+    local verify_func = LOOT_VERIFY_FUNCS[action_data.type]
+    local update_func =  LOOT_UPDATE_FUNCS[action_data.type]
+
+    if not verify_func or not update_func or not verify_func() then
+        return
+    end
+
+    local loot_registry = {}
+    action_data.drops = {}
+
+    for loot_slot = 1, _G.GetNumLootItems() do
+        local texture, item, quantity, quality, locked = _G.GetLootSlotInfo(loot_slot)
+
+        if _G.LootSlotIsItem(loot_slot) then
+            local item_id = ItemLinkToID(_G.GetLootSlotLink(loot_slot))
+            loot_registry[item_id] = (loot_registry[item_id]) or 0 + quantity
+        elseif _G.LootSlotIsCoin(loot_slot) then
+            table.insert(action_data.drops, ("money:%d"):format(_toCopper(item)))
+        elseif _G.LootSlotIsCurrency(loot_slot) then
+        end
+    end
+
+    for item_id, quantity in pairs(loot_registry) do
+        table.insert(action_data.drops, ("%d:%d"):format(item_id, quantity))
+    end
+    update_func()
 end
 
 
-function WDP:CHAT_MSG_MONSTER_YELL()
+function WDP:UNIT_SPELLCAST_SENT(event_name, unit_id, spell_name, spell_rank, target_name, spell_line)
+    if private.tracked_line or unit_id ~= "player" then
+        return
+    end
+    local spell_label = private.SPELL_LABELS_BY_NAME[spell_name]
+
+    if not spell_label then
+        return
+    end
+    action_data.type = nil -- This will be set as appropriate below
+
+    local tt_item_name, tt_item_link = _G.GameTooltip:GetItem()
+    local tt_unit_name, tt_unit_id = _G.GameTooltip:GetUnit()
+
+    if not tt_unit_name and _G.UnitName("target") == target_name then
+        tt_unit_name = target_name
+        tt_unit_id = "target"
+    end
+    local spell_flags = private.SPELL_FLAGS_BY_LABEL[spell_label]
+
+    if not tt_item_name and not tt_unit_name then
+        if target_name == "" then
+            return
+        end
+
+        local zone_name, x, y, map_level = CurrentLocationData()
+
+        if bit.band(spell_flags, AF.OBJECT) == AF.OBJECT then
+            action_data.map_level = map_level
+            action_data.name = target_name
+            action_data.type = AF.OBJECT
+            action_data.x = x
+            action_data.y = y
+            action_data.zone = zone_name
+            print("Found spell flagged for OBJECT")
+        elseif bit.band(spell_flags, AF.ZONE) == AF.ZONE then
+            print("Found spell flagged for ZONE")
+        end
+    elseif tt_unit_name and not tt_item_name then
+        if bit.band(spell_flags, AF.NPC) == AF.NPC then
+            print("Found spell flagged for NPC")
+        end
+    elseif bit.band(spell_flags, AF.ITEM) == AF.ITEM then
+        print("Found spell flagged for ITEM")
+    else
+        print(("%s: We have an issue with types and flags."), event_name)
+    end
+
+    print(("%s: '%s', '%s', '%s', '%s', '%s'"):format(event_name, unit_id, spell_name, spell_rank, target_name, spell_line))
+    private.tracked_line = spell_line
 end
 
 
-function WDP:CHAT_MSG_SYSTEM(event, message, sender_name, language)
+function WDP:UNIT_SPELLCAST_SUCCEEDED(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)
+    if unit_id ~= "player" then
+        return
+    end
+
+    if action_data.type == AF.OBJECT then
+    end
+
+    if private.SPELL_LABELS_BY_NAME[spell_name] then
+        print(("%s: '%s', '%s', '%s', '%s', '%s'"):format(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id))
+    end
+    private.tracked_line = nil
 end
 
+function WDP:HandleSpellFailure(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)
+    if unit_id ~= "player" then
+        return
+    end
 
-function WDP:GOSSIP_SHOW()
+    if private.tracked_line == spell_line then
+        private.tracked_line = nil
+    end
 end
-
-
-function WDP:ADDON_ALIVE()
-end
-
-
-function WDP:PLAYER_LOGIN()
-end
-
-
-function WDP:PLAYER_LOGOUT()
-end
-
-
-function WDP:PLAYER_TARGET_CHANGED()
-end
-
-
-function WDP:QUEST_LOG_UPDATE()
-end
-
-
-function WDP:TRADE_SKILL_UPDATE()
-end
--- a/WoWDBProfiler.toc	Thu Apr 26 13:03:54 2012 -0500
+++ b/WoWDBProfiler.toc	Fri Apr 27 03:49:03 2012 -0500
@@ -8,4 +8,5 @@
 
 libs.xml
 
+Constants.lua
 Main.lua