changeset 358:703714202ffe 6.0.2-1

Merge with WoD
author MMOSimca <MMOSimca@gmail.com>
date Tue, 14 Oct 2014 00:36:05 -0400
parents 3487529df8e5 (current diff) f8190e012c92 (diff)
children e35c15f27249
files
diffstat 4 files changed, 728 insertions(+), 440 deletions(-) [+]
line wrap: on
line diff
--- a/Comments.lua	Wed Aug 06 04:31:49 2014 -0400
+++ b/Comments.lua	Tue Oct 14 00:36:05 2014 -0400
@@ -115,7 +115,7 @@
         WDP:Printf("Unable to determine unit from '%s'", unit_id)
         return
     end
-    local type_name = private.UNIT_TYPE_NAMES[unit_type + 1]
+    local type_name = private.UNIT_TYPE_NAMES[unit_type]
     local unit_name = is_command and _G.UnitName(unit_id) or comment_units[unit_id].name
 
     table.wipe(comment_units)
@@ -198,21 +198,21 @@
 local function CreateQuestComment()
     local index = _G.GetQuestLogSelection()
 
-    if not index or not _G.QuestLogFrame:IsShown() then
-        WDP:Print("You must select a quest from the Quest frame.")
+    if not index or not _G.QuestMapFrame:IsVisible() then
+        WDP:Print("You must select a quest from the World Map's Quest frame.")
         return
     end
     local title, _, tag, _, is_header, _, _, _, idnum = _G.GetQuestLogTitle(index)
 
     if is_header then
-        WDP:Print("You must select a quest from the Quest frame.")
+        WDP:Print("You must select a quest from the World Map's Quest frame.")
         return
     end
     NewComment("QUEST", title, idnum)
 end
 
 local function CreateAchievementComment()
-    if not _G.AchievementFrame or not _G.AchievementFrame:IsShown() or not _G.AchievementFrameAchievements.selection then
+    if not _G.AchievementFrame or not _G.AchievementFrame:IsVisible() or not _G.AchievementFrameAchievements.selection then
         WDP:Print("You must select an achievement from the Achievement frame.")
         return
     end
@@ -319,7 +319,7 @@
             end
         end
 
-        if _G.AchievementFrame and _G.AchievementFrame:IsShown() and _G.AchievementFrameAchievements.selection then
+        if _G.AchievementFrame and _G.AchievementFrame:IsVisible() and _G.AchievementFrameAchievements.selection then
             for _, button in next, _G.AchievementFrameAchievementsContainer.buttons do
                 if button.selected then
                     line = display:AddLine(("Achievement: %s"):format(button.label:GetText()))
@@ -362,7 +362,7 @@
 
         local quest_index = _G.GetQuestLogSelection()
 
-        if quest_index and _G.QuestLogFrame:IsShown() then
+        if quest_index and _G.QuestMapFrame:IsVisible() and not _G.QuestScrollFrame:IsVisible() then
             local title, _, tag, _, is_header, _, _, _, idnum = _G.GetQuestLogTitle(quest_index)
 
             if not is_header then
--- a/Constants.lua	Wed Aug 06 04:31:49 2014 -0400
+++ b/Constants.lua	Tue Oct 14 00:36:05 2014 -0400
@@ -13,104 +13,37 @@
 
 
 -----------------------------------------------------------------------
--- Constants.
+-- Game Data Constants.
 -----------------------------------------------------------------------
-private.wow_version, private.build_num = _G.GetBuildInfo()
+-- Map of Alliance Logging NPC Summon spells to all possible Timber objectIDs of the proper tree size
+private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP = {
+    [167902] = 234021, --{ 233604, 233922, , 234080, 234097, 234109, 234110, 234122, 234126, 234193, 234197, 237727, },
+    [167969] = 234022, --{ 233634, 234000, , 234098, 234111, 234119, 234123, 234127, 234194, 234196, 234198, },
+    [168201] = 234023, --{ 233625, 234007, , 234099, 234120, 234124, 234128, 234195, 234199, },
+}
+-- Account for Horde spell IDs
+private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[167961] = private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[167902]
+private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[168043] = private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[167969]
+private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[168200] = private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[168201]
 
