jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Upvalued Lua API. jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: local _G = getfenv(0) jcallahan@0: jcallahan@0: local pairs = _G.pairs jcallahan@1: local tonumber = _G.tonumber jcallahan@1: jcallahan@1: local bit = _G.bit jcallahan@1: local math = _G.math jcallahan@1: local table = _G.table jcallahan@1: jcallahan@0: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- AddOn namespace. jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: local ADDON_NAME, private = ... jcallahan@0: jcallahan@0: local LibStub = _G.LibStub jcallahan@0: local WDP = LibStub("AceAddon-3.0"):NewAddon(ADDON_NAME, "AceEvent-3.0", "AceTimer-3.0") jcallahan@0: jcallahan@48: local deformat = LibStub("LibDeformat-3.0") jcallahan@48: jcallahan@4: local DatamineTT = _G.CreateFrame("GameTooltip", "WDPDatamineTT", _G.UIParent, "GameTooltipTemplate") jcallahan@5: DatamineTT:SetOwner(_G.WorldFrame, "ANCHOR_NONE") jcallahan@5: jcallahan@0: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Local constants. jcallahan@0: ----------------------------------------------------------------------- jcallahan@67: local DB_VERSION = 3 jcallahan@63: jcallahan@0: local DATABASE_DEFAULTS = { jcallahan@0: global = { jcallahan@0: items = {}, jcallahan@0: npcs = {}, jcallahan@0: objects = {}, jcallahan@0: quests = {}, jcallahan@17: zones = {}, jcallahan@0: } jcallahan@0: } jcallahan@0: jcallahan@0: jcallahan@1: local EVENT_MAPPING = { jcallahan@56: BLACK_MARKET_ITEM_UPDATE = true, jcallahan@48: CHAT_MSG_LOOT = true, jcallahan@40: CHAT_MSG_SYSTEM = true, jcallahan@23: COMBAT_LOG_EVENT_UNFILTERED = true, jcallahan@18: COMBAT_TEXT_UPDATE = true, jcallahan@42: ITEM_TEXT_BEGIN = true, jcallahan@1: LOOT_OPENED = true, jcallahan@7: MERCHANT_SHOW = "UpdateMerchantItems", jcallahan@61: MERCHANT_UPDATE = "UpdateMerchantItems", jcallahan@25: PET_BAR_UPDATE = true, jcallahan@2: PLAYER_TARGET_CHANGED = true, jcallahan@9: QUEST_COMPLETE = true, jcallahan@9: QUEST_DETAIL = true, jcallahan@9: QUEST_LOG_UPDATE = true, jcallahan@27: TRAINER_SHOW = true, jcallahan@4: UNIT_QUEST_LOG_CHANGED = true, jcallahan@1: UNIT_SPELLCAST_FAILED = "HandleSpellFailure", jcallahan@1: UNIT_SPELLCAST_FAILED_QUIET = "HandleSpellFailure", jcallahan@1: UNIT_SPELLCAST_INTERRUPTED = "HandleSpellFailure", jcallahan@1: UNIT_SPELLCAST_SENT = true, jcallahan@1: UNIT_SPELLCAST_SUCCEEDED = true, jcallahan@0: } jcallahan@0: jcallahan@4: jcallahan@1: local AF = private.ACTION_TYPE_FLAGS jcallahan@0: jcallahan@4: jcallahan@27: local PLAYER_CLASS = _G.select(2, _G.UnitClass("player")) jcallahan@34: local PLAYER_GUID = _G.UnitGUID("player") jcallahan@39: local PLAYER_RACE = _G.select(2, _G.UnitRace("player")) jcallahan@27: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Local variables. jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: local db jcallahan@0: local durability_timer_handle jcallahan@2: local target_location_timer_handle jcallahan@1: local action_data = {} jcallahan@40: local currently_drunk jcallahan@32: local faction_standings = {} jcallahan@67: local reputation_npc_id jcallahan@0: jcallahan@1: jcallahan@1: ----------------------------------------------------------------------- jcallahan@1: -- Helper Functions. jcallahan@1: ----------------------------------------------------------------------- jcallahan@39: local ActualCopperCost jcallahan@39: do jcallahan@39: local BARTERING_SPELL_ID = 83964 jcallahan@39: jcallahan@39: local STANDING_DISCOUNTS = { jcallahan@39: HATED = 0, jcallahan@39: HOSTILE = 0, jcallahan@39: UNFRIENDLY = 0, jcallahan@39: NEUTRAL = 0, jcallahan@39: FRIENDLY = 0.05, jcallahan@39: HONORED = 0.1, jcallahan@39: REVERED = 0.15, jcallahan@39: EXALTED = 0.2, jcallahan@39: } jcallahan@39: jcallahan@39: jcallahan@39: function ActualCopperCost(copper_cost, rep_standing) jcallahan@39: if not copper_cost or copper_cost == 0 then jcallahan@39: return 0 jcallahan@39: end jcallahan@39: local modifier = 1 jcallahan@39: jcallahan@39: if _G.IsSpellKnown(BARTERING_SPELL_ID) then jcallahan@39: modifier = modifier - 0.1 jcallahan@39: end jcallahan@39: jcallahan@39: if rep_standing then jcallahan@39: if PLAYER_RACE == "Goblin" then jcallahan@39: modifier = modifier - STANDING_DISCOUNTS["EXALTED"] jcallahan@39: elseif STANDING_DISCOUNTS[rep_standing] then jcallahan@39: modifier = modifier - STANDING_DISCOUNTS[rep_standing] jcallahan@39: end jcallahan@39: end jcallahan@39: return math.floor(copper_cost / modifier) jcallahan@39: end jcallahan@39: end -- do-block jcallahan@39: jcallahan@39: jcallahan@29: local function InstanceDifficultyToken() jcallahan@29: local _, instance_type, instance_difficulty, difficulty_name, _, _, is_dynamic = _G.GetInstanceInfo() jcallahan@59: if not difficulty_name or difficulty_name == "" then jcallahan@29: difficulty_name = "NONE" jcallahan@29: end jcallahan@59: jcallahan@59: if not instance_type or instance_type == "" then jcallahan@59: instance_type = "NONE" jcallahan@59: end jcallahan@29: return ("%s:%s:%s"):format(instance_type:upper(), difficulty_name:upper():gsub(" ", "_"), _G.tostring(is_dynamic)) jcallahan@29: end jcallahan@29: jcallahan@29: jcallahan@19: local function DBEntry(data_type, unit_id) jcallahan@19: if not data_type or not unit_id then jcallahan@6: return jcallahan@6: end jcallahan@19: local unit = db[data_type][unit_id] jcallahan@6: jcallahan@10: if not unit then jcallahan@19: db[data_type][unit_id] = {} jcallahan@19: unit = db[data_type][unit_id] jcallahan@6: end jcallahan@10: return unit jcallahan@6: end jcallahan@6: jcallahan@6: jcallahan@29: local function NPCEntry(identifier) jcallahan@29: local npc = DBEntry("npcs", identifier) jcallahan@29: jcallahan@29: if not npc then jcallahan@29: return jcallahan@22: end jcallahan@29: local instance_token = InstanceDifficultyToken() jcallahan@29: npc.encounter_data = npc.encounter_data or {} jcallahan@29: npc.encounter_data[instance_token] = npc.encounter_data[instance_token] or {} jcallahan@30: npc.encounter_data[instance_token].stats = npc.encounter_data[instance_token].stats or {} jcallahan@29: return npc jcallahan@22: end jcallahan@22: jcallahan@22: jcallahan@1: local function CurrentLocationData() jcallahan@1: local map_level = _G.GetCurrentMapDungeonLevel() or 0 jcallahan@1: local x, y = _G.GetPlayerMapPosition("player") jcallahan@1: jcallahan@1: x = x or 0 jcallahan@1: y = y or 0 jcallahan@1: jcallahan@1: if x == 0 and y == 0 then jcallahan@1: for level_index = 1, _G.GetNumDungeonMapLevels() do jcallahan@1: _G.SetDungeonMapLevel(level_index) jcallahan@1: x, y = _G.GetPlayerMapPosition("player") jcallahan@1: jcallahan@1: if x and y and (x > 0 or y > 0) then jcallahan@1: _G.SetDungeonMapLevel(map_level) jcallahan@1: map_level = level_index jcallahan@1: break jcallahan@1: end jcallahan@1: end jcallahan@1: end jcallahan@1: jcallahan@1: if _G.DungeonUsesTerrainMap() then jcallahan@1: map_level = map_level - 1 jcallahan@1: end jcallahan@31: local x = _G.floor(x * 1000) jcallahan@31: local y = _G.floor(y * 1000) jcallahan@28: jcallahan@31: if x % 2 ~= 0 then jcallahan@31: x = x + 1 jcallahan@28: end jcallahan@28: jcallahan@31: if y % 2 ~= 0 then jcallahan@31: y = y + 1 jcallahan@28: end jcallahan@31: return _G.GetRealZoneText(), _G.GetCurrentMapAreaID(), x, y, map_level, InstanceDifficultyToken() jcallahan@1: end jcallahan@1: jcallahan@1: jcallahan@1: local function ItemLinkToID(item_link) jcallahan@1: if not item_link then jcallahan@1: return jcallahan@1: end jcallahan@7: return tonumber(item_link:match("item:(%d+)")) jcallahan@1: end jcallahan@0: jcallahan@4: jcallahan@34: local ParseGUID jcallahan@4: do jcallahan@4: local UNIT_TYPE_BITMASK = 0x007 jcallahan@4: jcallahan@34: function ParseGUID(guid) jcallahan@5: if not guid then jcallahan@5: return jcallahan@5: end jcallahan@4: local types = private.UNIT_TYPES jcallahan@4: local unit_type = _G.bit.band(tonumber(guid:sub(1, 5)), UNIT_TYPE_BITMASK) jcallahan@4: jcallahan@10: if unit_type ~= types.PLAYER and unit_type ~= types.PET then jcallahan@66: return unit_type, tonumber(guid:sub(6, 10), 16) jcallahan@4: end jcallahan@4: return unit_type jcallahan@4: end jcallahan@4: end -- do-block jcallahan@4: jcallahan@4: jcallahan@34: local UpdateNPCLocation jcallahan@34: do jcallahan@34: local COORD_MAX = 5 jcallahan@34: jcallahan@34: function UpdateNPCLocation(unit_idnum) jcallahan@34: local zone_name, area_id, x, y, map_level, difficulty_token = CurrentLocationData() jcallahan@34: local npc_data = NPCEntry(unit_idnum).encounter_data[difficulty_token].stats[("level_%d"):format(_G.UnitLevel("target"))] jcallahan@34: local zone_token = ("%s:%d"):format(zone_name, area_id) jcallahan@34: npc_data.locations = npc_data.locations or {} jcallahan@34: jcallahan@34: local zone_data = npc_data.locations[zone_token] jcallahan@34: jcallahan@34: if not zone_data then jcallahan@34: zone_data = {} jcallahan@34: npc_data.locations[zone_token] = zone_data jcallahan@34: end jcallahan@34: jcallahan@34: for location_token in pairs(zone_data) do jcallahan@34: local loc_level, loc_x, loc_y = (":"):split(location_token) jcallahan@34: loc_level = tonumber(loc_level) jcallahan@34: jcallahan@34: if map_level == loc_level and math.abs(x - loc_x) <= COORD_MAX and math.abs(y - loc_y) <= COORD_MAX then jcallahan@34: return jcallahan@34: end jcallahan@34: end jcallahan@34: zone_data[("%s:%s:%s"):format(map_level, x, y)] = true jcallahan@34: end jcallahan@34: end -- do-block jcallahan@34: jcallahan@34: jcallahan@52: local function UpdateDBEntryLocation(entry_type, identifier) jcallahan@10: if not identifier then jcallahan@10: return jcallahan@10: end jcallahan@31: local zone_name, area_id, x, y, map_level, difficulty_token = CurrentLocationData() jcallahan@41: local entry = DBEntry(entry_type, identifier) jcallahan@41: entry[difficulty_token] = entry[difficulty_token] or {} jcallahan@52: entry[difficulty_token].locations = entry[difficulty_token].locations or {} jcallahan@10: jcallahan@31: local zone_token = ("%s:%d"):format(zone_name, area_id) jcallahan@52: local zone_data = entry[difficulty_token].locations[zone_token] jcallahan@24: jcallahan@31: if not zone_data then jcallahan@31: zone_data = {} jcallahan@52: entry[difficulty_token].locations[zone_token] = zone_data jcallahan@10: end jcallahan@41: local location_token = ("%s:%s:%s"):format(map_level, x, y) jcallahan@41: zone_data[location_token] = zone_data[location_token] or true jcallahan@41: return zone_data jcallahan@10: end jcallahan@10: jcallahan@10: jcallahan@19: local function HandleItemUse(item_link, bag_index, slot_index) jcallahan@19: if not item_link then jcallahan@19: return jcallahan@19: end jcallahan@19: local item_id = ItemLinkToID(item_link) jcallahan@19: jcallahan@19: if not bag_index or not slot_index then jcallahan@19: for new_bag_index = 0, _G.NUM_BAG_FRAMES do jcallahan@19: for new_slot_index = 1, _G.GetContainerNumSlots(new_bag_index) do jcallahan@19: if item_id == ItemLinkToID(_G.GetContainerItemLink(new_bag_index, new_slot_index)) then jcallahan@19: bag_index = new_bag_index jcallahan@19: slot_index = new_slot_index jcallahan@19: break jcallahan@19: end jcallahan@19: end jcallahan@19: end jcallahan@19: end jcallahan@19: jcallahan@19: if not bag_index or not slot_index then jcallahan@19: return jcallahan@19: end jcallahan@19: local _, _, _, _, _, is_lootable = _G.GetContainerItemInfo(bag_index, slot_index) jcallahan@19: jcallahan@19: if not is_lootable then jcallahan@19: return jcallahan@19: end jcallahan@19: DatamineTT:ClearLines() jcallahan@19: DatamineTT:SetBagItem(bag_index, slot_index) jcallahan@19: jcallahan@19: for line_index = 1, DatamineTT:NumLines() do jcallahan@19: local current_line = _G["WDPDatamineTTTextLeft" .. line_index] jcallahan@19: jcallahan@19: if not current_line then jcallahan@19: break jcallahan@19: end jcallahan@19: jcallahan@19: if current_line:GetText() == _G.ITEM_OPENABLE then jcallahan@19: table.wipe(action_data) jcallahan@19: action_data.type = AF.ITEM jcallahan@28: action_data.identifier = item_id jcallahan@25: action_data.label = "contains" jcallahan@19: break jcallahan@19: end jcallahan@19: end jcallahan@19: end jcallahan@19: jcallahan@19: jcallahan@39: local UnitFactionStanding jcallahan@32: local UpdateFactionData jcallahan@32: do jcallahan@32: local MAX_FACTION_INDEX = 1000 jcallahan@20: jcallahan@32: local STANDING_NAMES = { jcallahan@32: "HATED", jcallahan@32: "HOSTILE", jcallahan@32: "UNFRIENDLY", jcallahan@32: "NEUTRAL", jcallahan@32: "FRIENDLY", jcallahan@32: "HONORED", jcallahan@32: "REVERED", jcallahan@32: "EXALTED", jcallahan@32: } jcallahan@32: jcallahan@39: jcallahan@39: function UnitFactionStanding(unit) jcallahan@39: UpdateFactionData() jcallahan@39: DatamineTT:ClearLines() jcallahan@39: DatamineTT:SetUnit(unit) jcallahan@39: jcallahan@39: for line_index = 1, DatamineTT:NumLines() do jcallahan@64: local faction_name = _G["WDPDatamineTTTextLeft" .. line_index]:GetText():trim() jcallahan@39: jcallahan@39: if faction_name and faction_standings[faction_name] then jcallahan@39: return faction_name, faction_standings[faction_name] jcallahan@39: end jcallahan@39: end jcallahan@39: end jcallahan@39: jcallahan@39: jcallahan@32: function UpdateFactionData() jcallahan@32: for faction_index = 1, MAX_FACTION_INDEX do jcallahan@32: local faction_name, _, current_standing, _, _, _, _, _, is_header = _G.GetFactionInfo(faction_index) jcallahan@32: jcallahan@32: if faction_name and not is_header then jcallahan@32: faction_standings[faction_name] = STANDING_NAMES[current_standing] jcallahan@32: elseif not faction_name then jcallahan@32: break jcallahan@32: end jcallahan@20: end jcallahan@20: end jcallahan@32: end -- do-block jcallahan@20: jcallahan@48: jcallahan@62: local function GenericLootUpdate(data_type, top_field) jcallahan@48: local entry = DBEntry(data_type, action_data.identifier) jcallahan@48: jcallahan@48: if not entry then jcallahan@48: return jcallahan@48: end jcallahan@48: local loot_type = action_data.label or "drops" jcallahan@48: local loot_count = ("%s_count"):format(loot_type) jcallahan@48: local loot_data jcallahan@48: jcallahan@48: if top_field then jcallahan@48: entry[top_field] = entry[top_field] or {} jcallahan@48: entry[top_field][loot_count] = (entry[top_field][loot_count] or 0) + 1 jcallahan@48: entry[top_field][loot_type] = entry[top_field][loot_type] or {} jcallahan@48: loot_data = entry[top_field][loot_type] jcallahan@48: else jcallahan@48: entry[loot_count] = (entry[loot_count] or 0) + 1 jcallahan@48: entry[loot_type] = entry[loot_type] or {} jcallahan@48: loot_data = entry[loot_type] jcallahan@48: end jcallahan@48: jcallahan@48: for index = 1, #action_data.loot_list do jcallahan@48: table.insert(loot_data, action_data.loot_list[index]) jcallahan@48: end jcallahan@48: end jcallahan@48: jcallahan@48: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Methods. jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: function WDP:OnInitialize() jcallahan@71: -- TODO: Remove this once 5.0.4 hits Live. jcallahan@71: if private.wow_version == "4.3.4" then jcallahan@71: return jcallahan@71: end jcallahan@0: db = LibStub("AceDB-3.0"):New("WoWDBProfilerData", DATABASE_DEFAULTS, "Default").global jcallahan@14: jcallahan@14: local raw_db = _G["WoWDBProfilerData"] jcallahan@18: local build_num = tonumber(private.build_num) jcallahan@14: jcallahan@35: -- TODO: Un-comment this when MoP goes live. jcallahan@35: -- if raw_db.build_num and raw_db.build_num < build_num then jcallahan@35: -- for entry in pairs(DATABASE_DEFAULTS.global) do jcallahan@35: -- db[entry] = {} jcallahan@35: -- end jcallahan@35: -- end jcallahan@35: raw_db.build_num = build_num jcallahan@63: raw_db.version = DB_VERSION jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@0: function WDP:OnEnable() jcallahan@71: -- TODO: Remove this once 5.0.4 hits Live. jcallahan@71: if private.wow_version == "4.3.4" then jcallahan@71: return jcallahan@71: end jcallahan@71: jcallahan@0: for event_name, mapping in pairs(EVENT_MAPPING) do jcallahan@1: self:RegisterEvent(event_name, (_G.type(mapping) ~= "boolean") and mapping or nil) jcallahan@0: end jcallahan@0: durability_timer_handle = self:ScheduleRepeatingTimer("ProcessDurability", 30) jcallahan@31: target_location_timer_handle = self:ScheduleRepeatingTimer("UpdateTargetLocation", 0.5) jcallahan@19: jcallahan@19: _G.hooksecurefunc("UseContainerItem", function(bag_index, slot_index, target_unit) jcallahan@19: if target_unit then jcallahan@19: return jcallahan@19: end jcallahan@19: HandleItemUse(_G.GetContainerItemLink(bag_index, slot_index), bag_index, slot_index) jcallahan@19: end) jcallahan@19: jcallahan@19: _G.hooksecurefunc("UseItemByName", function(identifier, target_unit) jcallahan@19: if target_unit then jcallahan@19: return jcallahan@19: end jcallahan@19: local _, item_link = _G.GetItemInfo(identifier) jcallahan@19: HandleItemUse(item_link) jcallahan@19: end) jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@0: local function RecordDurability(item_id, durability) jcallahan@0: if not durability or durability <= 0 then jcallahan@0: return jcallahan@0: end jcallahan@0: jcallahan@0: if not db.items[item_id] then jcallahan@0: db.items[item_id] = {} jcallahan@0: end jcallahan@0: db.items[item_id].durability = durability jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@0: function WDP:ProcessDurability() jcallahan@0: for slot_index = 0, _G.INVSLOT_LAST_EQUIPPED do jcallahan@1: local item_id = _G.GetInventoryItemID("player", slot_index) jcallahan@0: jcallahan@0: if item_id and item_id > 0 then jcallahan@1: local _, max_durability = _G.GetInventoryItemDurability(slot_index) jcallahan@0: RecordDurability(item_id, max_durability) jcallahan@0: end jcallahan@0: end jcallahan@0: jcallahan@0: for bag_index = 0, _G.NUM_BAG_SLOTS do jcallahan@0: for slot_index = 1, _G.GetContainerNumSlots(bag_index) do jcallahan@1: local item_id = _G.GetContainerItemID(bag_index, slot_index) jcallahan@0: jcallahan@0: if item_id and item_id > 0 then jcallahan@1: local _, max_durability = _G.GetContainerItemDurability(bag_index, slot_index) jcallahan@0: RecordDurability(item_id, max_durability) jcallahan@0: end jcallahan@0: end jcallahan@0: end jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@2: function WDP:UpdateTargetLocation() jcallahan@40: if currently_drunk or not _G.UnitExists("target") or _G.UnitPlayerControlled("target") or (_G.UnitIsTapped("target") and not _G.UnitIsDead("target")) then jcallahan@2: return jcallahan@2: end jcallahan@2: jcallahan@2: for index = 1, 4 do jcallahan@2: if not _G.CheckInteractDistance("target", index) then jcallahan@2: return jcallahan@2: end jcallahan@2: end jcallahan@40: local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("target")) jcallahan@2: jcallahan@40: if not unit_idnum or unit_type ~= private.UNIT_TYPES.NPC then jcallahan@2: return jcallahan@2: end jcallahan@34: UpdateNPCLocation(unit_idnum) jcallahan@2: end jcallahan@2: jcallahan@2: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Event handlers. jcallahan@0: ----------------------------------------------------------------------- jcallahan@56: function WDP:BLACK_MARKET_ITEM_UPDATE(event) jcallahan@56: local num_items = _G.C_BlackMarket.GetNumItems() jcallahan@56: jcallahan@56: for index = 1, num_items do jcallahan@56: local name, texture, quantity, item_type, is_usable, level, level_type, seller_name, min_bid, min_increment, current_bid, has_high_bid, num_bids, time_left, item_link, market_id = _G.C_BlackMarket.GetItemInfoByIndex(index); jcallahan@56: jcallahan@56: if item_link then jcallahan@56: DBEntry("items", ItemLinkToID(item_link)).black_market = seller_name or "UNKNOWN" jcallahan@56: end jcallahan@56: end jcallahan@56: end jcallahan@56: jcallahan@56: jcallahan@48: function WDP:CHAT_MSG_LOOT(event, message) jcallahan@48: if action_data.spell_label ~= "EXTRACT_GAS" then jcallahan@48: return jcallahan@48: end jcallahan@48: local item_link, quantity = deformat(message, _G.LOOT_ITEM_PUSHED_SELF_MULTIPLE) jcallahan@48: jcallahan@48: if not item_link then jcallahan@48: quantity, item_link = 1, deformat(message, _G.LOOT_ITEM_PUSHED_SELF) jcallahan@48: end jcallahan@48: jcallahan@48: if not item_link then jcallahan@48: return jcallahan@48: end jcallahan@48: local item_id = ItemLinkToID(item_link) jcallahan@48: jcallahan@48: if not item_id then jcallahan@48: return jcallahan@48: end jcallahan@48: action_data.loot_list = { jcallahan@48: ("%d:%d"):format(item_id, quantity) jcallahan@48: } jcallahan@48: GenericLootUpdate("zones") jcallahan@48: table.wipe(action_data) jcallahan@48: end jcallahan@48: jcallahan@48: jcallahan@23: do jcallahan@40: local SOBER_MATCH = _G.DRUNK_MESSAGE_ITEM_SELF1:gsub("%%s", ".+") jcallahan@40: jcallahan@40: local DRUNK_COMPARES = { jcallahan@40: _G.DRUNK_MESSAGE_SELF2, jcallahan@40: _G.DRUNK_MESSAGE_SELF3, jcallahan@40: _G.DRUNK_MESSAGE_SELF4, jcallahan@40: } jcallahan@40: jcallahan@40: local DRUNK_MATCHES = { jcallahan@40: _G.DRUNK_MESSAGE_SELF2:gsub("%%s", ".+"), jcallahan@40: _G.DRUNK_MESSAGE_SELF3:gsub("%%s", ".+"), jcallahan@40: _G.DRUNK_MESSAGE_SELF4:gsub("%%s", ".+"), jcallahan@40: } jcallahan@40: jcallahan@40: function WDP:CHAT_MSG_SYSTEM(event, message) jcallahan@40: if currently_drunk then jcallahan@40: if message == _G.DRUNK_MESSAGE_SELF1 or message:match(SOBER_MATCH) then jcallahan@40: currently_drunk = nil jcallahan@40: end jcallahan@40: return jcallahan@40: end jcallahan@40: jcallahan@40: for index = 1, #DRUNK_MATCHES do jcallahan@40: if message == DRUNK_COMPARES[index] or message:match(DRUNK_MATCHES[index]) then jcallahan@40: currently_drunk = true jcallahan@40: break jcallahan@40: end jcallahan@40: end jcallahan@40: end jcallahan@40: end jcallahan@40: jcallahan@40: -- do-block jcallahan@40: jcallahan@40: do jcallahan@23: local FLAGS_NPC = bit.bor(_G.COMBATLOG_OBJECT_TYPE_GUARDIAN, _G.COMBATLOG_OBJECT_CONTROL_NPC) jcallahan@23: local FLAGS_NPC_CONTROL = bit.bor(_G.COMBATLOG_OBJECT_AFFILIATION_OUTSIDER, _G.COMBATLOG_OBJECT_CONTROL_NPC) jcallahan@23: jcallahan@23: local function RecordNPCSpell(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id, spell_name) jcallahan@23: if not spell_id then jcallahan@23: return jcallahan@23: end jcallahan@34: local source_type, source_id = ParseGUID(source_guid) jcallahan@23: jcallahan@23: if not source_id or source_type ~= private.UNIT_TYPES.NPC then jcallahan@23: return jcallahan@23: end jcallahan@23: jcallahan@23: if bit.band(FLAGS_NPC_CONTROL, source_flags) == FLAGS_NPC_CONTROL and bit.band(FLAGS_NPC, source_flags) ~= 0 then jcallahan@29: local encounter_data = NPCEntry(source_id).encounter_data[InstanceDifficultyToken()] jcallahan@28: encounter_data.spells = encounter_data.spells or {} jcallahan@28: encounter_data.spells[spell_id] = (encounter_data.spells[spell_id] or 0) + 1 jcallahan@23: end jcallahan@23: end jcallahan@23: jcallahan@23: local COMBAT_LOG_FUNCS = { jcallahan@23: SPELL_AURA_APPLIED = RecordNPCSpell, jcallahan@23: SPELL_CAST_START = RecordNPCSpell, jcallahan@48: SPELL_CAST_SUCCESS = RecordNPCSpell, jcallahan@65: UNIT_DIED = function(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id, spell_name) jcallahan@65: local unit_type, unit_idnum = ParseGUID(dest_guid) jcallahan@65: jcallahan@65: if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then jcallahan@67: reputation_npc_id = nil jcallahan@65: return jcallahan@65: end jcallahan@67: reputation_npc_id = unit_idnum jcallahan@65: end, jcallahan@23: } jcallahan@23: jcallahan@23: jcallahan@23: function WDP:COMBAT_LOG_EVENT_UNFILTERED(event, time_stamp, sub_event, hide_caster, source_guid, source_name, source_flags, source_raid_flags, dest_guid, dest_name, dest_flags, dest_raid_flags, ...) jcallahan@23: local combat_log_func = COMBAT_LOG_FUNCS[sub_event] jcallahan@23: jcallahan@23: if not combat_log_func then jcallahan@23: return jcallahan@23: end jcallahan@23: combat_log_func(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, ...) jcallahan@23: end jcallahan@23: jcallahan@44: local DIPLOMACY_SPELL_ID = 20599 jcallahan@44: local MR_POP_RANK1_SPELL_ID = 78634 jcallahan@44: local MR_POP_RANK2_SPELL_ID = 78635 jcallahan@44: jcallahan@44: local REP_BUFFS = { jcallahan@44: [_G.GetSpellInfo(30754)] = "CENARION_FAVOR", jcallahan@44: [_G.GetSpellInfo(24705)] = "GRIM_VISAGE", jcallahan@44: [_G.GetSpellInfo(32098)] = "HONOR_HOLD_FAVOR", jcallahan@44: [_G.GetSpellInfo(39913)] = "NAZGRELS_FERVOR", jcallahan@44: [_G.GetSpellInfo(39953)] = "SONG_OF_BATTLE", jcallahan@44: [_G.GetSpellInfo(61849)] = "SPIRIT_OF_SHARING", jcallahan@44: [_G.GetSpellInfo(32096)] = "THRALLMARS_FAVOR", jcallahan@44: [_G.GetSpellInfo(39911)] = "TROLLBANES_COMMAND", jcallahan@44: [_G.GetSpellInfo(95987)] = "UNBURDENED", jcallahan@44: [_G.GetSpellInfo(100951)] = "WOW_ANNIVERSARY", jcallahan@44: } jcallahan@44: jcallahan@44: jcallahan@44: local FACTION_NAMES = { jcallahan@44: CENARION_CIRCLE = _G.GetFactionInfoByID(609), jcallahan@44: HONOR_HOLD = _G.GetFactionInfoByID(946), jcallahan@44: THE_SHATAR = _G.GetFactionInfoByID(935), jcallahan@44: THRALLMAR = _G.GetFactionInfoByID(947), jcallahan@44: } jcallahan@44: jcallahan@44: jcallahan@44: local MODIFIERS = { jcallahan@44: CENARION_FAVOR = { jcallahan@44: faction = FACTION_NAMES.CENARION_CIRCLE, jcallahan@44: modifier = 0.25, jcallahan@44: }, jcallahan@44: GRIM_VISAGE = { jcallahan@44: modifier = 0.1, jcallahan@44: }, jcallahan@44: HONOR_HOLD_FAVOR = { jcallahan@44: faction = FACTION_NAMES.HONOR_HOLD, jcallahan@44: modifier = 0.25, jcallahan@44: }, jcallahan@44: NAZGRELS_FERVOR = { jcallahan@44: faction = FACTION_NAMES.THRALLMAR, jcallahan@44: modifier = 0.1, jcallahan@44: }, jcallahan@44: SONG_OF_BATTLE = { jcallahan@44: faction = FACTION_NAMES.THE_SHATAR, jcallahan@44: modifier = 0.1, jcallahan@44: }, jcallahan@44: SPIRIT_OF_SHARING = { jcallahan@44: modifier = 0.1, jcallahan@44: }, jcallahan@44: THRALLMARS_FAVOR = { jcallahan@44: faction = FACTION_NAMES.THRALLMAR, jcallahan@44: modifier = 0.25, jcallahan@44: }, jcallahan@44: TROLLBANES_COMMAND = { jcallahan@44: faction = FACTION_NAMES.HONOR_HOLD, jcallahan@44: modifier = 0.1, jcallahan@44: }, jcallahan@44: UNBURDENED = { jcallahan@44: modifier = 0.1, jcallahan@44: }, jcallahan@44: WOW_ANNIVERSARY = { jcallahan@44: modifier = 0.08, jcallahan@44: } jcallahan@44: } jcallahan@44: jcallahan@44: jcallahan@44: function WDP:COMBAT_TEXT_UPDATE(event, message_type, faction_name, amount) jcallahan@67: if message_type ~= "FACTION" or not reputation_npc_id then jcallahan@44: return jcallahan@44: end jcallahan@44: UpdateFactionData() jcallahan@44: jcallahan@46: if not faction_name or not faction_standings[faction_name] then jcallahan@46: return jcallahan@46: end jcallahan@67: local npc = NPCEntry(reputation_npc_id) jcallahan@46: jcallahan@44: if not npc then jcallahan@44: return jcallahan@44: end jcallahan@44: local modifier = 1 jcallahan@44: jcallahan@44: if _G.IsSpellKnown(DIPLOMACY_SPELL_ID) then jcallahan@44: modifier = modifier + 0.1 jcallahan@44: end jcallahan@44: jcallahan@44: if _G.IsSpellKnown(MR_POP_RANK2_SPELL_ID) then jcallahan@44: modifier = modifier + 0.1 jcallahan@44: elseif _G.IsSpellKnown(MR_POP_RANK1_SPELL_ID) then jcallahan@44: modifier = modifier + 0.05 jcallahan@44: end jcallahan@44: jcallahan@44: for buff_name, buff_label in pairs(REP_BUFFS) do jcallahan@44: if _G.UnitBuff("player", buff_name) then jcallahan@44: local modded_faction = MODIFIERS[buff_label].faction jcallahan@44: jcallahan@44: if not modded_faction or faction_name == modded_faction then jcallahan@44: modifier = modifier + MODIFIERS[buff_label].modifier jcallahan@44: end jcallahan@44: end jcallahan@44: end jcallahan@65: npc.reputations = npc.reputations or {} jcallahan@65: npc.reputations[("%s:%s"):format(faction_name, faction_standings[faction_name])] = math.floor(amount / modifier) jcallahan@67: reputation_npc_id = nil jcallahan@32: end jcallahan@44: end -- do-block jcallahan@18: jcallahan@18: jcallahan@42: function WDP:ITEM_TEXT_BEGIN() jcallahan@42: local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc")) jcallahan@42: jcallahan@42: if not unit_idnum or unit_type ~= private.UNIT_TYPES.OBJECT or _G.UnitName("npc") ~= _G.ItemTextGetItem() then jcallahan@42: return jcallahan@42: end jcallahan@42: UpdateDBEntryLocation("objects", unit_idnum) jcallahan@42: end jcallahan@42: jcallahan@42: jcallahan@13: do jcallahan@40: local RE_GOLD = _G.GOLD_AMOUNT:gsub("%%d", "(%%d+)") jcallahan@40: local RE_SILVER = _G.SILVER_AMOUNT:gsub("%%d", "(%%d+)") jcallahan@40: local RE_COPPER = _G.COPPER_AMOUNT:gsub("%%d", "(%%d+)") jcallahan@13: jcallahan@13: jcallahan@13: local function _moneyMatch(money, re) jcallahan@13: return money:match(re) or 0 jcallahan@1: end jcallahan@1: jcallahan@0: jcallahan@13: local function _toCopper(money) jcallahan@13: if not money then jcallahan@13: return 0 jcallahan@13: end jcallahan@40: return _moneyMatch(money, RE_GOLD) * 10000 + _moneyMatch(money, RE_SILVER) * 100 + _moneyMatch(money, RE_COPPER) jcallahan@1: end jcallahan@1: jcallahan@1: jcallahan@13: local LOOT_VERIFY_FUNCS = { jcallahan@16: [AF.ITEM] = function() jcallahan@16: local locked_item_id jcallahan@16: jcallahan@16: for bag_index = 0, _G.NUM_BAG_FRAMES do jcallahan@16: for slot_index = 1, _G.GetContainerNumSlots(bag_index) do jcallahan@16: local _, _, is_locked = _G.GetContainerItemInfo(bag_index, slot_index) jcallahan@16: jcallahan@16: if is_locked then jcallahan@16: locked_item_id = ItemLinkToID(_G.GetContainerItemLink(bag_index, slot_index)) jcallahan@16: end jcallahan@16: end jcallahan@16: end jcallahan@16: jcallahan@28: if not locked_item_id or (action_data.identifier and action_data.identifier ~= locked_item_id) then jcallahan@16: return false jcallahan@16: end jcallahan@28: action_data.identifier = locked_item_id jcallahan@16: return true jcallahan@16: end, jcallahan@13: [AF.NPC] = function() jcallahan@17: if not _G.UnitExists("target") or _G.UnitIsFriend("player", "target") or _G.UnitIsPlayer("target") or _G.UnitPlayerControlled("target") then jcallahan@15: return false jcallahan@13: end jcallahan@34: local unit_type, id_num = ParseGUID(_G.UnitGUID("target")) jcallahan@27: action_data.identifier = id_num jcallahan@13: return true jcallahan@13: end, jcallahan@14: [AF.OBJECT] = true, jcallahan@17: [AF.ZONE] = function() jcallahan@38: return _G.IsFishingLoot() jcallahan@17: end, jcallahan@13: } jcallahan@13: jcallahan@13: jcallahan@13: local LOOT_UPDATE_FUNCS = { jcallahan@16: [AF.ITEM] = function() jcallahan@28: GenericLootUpdate("items") jcallahan@28: end, jcallahan@28: [AF.NPC] = function() jcallahan@29: local npc = NPCEntry(action_data.identifier) jcallahan@28: jcallahan@28: if not npc then jcallahan@28: return jcallahan@28: end jcallahan@29: local encounter_data = npc.encounter_data[InstanceDifficultyToken()] jcallahan@25: local loot_type = action_data.label or "drops" jcallahan@62: encounter_data.loot_counts = encounter_data.loot_counts or {} jcallahan@62: encounter_data.loot_counts[loot_type] = (encounter_data.loot_counts[loot_type] or 0) + 1 jcallahan@29: encounter_data[loot_type] = encounter_data[loot_type] or {} jcallahan@16: jcallahan@17: for index = 1, #action_data.loot_list do jcallahan@29: table.insert(encounter_data[loot_type], action_data.loot_list[index]) jcallahan@16: end jcallahan@16: end, jcallahan@13: [AF.OBJECT] = function() jcallahan@28: GenericLootUpdate("objects", InstanceDifficultyToken()) jcallahan@17: end, jcallahan@17: [AF.ZONE] = function() jcallahan@41: local location_token = ("%s:%s:%s"):format(action_data.map_level, action_data.x, action_data.y) jcallahan@41: jcallahan@41: -- This will start life as a boolean true. jcallahan@41: if _G.type(action_data.zone_data[location_token]) ~= "table" then jcallahan@41: action_data.zone_data[location_token] = { jcallahan@41: drops = {} jcallahan@41: } jcallahan@41: end jcallahan@41: local loot_count = ("%s_count"):format(action_data.label or "drops") jcallahan@41: action_data.zone_data[location_token][loot_count] = (action_data.zone_data[location_token][loot_count] or 0) + 1 jcallahan@41: jcallahan@41: for index = 1, #action_data.loot_list do jcallahan@41: table.insert(action_data.zone_data[location_token].drops, action_data.loot_list[index]) jcallahan@41: end jcallahan@13: end, jcallahan@13: } jcallahan@13: jcallahan@13: jcallahan@13: function WDP:LOOT_OPENED() jcallahan@18: if action_data.looting then jcallahan@18: return jcallahan@18: end jcallahan@18: jcallahan@13: if not action_data.type then jcallahan@13: action_data.type = AF.NPC jcallahan@1: end jcallahan@13: local verify_func = LOOT_VERIFY_FUNCS[action_data.type] jcallahan@13: local update_func = LOOT_UPDATE_FUNCS[action_data.type] jcallahan@13: jcallahan@14: if not verify_func or not update_func then jcallahan@13: return jcallahan@13: end jcallahan@13: jcallahan@14: if _G.type(verify_func) == "function" and not verify_func() then jcallahan@14: return jcallahan@14: end jcallahan@13: local loot_registry = {} jcallahan@17: action_data.loot_list = {} jcallahan@18: action_data.looting = true jcallahan@13: jcallahan@55: for loot_slot = 1, _G.GetNumLootItems() do jcallahan@55: local icon_texture, item_text, quantity, quality, locked = _G.GetLootSlotInfo(loot_slot) jcallahan@55: local slot_type = _G.GetLootSlotType(loot_slot) jcallahan@13: jcallahan@55: if slot_type == _G.LOOT_SLOT_ITEM then jcallahan@55: local item_id = ItemLinkToID(_G.GetLootSlotLink(loot_slot)) jcallahan@55: loot_registry[item_id] = (loot_registry[item_id]) or 0 + quantity jcallahan@55: elseif slot_type == _G.LOOT_SLOT_MONEY then jcallahan@55: table.insert(action_data.loot_list, ("money:%d"):format(_toCopper(item_text))) jcallahan@55: elseif slot_type == _G.LOOT_SLOT_CURRENCY then jcallahan@55: table.insert(action_data.loot_list, ("currency:%d:%s"):format(quantity, icon_texture:match("[^\\]+$"):lower())) jcallahan@13: end jcallahan@13: end jcallahan@13: jcallahan@13: for item_id, quantity in pairs(loot_registry) do jcallahan@17: table.insert(action_data.loot_list, ("%d:%d"):format(item_id, quantity)) jcallahan@13: end jcallahan@13: update_func() jcallahan@1: end jcallahan@13: end -- do-block jcallahan@0: jcallahan@0: jcallahan@44: do jcallahan@44: local POINT_MATCH_PATTERNS = { jcallahan@44: ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING:gsub("%%d", "(%%d+)")), -- May no longer be necessary jcallahan@44: ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3:gsub("%%d", "(%%d+)")), -- May no longer be necessary jcallahan@44: ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_5V5:gsub("%%d", "(%%d+)")), -- May no longer be necessary jcallahan@44: ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_BG:gsub("%%d", "(%%d+)")), jcallahan@44: ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3_BG:gsub("%%d", "(%%d+)")), jcallahan@44: } jcallahan@5: jcallahan@68: local ITEM_REQ_REPUTATION_MATCH = "Requires (.-) %- (.*)" jcallahan@68: jcallahan@44: function WDP:UpdateMerchantItems(event) jcallahan@44: local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("target")) jcallahan@4: jcallahan@44: if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then jcallahan@44: return jcallahan@44: end jcallahan@55: local current_filters = _G.GetMerchantFilter() jcallahan@44: local _, merchant_standing = UnitFactionStanding("target") jcallahan@44: local merchant = NPCEntry(unit_idnum) jcallahan@44: merchant.sells = merchant.sells or {} jcallahan@5: jcallahan@57: _G.SetMerchantFilter(_G.LE_LOOT_FILTER_ALL) jcallahan@57: _G.MerchantFrame_Update() jcallahan@57: jcallahan@57: local num_items = _G.GetMerchantNumItems() jcallahan@35: jcallahan@44: for item_index = 1, num_items do jcallahan@44: local _, _, copper_price, stack_size, num_available, _, extended_cost = _G.GetMerchantItemInfo(item_index) jcallahan@44: local item_id = ItemLinkToID(_G.GetMerchantItemLink(item_index)) jcallahan@5: jcallahan@44: if item_id and item_id > 0 then jcallahan@44: local price_string = ActualCopperCost(copper_price, merchant_standing) jcallahan@5: jcallahan@68: DatamineTT:ClearLines() jcallahan@68: DatamineTT:SetMerchantItem(item_index) jcallahan@68: jcallahan@68: local num_lines = DatamineTT:NumLines() jcallahan@68: jcallahan@68: for line_index = 1, num_lines do jcallahan@68: local current_line = _G["WDPDatamineTTTextLeft" .. line_index] jcallahan@68: jcallahan@68: if not current_line then jcallahan@68: break jcallahan@68: end jcallahan@68: local faction, reputation = current_line:GetText():match(ITEM_REQ_REPUTATION_MATCH) jcallahan@68: jcallahan@68: if faction or reputation then jcallahan@68: DBEntry("items", item_id).req_reputation = ("%s:%s"):format(faction:gsub("-", ""), reputation:upper()) jcallahan@68: break jcallahan@68: end jcallahan@68: end jcallahan@68: jcallahan@44: if extended_cost then jcallahan@53: local battleground_rating = 0 jcallahan@53: local personal_rating = 0 jcallahan@53: local required_season_amount jcallahan@5: jcallahan@68: for line_index = 1, num_lines do jcallahan@44: local current_line = _G["WDPDatamineTTTextLeft" .. line_index] jcallahan@5: jcallahan@44: if not current_line then jcallahan@44: break jcallahan@44: end jcallahan@53: required_season_amount = current_line:GetText():match("Requires earning a total of (%d+)\n(.-) for the season.") jcallahan@5: jcallahan@44: for match_index = 1, #POINT_MATCH_PATTERNS do jcallahan@44: local match1, match2 = current_line:GetText():match(POINT_MATCH_PATTERNS[match_index]) jcallahan@53: personal_rating = personal_rating + (match1 or 0) jcallahan@53: battleground_rating = battleground_rating + (match2 or 0) jcallahan@5: jcallahan@44: if match1 or match2 then jcallahan@44: break jcallahan@44: end jcallahan@44: end jcallahan@5: end jcallahan@44: local currency_list = {} jcallahan@44: local item_count = _G.GetMerchantItemCostInfo(item_index) jcallahan@50: jcallahan@50: -- Keeping this around in case Blizzard makes the two points diverge at some point. jcallahan@53: -- price_string = ("%s:%s:%s:%s"):format(price_string, battleground_rating, personal_rating, required_season_amount or 0) jcallahan@53: price_string = ("%s:%s:%s"):format(price_string, personal_rating, required_season_amount or 0) jcallahan@5: jcallahan@44: for cost_index = 1, item_count do jcallahan@44: local icon_texture, amount_required, currency_link = _G.GetMerchantItemCostItem(item_index, cost_index) jcallahan@44: local currency_id = currency_link and ItemLinkToID(currency_link) or nil jcallahan@44: jcallahan@44: if (not currency_id or currency_id < 1) and icon_texture then jcallahan@44: currency_id = icon_texture:match("[^\\]+$"):lower() jcallahan@44: end jcallahan@44: jcallahan@44: if currency_id then jcallahan@44: currency_list[#currency_list + 1] = ("(%s:%s)"):format(amount_required, currency_id) jcallahan@44: end jcallahan@44: end jcallahan@44: jcallahan@44: for currency_index = 1, #currency_list do jcallahan@44: price_string = ("%s:%s"):format(price_string, currency_list[currency_index]) jcallahan@5: end jcallahan@5: end jcallahan@61: merchant.sells[item_id] = ("%s:%s:[%s]"):format(num_available, stack_size, price_string) jcallahan@44: end jcallahan@44: end jcallahan@5: jcallahan@44: if _G.CanMerchantRepair() then jcallahan@44: merchant.can_repair = true jcallahan@5: end jcallahan@57: _G.SetMerchantFilter(current_filters) jcallahan@57: _G.MerchantFrame_Update() jcallahan@4: end jcallahan@44: end -- do-block jcallahan@4: jcallahan@25: function WDP:PET_BAR_UPDATE() jcallahan@25: if not action_data.label or not action_data.label == "mind_control" then jcallahan@25: return jcallahan@25: end jcallahan@34: local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("pet")) jcallahan@25: jcallahan@25: if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then jcallahan@25: return jcallahan@25: end jcallahan@29: NPCEntry(unit_idnum).mind_control = true jcallahan@25: table.wipe(action_data) jcallahan@25: end jcallahan@25: jcallahan@25: jcallahan@18: do jcallahan@18: local GENDER_NAMES = { jcallahan@18: "UNKNOWN", jcallahan@18: "MALE", jcallahan@18: "FEMALE", jcallahan@18: } jcallahan@2: jcallahan@2: jcallahan@18: local REACTION_NAMES = { jcallahan@18: "HATED", jcallahan@18: "HOSTILE", jcallahan@18: "UNFRIENDLY", jcallahan@18: "NEUTRAL", jcallahan@18: "FRIENDLY", jcallahan@18: "HONORED", jcallahan@18: "REVERED", jcallahan@18: "EXALTED", jcallahan@18: } jcallahan@2: jcallahan@2: jcallahan@18: local POWER_TYPE_NAMES = { jcallahan@18: ["0"] = "MANA", jcallahan@18: ["1"] = "RAGE", jcallahan@18: ["2"] = "FOCUS", jcallahan@18: ["3"] = "ENERGY", jcallahan@18: ["6"] = "RUNIC_POWER", jcallahan@18: } jcallahan@2: jcallahan@2: jcallahan@18: function WDP:PLAYER_TARGET_CHANGED() jcallahan@40: if not _G.UnitExists("target") or _G.UnitPlayerControlled("target") or currently_drunk then jcallahan@18: return jcallahan@18: end jcallahan@34: local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("target")) jcallahan@2: jcallahan@18: if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then jcallahan@18: return jcallahan@18: end jcallahan@29: local npc = NPCEntry(unit_idnum) jcallahan@18: local _, class_token = _G.UnitClass("target") jcallahan@18: npc.class = class_token jcallahan@39: npc.faction = UnitFactionStanding("target") jcallahan@26: npc.genders = npc.genders or {} jcallahan@26: npc.genders[GENDER_NAMES[_G.UnitSex("target")] or "UNDEFINED"] = true jcallahan@18: npc.is_pvp = _G.UnitIsPVP("target") and true or nil jcallahan@18: npc.reaction = ("%s:%s:%s"):format(_G.UnitLevel("player"), _G.UnitFactionGroup("player"), REACTION_NAMES[_G.UnitReaction("player", "target")]) jcallahan@2: jcallahan@30: local encounter_data = npc.encounter_data[InstanceDifficultyToken()].stats jcallahan@18: local npc_level = ("level_%d"):format(_G.UnitLevel("target")) jcallahan@3: jcallahan@28: if not encounter_data[npc_level] then jcallahan@28: encounter_data[npc_level] = { jcallahan@18: max_health = _G.UnitHealthMax("target"), jcallahan@18: } jcallahan@3: jcallahan@18: local max_power = _G.UnitManaMax("target") jcallahan@18: jcallahan@18: if max_power > 0 then jcallahan@18: local power_type = _G.UnitPowerType("target") jcallahan@28: encounter_data[npc_level].power = ("%s:%d"):format(POWER_TYPE_NAMES[_G.tostring(power_type)] or power_type, max_power) jcallahan@18: end jcallahan@3: end jcallahan@21: table.wipe(action_data) jcallahan@21: action_data.type = AF.NPC jcallahan@27: action_data.identifier = unit_idnum jcallahan@21: action_data.npc_level = npc_level jcallahan@31: jcallahan@31: self:UpdateTargetLocation() jcallahan@2: end jcallahan@18: end -- do-block jcallahan@2: jcallahan@12: do jcallahan@12: local function UpdateQuestJuncture(point) jcallahan@12: local unit_name = _G.UnitName("questnpc") jcallahan@9: jcallahan@12: if not unit_name then jcallahan@12: return jcallahan@12: end jcallahan@34: local unit_type, unit_id = ParseGUID(_G.UnitGUID("questnpc")) jcallahan@9: jcallahan@12: if unit_type == private.UNIT_TYPES.OBJECT then jcallahan@38: UpdateDBEntryLocation("objects", unit_id) jcallahan@12: end jcallahan@19: local quest = DBEntry("quests", _G.GetQuestID()) jcallahan@12: quest[point] = quest[point] or {} jcallahan@12: quest[point][("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type + 1], unit_id)] = true jcallahan@24: jcallahan@24: return quest jcallahan@12: end jcallahan@10: jcallahan@12: jcallahan@12: function WDP:QUEST_COMPLETE() jcallahan@67: -- Make sure the quest NPC isn't erroneously recorded as giving reputation for quests which award it. jcallahan@67: reputation_npc_id = nil jcallahan@12: UpdateQuestJuncture("end") jcallahan@10: end jcallahan@10: jcallahan@12: jcallahan@12: function WDP:QUEST_DETAIL() jcallahan@24: local quest = UpdateQuestJuncture("begin") jcallahan@24: jcallahan@46: if not quest then jcallahan@46: return jcallahan@46: end jcallahan@24: quest.classes = quest.classes or {} jcallahan@27: quest.classes[PLAYER_CLASS] = true jcallahan@24: jcallahan@24: local _, race = _G.UnitRace("player") jcallahan@24: quest.races = quest.races or {} jcallahan@24: quest.races[race] = true jcallahan@10: end jcallahan@12: end -- do-block jcallahan@9: jcallahan@9: jcallahan@4: function WDP:QUEST_LOG_UPDATE() jcallahan@38: local selected_quest = _G.GetQuestLogSelection() -- Save current selection to be restored when we're done. jcallahan@36: local entry_index, processed_quests = 1, 0 jcallahan@36: local _, num_quests = _G.GetNumQuestLogEntries() jcallahan@36: jcallahan@36: while processed_quests <= num_quests do jcallahan@36: local _, _, _, _, is_header, _, _, _, quest_id = _G.GetQuestLogTitle(entry_index) jcallahan@36: jcallahan@36: if not is_header then jcallahan@36: _G.SelectQuestLogEntry(entry_index); jcallahan@36: jcallahan@36: local quest = DBEntry("quests", quest_id) jcallahan@36: quest.timer = _G.GetQuestLogTimeLeft() jcallahan@37: quest.can_share = _G.GetQuestLogPushable() and true or nil jcallahan@36: processed_quests = processed_quests + 1 jcallahan@36: end jcallahan@36: entry_index = entry_index + 1 jcallahan@36: end jcallahan@36: _G.SelectQuestLogEntry(selected_quest) jcallahan@4: self:UnregisterEvent("QUEST_LOG_UPDATE") jcallahan@4: end jcallahan@4: jcallahan@4: jcallahan@4: function WDP:UNIT_QUEST_LOG_CHANGED(event, unit_id) jcallahan@4: if unit_id ~= "player" then jcallahan@4: return jcallahan@4: end jcallahan@4: self:RegisterEvent("QUEST_LOG_UPDATE") jcallahan@4: end jcallahan@4: jcallahan@4: jcallahan@27: function WDP:TRAINER_SHOW() jcallahan@34: local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("target")) jcallahan@29: local npc = NPCEntry(unit_idnum) jcallahan@58: jcallahan@58: if not npc then jcallahan@58: return jcallahan@58: end jcallahan@27: npc.teaches = npc.teaches or {} jcallahan@27: jcallahan@27: -- Get the initial trainer filters jcallahan@27: local available = _G.GetTrainerServiceTypeFilter("available") jcallahan@27: local unavailable = _G.GetTrainerServiceTypeFilter("unavailable") jcallahan@27: local used = _G.GetTrainerServiceTypeFilter("used") jcallahan@27: jcallahan@27: -- Clear the trainer filters jcallahan@27: _G.SetTrainerServiceTypeFilter("available", 1) jcallahan@27: _G.SetTrainerServiceTypeFilter("unavailable", 1) jcallahan@27: _G.SetTrainerServiceTypeFilter("used", 1) jcallahan@27: jcallahan@27: for index = 1, _G.GetNumTrainerServices(), 1 do jcallahan@27: local spell_name, rank_name, _, _, required_level = _G.GetTrainerServiceInfo(index) jcallahan@27: jcallahan@27: if spell_name then jcallahan@27: DatamineTT:ClearLines() jcallahan@27: DatamineTT:SetTrainerService(index) jcallahan@27: jcallahan@27: local _, _, spell_id = DatamineTT:GetSpell() jcallahan@27: jcallahan@43: if spell_id then jcallahan@43: local profession, min_skill = _G.GetTrainerServiceSkillReq(index) jcallahan@43: profession = profession or "General" jcallahan@43: jcallahan@43: local class_professions = npc.teaches[PLAYER_CLASS] jcallahan@43: if not class_professions then jcallahan@43: npc.teaches[PLAYER_CLASS] = {} jcallahan@43: class_professions = npc.teaches[PLAYER_CLASS] jcallahan@43: end jcallahan@43: jcallahan@43: local profession_skills = class_professions[profession] jcallahan@43: if not profession_skills then jcallahan@43: class_professions[profession] = {} jcallahan@43: profession_skills = class_professions[profession] jcallahan@43: end jcallahan@43: profession_skills[spell_id] = ("%d:%d"):format(required_level, min_skill) jcallahan@27: end jcallahan@27: end jcallahan@27: end jcallahan@27: jcallahan@27: -- Reset the filters to what they were before jcallahan@27: _G.SetTrainerServiceTypeFilter("available", available or 0) jcallahan@27: _G.SetTrainerServiceTypeFilter("unavailable", unavailable or 0) jcallahan@27: _G.SetTrainerServiceTypeFilter("used", used or 0) jcallahan@27: end jcallahan@27: jcallahan@27: jcallahan@1: function WDP:UNIT_SPELLCAST_SENT(event_name, unit_id, spell_name, spell_rank, target_name, spell_line) jcallahan@1: if private.tracked_line or unit_id ~= "player" then jcallahan@1: return jcallahan@1: end jcallahan@1: local spell_label = private.SPELL_LABELS_BY_NAME[spell_name] jcallahan@1: jcallahan@1: if not spell_label then jcallahan@1: return jcallahan@1: end jcallahan@18: table.wipe(action_data) jcallahan@1: jcallahan@1: local tt_item_name, tt_item_link = _G.GameTooltip:GetItem() jcallahan@1: local tt_unit_name, tt_unit_id = _G.GameTooltip:GetUnit() jcallahan@1: jcallahan@1: if not tt_unit_name and _G.UnitName("target") == target_name then jcallahan@1: tt_unit_name = target_name jcallahan@1: tt_unit_id = "target" jcallahan@1: end jcallahan@1: local spell_flags = private.SPELL_FLAGS_BY_LABEL[spell_label] jcallahan@28: local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData() jcallahan@28: jcallahan@28: action_data.instance_token = instance_token jcallahan@28: action_data.map_level = map_level jcallahan@28: action_data.x = x jcallahan@28: action_data.y = y jcallahan@28: action_data.zone = ("%s:%d"):format(zone_name, area_id) jcallahan@1: jcallahan@16: if tt_unit_name and not tt_item_name then jcallahan@16: if bit.band(spell_flags, AF.NPC) == AF.NPC then jcallahan@16: if not tt_unit_id or tt_unit_name ~= target_name then jcallahan@16: return jcallahan@16: end jcallahan@16: action_data.type = AF.NPC jcallahan@25: action_data.label = spell_label:lower() jcallahan@25: action_data.unit_name = tt_unit_name jcallahan@16: end jcallahan@16: elseif bit.band(spell_flags, AF.ITEM) == AF.ITEM then jcallahan@16: action_data.type = AF.ITEM jcallahan@25: action_data.label = spell_label:lower() jcallahan@16: jcallahan@16: if tt_item_name and tt_item_name == target_name then jcallahan@28: action_data.identifier = ItemLinkToID(tt_item_link) jcallahan@16: elseif target_name and target_name ~= "" then jcallahan@16: local _, target_item_link = _G.GetItemInfo(target_name) jcallahan@28: action_data.identifier = ItemLinkToID(target_item_link) jcallahan@16: end jcallahan@16: elseif not tt_item_name and not tt_unit_name then jcallahan@17: action_data.name = target_name jcallahan@17: jcallahan@1: if bit.band(spell_flags, AF.OBJECT) == AF.OBJECT then jcallahan@17: if target_name == "" then jcallahan@17: return jcallahan@17: end jcallahan@11: local identifier = ("%s:%s"):format(spell_label, target_name) jcallahan@38: UpdateDBEntryLocation("objects", identifier) jcallahan@11: jcallahan@1: action_data.type = AF.OBJECT jcallahan@11: action_data.identifier = identifier jcallahan@1: elseif bit.band(spell_flags, AF.ZONE) == AF.ZONE then jcallahan@38: local identifier = ("%s:%s"):format(spell_label, _G["GameTooltipTextLeft1"]:GetText() or "NONE") -- Possible fishing pool name. jcallahan@52: action_data.zone_data = UpdateDBEntryLocation("zones", identifier) jcallahan@17: action_data.type = AF.ZONE jcallahan@38: action_data.identifier = identifier jcallahan@48: action_data.spell_label = spell_label jcallahan@1: end jcallahan@1: end jcallahan@1: private.tracked_line = spell_line jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@1: function WDP:UNIT_SPELLCAST_SUCCEEDED(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id) jcallahan@1: if unit_id ~= "player" then jcallahan@1: return jcallahan@1: end jcallahan@1: private.tracked_line = nil jcallahan@0: end jcallahan@0: jcallahan@1: function WDP:HandleSpellFailure(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id) jcallahan@1: if unit_id ~= "player" then jcallahan@1: return jcallahan@1: end jcallahan@0: jcallahan@1: if private.tracked_line == spell_line then jcallahan@1: private.tracked_line = nil jcallahan@1: end jcallahan@0: end