annotate Main.lua @ 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
rev   line source
jcallahan@0 1 -----------------------------------------------------------------------
jcallahan@0 2 -- Upvalued Lua API.
jcallahan@0 3 -----------------------------------------------------------------------
jcallahan@0 4 local _G = getfenv(0)
jcallahan@0 5
jcallahan@0 6 local pairs = _G.pairs
jcallahan@1 7 local tonumber = _G.tonumber
jcallahan@1 8
jcallahan@1 9 local bit = _G.bit
jcallahan@1 10 local math = _G.math
jcallahan@1 11 local table = _G.table
jcallahan@1 12
jcallahan@0 13
jcallahan@0 14 -----------------------------------------------------------------------
jcallahan@0 15 -- AddOn namespace.
jcallahan@0 16 -----------------------------------------------------------------------
jcallahan@0 17 local ADDON_NAME, private = ...
jcallahan@0 18
jcallahan@0 19 local LibStub = _G.LibStub
jcallahan@0 20 local WDP = LibStub("AceAddon-3.0"):NewAddon(ADDON_NAME, "AceEvent-3.0", "AceTimer-3.0")
jcallahan@0 21
jcallahan@0 22
jcallahan@0 23 -----------------------------------------------------------------------
jcallahan@0 24 -- Local constants.
jcallahan@0 25 -----------------------------------------------------------------------
jcallahan@0 26 local DATABASE_DEFAULTS = {
jcallahan@0 27 global = {
jcallahan@0 28 items = {},
jcallahan@0 29 npcs = {},
jcallahan@0 30 objects = {},
jcallahan@0 31 quests = {},
jcallahan@0 32 }
jcallahan@0 33 }
jcallahan@0 34
jcallahan@0 35
jcallahan@1 36 local EVENT_MAPPING = {
jcallahan@1 37 -- ARTIFACT_COMPLETE = true,
jcallahan@0 38 -- ARTIFACT_HISTORY_READY = true,
jcallahan@0 39 -- AUCTION_HOUSE_SHOW = true,
jcallahan@0 40 -- BANKFRAME_OPENED = true,
jcallahan@0 41 -- BATTLEFIELDS_SHOW = true,
jcallahan@0 42 -- CHAT_MSG_ADDON = true,
jcallahan@0 43 -- CHAT_MSG_MONSTER_EMOTE = true,
jcallahan@0 44 -- CHAT_MSG_MONSTER_SAY = true,
jcallahan@0 45 -- CHAT_MSG_MONSTER_WHISPER = true,
jcallahan@0 46 -- CHAT_MSG_MONSTER_YELL = true,
jcallahan@0 47 -- CHAT_MSG_SYSTEM = true,
jcallahan@0 48 -- COMBAT_LOG_EVENT_UNFILTERED = true,
jcallahan@0 49 -- COMBAT_TEXT_UPDATE = true,
jcallahan@0 50 -- CONFIRM_BINDER = true,
jcallahan@0 51 -- CONFIRM_PET_UNLEARN = true,
jcallahan@0 52 -- CONFIRM_TALENT_WIPE = true,
jcallahan@0 53 -- CURRENCY_DISPLAY_UPDATE = true,
jcallahan@0 54 -- GOSSIP_ENTER_CODE = true,
jcallahan@0 55 -- GOSSIP_SHOW = true,
jcallahan@0 56 -- ITEM_TEXT_BEGIN = true,
jcallahan@0 57 -- LOCALPLAYER_PET_RENAMED = true,
jcallahan@0 58 -- LOOT_CLOSED = true,
jcallahan@1 59 LOOT_OPENED = true,
jcallahan@0 60 -- MAIL_SHOW = true,
jcallahan@0 61 -- MERCHANT_SHOW = true,
jcallahan@0 62 -- MERCHANT_UPDATE = true,
jcallahan@0 63 -- OPEN_TABARD_FRAME = true,
jcallahan@0 64 -- PET_BAR_UPDATE = true,
jcallahan@0 65 -- PET_STABLE_SHOW = true,
jcallahan@0 66 -- PLAYER_ALIVE = true,
jcallahan@0 67 -- PLAYER_ENTERING_WORLD = HandleZoneChange,
jcallahan@0 68 -- PLAYER_LOGIN = true,
jcallahan@0 69 -- PLAYER_LOGOUT = true,
jcallahan@0 70 -- PLAYER_TARGET_CHANGED = true,
jcallahan@0 71 -- QUEST_COMPLETE = true,
jcallahan@0 72 -- QUEST_DETAIL = true,
jcallahan@0 73 -- QUEST_LOG_UPDATE = true,
jcallahan@0 74 -- QUEST_PROGRESS = true,
jcallahan@0 75 -- TAXIMAP_OPENED = true,
jcallahan@0 76 -- TRADE_SKILL_SHOW = true,
jcallahan@0 77 -- TRADE_SKILL_UPDATE = true,
jcallahan@0 78 -- TRAINER_SHOW = true,
jcallahan@0 79 -- UNIT_QUEST_LOG_CHANGED = true,
jcallahan@1 80 UNIT_SPELLCAST_FAILED = "HandleSpellFailure",
jcallahan@1 81 UNIT_SPELLCAST_FAILED_QUIET = "HandleSpellFailure",
jcallahan@1 82 UNIT_SPELLCAST_INTERRUPTED = "HandleSpellFailure",
jcallahan@1 83 UNIT_SPELLCAST_SENT = true,
jcallahan@1 84 UNIT_SPELLCAST_SUCCEEDED = true,
jcallahan@0 85 -- ZONE_CHANGED = HandleZoneChange,
jcallahan@0 86 -- ZONE_CHANGED_NEW_AREA = HandleZoneChange,
jcallahan@0 87 }
jcallahan@0 88
jcallahan@1 89 local AF = private.ACTION_TYPE_FLAGS
jcallahan@0 90
jcallahan@0 91 -----------------------------------------------------------------------
jcallahan@0 92 -- Local variables.
jcallahan@0 93 -----------------------------------------------------------------------
jcallahan@0 94 local db
jcallahan@0 95 local durability_timer_handle
jcallahan@1 96 local action_data = {}
jcallahan@0 97
jcallahan@1 98 do
jcallahan@1 99 local UNIT_TYPE_BITMASK = 0x007
jcallahan@1 100
jcallahan@1 101 function WDP:ParseGUID(guid)
jcallahan@1 102 local types = private.UNIT_TYPES
jcallahan@1 103 local unit_type = _G.bit.band(tonumber(guid:sub(1, 5)), UNIT_TYPE_BITMASK)
jcallahan@1 104
jcallahan@1 105 if unit_type ~= types.PLAYER or unit_type ~= types.OBJECT or unit_type ~= types.PET then
jcallahan@1 106 return unit_type, tonumber(guid:sub(-12, -9), 16)
jcallahan@1 107 end
jcallahan@1 108
jcallahan@1 109 return unit_type
jcallahan@1 110 end
jcallahan@1 111 end -- do-block
jcallahan@1 112
jcallahan@1 113
jcallahan@1 114 -----------------------------------------------------------------------
jcallahan@1 115 -- Helper Functions.
jcallahan@1 116 -----------------------------------------------------------------------
jcallahan@1 117 local function CurrentLocationData()
jcallahan@1 118 local map_level = _G.GetCurrentMapDungeonLevel() or 0
jcallahan@1 119 local x, y = _G.GetPlayerMapPosition("player")
jcallahan@1 120
jcallahan@1 121 x = x or 0
jcallahan@1 122 y = y or 0
jcallahan@1 123
jcallahan@1 124 if x == 0 and y == 0 then
jcallahan@1 125 for level_index = 1, _G.GetNumDungeonMapLevels() do
jcallahan@1 126 _G.SetDungeonMapLevel(level_index)
jcallahan@1 127 x, y = _G.GetPlayerMapPosition("player")
jcallahan@1 128
jcallahan@1 129 if x and y and (x > 0 or y > 0) then
jcallahan@1 130 _G.SetDungeonMapLevel(map_level)
jcallahan@1 131 map_level = level_index
jcallahan@1 132 break
jcallahan@1 133 end
jcallahan@1 134 end
jcallahan@1 135 end
jcallahan@1 136
jcallahan@1 137 if _G.DungeonUsesTerrainMap() then
jcallahan@1 138 map_level = map_level - 1
jcallahan@1 139 end
jcallahan@1 140
jcallahan@1 141 return _G.GetRealZoneText(), math.floor(x * 1000 + 0.5), math.floor(y * 1000 + 0.5), map_level or 0
jcallahan@1 142 end
jcallahan@1 143
jcallahan@1 144
jcallahan@1 145 local function ItemLinkToID(item_link)
jcallahan@1 146 if not item_link then
jcallahan@1 147 return
jcallahan@1 148 end
jcallahan@1 149 local id = item_link:match("item:(%d+)")
jcallahan@1 150 return id and tonumber(id) or nil
jcallahan@1 151 end
jcallahan@0 152
jcallahan@0 153 -----------------------------------------------------------------------
jcallahan@0 154 -- Methods.
jcallahan@0 155 -----------------------------------------------------------------------
jcallahan@0 156 function WDP:OnInitialize()
jcallahan@0 157 db = LibStub("AceDB-3.0"):New("WoWDBProfilerData", DATABASE_DEFAULTS, "Default").global
jcallahan@0 158 end
jcallahan@0 159
jcallahan@0 160
jcallahan@0 161 function WDP:OnEnable()
jcallahan@0 162 for event_name, mapping in pairs(EVENT_MAPPING) do
jcallahan@1 163 self:RegisterEvent(event_name, (_G.type(mapping) ~= "boolean") and mapping or nil)
jcallahan@0 164 end
jcallahan@0 165 durability_timer_handle = self:ScheduleRepeatingTimer("ProcessDurability", 30)
jcallahan@0 166 end
jcallahan@0 167
jcallahan@0 168
jcallahan@0 169 local function RecordDurability(item_id, durability)
jcallahan@0 170 if not durability or durability <= 0 then
jcallahan@0 171 return
jcallahan@0 172 end
jcallahan@0 173
jcallahan@0 174 if not db.items[item_id] then
jcallahan@0 175 db.items[item_id] = {}
jcallahan@0 176 end
jcallahan@0 177 db.items[item_id].durability = durability
jcallahan@0 178 end
jcallahan@0 179
jcallahan@0 180
jcallahan@0 181 function WDP:ProcessDurability()
jcallahan@0 182 for slot_index = 0, _G.INVSLOT_LAST_EQUIPPED do
jcallahan@1 183 local item_id = _G.GetInventoryItemID("player", slot_index)
jcallahan@0 184
jcallahan@0 185 if item_id and item_id > 0 then
jcallahan@1 186 local _, max_durability = _G.GetInventoryItemDurability(slot_index)
jcallahan@0 187 RecordDurability(item_id, max_durability)
jcallahan@0 188 end
jcallahan@0 189 end
jcallahan@0 190
jcallahan@0 191 for bag_index = 0, _G.NUM_BAG_SLOTS do
jcallahan@0 192 for slot_index = 1, _G.GetContainerNumSlots(bag_index) do
jcallahan@1 193 local item_id = _G.GetContainerItemID(bag_index, slot_index)
jcallahan@0 194
jcallahan@0 195 if item_id and item_id > 0 then
jcallahan@1 196 local _, max_durability = _G.GetContainerItemDurability(bag_index, slot_index)
jcallahan@0 197 RecordDurability(item_id, max_durability)
jcallahan@0 198 end
jcallahan@0 199 end
jcallahan@0 200 end
jcallahan@0 201 end
jcallahan@0 202
jcallahan@0 203
jcallahan@0 204 -----------------------------------------------------------------------
jcallahan@0 205 -- Event handlers.
jcallahan@0 206 -----------------------------------------------------------------------
jcallahan@1 207 function WDP:CHAT_MSG_SYSTEM(event_name, message, sender_name, language)
jcallahan@0 208 end
jcallahan@0 209
jcallahan@0 210
jcallahan@1 211 local re_gold = _G.GOLD_AMOUNT:gsub("%%d", "(%%d+)")
jcallahan@1 212 local re_silver = _G.SILVER_AMOUNT:gsub("%%d", "(%%d+)")
jcallahan@1 213 local re_copper = _G.COPPER_AMOUNT:gsub("%%d", "(%%d+)")
jcallahan@1 214
jcallahan@1 215
jcallahan@1 216 local function _moneyMatch(money, re)
jcallahan@1 217 return money:match(re) or 0
jcallahan@0 218 end
jcallahan@0 219
jcallahan@0 220
jcallahan@1 221 local function _toCopper(money)
jcallahan@1 222 if not money then
jcallahan@1 223 return 0
jcallahan@1 224 end
jcallahan@1 225
jcallahan@1 226 return _moneyMatch(money, re_gold) * 10000 + _moneyMatch(money, re_silver) * 100 + _moneyMatch(money, re_copper)
jcallahan@0 227 end
jcallahan@0 228
jcallahan@0 229
jcallahan@1 230 local LOOT_VERIFY_FUNCS = {
jcallahan@1 231 [AF.NPC] = function()
jcallahan@1 232 local fishing_loot = _G.IsFishingLoot()
jcallahan@1 233
jcallahan@1 234 if not fishing_loot and _G.UnitExists("target") and not _G.UnitIsFriend("player", "target") and _G.UnitIsDead("target") then
jcallahan@1 235 if _G.UnitIsPlayer("target") or _G.UnitPlayerControlled("target") then
jcallahan@1 236 return false
jcallahan@1 237 end
jcallahan@1 238 local unit_type, id_num = WDP:ParseGUID(_G.UnitGUID("target"))
jcallahan@1 239 action_data.id_num = id_num
jcallahan@1 240 end
jcallahan@1 241 return true
jcallahan@1 242 end,
jcallahan@1 243 }
jcallahan@1 244
jcallahan@1 245 local LOOT_UPDATE_FUNCS = {
jcallahan@1 246 [AF.NPC] = function()
jcallahan@1 247 local npc = db.npcs[action_data.id_num]
jcallahan@1 248
jcallahan@1 249 if not npc then
jcallahan@1 250 db.npcs[action_data.id_num] = {}
jcallahan@1 251 npc = db.npcs[action_data.id_num]
jcallahan@1 252 end
jcallahan@1 253 npc.drops = npc.drops or {}
jcallahan@1 254
jcallahan@1 255 for index = 1, #action_data.drops do
jcallahan@1 256 table.insert(npc.drops, action_data.drops[index])
jcallahan@1 257 end
jcallahan@1 258 end,
jcallahan@1 259 }
jcallahan@1 260
jcallahan@1 261
jcallahan@1 262 function WDP:LOOT_OPENED()
jcallahan@1 263 if not action_data.type then
jcallahan@1 264 action_data.type = AF.NPC
jcallahan@1 265 end
jcallahan@1 266 local verify_func = LOOT_VERIFY_FUNCS[action_data.type]
jcallahan@1 267 local update_func = LOOT_UPDATE_FUNCS[action_data.type]
jcallahan@1 268
jcallahan@1 269 if not verify_func or not update_func or not verify_func() then
jcallahan@1 270 return
jcallahan@1 271 end
jcallahan@1 272
jcallahan@1 273 local loot_registry = {}
jcallahan@1 274 action_data.drops = {}
jcallahan@1 275
jcallahan@1 276 for loot_slot = 1, _G.GetNumLootItems() do
jcallahan@1 277 local texture, item, quantity, quality, locked = _G.GetLootSlotInfo(loot_slot)
jcallahan@1 278
jcallahan@1 279 if _G.LootSlotIsItem(loot_slot) then
jcallahan@1 280 local item_id = ItemLinkToID(_G.GetLootSlotLink(loot_slot))
jcallahan@1 281 loot_registry[item_id] = (loot_registry[item_id]) or 0 + quantity
jcallahan@1 282 elseif _G.LootSlotIsCoin(loot_slot) then
jcallahan@1 283 table.insert(action_data.drops, ("money:%d"):format(_toCopper(item)))
jcallahan@1 284 elseif _G.LootSlotIsCurrency(loot_slot) then
jcallahan@1 285 end
jcallahan@1 286 end
jcallahan@1 287
jcallahan@1 288 for item_id, quantity in pairs(loot_registry) do
jcallahan@1 289 table.insert(action_data.drops, ("%d:%d"):format(item_id, quantity))
jcallahan@1 290 end
jcallahan@1 291 update_func()
jcallahan@0 292 end
jcallahan@0 293
jcallahan@0 294
jcallahan@1 295 function WDP:UNIT_SPELLCAST_SENT(event_name, unit_id, spell_name, spell_rank, target_name, spell_line)
jcallahan@1 296 if private.tracked_line or unit_id ~= "player" then
jcallahan@1 297 return
jcallahan@1 298 end
jcallahan@1 299 local spell_label = private.SPELL_LABELS_BY_NAME[spell_name]
jcallahan@1 300
jcallahan@1 301 if not spell_label then
jcallahan@1 302 return
jcallahan@1 303 end
jcallahan@1 304 action_data.type = nil -- This will be set as appropriate below
jcallahan@1 305
jcallahan@1 306 local tt_item_name, tt_item_link = _G.GameTooltip:GetItem()
jcallahan@1 307 local tt_unit_name, tt_unit_id = _G.GameTooltip:GetUnit()
jcallahan@1 308
jcallahan@1 309 if not tt_unit_name and _G.UnitName("target") == target_name then
jcallahan@1 310 tt_unit_name = target_name
jcallahan@1 311 tt_unit_id = "target"
jcallahan@1 312 end
jcallahan@1 313 local spell_flags = private.SPELL_FLAGS_BY_LABEL[spell_label]
jcallahan@1 314
jcallahan@1 315 if not tt_item_name and not tt_unit_name then
jcallahan@1 316 if target_name == "" then
jcallahan@1 317 return
jcallahan@1 318 end
jcallahan@1 319
jcallahan@1 320 local zone_name, x, y, map_level = CurrentLocationData()
jcallahan@1 321
jcallahan@1 322 if bit.band(spell_flags, AF.OBJECT) == AF.OBJECT then
jcallahan@1 323 action_data.map_level = map_level
jcallahan@1 324 action_data.name = target_name
jcallahan@1 325 action_data.type = AF.OBJECT
jcallahan@1 326 action_data.x = x
jcallahan@1 327 action_data.y = y
jcallahan@1 328 action_data.zone = zone_name
jcallahan@1 329 print("Found spell flagged for OBJECT")
jcallahan@1 330 elseif bit.band(spell_flags, AF.ZONE) == AF.ZONE then
jcallahan@1 331 print("Found spell flagged for ZONE")
jcallahan@1 332 end
jcallahan@1 333 elseif tt_unit_name and not tt_item_name then
jcallahan@1 334 if bit.band(spell_flags, AF.NPC) == AF.NPC then
jcallahan@1 335 print("Found spell flagged for NPC")
jcallahan@1 336 end
jcallahan@1 337 elseif bit.band(spell_flags, AF.ITEM) == AF.ITEM then
jcallahan@1 338 print("Found spell flagged for ITEM")
jcallahan@1 339 else
jcallahan@1 340 print(("%s: We have an issue with types and flags."), event_name)
jcallahan@1 341 end
jcallahan@1 342
jcallahan@1 343 print(("%s: '%s', '%s', '%s', '%s', '%s'"):format(event_name, unit_id, spell_name, spell_rank, target_name, spell_line))
jcallahan@1 344 private.tracked_line = spell_line
jcallahan@0 345 end
jcallahan@0 346
jcallahan@0 347
jcallahan@1 348 function WDP:UNIT_SPELLCAST_SUCCEEDED(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)
jcallahan@1 349 if unit_id ~= "player" then
jcallahan@1 350 return
jcallahan@1 351 end
jcallahan@1 352
jcallahan@1 353 if action_data.type == AF.OBJECT then
jcallahan@1 354 end
jcallahan@1 355
jcallahan@1 356 if private.SPELL_LABELS_BY_NAME[spell_name] then
jcallahan@1 357 print(("%s: '%s', '%s', '%s', '%s', '%s'"):format(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id))
jcallahan@1 358 end
jcallahan@1 359 private.tracked_line = nil
jcallahan@0 360 end
jcallahan@0 361
jcallahan@1 362 function WDP:HandleSpellFailure(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)
jcallahan@1 363 if unit_id ~= "player" then
jcallahan@1 364 return
jcallahan@1 365 end
jcallahan@0 366
jcallahan@1 367 if private.tracked_line == spell_line then
jcallahan@1 368 private.tracked_line = nil
jcallahan@1 369 end
jcallahan@0 370 end