-private.UNIT_TYPES = {
-    PLAYER = 0,
-    OBJECT = 1,
-    UNKNOWN = 2,
-    NPC = 3,
-    PET = 4,
-    VEHICLE = 5,
+-- Map of Salvage spells to item IDs of the Salvage containers (no longer loot toasts)
+private.SALVAGE_SPELL_ID_TO_ITEM_ID_MAP = {
+    [168178] = 114116, -- Bag of Salvaged Goods
+    [168179] = 114119, -- Crate of Salvage
+    [168180] = 114120, -- Big Crate of Salvage
 }
 
-
-private.UNIT_TYPE_NAMES = {
-    "PLAYER",
-    "OBJECT",
-    "UNKNOWN",
-    "NPC",
-    "PET",
-    "VEHICLE",
+-- Map of Garrison Cache object names to Garrison Cache object IDs
+private.GARRISON_CACHE_OBJECT_NAME_TO_OBJECT_ID_MAP = {
+    ["Garrison Cache"] = 236916,
+    ["Full Garrison Cache"] = 237722,
+    ["Hefty Garrison Cache"] = 237723,
 }
-
-
-private.ACTION_TYPE_FLAGS = {
-    ITEM = 0x00000001,
-    NPC = 0x00000002,
-    OBJECT = 0x00000004,
-    ZONE = 0x00000008,
-}
-
-
-private.ACTION_TYPE_NAMES = {}
-
-for name, bit in _G.pairs(private.ACTION_TYPE_FLAGS) do
-    private.ACTION_TYPE_NAMES[bit] = name
-end
-
-
-private.EXTRAPOLATION_BANNED_SPELL_IDS = {
-    [13262] = "DISENCHANT",
-    [4036] = "ENGINEERING",
-    [30427] = "EXTRACT_GAS",
-    [131476] = "FISHING",
-    [2366] = "HERB_GATHERING",
-    [51005] = "MILLING",
-    [605] = "MIND_CONTROL",
-    [2575] = "MINING",
-    [921] = "PICK_POCKET",
-    [31252] = "PROSPECTING",
-    [73979] = "SEARCHING_FOR_ARTIFACTS",
-    [8613] = "SKINNING",
-}
-
-
-private.SPELL_LABELS_BY_NAME = {
-    [_G.GetSpellInfo(13262)] = "DISENCHANT",
-    [_G.GetSpellInfo(4036)] = "ENGINEERING",
-    [_G.GetSpellInfo(30427)] = "EXTRACT_GAS",
-    [_G.GetSpellInfo(131476)] = "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",
-}
-
-
-private.NON_LOOT_SPELL_LABELS = {
-    MIND_CONTROL = true,
-}
-
-
-local AF = private.ACTION_TYPE_FLAGS
-
-private.SPELL_FLAGS_BY_LABEL = {
-    DISENCHANT = AF.ITEM,
-    ENGINEERING = AF.NPC,
-    EXTRACT_GAS = AF.ZONE,
-    FISHING = AF.ZONE,
-    HERB_GATHERING = bit.bor(AF.NPC, AF.OBJECT),
-    MILLING = AF.ITEM,
-    MIND_CONTROL = AF.NPC,
-    MINING = bit.bor(AF.NPC, AF.OBJECT),
-    OPENING = AF.OBJECT,
-    PICK_POCKET = AF.NPC,
-    PROSPECTING = AF.ITEM,
-    SEARCHING_FOR_ARTIFACTS = AF.OBJECT,
-    SKINNING = AF.NPC,
-}
-
+private.GARRISON_CACHE_LOOT_SOURCE_ID = 10
 
 private.LOOT_SPELL_ID_TO_ITEM_ID_MAP = {
     [142397] = 98134, -- Heroic Cache of Treasures
+    [142901] = 98546, -- Bulging Heroic Cache of Treasures
     [143506] = 98095, -- Brawler's Pet Supplies
     [143507] = 94207, -- Fabled Pandaren Pet Supplies
     [143508] = 89125, -- Sack of Pet Supplies
@@ -122,115 +55,243 @@
     [147598] = 104014, -- Pouch of Timeless Coins
     [149222] = 105911, -- Pouch of Enduring Wisdom
     [149223] = 105912, -- Oversized Pouch of Enduring Wisdom
+    --[168178] = 114116, -- Bag of Salvaged Goods
+    --[168179] = 114119, -- Crate of Salvage
+    --[168180] = 114120, -- Big Crate of Salvage
+    [171513] = 116414, -- Pet Supplies
+    [175767] = 118697, -- Big Bag of Pet Supplies
+    [178508] = 120321, -- Mystery Bag
 }
 
-
-private.RAID_FINDER_BOSS_IDS = {
-    -----------------------------------------------------------------------
-    -- Mogu'shan Vaults
-    -----------------------------------------------------------------------
-    [59915] = true, -- Jasper Guardian
-    [60009] = true, -- Feng the Accursed
-    [60043] = true, -- Jade Guardian
-    [60047] = true, -- Amethyst Guardian
-    [60051] = true, -- Cobalt Guardian
-    [60143] = true, -- Gara'jal the Spiritbinder
-    [60399] = true, -- Qin-xi
-    [60400] = true, -- Jan-xi
-    [60410] = true, -- Elegon
-    [60701] = true, -- Zian of the Endless Shadow
-    [60708] = true, -- Meng the Demented
-    [60709] = true, -- Qiang the Merciless
-    [60710] = true, -- Subetai the Swift
-
-    -----------------------------------------------------------------------
-    -- Terrace of Endless Spring
-    -----------------------------------------------------------------------
-    [60583] = true, -- Protector Kaolan
-    [60585] = true, -- Elder Regail
-    [60586] = true, -- Elder Asani
-    [60999] = true, -- Sha of Fear
-    [62442] = true, -- Tsulong
-    [62983] = true, -- Lei Shi
-
-    -----------------------------------------------------------------------
-    -- Heart of Fear
-    -----------------------------------------------------------------------
-    [62164] = true, -- Garalon
-    [62397] = true, -- Wind Lord Mel'jarak
-    [62511] = true, -- Amber-Shaper Un'sok
-    [62543] = true, -- Blade Lord Ta'yak
-    [62837] = true, -- Grand Empress Shek'zeer
-    [62980] = true, -- Imperial Vizier Zor'lok
-
-    -----------------------------------------------------------------------
-    -- Throne of Thunder
-    -----------------------------------------------------------------------
-    [69465] = true, -- Jin'rokh the Breaker
-    [68476] = true, -- Horridon
-    [69078] = true, -- Sul the Sandcrawler
-    [69131] = true, -- Frost King Malakk
-    [69132] = true, -- High Priestess Mar'li
-    [69134] = true, -- Kazra'jin
-    [67977] = true, -- Tortos
-    [70212] = true, -- Flaming Head (of Megaera)
-    [70235] = true, -- Frozen Head (of Megaera)
-    [70247] = true, -- Venomous Head (of Megaera)
-    [69712] = true, -- Ji-kun
-    [68036] = true, -- Durumu
-    [69017] = true, -- Primordius
-    [69427] = true, -- Dark Animus
-    [68078] = true, -- Iron Qon
-    [68904] = true, -- Suen
-    [68905] = true, -- Lu'lin
-    [68397] = true, -- Lei Shen
-
-    -----------------------------------------------------------------------
-    -- Siege of Orgrimmar
-    -----------------------------------------------------------------------
-    [71543] = true, -- Immerseus
-    [71475] = true, -- Rook Stonetoe (Fallen Protectors encounter)
-    [71479] = true, -- He Softfoot (Fallen Protectors encounter)
-    [71480] = true, -- Sun Tenderheart (Fallen Protectors encounter)
-    [71967] = true, -- Norushen (Norushen encounter)
-    [72276] = true, -- Amalgam of Corruption (Norushen encounter)
-    [71734] = true, -- Sha of Pride
-    [72249] = true, -- Galakras
-    [71466] = true, -- Iron Juggernaut
-    [71858] = true, -- Wavebinder Kardris (Kor'kron Dark Shaman encounter)
-    [71859] = true, -- Earthbreaker Haromm (Kor'kron Dark Shaman encounter)
-    [71515] = true, -- General Nazgrim
-    [71454] = true, -- Malkorok
-    [71889] = true, -- Secured Stockpile of Pandaren Spoils (Spoils of Pandaria encounter)
-    [71529] = true, -- Thok the Bloodthirsty
-    [71504] = true, -- Siegecrafter Blackfuse
-    [71152] = true, -- Skeer the Bloodseeker (Paragons of the Klaxxi encounter)
-    [71153] = true, -- Hisek the Swarmkeeper (Paragons of the Klaxxi encounter)
-    [71154] = true, -- Ka'roz the Locust (Paragons of the Klaxxi encounter)
-    [71155] = true, -- Korven the Prime (Paragons of the Klaxxi encounter)
-    [71156] = true, -- Kaz'tik the Manipulator (Paragons of the Klaxxi encounter)
-    [71157] = true, -- Xaril the Poisoned Mind (Paragons of the Klaxxi encounter)
-    [71158] = true, -- Rik'kal the Dissector (Paragons of the Klaxxi encounter)
-    [71160] = true, -- Iyyokuk the Lucid (Paragons of the Klaxxi encounter)
-    [71161] = true, -- Kil'ruk the Wind-Reaver (Paragons of the Klaxxi encounter)
-    [71865] = true, -- Garrosh Hellscream
+private.FACTION_DATA = {
+    -- Used only for private.REP_BUFFS
+    ARGENT_CRUSADE = { 1106, _G.GetFactionInfoByID(1106) },
+    BILGEWATER_CARTEL = { 1133, _G.GetFactionInfoByID(1133) },
+    CENARION_CIRCLE = { 609, _G.GetFactionInfoByID(609) },
+    DARKSPEAR = { 530, _G.GetFactionInfoByID(530) },
+    DARNASSUS = { 69, _G.GetFactionInfoByID(69) },
+    DRAGONMAW_CLAN = { 1172, _G.GetFactionInfoByID(1172) },
+    EARTHEN_RING = { 1135, _G.GetFactionInfoByID(1135) },
+    EBON_BLADE = { 1098, _G.GetFactionInfoByID(1098) },
+    EXODAR = { 930, _G.GetFactionInfoByID(930) },
+    GILNEAS = { 1134, _G.GetFactionInfoByID(1134) },
+    GNOMEREGAN = { 54, _G.GetFactionInfoByID(54) },
+    GUARDIANS_OF_HYJAL = { 1158, _G.GetFactionInfoByID(1158) },
+    GUILD = { 1168, _G.GetFactionInfoByID(1168) },
+    HONOR_HOLD = { 946, _G.GetFactionInfoByID(946) },
+    HUOJIN = { 1352, _G.GetFactionInfoByID(1352) },
+    IRONFORGE = { 47, _G.GetFactionInfoByID(47) },
+    KIRIN_TOR = { 1090, _G.GetFactionInfoByID(1090) },
+    ORGRIMMAR = { 76, _G.GetFactionInfoByID(76) },
+    RAMKAHEN = { 1173, _G.GetFactionInfoByID(1173) },
+    SHATAR = { 935, _G.GetFactionInfoByID(935) },
+    SILVERMOON = { 911, _G.GetFactionInfoByID(911) },
+    STORMWIND = { 72, _G.GetFactionInfoByID(72) },
+    THERAZANE = { 1171, _G.GetFactionInfoByID(1171) },
+    THRALLMAR = { 947, _G.GetFactionInfoByID(947) },
+    THUNDER_BLUFF = { 81, _G.GetFactionInfoByID(81) },
+    TUSHUI = { 1353, _G.GetFactionInfoByID(1353) },
+    UNDERCITY = { 68, _G.GetFactionInfoByID(68) },
+    WILDHAMMER_CLAN = { 1174, _G.GetFactionInfoByID(1174) },
+    WYRMREST_ACCORD = { 1091, _G.GetFactionInfoByID(1091) },
+    -- Commendation Factions
+    ANGLERS = { 1302, _G.GetFactionInfoByID(1302) },
+    AUGUST_CELESTIALS = { 1341, _G.GetFactionInfoByID(1341) },
+    DOMINANCE_OFFENSIVE = { 1375, _G.GetFactionInfoByID(1375) },
+    GOLDEN_LOTUS = { 1269, _G.GetFactionInfoByID(1269) },
+    KIRIN_TOR_OFFENSIVE = { 1387, _G.GetFactionInfoByID(1387) },
+    KLAXXI = { 1337, _G.GetFactionInfoByID(1337) },
+    LOREWALKERS = { 1345, _G.GetFactionInfoByID(1345) },
+    OPERATION_SHIELDWALL = { 1376, _G.GetFactionInfoByID(1376) },
+    ORDER_OF_THE_CLOUD_SERPENTS = { 1271, _G.GetFactionInfoByID(1271) },
+    SHADO_PAN = { 1270, _G.GetFactionInfoByID(1270) },
+    SHADO_PAN_ASSAULT = { 1435, _G.GetFactionInfoByID(1435) },
+    SUNREAVER_ONSLAUGHT = { 1388, _G.GetFactionInfoByID(1388) },
+    TILLERS = { 1272, _G.GetFactionInfoByID(1272) },
 }
 
+private.REP_BUFFS = {
+    -- Tabard Buffs
+    [_G.GetSpellInfo(93830)] = { -- BILGEWATER CARTEL TABARD
+        faction = private.FACTION_DATA.BILGEWATER_CARTEL[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93827)] = { -- DARKSPEAR TABARD
+        faction = private.FACTION_DATA.DARKSPEAR[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93806)] = { -- DARNASSUS TABARD
+        faction = private.FACTION_DATA.DARNASSUS[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93811)] = { -- EXODAR TABARD
+        faction = private.FACTION_DATA.EXODAR[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93816)] = { -- GILNEAS TABARD
+        faction = private.FACTION_DATA.GILNEAS[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93821)] = { -- GNOMEREGAN TABARD
+        faction = private.FACTION_DATA.GNOMEREGAN[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(126436)] = { -- HUOJIN TABARD
+        faction = private.FACTION_DATA.HUOJIN[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(97340)] = { -- ILLUSTRIOUS GUILD TABARD
+        faction = private.FACTION_DATA.GUILD[2],
+        modifier = 1,
+    },
+    [_G.GetSpellInfo(93805)] = { -- IRONFORGE TABARD
+        faction = private.FACTION_DATA.IRONFORGE[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93825)] = { -- ORGRIMMAR TABARD
+        faction = private.FACTION_DATA.ORGRIMMAR[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(97341)] = { -- RENOWNED GUILD TABARD
+        faction = private.FACTION_DATA.GUILD[2],
+        modifier = 0.5,
+    },
+    [_G.GetSpellInfo(93828)] = { -- SILVERMOON CITY TABARD
+        faction = private.FACTION_DATA.SILVERMOON[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93795)] = { -- STORMWIND TABARD
+        faction = private.FACTION_DATA.STORMWIND[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93337)] = { -- TABARD OF RAMKAHEN
+        faction = private.FACTION_DATA.RAMKAHEN[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(57819)] = { -- TABARD OF THE ARGENT CRUSADE
+        faction = private.FACTION_DATA.ARGENT_CRUSADE[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(94158)] = { -- TABARD OF THE DRAGONMAW CLAN
+        faction = private.FACTION_DATA.DRAGONMAW_CLAN[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93339)] = { -- TABARD OF THE EARTHEN RING
+        faction = private.FACTION_DATA.EARTHEN_RING[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(57820)] = { -- TABARD OF THE EBON BLADE
+        faction = private.FACTION_DATA.EBON_BLADE[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93341)] = { -- TABARD OF THE GUARDIANS OF HYJAL
+        faction = private.FACTION_DATA.GUARDIANS_OF_HYJAL[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(57821)] = { -- TABARD OF THE KIRIN TOR
+        faction = private.FACTION_DATA.KIRIN_TOR[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93368)] = { -- TABARD OF THE WILDHAMMER CLAN
+        faction = private.FACTION_DATA.WILDHAMMER_CLAN[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(57822)] = { -- TABARD OF THE WYRMREST ACCORD
+        faction = private.FACTION_DATA.WYRMREST_ACCORD[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(93347)] = { -- TABARD OF THERAZANE
+        faction = private.FACTION_DATA.THERAZANE[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(94463)] = { -- THUNDERBLUFF TABARD
+        faction = private.FACTION_DATA.THUNDER_BLUFF[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(126434)] = { -- TUSHUI TABARD
+        faction = private.FACTION_DATA.TUSHUI[2],
+        ignore = true,
+    },
+    [_G.GetSpellInfo(94462)] = { -- UNDERCITY TABARD
+        faction = private.FACTION_DATA.UNDERCITY[2],
+        ignore = true,
+    },
 
-private.WORLD_BOSS_IDS = {
-    [60491] = true, -- Sha of Anger
-    [62346] = true, -- Galleon
-    [69099] = true, -- Nalak
-    [69161] = true, -- Oondasta
-    [71952] = true, -- Chi-Ji
-    [71953] = true, -- Xuen
-    [71954] = true, -- Niuzao
-    [71955] = true, -- Yu'lon
-    [72057] = true, -- Ordos
+    -- Banner Buffs
+    [_G.GetSpellInfo(90216)] = { -- ALLIANCE GUILD STANDARD
+        ignore = true,
+    },
+    [_G.GetSpellInfo(90708)] = { -- HORDE GUILD STANDARD
+        ignore = true,
+    },
+
+    -- Holiday Buffs
+    [_G.GetSpellInfo(136583)] = { -- DARKMOON TOP HAT
+        modifier = 0.1,
+    },
+    [_G.GetSpellInfo(24705)] = { -- GRIM VISAGE
+        modifier = 0.1,
+    },
+    [_G.GetSpellInfo(61849)] = { -- SPIRIT OF SHARING
+        modifier = 0.1,
+    },
+    [_G.GetSpellInfo(95987)] = { -- UNBURDENED
+        modifier = 0.1,
+    },
+    [_G.GetSpellInfo(46668)] = { -- WHEE!
+        modifier = 0.1,
+    },
+    [_G.GetSpellInfo(100951)] = { -- WOW 8TH ANNIVERSARY
+        modifier = 0.08,
+    },
+    [_G.GetSpellInfo(132700)] = { -- WOW 9TH ANNIVERSARY
+        modifier = 0.09,
+    },
+    [_G.GetSpellInfo(150986)] = { -- WOW 10TH ANNIVERSARY
+        modifier = 0.1,
+    },
+
+    -- Situational Buffs
+    [_G.GetSpellInfo(39953)] = { -- ADALS SONG OF BATTLE
+        faction = private.FACTION_DATA.SHATAR[2],
+        modifier = 0.1,
+    },
+    [_G.GetSpellInfo(30754)] = { -- CENARION FAVOR
+        faction = private.FACTION_DATA.CENARION_CIRCLE[2],
+        modifier = 0.25,
+    },
+    [_G.GetSpellInfo(32098)] = { -- HONOR HOLD FAVOR
+        faction = private.FACTION_DATA.HONOR_HOLD[2],
+        modifier = 0.25,
+    },
+    [_G.GetSpellInfo(39913)] = { -- NAZGRELS FERVOR
+        faction = private.FACTION_DATA.THRALLMAR[2],
+        modifier = 0.1,
+    },
+    [_G.GetSpellInfo(32096)] = { -- THRALLMARS FAVOR
+        faction = private.FACTION_DATA.THRALLMAR[2],
+        modifier = 0.25,
+    },
+    [_G.GetSpellInfo(39911)] = { -- TROLLBANES COMMAND
+        faction = private.FACTION_DATA.HONOR_HOLD[2],
+        modifier = 0.1,
+    },
 }
 
+private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP = {
+    -----------------------------------------------------------------------
+    -- World Bosses
+    -----------------------------------------------------------------------
+    [132205] = 60491, -- Sha of Anger Bonus (Sha of Anger)
+    [132206] = 62346, -- Galleon Bonus (Galleon)
+    [136381] = 69099, -- Nalak Bonus (Nalak)
+    [137554] = 69161, -- Oondasta Bonus (Oondasta)
+    [148317] = 71952, -- Celestials Bonus (Chi-Ji)
+    [148316] = 72057, -- Ordos Bonus (Ordos)
+    --[????] = 81535, -- Tarlna the Ageless Bonus Loot (Tarlna the Ageless)
+    --[????] = 87437, -- Drov the Ruiner Bonus Loot (Drov the Ruiner)
+    --[????] = 87493, -- Rukhmar Bonus Loot (Rukhmar)
 
-private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP = {
     -----------------------------------------------------------------------
     -- Mogu'shan Vaults
     -----------------------------------------------------------------------
@@ -294,13 +355,109 @@
     [145918] = 71504, -- Siegecrafter Blackfuse Bonus (Siegecrafter Blackfuse)
     [145921] = 71161, -- Klaxxi Paragons Bonus (Kil'ruk the Wind-Reaver)
     [145922] = 71865, -- Garrosh Hellscream Bonus (Garrosh Hellscream)
+
+    -----------------------------------------------------------------------
+    -- Blackrock Foundry
+    -----------------------------------------------------------------------
+    [177510] = 76877, -- Gruul Bonus Loot (Gruul)
+    [177511] = 77182, -- Oregorger Bonus Loot (Oregorger)
+    [177512] = 76809, -- Blast Furnace Loot (Foreman Feldspar)
+    [177513] = 76973, -- Hans'gar & Franzok Bonus Loot (Hans'gar)
+    [177515] = 76814, -- Flamebender Ka'graz Bonus Loot (Flamebender Ka'graz)
+    [177516] = 77692, -- Kromog Bonus Loot (Kromog)
+    [177517] = 76865, -- Beastlord Darmac Bonus Loot (Beastlord Darmac)
+    [177518] = 76906, -- Operator Thogar Bonus Loot (Operator Thogar)
+    [177519] = 77557, -- The Iron Maidens Bonus Loot (Admiral Gar'an)
+    [177520] = 87420, -- Blackhand Bonus Loot (Blackhand)
+
+    -----------------------------------------------------------------------
+    -- Highmaul
+    -----------------------------------------------------------------------
+    [177503] = 87444, -- Kargath Bladefist Bonus Loot (Kargath Bladefist)
+    [177504] = 87447, -- Butcher Bonus Loot (The Butcher)
+    [177505] = 87446, -- Tectus Bonus Loot (Tectus)
+    [177506] = 87441, -- Brackenspore Bonus Loot (Brackenspore)
+    [177507] = 87449, -- Twin Ogron Bonus Loot (Twin Ogron)
+    [177508] = 87445, -- Ko'ragh Bonus Loot (Ko'ragh)
+    [177509] = 87818, -- Imperator Mar'gok Bonus Loot (Imperator Mar'gok)
 }
 
-private.WORLD_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP = {
-    [132205] = 60491, -- Sha of Anger Bonus (Sha of Anger)
-    [132206] = 62346, -- Galleon Bonus (Galleon)
-    [136381] = 69099, -- Nalak Bonus (Nalak)
-    [137554] = 69161, -- Oondasta Bonus (Oondasta)
-    [148317] = 71952, -- Celestials Bonus (Chi-Ji)
-    [148316] = 72057, -- Ordos Bonus (Ordos)
+
+-----------------------------------------------------------------------
+-- Fundamental Constants.
+-----------------------------------------------------------------------
+private.wow_version, private.build_num = _G.GetBuildInfo()
+private.region = GetCVar("portal"):sub(0,2):upper()
+-- PTR/Beta return "public-test", but they are properly called "XX"
+if private.region == "PU" then private.region = "XX" end
+
+private.UNIT_TYPES = {
+    PLAYER = "Player",
+    OBJECT = "GameObject",
+    UNKNOWN = "Unknown",
+    NPC = "Creature",
+    PET = "Pet",
+    VEHICLE = "Vehicle",
+    ITEM = "Item",
 }
+
+private.UNIT_TYPE_NAMES = {
+    ["Player"] = "PLAYER",
+    ["GameObject"] = "OBJECT",
+    ["Unknown"] = "UNKNOWN",
+    ["Creature"] = "NPC",
+    ["Pet"] = "PET",
+    ["Vehicle"] = "VEHICLE",
+    ["Item"] = "ITEM",
+}
+
+private.ACTION_TYPE_FLAGS = {
+    ITEM = 0x00000001,
+    NPC = 0x00000002,
+    OBJECT = 0x00000004,
+    ZONE = 0x00000008,
+}
+
+private.ACTION_TYPE_NAMES = {}
+
+for name, bit in _G.pairs(private.ACTION_TYPE_FLAGS) do
+    private.ACTION_TYPE_NAMES[bit] = name
+end
+
+private.SPELL_LABELS_BY_NAME = {
+    [_G.GetSpellInfo(13262)] = "DISENCHANT",
+    [_G.GetSpellInfo(4036)] = "ENGINEERING",
+    [_G.GetSpellInfo(30427)] = "EXTRACT_GAS",
+    [_G.GetSpellInfo(131476)] = "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",
+}
+
+private.NON_LOOT_SPELL_LABELS = {
+    MIND_CONTROL = true,
+}
+
+local AF = private.ACTION_TYPE_FLAGS
+
+private.SPELL_FLAGS_BY_LABEL = {
+    DISENCHANT = AF.ITEM,
+    ENGINEERING = AF.NPC,
+    EXTRACT_GAS = AF.ZONE,
+    FISHING = AF.ZONE,
+    HERB_GATHERING = bit.bor(AF.NPC, AF.OBJECT),
+    MILLING = AF.ITEM,
+    MIND_CONTROL = AF.NPC,
+    MINING = bit.bor(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	Wed Aug 06 04:31:49 2014 -0400
+++ b/Main.lua	Tue Oct 14 00:36:05 2014 -0400
@@ -10,6 +10,7 @@
 local math = _G.math
 local table = _G.table
 
+local next = _G.next
 local select = _G.select
 local unpack = _G.unpack
 
@@ -47,12 +48,27 @@
 local PLAYER_NAME = _G.UnitName("player")
 local PLAYER_RACE = _G.select(2, _G.UnitRace("player"))
 
-local SPELL_ID_CHI_WAVE = 132464
-local SPELL_ID_DISGUISE = 121308
+local TIMBER_ITEM_ID = 114781
+
+-- Ignoring NPC casts of the following spells
+local CHI_WAVE_SPELL_ID = 132464
+local DISGUISE_SPELL_ID = 121308
+
+-- For timer-based loot gathering of abnormal containers (that don't use SHOW_LOOT_TOAST, sadly)
+local BAG_OF_SALVAGE_ITEM_ID = private.SALVAGE_SPELL_ID_TO_ITEM_ID_MAP[168178]
+local CRATE_OF_SALVAGE_ITEM_ID = private.SALVAGE_SPELL_ID_TO_ITEM_ID_MAP[168179]
+local BIG_CRATE_OF_SALVAGE_ITEM_ID = private.SALVAGE_SPELL_ID_TO_ITEM_ID_MAP[168180]
+
+-- Constant for duplicate boss data; a dirty hack to get around world bosses that cannot be identified individually and cannot be linked on wowdb because they are not in a raid
+local DUPLICATE_WORLD_BOSS_IDS = {
+    [71952] = { 71953, 71954, 71955, },
+}
 
 local ALLOWED_LOCALES = {
     enUS = true,
     enGB = true,
+    enTW = true,
+    enCN = true,
 }
 
 local DATABASE_DEFAULTS = {
@@ -149,6 +165,9 @@
 local killed_boss_id_timer_handle
 local killed_npc_id
 local target_location_timer_handle
+local last_timber_spell_id
+local last_garrison_cache_object_id
+local chat_loot_timer_handle
 local current_target_id
 local current_area_id
 local current_loot
@@ -172,17 +191,22 @@
 -- HELPERS ------------------------------------------------------------
 
 local function Debug(message, ...)
-    if not DEBUGGING or not message or not ... then
+    if not DEBUGGING or not message then
         return
     end
-    local args = { ... }
-
-    for index = 1, #args do
-        if args[index] == nil then
-            args[index] = "nil"
+    
+    if ... then
+        local args = { ... }
+
+        for index = 1, #args do
+            if args[index] == nil then
+                args[index] = "nil"
+            end
         end
+        _G.print(message:format(unpack(args)))
+    else
+        _G.print(message)
     end
-    _G.print(message:format(unpack(args)))
 end
 
 
@@ -217,7 +241,7 @@
             _G.TradeSkillFrame.filterTbl.hasSkillUp = false
             _G.TradeSkillOnlyShowSkillUps(false)
         end
-        _G.SetTradeSkillInvSlotFilter(0, 1, 1)
+        _G.SetTradeSkillInvSlotFilter(0, true, true)
         _G.TradeSkillUpdateFilterBar()
         _G.TradeSkillFrame_Update()
 
@@ -292,54 +316,6 @@
 end -- do-block
 
 
--- constant for duplicate boss data; a dirty hack to get around world bosses that cannot be identified individually and cannot be linked on wowdb because they are not in a raid
-local DUPLICATE_WORLD_BOSS_IDS = {
-    [71952] = { 71953, 71954, 71955, },
-}
-
-
--- Called on a timer
-local function ClearKilledNPC()
-    killed_npc_id = nil
-end
-
-
-local function ClearKilledBossID()
-    if killed_boss_id_timer_handle then
-        WDP:CancelTimer(killed_boss_id_timer_handle)
-        killed_boss_id_timer_handle = nil
-    end
-
-    table.wipe(boss_loot_toasting)
-    private.raid_finder_boss_id = nil
-    private.world_boss_id = nil
-end
-
-
-local function ClearLootToastContainerID()
-    if loot_toast_container_timer_handle then
-        WDP:CancelTimer(loot_toast_container_timer_handle)
-        loot_toast_container_timer_handle = nil
-    end
-
-    private.container_loot_toasting = false
-    private.loot_toast_container_id = nil
-end
-
-
-local function ClearLootToastData()
-    -- cancel existing timer if found
-    if loot_toast_data_timer_handle then
-        WDP:CancelTimer(loot_toast_data_timer_handle)
-        loot_toast_data_timer_handle = nil
-    end
-
-    if loot_toast_data then
-        table.wipe(loot_toast_data)
-    end
-end
-
-
 local function InstanceDifficultyToken()
     local _, instance_type, instance_difficulty, _, _, _, is_dynamic = _G.GetInstanceInfo()
 
@@ -380,11 +356,7 @@
 
     function NPCEntry(identifier)
         local npc = DBEntry("npcs", identifier)
-
-        if not npc then
-            return
-        end
-        return _G.setmetatable(npc, npc_meta)
+        return npc and _G.setmetatable(npc, npc_meta) or nil
     end
 
     function npc_prototype:EncounterData(difficulty_token)
@@ -463,26 +435,37 @@
 local ParseGUID
 do
     local UNIT_TYPES = private.UNIT_TYPES
-    local UNIT_TYPE_BITMASK = 0x007
 
     local NPC_ID_MAPPING = {
         [62164] = 63191, -- Garalon
     }
 
 
+    local function MatchUnitTypes(unit_type_name)
+        if not unit_type_name then
+            return UNIT_TYPES.UNKNOWN
+        end
+
+        for def, text in next, UNIT_TYPES do
+            if unit_type_name == text then
+                return UNIT_TYPES[def]
+            end
+        end
+        return UNIT_TYPES.UNKNOWN
+    end
+
+
     function ParseGUID(guid)
         if not guid then
             return
         end
-        local bitfield = tonumber(guid:sub(1, 5))
-
-        if not bitfield then
-            return UNIT_TYPES.UNKNOWN
-        end
-        local unit_type = _G.bit.band(bitfield, UNIT_TYPE_BITMASK)
-
-        if unit_type ~= UNIT_TYPES.PLAYER and unit_type ~= UNIT_TYPES.PET then
-            local unit_idnum = tonumber(guid:sub(6, 10), 16)
+
+        -- We might want to use some of this new information later, but leaving the returns alone for now
+        local unit_type_name, unk_id1, server_id, instance_id, unk_id2, unit_idnum, spawn_id = ("-"):split(guid)
+
+        local unit_type = MatchUnitTypes(unit_type_name)
+        if unit_type ~= UNIT_TYPES.PLAYER and unit_type ~= UNIT_TYPES.PET and unit_type ~= UNIT_TYPES.ITEM then
+
             local id_mapping = NPC_ID_MAPPING[unit_idnum]
 
             if id_mapping and UnitTypeIsNPC(unit_type) then
@@ -583,7 +566,7 @@
         local current_line = _G["WDPDatamineTTTextLeft" .. line_index]
 
         if not current_line then
-            Debug("HandleItemUse: Item with ID %d and link %s had an invalid tooltip.", item_id, item_link, _G.ITEM_OPENABLE)
+            Debug("HandleItemUse: Item with ID %d and link %s had an invalid tooltip.", item_id, item_link)
             return
         end
 
@@ -597,7 +580,6 @@
             return
         end
     end
-
     Debug("HandleItemUse: Item with ID %d and link %s did not have a tooltip that contained the string %s.", item_id, item_link, _G.ITEM_OPENABLE)
 end
 
@@ -674,7 +656,7 @@
                     -- Items return the player as the source, so we need to use the item's ID (disenchant, milling, etc)
                     source_id = current_loot.identifier
                 else
-                    local unit_ID = select(2, ParseGUID(source_guid))
+                    local _, unit_ID = ParseGUID(source_guid)
                     if unit_ID then
                         if current_loot.target_type == AF.OBJECT then
                             source_id = ("%s:%s"):format(current_loot.spell_label, unit_ID)
@@ -805,7 +787,8 @@
             _G.SetCVar("Sound_EnableSFX", 0)
             world_map:Show()
         end
-        local micro_dungeon_id = MICRO_DUNGEON_IDS[select(5, _G.GetMapInfo())]
+        local _, _, _, _, micro_dungeon_map_name = _G.GetMapInfo()
+        local micro_dungeon_id = MICRO_DUNGEON_IDS[micro_dungeon_map_name]
 
         _G.SetMapToCurrentZone()
 
@@ -847,6 +830,63 @@
     table.wipe(current_action)
 end
 
+
+-- TIMERS -------------------------------------------------------------
+
+local function ClearKilledNPC()
+    killed_npc_id = nil
+end
+
+
+local function ClearKilledBossID()
+    if killed_boss_id_timer_handle then
+        WDP:CancelTimer(killed_boss_id_timer_handle)
+        killed_boss_id_timer_handle = nil
+    end
+
+    table.wipe(boss_loot_toasting)
+    private.raid_boss_id = nil
+end
+
+
+local function ClearLootToastContainerID()
+    if loot_toast_container_timer_handle then
+        WDP:CancelTimer(loot_toast_container_timer_handle)
+        loot_toast_container_timer_handle = nil
+    end
+
+    private.container_loot_toasting = false
+    private.loot_toast_container_id = nil
+end
+
+
+local function ClearLootToastData()
+    -- cancel existing timer if found
+    if loot_toast_data_timer_handle then
+        WDP:CancelTimer(loot_toast_data_timer_handle)
+        loot_toast_data_timer_handle = nil
+    end
+
+    if loot_toast_data then
+        table.wipe(loot_toast_data)
+    end
+end
+
+
+local function ClearTimeBasedLootData()
+    Debug("ClearTimeBasedLootData: Ending salvage loot timer.")
+    if chat_loot_timer_handle then
+        WDP:CancelTimer(chat_loot_timer_handle)
+        chat_loot_timer_handle = nil
+    end
+
+    if current_loot and current_loot.identifier and (current_loot.identifier == BAG_OF_SALVAGE_ITEM_ID or current_loot.identifier == CRATE_OF_SALVAGE_ITEM_ID or current_loot.identifier == BIG_CRATE_OF_SALVAGE_ITEM_ID) then
+        GenericLootUpdate("items")
+    end
+    current_loot = nil
+end
+
+
 -- METHODS ------------------------------------------------------------
 
 function WDP:OnInitialize()
@@ -858,12 +898,19 @@
     local raw_db = _G.WoWDBProfilerData
     local build_num = tonumber(private.build_num)
 
+    -- Disable if using a MoP build
+    if build_num < 19000 then
+        WDP:Disable()
+        return
+    end
+
     if (raw_db.version and raw_db.version < DB_VERSION) or (raw_db.build_num and raw_db.build_num < build_num) then
         for entry in pairs(DATABASE_DEFAULTS.global) do
             global_db[entry] = {}
         end
     end
     raw_db.build_num = build_num
+    raw_db.region = private.region
     raw_db.version = DB_VERSION
 
     private.InitializeCommentSystem()
@@ -949,14 +996,6 @@
             local amount, stat = left_text:match("+(.-) (.*)")
 
             if amount and stat then
-                if reforge_id and reforge_id ~= 0 then
-                    local reforge_string = stat:find("Reforged")
-
-                    if reforge_string then
-                        stat = stat:sub(0, reforge_string - 3)
-                        intermediary.reforge_id = reforge_id
-                    end
-                end
                 create_entry = true
                 intermediary[stat:lower():gsub(" ", "_"):gsub("|r", "")] = tonumber((amount:gsub(",", "")))
             end
@@ -974,29 +1013,54 @@
             item.upgrades[upgrade_id][stat] = amount
         end
     end
-end
-
--- do-block
-
-
-local function RecordItemData(item_id, item_link, durability)
-    local item_string = select(3, item_link:find("^|%x+|H(.+)|h%[.+%]"))
+end -- do-block
+
+
+local function RecordItemData(item_id, item_link, process_bonus_ids, durability)
+    local _, _, item_string = item_link:find("^|%x+|H(.+)|h%[.+%]")
     local item
 
     if item_string then
-        local _, _, _, _, _, _, _, suffix_id, unique_id, _, reforge_id, upgrade_id = (":"):split(item_string)
-        suffix_id = tonumber(suffix_id)
-        upgrade_id = tonumber(upgrade_id)
-
-        if suffix_id and suffix_id ~= 0 then
+        local item_results = { (":"):split(item_string) }
+
+        local suffix_id = tonumber(item_results[8])
+        local unique_id = item_results[9]
+        local upgrade_id = tonumber(item_results[11])
+        local instance_difficulty_id = tonumber(item_results[12])
+        local num_bonus_ids = tonumber(item_results[13])
+
+        if not num_bonus_ids or num_bonus_ids == 0 or not process_bonus_ids then
+            if (suffix_id and suffix_id ~= 0) or (instance_difficulty_id and instance_difficulty_id ~= 0) then
+                item = DBEntry("items", item_id)
+                item.unique_id = bit.band(unique_id, 0xFFFF)
+
+                if suffix_id and suffix_id ~= 0 then
+                    item.suffix_id = suffix_id
+                end
+
+                if instance_difficulty_id and instance_difficulty_id ~= 0 then
+                    item.instance_difficulty_id = instance_difficulty_id
+                end
+            end
+        elseif num_bonus_ids > 0 then
             item = DBEntry("items", item_id)
-            item.suffix_id = suffix_id
+
             item.unique_id = bit.band(unique_id, 0xFFFF)
+            item.instance_difficulty_id = instance_difficulty_id
+
+            if not item.bonus_ids then
+                item.bonus_ids = {}
+            end
+            
+            for bonus_index = 1, num_bonus_ids do
+                item.bonus_ids[tonumber(item_results[13 + bonus_index])] = true
+            end
+        else
+            Debug("RecordItemData: Item_system is supposed to be 0 or positive, instead it was %s.", item_system)
         end
-
         if upgrade_id and upgrade_id ~= 0 then
             DatamineTT:SetHyperlink(item_link)
-            ScrapeItemUpgradeStats(item_id, upgrade_id, reforge_id)
+            ScrapeItemUpgradeStats(item_id, upgrade_id)
         end
     end
 
@@ -1013,7 +1077,7 @@
 
         if item_id and item_id > 0 then
             local _, max_durability = _G.GetInventoryItemDurability(slot_index)
-            RecordItemData(item_id, _G.GetInventoryItemLink("player", slot_index), max_durability)
+            RecordItemData(item_id, _G.GetInventoryItemLink("player", slot_index), false, max_durability)
         end
     end
 
@@ -1023,7 +1087,7 @@
 
             if item_id and item_id > 0 then
                 local _, max_durability = _G.GetContainerItemDurability(bag_index, slot_index)
-                RecordItemData(item_id, _G.GetContainerItemLink(bag_index, slot_index), max_durability)
+                RecordItemData(item_id, _G.GetContainerItemLink(bag_index, slot_index), false, max_durability)
             end
         end
     end
@@ -1092,7 +1156,7 @@
         level_data.max_health = level_data.max_health or _G.UnitHealthMax("target")
 
         if not level_data.power then
-            local max_power = _G.UnitManaMax("target")
+            local max_power = _G.UnitPowerMax("target")
 
             if max_power > 0 then
                 local power_type = _G.UnitPowerType("target")
@@ -1209,16 +1273,40 @@
 end
 
 
-function WDP:SHOW_LOOT_TOAST(event_name, loot_type, item_link, quantity)
+function WDP:SHOW_LOOT_TOAST(event_name, loot_type, item_link, quantity, specID, sex, isPersonal, lootSource)
     if not loot_type or (loot_type ~= "item" and loot_type ~= "money" and loot_type ~= "currency") then
         Debug("%s: loot_type is %s. Item link is %s, and quantity is %d.", event_name, loot_type, item_link, quantity)
         return
     end
     local container_id = private.loot_toast_container_id
-    local npc_id = private.raid_finder_boss_id or private.world_boss_id
-
-    if npc_id then
-        -- slightly messy hack to workaround duplicate world bosses
+    local npc_id = private.raid_boss_id
+    
+    -- Handle Garrison cache specially
+    if lootSource and last_garrison_cache_object_id and (lootSource == private.GARRISON_CACHE_LOOT_SOURCE_ID) then
+        -- Record location data for cache
+        UpdateDBEntryLocation("objects", ("OPENING:%d"):format(last_garrison_cache_object_id))
+
+        -- Add drop data
+        local currency_texture = CurrencyLinkToTexture(item_link)
+        if currency_texture and currency_texture ~= "" then
+            -- Check for top level object data
+            local object_entry = DBEntry("objects", ("OPENING:%d"):format(last_garrison_cache_object_id))
+            local difficulty_token = InstanceDifficultyToken()
+            if object_entry[difficulty_token] then
+                -- Increment loot count
+                object_entry[difficulty_token]["opening_count"] = (object_entry[difficulty_token]["opening_count"] or 0) + 1
+
+                Debug("%s: %s X %d", event_name, currency_texture, quantity)
+                object_entry[difficulty_token]["opening"] = object_entry[difficulty_token]["opening"] or {}
+                table.insert(object_entry[difficulty_token]["opening"], ("currency:%d:%s"):format(quantity, currency_texture))
+            else
+                Debug("%s: When handling the Garrison cache, the top level loot data was missing for objectID %d.", event_name, last_garrison_cache_object_id)
+            end
+        else
+            Debug("%s: Currency texture is nil, from currency link %s", event_name, item_link)
+        end
+    elseif npc_id then
+        -- Slightly messy hack to workaround duplicate world bosses
         local upper_limit = 0
         if DUPLICATE_WORLD_BOSS_IDS[npc_id] then
             upper_limit = #DUPLICATE_WORLD_BOSS_IDS[npc_id]
@@ -1241,6 +1329,7 @@
                     local item_id = ItemLinkToID(item_link)
                     if item_id then
                         Debug("%s: %s X %d (%d)", event_name, item_link, quantity, item_id)
+                        RecordItemData(item_id, item_link, true)
                         table.insert(encounter_data[loot_label], ("%d:%d"):format(item_id, quantity))
                     else
                         Debug("%s: ItemID is nil, from item link %s", event_name, item_link)
@@ -1253,10 +1342,6 @@
                     local currency_texture = CurrencyLinkToTexture(item_link)
                     if currency_texture and currency_texture ~= "" then
                         Debug("%s: %s X %d", event_name, currency_texture, quantity)
-                        -- workaround for Patch 5.4.0 bug with Flexible raid Siege of Orgrimmar bosses and Valor Points
-                        if quantity > 1000 and currency_texture == "pvecurrency-valor" then
-                            quantity = math.floor(quantity / 100)
-                        end
                         table.insert(encounter_data[loot_label], ("currency:%d:%s"):format(quantity, currency_texture))
                     else
                         Debug("%s: Currency texture is nil, from currency link %s", event_name, item_link)
@@ -1284,6 +1369,7 @@
             local item_id = ItemLinkToID(item_link)
             if item_id then
                 Debug("%s: %s X %d (%d)", event_name, item_link, quantity, item_id)
+                RecordItemData(item_id, item_link, true)
                 current_loot.sources[container_id][item_id] = current_loot.sources[container_id][item_id] or 0 + quantity
             else
                 Debug("%s: ItemID is nil, from item link %s", event_name, item_link)
@@ -1315,6 +1401,11 @@
         loot_toast_data = loot_toast_data or {}
         loot_toast_data[#loot_toast_data + 1] = { loot_type, item_link, quantity }
 
+        local item_id = ItemLinkToID(item_link)
+        if item_id then
+            RecordItemData(item_id, item_link, true)
+        end
+
         loot_toast_data_timer_handle = WDP:ScheduleTimer(ClearLootToastData, 5)
     end
 end
@@ -1322,10 +1413,41 @@
 
 do
     local CHAT_MSG_LOOT_UPDATE_FUNCS = {
+        [AF.ITEM] = function(item_id, quantity)
+            local container_id = current_loot.identifier -- For faster access, since this is going to be called 9 times in the next 3 lines
+            -- Verify that we're still assigning data to the right items
+            if container_id and (container_id == BAG_OF_SALVAGE_ITEM_ID or container_id == CRATE_OF_SALVAGE_ITEM_ID or container_id == BIG_CRATE_OF_SALVAGE_ITEM_ID) then
+                Debug("CHAT_MSG_LOOT: AF.ITEM %d (%d)", item_id, quantity)
+                current_loot.sources[container_id] = current_loot.sources[container_id] or {}
+                current_loot.sources[container_id][item_id] = (current_loot.sources[container_id][item_id] or 0) + quantity
+            else -- If not, cancel the timer and wipe the loot table early
+                Debug("CHAT_MSG_LOOT: We would have assigned the wrong loot to salvage crates!")
+                ClearTimeBasedLootData()
+            end
+        end,
         [AF.NPC] = function(item_id, quantity)
-            Debug("CHAT_MSG_LOOT: %d (%d)", item_id, quantity)
+            Debug("CHAT_MSG_LOOT: AF.NPC %d (%d)", item_id, quantity)
+        end,
+        [AF.OBJECT] = function(item_id, quantity)
+            Debug("CHAT_MSG_LOOT: AF.OBJECT %d (%d)", item_id, quantity)
+            --for timber_variant = 1, #private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[last_timber_spell_id] do
+                -- Check for top level object data
+                local object_entry = DBEntry("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[last_timber_spell_id]))
+                local difficulty_token = InstanceDifficultyToken()
+                if object_entry[difficulty_token] then
+                    -- Increment loot count
+                    object_entry[difficulty_token]["opening_count"] = (object_entry[difficulty_token]["opening_count"] or 0) + 1
+
+                    -- Add drop data
+                    object_entry[difficulty_token]["opening"] = object_entry[difficulty_token]["opening"] or {}
+                    table.insert(object_entry[difficulty_token]["opening"], ("%d:%d"):format(item_id, quantity))
+                else
+                    Debug("CHAT_MSG_LOOT: When handling timber, the top level loot data was missing for objectID %s.", private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[last_timber_spell_id])
+                end
+            --end
         end,
         [AF.ZONE] = function(item_id, quantity)
+            Debug("CHAT_MSG_LOOT: AF.ZONE %d (%d)", item_id, quantity)
             InitializeCurrentLoot()
             current_loot.list[1] = ("%d:%d"):format(item_id, quantity)
             GenericLootUpdate("zones")
@@ -1337,18 +1459,7 @@
     function WDP:CHAT_MSG_LOOT(event_name, message)
         local category
 
-        if current_action.spell_label ~= "EXTRACT_GAS" then
-            category = AF.ZONE
-        elseif private.raid_finder_boss_id then
-            category = AF.NPC
-        end
-        local update_func = CHAT_MSG_LOOT_UPDATE_FUNCS[category]
-
-        if not category or not update_func then
-            return
-        end
         local item_link, quantity = deformat(message, _G.LOOT_ITEM_PUSHED_SELF_MULTIPLE)
-
         if not item_link then
             quantity, item_link = 1, deformat(message, _G.LOOT_ITEM_PUSHED_SELF)
         end
@@ -1357,6 +1468,26 @@
         if not item_id then
             return
         end
+
+        -- Set update category
+        if last_timber_spell_id and item_id == TIMBER_ITEM_ID then
+            category = AF.OBJECT
+        -- Recently changed from ~= "EXTRACT_GAS" because of some occassional bad data, and, as far as I know, no benefit.
+        elseif current_action.spell_label == "FISHING" then
+            category = AF.ZONE
+        elseif private.raid_boss_id then
+            category = AF.NPC
+        elseif chat_loot_timer_handle then
+            category = AF.ITEM
+        end
+
+        -- Take action based on update category
+        local update_func = CHAT_MSG_LOOT_UPDATE_FUNCS[category]
+        if not category or not update_func then
+            -- We still want to record the item's data, even if it doesn't need its drop location recorded
+            RecordItemData(item_id, item_link, true)
+            return
+        end
         update_func(item_id, quantity)
     end
 end
@@ -1443,12 +1574,12 @@
 end
 
 
-do -- do-block
+do
     local FLAGS_NPC = bit.bor(_G.COMBATLOG_OBJECT_TYPE_GUARDIAN, _G.COMBATLOG_OBJECT_CONTROL_NPC)
     local FLAGS_NPC_CONTROL = bit.bor(_G.COMBATLOG_OBJECT_AFFILIATION_OUTSIDER, _G.COMBATLOG_OBJECT_CONTROL_NPC)
 
     local function RecordNPCSpell(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id, spell_name)
-        if not spell_id or spell_id == SPELL_ID_CHI_WAVE or spell_id == SPELL_ID_DISGUISE then
+        if not spell_id or spell_id == CHI_WAVE_SPELL_ID or spell_id == DISGUISE_SPELL_ID then
             return
         end
         local source_type, source_id = ParseGUID(source_guid)
@@ -1548,70 +1679,12 @@
         end
     end
 
+    
     local DIPLOMACY_SPELL_ID = 20599
     local MR_POP_RANK1_SPELL_ID = 78634
     local MR_POP_RANK2_SPELL_ID = 78635
-
-    local REP_BUFFS = {
-        [_G.GetSpellInfo(30754)] = "CENARION_FAVOR",
-        [_G.GetSpellInfo(24705)] = "GRIM_VISAGE",
-        [_G.GetSpellInfo(32098)] = "HONOR_HOLD_FAVOR",
-        [_G.GetSpellInfo(39913)] = "NAZGRELS_FERVOR",
-        [_G.GetSpellInfo(39953)] = "SONG_OF_BATTLE",
-        [_G.GetSpellInfo(61849)] = "SPIRIT_OF_SHARING",
-        [_G.GetSpellInfo(32096)] = "THRALLMARS_FAVOR",
-        [_G.GetSpellInfo(39911)] = "TROLLBANES_COMMAND",
-        [_G.GetSpellInfo(95987)] = "UNBURDENED",
-        [_G.GetSpellInfo(100951)] = "WOW_ANNIVERSARY",
-    }
-
-
-    local FACTION_NAMES = {
-        CENARION_CIRCLE = _G.GetFactionInfoByID(609),
-        HONOR_HOLD = _G.GetFactionInfoByID(946),
-        THE_SHATAR = _G.GetFactionInfoByID(935),
-        THRALLMAR = _G.GetFactionInfoByID(947),
-    }
-
-
-    local MODIFIERS = {
-        CENARION_FAVOR = {
-            faction = FACTION_NAMES.CENARION_CIRCLE,
-            modifier = 0.25,
-        },
-        GRIM_VISAGE = {
-            modifier = 0.1,
-        },
-        HONOR_HOLD_FAVOR = {
-            faction = FACTION_NAMES.HONOR_HOLD,
-            modifier = 0.25,
-        },
-        NAZGRELS_FERVOR = {
-            faction = FACTION_NAMES.THRALLMAR,
-            modifier = 0.1,
-        },
-        SONG_OF_BATTLE = {
-            faction = FACTION_NAMES.THE_SHATAR,
-            modifier = 0.1,
-        },
-        SPIRIT_OF_SHARING = {
-            modifier = 0.1,
-        },
-        THRALLMARS_FAVOR = {
-            faction = FACTION_NAMES.THRALLMAR,
-            modifier = 0.25,
-        },
-        TROLLBANES_COMMAND = {
-            faction = FACTION_NAMES.HONOR_HOLD,
-            modifier = 0.1,
-        },
-        UNBURDENED = {
-            modifier = 0.1,
-        },
-        WOW_ANNIVERSARY = {
-            modifier = 0.08,
-        }
-    }
+    local FACTION_DATA = private.FACTION_DATA
+    local REP_BUFFS = private.REP_BUFFS
 
 
     function WDP:COMBAT_TEXT_UPDATE(event_name, message_type, faction_name, amount)
@@ -1635,25 +1708,48 @@
 
         local modifier = 1
 
+        -- Check for modifiers from known spells
         if _G.IsSpellKnown(DIPLOMACY_SPELL_ID) then
             modifier = modifier + 0.1
         end
-
         if _G.IsSpellKnown(MR_POP_RANK2_SPELL_ID) then
             modifier = modifier + 0.1
         elseif _G.IsSpellKnown(MR_POP_RANK1_SPELL_ID) then
             modifier = modifier + 0.05
         end
 
-        for buff_name, buff_label in pairs(REP_BUFFS) do
+        -- Determine faction ID
+        local faction_ID
+        for pseudo_faction_name, faction_data_table in pairs(FACTION_DATA) do
+            if faction_name == faction_data_table[2] then
+                faction_ID = faction_data_table[1]
+            end
+        end
+        if faction_ID and faction_ID > 0 then
+            -- Check for modifiers from Commendations (applied directly to the faction, account-wide)
+            local _, _, _, _, _, _, _, _, _, _, _, _, _, _, has_bonus_rep_gain = GetFactionInfoByID(faction_ID)
+            if has_bonus_rep_gain then
+                modifier = modifier + 1
+            end
+        end
+
+        -- Check for modifiers from buffs
+        for buff_name, buff_data_table in pairs(REP_BUFFS) do
             if _G.UnitBuff("player", buff_name) then
-                local modded_faction = MODIFIERS[buff_label].faction
+                local modded_faction = buff_data_table.faction
 
                 if not modded_faction or faction_name == modded_faction then
-                    modifier = modifier + MODIFIERS[buff_label].modifier
+                    if buff_data_table.ignore then
+                        -- Some buffs from tabards convert all rep of other factions into rep for a specific faction.
+                        -- We can't know what faction the rep was orginally from, so we must ignore the data entirely in these cases.
+                        return
+                    else
+                        modifier = modifier + buff_data_table.modifier
+                    end
                 end
             end
         end
+        
         npc.reputations = npc.reputations or {}
         npc.reputations[("%s:%s"):format(faction_name, faction_standings[faction_name])] = math.floor(amount / modifier)
     end
@@ -1661,17 +1757,23 @@
 
 
 function WDP:CURSOR_UPDATE(event_name)
-    if current_action.fishing_target or _G.Minimap:IsMouseOver() or current_action.spell_label ~= "FISHING" then
+    if current_action.fishing_target or _G.Minimap:IsMouseOver() then
         return
     end
     local text = _G["GameTooltipTextLeft1"]:GetText()
 
-    if not text or text == "Fishing Bobber" then
-        text = "NONE"
-    else
-        current_action.fishing_target = true
+    -- Handle Fishing
+    if (current_action.spell_label == "FISHING") then
+        if not text or text == "Fishing Bobber" then
+            text = "NONE"
+        else
+            current_action.fishing_target = true
+        end
+        current_action.identifier = ("%s:%s"):format(current_action.spell_label, text)
+     -- Handle Garrison Cache
+    elseif private.GARRISON_CACHE_OBJECT_NAME_TO_OBJECT_ID_MAP[text] then
+        last_garrison_cache_object_id = private.GARRISON_CACHE_OBJECT_NAME_TO_OBJECT_ID_MAP[text]
     end
-    current_action.identifier = ("%s:%s"):format(current_action.spell_label, text)
 end
 
 
@@ -1731,7 +1833,7 @@
             local source_list = {}
 
             for source_guid, loot_data in pairs(current_loot.sources) do
-                local source_id = select(2, ParseGUID(source_guid))
+                local _, source_id = ParseGUID(source_guid)
                 local npc = NPCEntry(source_id)
 
                 if npc then
@@ -1808,6 +1910,13 @@
         local extrapolated_guid_registry = {}
         local num_guids = 0
 
+        -- Loot extrapolation cannot handle objects that need special spell labels (like HERBALISM or MINING) (MIND_CONTROL is okay)
+        if private.SPELL_FLAGS_BY_LABEL[current_action.spell_label] and not private.NON_LOOT_SPELL_LABELS[current_action.spell_label] then
+            Debug("%s: Problematic spell label %s found. Loot extrapolation for this set of loot would have run an increased risk of introducing bad data into the system.", log_source, private.previous_spell_id)
+            table.wipe(current_action)
+            return false
+        end
+
         table.wipe(current_action)
 
         for loot_slot = 1, _G.GetNumLootItems() do
@@ -1839,11 +1948,6 @@
             return false
         end
 
-        if private.previous_spell_id and private.EXTRAPOLATION_BANNED_SPELL_IDS[private.previous_spell_id] then
-            Debug("%s: Problematic spell id %s found. Loot extrapolation for this set of loot would have run an increased risk of introducing bad data into the system.", log_source, private.previous_spell_id)
-            return false
-        end
-
         local num_npcs = 0
         local num_objects = 0
         local num_itemcontainers = 0
@@ -1868,8 +1972,8 @@
                     current_action.target_type = AF.NPC
                     current_action.identifier = unit_idnum
                     num_npcs = num_npcs + 1
-                -- Item container GUIDs are currently of the 'PLAYER' type; this may be unintended and could change in the future.
                 elseif unit_type == private.UNIT_TYPES.PLAYER then
+                    -- Item container GUIDs are currently of the 'PLAYER' type; this may be unintended and could change in the future.
                     current_action.loot_label = loot_label
                     current_action.target_type = AF.ITEM
                     -- current_action.identifier assigned during loot verification.
@@ -1947,16 +2051,14 @@
 
                 if not loot_guid_registry[current_loot.label][source_guid] then
                     local loot_quantity = loot_info[loot_index + 1]
-
                     -- There is a new bug in 5.4.0 that causes GetLootSlotInfo() to (rarely) return nil values for slot_quantity.
                     if slot_quantity then
                         -- We need slot_quantity to account for an old bug where loot_quantity is sometimes '1' for stacks of items, such as cloth.
                         if slot_quantity > loot_quantity then
                             loot_quantity = slot_quantity
                         end
-
                         local source_type, source_id = ParseGUID(source_guid)
-                        local source_key = ("%s:%d"):format(private.UNIT_TYPE_NAMES[source_type + 1], source_id)
+                        local source_key = ("%s:%d"):format(source_type, source_id)
 
                         if slot_type == _G.LOOT_SLOT_ITEM then
                             local item_id = ItemLinkToID(_G.GetLootSlotLink(loot_slot))
@@ -1992,7 +2094,7 @@
                         end
                     else
                         -- If this is nil, then the item's quantity could be wrong if loot_quantity is wrong, so we won't process this slot's loot.
-                        Debug("%s: Slot quantity is nil for loot slot %d of the entity with GUID %s and Type:ID: %s.", event_name, loot_slot, loot_info[loot_index], source_key)
+                        Debug("%s: Slot quantity is nil for loot slot %d.", event_name, loot_slot)
                     end
                 end
             end
@@ -2045,7 +2147,8 @@
             if not unit_idnum or not UnitTypeIsNPC(unit_type) then
                 return
             end
-            merchant_standing = select(2, UnitFactionStanding("npc"))
+            local _, faction_standing = UnitFactionStanding("npc")
+            merchant_standing = faction_standing
             current_merchant = NPCEntry(unit_idnum)
             current_merchant.sells = current_merchant.sells or {}
         end
@@ -2065,11 +2168,7 @@
             if not item_id then
                 local item_name, item_link = DatamineTT:GetItem()
                 item_id = ItemLinkToID(item_link)
-                if item_id then
-                    Debug("%s: GetMerchantItemLink() still ocassionally fails, apparently. Failed item's ID - %s", event_name, item_id)
-                else
-                    Debug("%s: GetMerchantItemLink() still ocassionally fails, apparently. Failed item's ID - nil", event_name)
-                end
+                -- GetMerchantItemLink() still ocassionally fails as of Patch 6.0.2. It fails so badly that debug functions cause considerable slowdown.
             end
 
             if item_id and item_id > 0 then
@@ -2168,7 +2267,7 @@
 
 
 function WDP:PET_BAR_UPDATE(event_name)
-    if current_action.spell_label ~= "MIND_CONTROL" then
+    if not private.NON_LOOT_SPELL_LABELS[current_action.spell_label] then
         return
     end
     local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("pet"))
@@ -2245,13 +2344,13 @@
             return
         end
         local unit_type, unit_id = ParseGUID(_G.UnitGUID("questnpc"))
-
+        Debug("UpdateQuestJuncture: Updating quest juncture for %s.", ("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type], unit_id))
         if unit_type == private.UNIT_TYPES.OBJECT then
             UpdateDBEntryLocation("objects", unit_id)
         end
         local quest = DBEntry("quests", _G.GetQuestID())
         quest[point] = quest[point] or {}
-        quest[point][("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type + 1], unit_id)] = true
+        quest[point][("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type], unit_id)] = true
 
         return quest
     end
@@ -2289,7 +2388,7 @@
     local _, num_quests = _G.GetNumQuestLogEntries()
 
     while processed_quests <= num_quests do
-        local _, _, _, _, is_header, _, _, _, quest_id = _G.GetQuestLogTitle(entry_index)
+        local _, _, _, is_header, _, _, _, quest_id = _G.GetQuestLogTitle(entry_index)
 
         if quest_id == 0 then
             processed_quests = processed_quests + 1
@@ -2332,13 +2431,16 @@
 
 
     local function RegisterTools(tradeskill_name, tradeskill_index)
-        local spell_id = tonumber(_G.GetTradeSkillRecipeLink(tradeskill_index):match("^|c%x%x%x%x%x%x%x%x|H%w+:(%d+)"))
-        local required_tool = _G.GetTradeSkillTools(tradeskill_index)
-
-        if required_tool then
-            for tool_name, registry in pairs(TRADESKILL_TOOLS) do
-                if required_tool:find(tool_name) then
-                    registry[spell_id] = true
+        local link = _G.GetTradeSkillRecipeLink(tradeskill_index)
+        if link then
+            local spell_id = tonumber(link:match("^|c%x%x%x%x%x%x%x%x|H%w+:(%d+)"))
+            local required_tool = _G.GetTradeSkillTools(tradeskill_index)
+
+            if required_tool then
+                for tool_name, registry in pairs(TRADESKILL_TOOLS) do
+                    if required_tool:find(tool_name) then
+                        registry[spell_id] = true
+                    end
                 end
             end
         end
@@ -2368,15 +2470,15 @@
     if not trainer then
         return
     end
-    local trainer_standing = select(2, UnitFactionStanding("npc"))
+    local _, trainer_standing = UnitFactionStanding("npc")
     trainer.teaches = trainer.teaches or {}
 
     private.trainer_shown = true
 
     -- Get the initial trainer filters
-    local available = _G.GetTrainerServiceTypeFilter("available")
-    local unavailable = _G.GetTrainerServiceTypeFilter("unavailable")
-    local used = _G.GetTrainerServiceTypeFilter("used")
+    local available = _G.GetTrainerServiceTypeFilter("available") and 1 or 0
+    local unavailable = _G.GetTrainerServiceTypeFilter("unavailable") and 1 or 0
+    local used = _G.GetTrainerServiceTypeFilter("used") and 1 or 0
 
     -- Clear the trainer filters
     _G.SetTrainerServiceTypeFilter("available", 1)
@@ -2429,6 +2531,7 @@
         return
     end
 
+    Debug("UNIT_SPELLCAST_SENT: %s was cast.", spell_name)
     local item_name, item_link = _G.GameTooltip:GetItem()
     local unit_name, unit_id = _G.GameTooltip:GetUnit()
 
@@ -2468,7 +2571,8 @@
         if item_name and item_name == target_name then
             current_action.identifier = ItemLinkToID(item_link)
         elseif target_name and target_name ~= "" then
-            current_action.identifier = ItemLinkToID(select(2, _G.GetItemInfo(target_name)))
+            local _, item_link = _G.GetItemInfo(target_name)
+            current_action.identifier = ItemLinkToID(item_link)
         end
     elseif not item_name and not unit_name then
         if bit.band(spell_flags, AF.OBJECT) == AF.OBJECT then
@@ -2489,19 +2593,15 @@
     if private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] then
         ClearKilledBossID()
         ClearLootToastContainerID()
-        private.raid_finder_boss_id = private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id]
-    elseif private.WORLD_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] then
-        ClearKilledBossID()
-        ClearLootToastContainerID()
-        private.world_boss_id = private.WORLD_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id]
+        private.raid_boss_id = private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id]
     else
-        Debug("%s: Spell ID %d is not a known raid or world boss 'Bonus' spell.", event_name, spell_id)
+        Debug("%s: Spell ID %d is not a known raid boss 'Bonus' spell.", event_name, spell_id)
         return
     end
 
     -- Assign existing loot data to boss if it exists
     if loot_toast_data then
-        local npc_id = private.raid_finder_boss_id or private.world_boss_id
+        local npc_id = private.raid_boss_id
 
         -- Slightly messy hack to workaround duplicate world bosses
         local upper_limit = 0
@@ -2571,6 +2671,16 @@
     private.tracked_line = nil
     private.previous_spell_id = spell_id
 
+    -- Handle Logging spell casts
+    if private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id] then
+        last_timber_spell_id = spell_id
+        --for timber_variant = 1, #private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id] do
+        UpdateDBEntryLocation("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id]))
+        --end
+        return
+    end
+
+    -- Handle Loot Toast spell casts
     if private.LOOT_SPELL_ID_TO_ITEM_ID_MAP[spell_id] then
         ClearKilledBossID()
         ClearLootToastContainerID()
@@ -2578,6 +2688,23 @@
 
         private.loot_toast_container_id = private.LOOT_SPELL_ID_TO_ITEM_ID_MAP[spell_id]
         loot_toast_container_timer_handle = WDP:ScheduleTimer(ClearLootToastContainerID, 1) -- we need to assign a handle here to cancel it later
+        return
+    end
+
+    -- For Crates of Salvage (and potentially other items based on spell casts in the future which need manual handling)
+    if private.SALVAGE_SPELL_ID_TO_ITEM_ID_MAP[spell_id] then
+        -- Set up timer
+        Debug("%s: Beginning Salvage loot timer for spellID %d", event_name, spell_id)
+        chat_loot_timer_handle = WDP:ScheduleTimer(ClearTimeBasedLootData, 1)
+
+        -- Standard item handling setup
+        table.wipe(current_action)
+        current_loot = nil
+        current_action.target_type = AF.ITEM
+        current_action.identifier = private.SALVAGE_SPELL_ID_TO_ITEM_ID_MAP[spell_id]
+        current_action.loot_label = "contains"
+        InitializeCurrentLoot()
+        return
     end
 
     if anvil_spell_ids[spell_id] then
@@ -2586,7 +2713,7 @@
         UpdateDBEntryLocation("objects", OBJECT_ID_FORGE)
     elseif spell_name:match("^Harvest.+") then
         killed_npc_id = current_target_id
-        private.harvesting = true
+        private.harvesting = true -- Used to track which NPCs can be harvested (can we get this from CreatureCache instead?)
     end
 end
 
--- a/WoWDBProfiler.toc	Wed Aug 06 04:31:49 2014 -0400
+++ b/WoWDBProfiler.toc	Tue Oct 14 00:36:05 2014 -0400
@@ -1,4 +1,4 @@
-## Interface: 50400
+## Interface: 60000
 ## Title: WoWDB Profiler
 ## Notes: WoW datamining tool.
 ## Author: James D. Callahan III (Torhal)