mirror of
https://codeberg.org/Duvelcorp/MetaHunt.git
synced 2026-09-27 10:06:17 +00:00
Add trainer planning and level-up tracking
This commit is contained in:
@@ -25,6 +25,11 @@ local MTH_MESSAGE_DEFAULTS = {
|
||||
petHungry = false,
|
||||
beastTrainingScan = true,
|
||||
spellbookScan = false,
|
||||
trainerScan = true,
|
||||
hunterLevelUp = true,
|
||||
hunterLevelQuiet = false,
|
||||
petLevelUp = true,
|
||||
petLoyaltyUp = true,
|
||||
petRanAway = true,
|
||||
mapMarkers = true,
|
||||
stableScan = true,
|
||||
@@ -216,6 +221,11 @@ function MTH:IsRealmGateBlocked()
|
||||
end
|
||||
|
||||
function MTH:CheckRealmGate()
|
||||
-- TEMP TESTING: allow Ravencraft client access while HunterBook layout is reviewed.
|
||||
if true then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Realm name is only reliable once logged in; an empty result never blocks.
|
||||
local realm = self:GetCurrentRealmName()
|
||||
if type(realm) == "string" and realm ~= "" then
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
-- MetaHunt level tracker. All training facts come from MTH_DS_TrainerCatalog
|
||||
-- through trainer-spell-planner.lua; this module only detects progression and
|
||||
-- presents the resulting facts through chat and animations.
|
||||
|
||||
local MTH_LT_Frame = nil
|
||||
local MTH_LT_PendingPetCheck = false
|
||||
|
||||
local MTH_LT_HunterLines = {
|
||||
[1] = "You can now track beasts. Revolutionary: look for the things you intend to shoot.",
|
||||
[2] = "The wilderness remains deeply unimpressed.",
|
||||
[3] = "Still mostly leather, arrows, and misplaced confidence.",
|
||||
[4] = "You have actual trainer lessons now. Try not to spend all your coin on repairs.",
|
||||
[5] = "Your pet is doing its share. Please consider doing yours.",
|
||||
[6] = "A hunter's mark: because shouting at the target was not precise enough.",
|
||||
[7] = "Every pull is a plan until it becomes cardio.",
|
||||
[8] = "Concussive Shot. Distance is now a tactical concept, not an accident.",
|
||||
[9] = "Your arrows have begun their long argument with gravity.",
|
||||
[10] = "You have a hawk aspect. The bird remains unavailable for comment.",
|
||||
[11] = "A fine age for discovering that pets also require paperwork.",
|
||||
[12] = "Mend Pet unlocked. Your pet has noticed this was overdue.",
|
||||
[13] = "The wilderness has upgraded its complaints department.",
|
||||
[14] = "Eagle Eye. Finally, a way to inspect trouble before walking into it.",
|
||||
[15] = "You have survived another level through skill, luck, or a pet-shaped shield.",
|
||||
[16] = "Traps and sharp things. Subtlety continues to be optional.",
|
||||
[17] = "Your pet is still carrying the emotional weight of this partnership.",
|
||||
[18] = "Multi-Shot. One target was apparently an unacceptable limitation.",
|
||||
[19] = "Soon you may run faster. The enemies will take this personally.",
|
||||
[20] = "Congratulations: escape now has a speed setting.",
|
||||
[21] = "Fast enough to arrive at the next questionable decision sooner.",
|
||||
[22] = "Your trainer has new bills and somehow they are all addressed to you.",
|
||||
[23] = "The pet remains employed despite the working conditions.",
|
||||
[24] = "Beast Lore. At last: research before violence. Briefly.",
|
||||
[25] = "Halfway to better excuses, nowhere near fewer mistakes.",
|
||||
[26] = "Rapid Fire. Your bow has entered its impatient phase.",
|
||||
[27] = "Things are going well enough to become dangerous.",
|
||||
[28] = "Your arrows now contain a measurable amount of intent.",
|
||||
[29] = "Prepare for the hunter's most respected tactical maneuver.",
|
||||
[30] = "Feign Death. Why win a fight when you can become administrative debris?",
|
||||
[31] = "You are alive, which validates absolutely nothing.",
|
||||
[32] = "Flare. Stealth has been downgraded to a suggestion.",
|
||||
[33] = "The next trap is definitely part of the plan.",
|
||||
[34] = "Explosive Trap. Negotiation has left the chat.",
|
||||
[35] = "Your pet thinks this is all normal. That is worrying.",
|
||||
[36] = "More damage, more maintenance, same heroic disregard for repair bills.",
|
||||
[37] = "Keep going. The next disaster is probably already pathing toward you.",
|
||||
[38] = "Your toolkit is becoming impressively hard to explain to civilized people.",
|
||||
[39] = "One level until armor finally admits you have been taking hits.",
|
||||
[40] = "Mail training. Leather got you this far; enemies kept putting holes in it.",
|
||||
[41] = "You are now visibly better protected from consequences.",
|
||||
[42] = "More Multi-Shot: because collateral damage needed a sequel.",
|
||||
[43] = "Your pet has grown used to the sound of avoidable danger.",
|
||||
[44] = "The trainer is delighted. Your coin pouch is less enthusiastic.",
|
||||
[45] = "You are a seasoned hunter now, which means the mistakes are expensive.",
|
||||
[46] = "Nature resistance: proof that the outdoors have been escalating.",
|
||||
[47] = "Still alive. Still pointing the dangerous end away from yourself. Usually.",
|
||||
[48] = "Your damage has matured into a fully funded public nuisance.",
|
||||
[49] = "One more level until the trainer starts charging executive prices.",
|
||||
[50] = "Your arrows have tenure now.",
|
||||
[51] = "The wilderness would like a word. It has a queue.",
|
||||
[52] = "Aimed Shot hits harder, which is your preferred form of diplomacy.",
|
||||
[53] = "Your pet remains your most reliable party member. Make of that what you will.",
|
||||
[54] = "Explosive Trap again. The floor continues to be a valid target.",
|
||||
[55] = "Almost there. Try not to die to something with a tutorial tooltip.",
|
||||
[56] = "Aspect of the Viper. Mana problems now have a bird-shaped solution.",
|
||||
[57] = "The finish line is visible. So are several elite mobs. Choose carefully.",
|
||||
[58] = "The final trainer bills are arriving with alarming confidence.",
|
||||
[59] = "One last level. Your pet has begun drafting a victory speech.",
|
||||
[60] = "You made it. The wildlife will remember this unfavorably.",
|
||||
}
|
||||
|
||||
local MTH_LT_Priority = {
|
||||
["Feign Death"] = 100,
|
||||
["Mail"] = 95,
|
||||
["Aspect of the Cheetah"] = 90,
|
||||
["Rapid Fire"] = 88,
|
||||
["Aspect of the Pack"] = 86,
|
||||
["Explosive Trap"] = 84,
|
||||
["Freezing Trap"] = 82,
|
||||
["Flare"] = 80,
|
||||
["Eagle Eye"] = 78,
|
||||
["Multi-Shot"] = 76,
|
||||
["Aimed Shot"] = 74,
|
||||
["Aspect of the Viper"] = 72,
|
||||
["Trueshot Aura"] = 70,
|
||||
["Volley"] = 68,
|
||||
["Track Hidden"] = 66,
|
||||
}
|
||||
|
||||
local MTH_LT_PetLines = {
|
||||
"has leveled up. It is now statistically better than your last pull.",
|
||||
"has leveled up. The food budget has noticed.",
|
||||
"has leveled up. Please do not celebrate by pulling three extra mobs.",
|
||||
"has leveled up. It remains committed to solving problems with teeth.",
|
||||
"has leveled up. The wilderness has filed no objection.",
|
||||
"has leveled up. Your dependable colleague is carrying this team again.",
|
||||
}
|
||||
|
||||
local MTH_LT_LoyaltyLines = {
|
||||
"has decided you are less terrible than expected.",
|
||||
"has upgraded its opinion of you. Keep the food coming.",
|
||||
"is learning to trust your decision-making. This may be a mistake.",
|
||||
"has accepted the arrangement. The arrangement includes snacks.",
|
||||
"now trusts you more. Do not immediately test this with a cliff.",
|
||||
}
|
||||
|
||||
local function MTH_LT_Store()
|
||||
if not MTH_CharSavedVariables then MTH_CharSavedVariables = {} end
|
||||
if type(MTH_CharSavedVariables.levelTracker) ~= "table" then
|
||||
MTH_CharSavedVariables.levelTracker = { petLine = 0, loyaltyLine = 0 }
|
||||
end
|
||||
return MTH_CharSavedVariables.levelTracker
|
||||
end
|
||||
|
||||
local function MTH_LT_MessageEnabled(key, defaultValue)
|
||||
return MTH and type(MTH.IsMessageEnabled) == "function" and MTH:IsMessageEnabled(key, defaultValue)
|
||||
end
|
||||
|
||||
local function MTH_LT_FormatCost(copper)
|
||||
local total = tonumber(copper) or 0
|
||||
local gold = math.floor(total / 10000)
|
||||
local silver = math.floor((total - gold * 10000) / 100)
|
||||
local bronze = total - gold * 10000 - silver * 100
|
||||
local parts = {}
|
||||
if gold > 0 then table.insert(parts, "|cffffd100" .. gold .. "g|r") end
|
||||
if silver > 0 or gold > 0 then table.insert(parts, "|cffc7c7cf" .. silver .. "s|r") end
|
||||
table.insert(parts, "|cffeda55f" .. bronze .. "c|r")
|
||||
return table.concat(parts, " ")
|
||||
end
|
||||
|
||||
local function MTH_LT_NextLine(store, key, lines)
|
||||
store[key] = (tonumber(store[key]) or 0) + 1
|
||||
if store[key] > table.getn(lines) then store[key] = 1 end
|
||||
return lines[store[key]]
|
||||
end
|
||||
|
||||
local function MTH_LT_GetHeadline(plan)
|
||||
local best = nil
|
||||
local bestPriority = -1
|
||||
for _, entry in ipairs(plan.rows or {}) do
|
||||
local priority = MTH_LT_Priority[tostring(entry.row and entry.row.name or "")] or 0
|
||||
if priority > bestPriority then best, bestPriority = entry, priority end
|
||||
end
|
||||
return best
|
||||
end
|
||||
|
||||
local function MTH_LT_FormatRows(plan)
|
||||
local labels = {}
|
||||
for index, entry in ipairs(plan.rows or {}) do
|
||||
local row = entry.row or {}
|
||||
local label = tostring(row.name or "Unknown")
|
||||
if row.rank and row.rank ~= "" then label = label .. " " .. tostring(row.rank) end
|
||||
table.insert(labels, label)
|
||||
end
|
||||
return table.concat(labels, ", ")
|
||||
end
|
||||
|
||||
local function MTH_LT_PrintSpellList(plan)
|
||||
local line = ""
|
||||
local isFirstLine = true
|
||||
for _, entry in ipairs(plan.rows or {}) do
|
||||
local row = entry.row or {}
|
||||
local label = tostring(row.name or "Unknown")
|
||||
if row.rank and row.rank ~= "" then label = label .. " " .. tostring(row.rank) end
|
||||
if line == "" then
|
||||
line = label
|
||||
elseif string.len(line) + string.len(label) + 2 > 92 then
|
||||
MTH:Print((isFirstLine and "New spells available: " or "And also: ") .. line)
|
||||
isFirstLine = false
|
||||
line = label
|
||||
else
|
||||
line = line .. ", " .. label
|
||||
end
|
||||
end
|
||||
if line ~= "" then MTH:Print((isFirstLine and "New spells available: " or "And also: ") .. line) end
|
||||
end
|
||||
|
||||
local function MTH_LT_AnnounceTraining(kind, level, eventKind, preview)
|
||||
if type(MTH_TrainerPlan_BuildLevel) ~= "function" then return end
|
||||
local base = MTH_TrainerPlan_BuildLevel(kind, level, { honored = false, includeKnown = preview })
|
||||
local honored = MTH_TrainerPlan_BuildLevel(kind, level, { honored = true, includeKnown = preview })
|
||||
if base.unlearned.count <= 0 then
|
||||
if kind == "hunter" and MTH_LT_MessageEnabled("hunterLevelUp", true) then
|
||||
MTH:Print("Congratz! You hit level " .. tostring(level) .. ".")
|
||||
end
|
||||
if preview then MTH:Print("Level test: no " .. kind .. " trainer spells in the 1.18.1 catalog at level " .. tostring(level) .. ".") end
|
||||
return
|
||||
end
|
||||
local headline = MTH_LT_GetHeadline(base)
|
||||
local headlineText = headline and headline.row and tostring(headline.row.name or "") or "New training"
|
||||
if headline and headline.row and headline.row.rank and headline.row.rank ~= "" then headlineText = headlineText .. " " .. headline.row.rank end
|
||||
local isLevelCap = kind == "hunter" and level == 60
|
||||
if isLevelCap then headlineText = "The hunt is yours" end
|
||||
if MTH_LT_MessageEnabled(eventKind == "hunter" and "hunterLevelUp" or "petLevelUp", true) then
|
||||
if kind == "hunter" then
|
||||
MTH:Print("Congratz! You hit level " .. tostring(level) .. ".")
|
||||
MTH:Print(MTH_LT_HunterLines[level] or "New training is available.")
|
||||
MTH:Print("The spells you can learn will cost you " .. MTH_LT_FormatCost(base.unlearned.cost)
|
||||
.. ", but only " .. MTH_LT_FormatCost(honored.unlearned.cost) .. " if you are Honored with the master.")
|
||||
MTH_LT_PrintSpellList(base)
|
||||
else
|
||||
MTH:Print("" .. MTH_LT_FormatRows(base))
|
||||
MTH:Print("Trainer cost: Base " .. MTH_LT_FormatCost(base.unlearned.cost) .. " | Honored " .. MTH_LT_FormatCost(honored.unlearned.cost))
|
||||
end
|
||||
end
|
||||
local isMilestone = kind == "hunter" and (level == 10 or level == 20 or level == 30 or level == 40 or level == 50 or level == 60)
|
||||
local flag = isMilestone and "level.huntermilestone" or (kind == "hunter" and "level.hunter" or "level.pet")
|
||||
if type(MTH_FX_IsEnabled) == "function" and MTH_FX_IsEnabled(flag) and type(MTH_Celebrate) == "function" then
|
||||
local subtitle = kind == "hunter" and (MTH_LT_HunterLines[level] or "New training is available.") or "New pet training is available. The trainer wants coin."
|
||||
if isMilestone and type(MTH_CelebrateBlackHole) == "function" then
|
||||
MTH_CelebrateBlackHole("Level " .. tostring(level) .. " - " .. headlineText, subtitle)
|
||||
else
|
||||
MTH_Celebrate("Level " .. tostring(level) .. " - " .. headlineText, subtitle, "fanfare", 4.8)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_LT_HandleHunterLevel(level)
|
||||
local store = MTH_LT_Store()
|
||||
local previous = tonumber(store.hunterLevel)
|
||||
store.hunterLevel = level
|
||||
if previous and level > previous then
|
||||
MTH_LT_AnnounceTraining("hunter", level, "hunter")
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_LT_GetPetState()
|
||||
if type(UnitExists) ~= "function" or not UnitExists("pet") then return nil end
|
||||
local level = type(UnitLevel) == "function" and tonumber(UnitLevel("pet")) or nil
|
||||
local name = type(UnitName) == "function" and UnitName("pet") or "Your pet"
|
||||
if not level or level <= 0 then return nil end
|
||||
local loyalty = nil
|
||||
local getPetLoyalty = (type(getglobal) == "function" and getglobal("GetPetLoyalty")) or (_G and _G["GetPetLoyalty"])
|
||||
if type(getPetLoyalty) == "function" then loyalty = tonumber(getPetLoyalty()) end
|
||||
return { key = tostring(name or "pet"), name = tostring(name or "Your pet"), level = level, loyalty = loyalty }
|
||||
end
|
||||
|
||||
local function MTH_LT_HandlePetState()
|
||||
MTH_LT_PendingPetCheck = false
|
||||
local state = MTH_LT_GetPetState()
|
||||
if not state then return end
|
||||
local store = MTH_LT_Store()
|
||||
local pet = store.pet
|
||||
if type(pet) ~= "table" or pet.key ~= state.key then
|
||||
store.pet = state
|
||||
return
|
||||
end
|
||||
if state.level > (tonumber(pet.level) or 0) then
|
||||
local line = MTH_LT_NextLine(store, "petLine", MTH_LT_PetLines)
|
||||
if MTH_LT_MessageEnabled("petLevelUp", true) then MTH:Print(state.name .. " reached level " .. state.level .. ". " .. line) end
|
||||
MTH_LT_AnnounceTraining("pet", state.level, "pet")
|
||||
end
|
||||
if state.loyalty and state.loyalty > (tonumber(pet.loyalty) or 0) then
|
||||
local line = MTH_LT_NextLine(store, "loyaltyLine", MTH_LT_LoyaltyLines)
|
||||
if state.loyalty == 6 then line = "is your Best Friend. Against all evidence, it trusts you completely." end
|
||||
if MTH_LT_MessageEnabled("petLoyaltyUp", true) then MTH:Print(state.name .. " reached Loyalty Level " .. state.loyalty .. ". " .. line) end
|
||||
if type(MTH_FX_IsEnabled) == "function" and MTH_FX_IsEnabled("level.loyalty") and type(MTH_Celebrate) == "function" then
|
||||
MTH_Celebrate(state.name .. " - Loyalty " .. state.loyalty, line, state.loyalty == 6 and "victory" or "fanfare")
|
||||
end
|
||||
end
|
||||
store.pet = state
|
||||
end
|
||||
|
||||
local function MTH_LT_RequestPetCheck()
|
||||
if MTH_LT_PendingPetCheck or not MTH_LT_Frame then return end
|
||||
MTH_LT_PendingPetCheck = true
|
||||
MTH_LT_Frame.elapsed = 0
|
||||
MTH_LT_Frame:SetScript("OnUpdate", function()
|
||||
this.elapsed = (this.elapsed or 0) + (arg1 or 0)
|
||||
if this.elapsed >= 0.35 then
|
||||
this:SetScript("OnUpdate", nil)
|
||||
MTH_LT_HandlePetState()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function MTH_LevelTracker_Test(kind, level)
|
||||
if kind == "hunter" or kind == "pet" then
|
||||
local targetLevel = tonumber(level) or ((kind == "hunter") and (tonumber(UnitLevel("player")) or 1) or 1)
|
||||
MTH:Print("Level test: previewing " .. kind .. " trainer spells at level " .. tostring(targetLevel) .. ".")
|
||||
MTH_LT_AnnounceTraining(kind, targetLevel, kind, true)
|
||||
return true
|
||||
end
|
||||
if kind == "loyalty" then
|
||||
local state = MTH_LT_GetPetState()
|
||||
local loyalty = tonumber(level) or (state and state.loyalty) or 1
|
||||
local petName = state and state.name or "Your pet"
|
||||
local line = loyalty == 6 and "is your Best Friend. Against all evidence, it trusts you completely."
|
||||
or MTH_LT_LoyaltyLines[1]
|
||||
MTH:Print(petName .. " reached Loyalty Level " .. tostring(loyalty) .. ". " .. line)
|
||||
if type(MTH_Celebrate) == "function" then
|
||||
MTH_Celebrate(petName .. " - Loyalty " .. tostring(loyalty), line, loyalty == 6 and "victory" or "fanfare")
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_LevelTracker_Initialize()
|
||||
if MTH_LT_Frame then return end
|
||||
MTH_LT_Frame = CreateFrame("Frame", "MTH_LevelTracker")
|
||||
MTH_LT_Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_LT_Frame:RegisterEvent("PLAYER_LEVEL_UP")
|
||||
MTH_LT_Frame:RegisterEvent("UNIT_LEVEL")
|
||||
MTH_LT_Frame:RegisterEvent("UNIT_PET")
|
||||
MTH_LT_Frame:RegisterEvent("PET_BAR_UPDATE")
|
||||
MTH_LT_Frame:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
local level = type(UnitLevel) == "function" and tonumber(UnitLevel("player")) or nil
|
||||
if level then MTH_LT_Store().hunterLevel = level end
|
||||
MTH_LT_RequestPetCheck()
|
||||
elseif event == "PLAYER_LEVEL_UP" then
|
||||
MTH_LT_HandleHunterLevel(tonumber(arg1) or tonumber(UnitLevel("player")) or 1)
|
||||
elseif event == "UNIT_LEVEL" and arg1 == "pet" then
|
||||
MTH_LT_RequestPetCheck()
|
||||
elseif event == "UNIT_PET" and arg1 == "player" then
|
||||
MTH_LT_RequestPetCheck()
|
||||
elseif event == "PET_BAR_UPDATE" then
|
||||
MTH_LT_RequestPetCheck()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
MTH_LevelTracker_Initialize()
|
||||
@@ -0,0 +1,251 @@
|
||||
-- MetaHunt: one-time live trainer catalog collector.
|
||||
-- Developer workflow: /mth trainerscan, then open a Hunter or Pet Trainer.
|
||||
|
||||
local MTH_TS_Frame
|
||||
local MTH_TS_Armed = false
|
||||
local MTH_TS_AutoArmed = false
|
||||
|
||||
local function MTH_TS_Log(text)
|
||||
if MTH_DebugFrame and type(MTH_DebugFrame.AddInfo) == "function" then
|
||||
MTH_DebugFrame:AddInfo("[TrainerScan] " .. tostring(text))
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_TS_Call(name, ...)
|
||||
local fn = getglobal and getglobal(name) or (_G and _G[name])
|
||||
if type(fn) ~= "function" then return nil, "missing" end
|
||||
local ok, a, b, c, d, e, f, g = pcall(fn, unpack(arg))
|
||||
if not ok then return nil, "error=" .. tostring(a) end
|
||||
return { a, b, c, d, e, f, g }, nil
|
||||
end
|
||||
|
||||
local function MTH_TS_Values(values)
|
||||
if not values then return "" end
|
||||
local parts = {}
|
||||
local i = 1
|
||||
while i <= table.getn(values) do
|
||||
if values[i] ~= nil then
|
||||
table.insert(parts, tostring(values[i]))
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return table.concat(parts, " | ")
|
||||
end
|
||||
|
||||
local function MTH_TS_TargetName()
|
||||
if type(UnitName) == "function" then
|
||||
local name = UnitName("target")
|
||||
if name and name ~= "" then return name end
|
||||
end
|
||||
local title = getglobal and getglobal("TrainerFrameTitleText") or nil
|
||||
if title and title.GetText then return title:GetText() or "?" end
|
||||
return "?"
|
||||
end
|
||||
|
||||
local function MTH_TS_GetLiveStatusStore()
|
||||
if type(MTH_CharSavedVariables) ~= "table" then MTH_CharSavedVariables = {} end
|
||||
if type(MTH_CharSavedVariables.trainerLiveStatus) ~= "table" then
|
||||
MTH_CharSavedVariables.trainerLiveStatus = {}
|
||||
end
|
||||
return MTH_CharSavedVariables.trainerLiveStatus
|
||||
end
|
||||
|
||||
local function MTH_TS_GetCatalogKind(name, rank)
|
||||
local catalog = MTH_DS_TrainerCatalog
|
||||
if type(catalog) ~= "table" or type(catalog.byNameRank) ~= "table" then return nil end
|
||||
local keySuffix = "\031" .. tostring(name or "") .. "\031" .. tostring(rank or "")
|
||||
if catalog.byNameRank["hunter" .. keySuffix] then return "hunter" end
|
||||
if catalog.byNameRank["pet" .. keySuffix] then return "pet" end
|
||||
return nil
|
||||
end
|
||||
|
||||
function MTH_GetTrainerLiveStatus(kind, name, rank)
|
||||
local store = type(MTH_CharSavedVariables) == "table" and MTH_CharSavedVariables.trainerLiveStatus or nil
|
||||
if type(store) ~= "table" then return nil end
|
||||
return store[tostring(kind or "") .. "\031" .. tostring(name or "") .. "\031" .. tostring(rank or "")]
|
||||
end
|
||||
|
||||
local function MTH_TS_CollectLiveStatuses()
|
||||
local countValues = MTH_TS_Call("GetNumTrainerServices")
|
||||
local total = countValues and tonumber(countValues[1]) or 0
|
||||
if total <= 0 then return false end
|
||||
|
||||
local trainerName = MTH_TS_TargetName()
|
||||
local store = MTH_TS_GetLiveStatusStore()
|
||||
local observed = 0
|
||||
for index = 1, total do
|
||||
local info = MTH_TS_Call("GetTrainerServiceInfo", index)
|
||||
local name = info and info[1] or nil
|
||||
local rank = info and info[2] or ""
|
||||
local status = info and info[3] or nil
|
||||
local kind = MTH_TS_GetCatalogKind(name, rank)
|
||||
if kind and (status == "available" or status == "unavailable" or status == "used") then
|
||||
local key = kind .. "\031" .. tostring(name) .. "\031" .. tostring(rank)
|
||||
store[key] = {
|
||||
status = status,
|
||||
observedAt = time(),
|
||||
trainer = trainerName,
|
||||
}
|
||||
observed = observed + 1
|
||||
end
|
||||
end
|
||||
|
||||
if observed > 0 then
|
||||
MTH_TS_Log("Live trainer overlay: " .. tostring(observed) .. " observed catalog row(s) from " .. tostring(trainerName) .. ".")
|
||||
if MTH and MTH.Print and (not MTH.IsMessageEnabled or MTH:IsMessageEnabled("trainerScan", true)) then
|
||||
MTH:Print("Trainer scan: " .. tostring(observed) .. " Hunter/Pet spell status" .. (observed == 1 and "" or "es") .. " observed.")
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function MTH_TS_Collect()
|
||||
local countValues, countError = MTH_TS_Call("GetNumTrainerServices")
|
||||
local total = countValues and tonumber(countValues[1]) or 0
|
||||
if not total or total <= 0 then
|
||||
MTH_TS_Log("No trainer services yet. GetNumTrainerServices=" .. tostring(MTH_TS_Values(countValues))
|
||||
.. " " .. tostring(countError or ""))
|
||||
return false
|
||||
end
|
||||
|
||||
local trainerName = MTH_TS_TargetName()
|
||||
local capture = {
|
||||
trainer = trainerName,
|
||||
capturedAt = time(),
|
||||
services = {},
|
||||
}
|
||||
MTH_TRAINER_SCAN_LAST = capture
|
||||
if type(MTH_CharSavedVariables) == "table" then
|
||||
if type(MTH_CharSavedVariables.trainerCatalogScans) ~= "table" then
|
||||
MTH_CharSavedVariables.trainerCatalogScans = {}
|
||||
end
|
||||
MTH_CharSavedVariables.trainerCatalogScans[trainerName] = capture
|
||||
end
|
||||
MTH_TS_Log("=== " .. tostring(capture.trainer) .. " | " .. tostring(total) .. " trainer services ===")
|
||||
|
||||
local index = 1
|
||||
while index <= total do
|
||||
local info, infoError = MTH_TS_Call("GetTrainerServiceInfo", index)
|
||||
local icon, iconError = MTH_TS_Call("GetTrainerServiceIcon", index)
|
||||
local cost, costError = MTH_TS_Call("GetTrainerServiceCost", index)
|
||||
local level, levelError = MTH_TS_Call("GetTrainerServiceLevelReq", index)
|
||||
local skill, skillError = MTH_TS_Call("GetTrainerServiceSkillReq", index)
|
||||
local description, descriptionError = MTH_TS_Call("GetTrainerServiceDescription", index)
|
||||
local reqCountValues = MTH_TS_Call("GetTrainerServiceNumAbilityReq", index)
|
||||
local reqCount = reqCountValues and tonumber(reqCountValues[1]) or 0
|
||||
local requirements = {}
|
||||
local reqIndex = 1
|
||||
while reqIndex <= reqCount do
|
||||
local requirement, reqError = MTH_TS_Call("GetTrainerServiceAbilityReq", index, reqIndex)
|
||||
table.insert(requirements, {
|
||||
name = requirement and requirement[1] or nil,
|
||||
flag = requirement and requirement[2] or nil,
|
||||
error = reqError,
|
||||
})
|
||||
reqIndex = reqIndex + 1
|
||||
end
|
||||
|
||||
local row = {
|
||||
index = index,
|
||||
name = info and info[1] or nil,
|
||||
rank = info and info[2] or nil,
|
||||
status = info and info[3] or nil,
|
||||
expanded = info and info[4] or nil,
|
||||
icon = icon and icon[1] or nil,
|
||||
cost = cost and cost[1] or nil,
|
||||
requiredLevel = level and level[1] or nil,
|
||||
skillRequirement = skill,
|
||||
description = description and description[1] or nil,
|
||||
abilityRequirements = requirements,
|
||||
}
|
||||
table.insert(capture.services, row)
|
||||
|
||||
MTH_TS_Log(string.format("%02d name=%s | rank=%s | status=%s | expanded=%s | level=%s | cost=%s | icon=%s",
|
||||
index, tostring(row.name), tostring(row.rank), tostring(row.status), tostring(row.expanded),
|
||||
tostring(row.requiredLevel), tostring(row.cost), tostring(row.icon)))
|
||||
if row.description and row.description ~= "" then
|
||||
MTH_TS_Log(" description=" .. tostring(row.description))
|
||||
end
|
||||
if table.getn(requirements) > 0 then
|
||||
local requirementText = {}
|
||||
local requirementIndex = 1
|
||||
while requirementIndex <= table.getn(requirements) do
|
||||
local requirement = requirements[requirementIndex]
|
||||
table.insert(requirementText, tostring(requirement.name)
|
||||
.. " [flag=" .. tostring(requirement.flag) .. "]")
|
||||
requirementIndex = requirementIndex + 1
|
||||
end
|
||||
MTH_TS_Log(" abilityRequirements=" .. table.concat(requirementText, ", "))
|
||||
end
|
||||
if skill and table.getn(skill) > 0 then
|
||||
MTH_TS_Log(" skillRequirement=" .. MTH_TS_Values(skill))
|
||||
end
|
||||
if infoError or iconError or costError or levelError or skillError or descriptionError then
|
||||
MTH_TS_Log(" API=" .. tostring(infoError or "ok") .. ", " .. tostring(iconError or "ok")
|
||||
.. ", " .. tostring(costError or "ok") .. ", " .. tostring(levelError or "ok")
|
||||
.. ", " .. tostring(skillError or "ok") .. ", " .. tostring(descriptionError or "ok"))
|
||||
end
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
MTH_TS_Log("=== Capture complete. " .. tostring(total)
|
||||
.. " rows retained in MTH_TRAINER_SCAN_LAST and trainerCatalogScans. ===")
|
||||
return true
|
||||
end
|
||||
|
||||
local function MTH_TS_EnsureFrame()
|
||||
if not MTH_TS_Frame then
|
||||
MTH_TS_Frame = CreateFrame("Frame", "MTH_TrainerCatalogScanner")
|
||||
MTH_TS_Frame:SetScript("OnEvent", function()
|
||||
if event == "TRAINER_SHOW" then
|
||||
MTH_TS_AutoArmed = true
|
||||
this.elapsed = 0
|
||||
this:SetScript("OnUpdate", function()
|
||||
this.elapsed = (this.elapsed or 0) + (arg1 or 0)
|
||||
if this.elapsed < 0.25 then return end
|
||||
this:SetScript("OnUpdate", nil)
|
||||
MTH_TS_CollectLiveStatuses()
|
||||
end)
|
||||
return
|
||||
end
|
||||
if event ~= "TRAINER_LIST_UPDATE" or not (MTH_TS_Armed or MTH_TS_AutoArmed) then return end
|
||||
this.elapsed = 0
|
||||
this:SetScript("OnUpdate", function()
|
||||
this.elapsed = (this.elapsed or 0) + (arg1 or 0)
|
||||
if this.elapsed < 0.25 then return end
|
||||
this:SetScript("OnUpdate", nil)
|
||||
local manualArmed = MTH_TS_Armed
|
||||
MTH_TS_Armed = false
|
||||
MTH_TS_AutoArmed = false
|
||||
MTH_TS_CollectLiveStatuses()
|
||||
if manualArmed and not MTH_TS_Collect() and MTH and MTH.Print then
|
||||
MTH:Print("Trainer list was empty. Open the trainer again and run /mth trainerscan.")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
MTH_TS_Frame:RegisterEvent("TRAINER_SHOW")
|
||||
MTH_TS_Frame:RegisterEvent("TRAINER_LIST_UPDATE")
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_CommandTrainerScan()
|
||||
if MTH_DebugFrame and type(MTH_DebugFrame.Show) == "function" then MTH_DebugFrame:Show() end
|
||||
local currentRows = MTH_TS_Call("GetNumTrainerServices")
|
||||
if currentRows and (tonumber(currentRows[1]) or 0) > 0 then
|
||||
MTH_TS_Collect()
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Trainer catalog captured from the open trainer window.")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
MTH_TS_EnsureFrame()
|
||||
|
||||
MTH_TS_Armed = true
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Trainer catalog scan armed. Open a Hunter Trainer or Pet Trainer; results will appear in the debug window.")
|
||||
end
|
||||
end
|
||||
|
||||
MTH_TS_EnsureFrame()
|
||||
+33
-8
@@ -331,15 +331,40 @@ function SlashCmdList.MTH(msg, editbox)
|
||||
else
|
||||
MTH:Print("Train scan is not available.")
|
||||
end
|
||||
elseif lowerCmd == "trainerscan" then
|
||||
if type(MTH_CommandTrainerScan) == "function" then
|
||||
MTH_CommandTrainerScan()
|
||||
else
|
||||
MTH:Print("Trainer catalog scanner is not available.")
|
||||
end
|
||||
elseif lowerCmd == "spellscan" then
|
||||
if type(MTH_HT_RescanSpellbook) ~= "function" then
|
||||
MTH:Print("Hunter spellbook scanner is not available.")
|
||||
return
|
||||
end
|
||||
MTH_HT_RescanSpellbook()
|
||||
local mail = type(MTH_HT_GetSpellInfo) == "function" and MTH_HT_GetSpellInfo("Mail") or nil
|
||||
if mail then
|
||||
MTH:Print("Hunter spellbook scan: Mail found (rank=" .. tostring(mail.rank or "") .. ", slot=" .. tostring(mail.slot or "?") .. ").")
|
||||
else
|
||||
MTH:Print("Hunter spellbook scan: Mail was not returned by the client spellbook API.")
|
||||
end
|
||||
elseif lowerCmd == "leveltest" then
|
||||
local _, _, kind, levelText = string.find(lowerArg or "", "^(%S+)%s*(%d*)")
|
||||
if type(MTH_LevelTracker_Test) ~= "function" then
|
||||
MTH:Print("Level tracker is not available.")
|
||||
elseif not MTH_LevelTracker_Test(kind, tonumber(levelText)) then
|
||||
MTH:Print("Use /mth leveltest hunter [level], pet [level], or loyalty [level]")
|
||||
end
|
||||
elseif lowerCmd == "fx" or lowerCmd == "fireworks" or lowerCmd == "celebrate" then
|
||||
-- TEMP: preview the pet-ability celebration effect.
|
||||
if type(MTH_Celebrate) == "function" then
|
||||
local label = msg
|
||||
local _, _, rest = string.find(msg, "^%S+%s+(.-)%s*$")
|
||||
if rest and rest ~= "" then
|
||||
MTH_Celebrate("New Pet Ability!", rest)
|
||||
else
|
||||
MTH_Celebrate("New Pet Ability!", "Growl (Rank 3)")
|
||||
if type(MTH_CelebratePreview) == "function" then
|
||||
local style = lowerArg or ""
|
||||
if style == "" then style = "firework" end
|
||||
if style == "help" or style == "list" then
|
||||
MTH:Print("Celebrations: firework, blackhole, shockwave, shatter, all")
|
||||
elseif not MTH_CelebratePreview(style) then
|
||||
MTH:Print("Unknown celebration: " .. tostring(style))
|
||||
MTH:Print("Use /mth fx help for: firework, blackhole, shockwave, shatter, all")
|
||||
end
|
||||
else
|
||||
-- Self-diagnosing: report which piece failed to load so we can tell
|
||||
|
||||
+328
-15
@@ -33,6 +33,7 @@ MTH_FX_DBG = {
|
||||
-- Sections shown, in order, in the options panel. `titleKey` localizes `title`.
|
||||
MTH_FX_ANIM_SECTIONS = {
|
||||
{ id = "pet", title = "Pet", titleKey = "ANIM_SECTION_PET" },
|
||||
{ id = "level", title = "Leveling", titleKey = "ANIM_SECTION_LEVEL" },
|
||||
{ id = "combat", title = "Combat", titleKey = "ANIM_SECTION_COMBAT" },
|
||||
{ id = "book", title = "Book", titleKey = "ANIM_SECTION_BOOK" },
|
||||
{ id = "ui", title = "Interface", titleKey = "ANIM_SECTION_UI" },
|
||||
@@ -43,6 +44,18 @@ MTH_FX_ANIM_SECTIONS = {
|
||||
-- labelKey/tooltipKey localize label/tooltip (resolved at render time).
|
||||
-- Adding a new animation = one entry here + one MTH_FX_IsEnabled("key") guard.
|
||||
MTH_FX_ANIM_DEFS = {
|
||||
{ key = "level.hunter", section = "level", default = true,
|
||||
label = "Celebrate Hunter training levels",
|
||||
tooltip = "Show a banner and burst when a level unlocks Hunter trainer spells." },
|
||||
{ key = "level.huntermilestone", section = "level", default = true,
|
||||
label = "Celebrate Hunter milestones",
|
||||
tooltip = "Use a large celebration at levels 10, 20, 30, 40, 50, and 60." },
|
||||
{ key = "level.pet", section = "level", default = true,
|
||||
label = "Celebrate pet training levels",
|
||||
tooltip = "Show a banner when your current pet gains a level with Pet Trainer spells." },
|
||||
{ key = "level.loyalty", section = "level", default = true,
|
||||
label = "Celebrate pet loyalty gains",
|
||||
tooltip = "Show a golden banner when your current pet gains a loyalty level." },
|
||||
{ key = "pet.celebrate", section = "pet", default = true,
|
||||
label = "Celebrate new pet abilities", labelKey = "ANIM_PET_CELEBRATE",
|
||||
tooltip = "Play a firework and a banner in the middle of the screen when your pet learns a new ability or a new rank.",
|
||||
@@ -244,6 +257,27 @@ if Sauce and type(Sauce.RegisterType) == "function" and not Sauce.MTH_HasShake t
|
||||
MTH_FX_DBG.shakeErr = err
|
||||
end
|
||||
|
||||
if Sauce and type(Sauce.RegisterType) == "function" and not Sauce.MTH_HasSpiral then
|
||||
local ok, err = pcall(function()
|
||||
Sauce.MTH_HasSpiral = true
|
||||
Sauce:RegisterType("mth_spiral", {
|
||||
init = function(target, from)
|
||||
return from or 0
|
||||
end,
|
||||
apply = function(target, from, to, t, tween)
|
||||
local radius = (tween.radius or 0) * (1 - t)
|
||||
local angle = (tween.startAngle or 0) + (tween.rotations or 1) * 2 * math.pi * t
|
||||
target:ClearAllPoints()
|
||||
target:SetPoint("CENTER", tween.anchor or UIParent, "CENTER",
|
||||
(tween.cx or 0) + math.cos(angle) * radius,
|
||||
(tween.cy or 0) + math.sin(angle) * radius)
|
||||
end,
|
||||
})
|
||||
end)
|
||||
MTH_FX_DBG.spiralOk = ok
|
||||
MTH_FX_DBG.spiralErr = err
|
||||
end
|
||||
|
||||
-- Reusable flash: overlays a short additive colour glow on any frame/button and
|
||||
-- fades it out. Safe on any frame that supports CreateTexture; silent no-op
|
||||
-- otherwise. The overlay texture is cached on the frame for reuse.
|
||||
@@ -277,11 +311,15 @@ end
|
||||
|
||||
|
||||
local MTH_FX_NUM_SPARKS = 22
|
||||
local MTH_FX_NUM_CONFETTI = 48
|
||||
local MTH_FX_NUM_BLACKHOLE = 40
|
||||
local MTH_FX_Container
|
||||
local MTH_FX_MsgFrame
|
||||
local MTH_FX_TitleFS
|
||||
local MTH_FX_SubFS
|
||||
local MTH_FX_Sparks = {}
|
||||
local MTH_FX_Confetti = {}
|
||||
local MTH_FX_BlackHole = {}
|
||||
|
||||
-- Palette for the firework sparks (r, g, b), warm celebratory tones.
|
||||
local MTH_FX_Colors = {
|
||||
@@ -337,33 +375,75 @@ local function MTH_FX_EnsureFrames()
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
i = 1
|
||||
while i <= MTH_FX_NUM_CONFETTI do
|
||||
local bit = MTH_FX_Container:CreateTexture(nil, "OVERLAY")
|
||||
bit:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
bit:SetWidth(5)
|
||||
bit:SetHeight(8)
|
||||
bit:SetPoint("CENTER", MTH_FX_Container, "CENTER", 0, 0)
|
||||
bit:Hide()
|
||||
MTH_FX_Confetti[i] = bit
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
i = 1
|
||||
while i <= MTH_FX_NUM_BLACKHOLE do
|
||||
local particle = MTH_FX_Container:CreateTexture(nil, "OVERLAY")
|
||||
particle:SetTexture("Interface\\Cooldown\\star4")
|
||||
particle:SetWidth(10)
|
||||
particle:SetHeight(10)
|
||||
particle:SetBlendMode("ADD")
|
||||
particle:SetPoint("CENTER", MTH_FX_Container, "CENTER", 0, 0)
|
||||
particle:Hide()
|
||||
MTH_FX_BlackHole[i] = particle
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Public entry point: play a celebratory message + firework burst in the middle
|
||||
-- of the screen. `title` and `subtitle` are optional strings.
|
||||
function MTH_Celebrate(title, subtitle)
|
||||
-- Public entry point: play a full-screen celebration. `style` defaults to the
|
||||
-- original firework burst and may be firework, victory, starfall, fanfare, or comet.
|
||||
function MTH_Celebrate(title, subtitle, style, displaySeconds)
|
||||
if not Sauce then
|
||||
return
|
||||
end
|
||||
if not MTH_FX_EnsureFrames() then
|
||||
return
|
||||
end
|
||||
style = string.lower(tostring(style or "firework"))
|
||||
local fadeDelay = tonumber(displaySeconds) or 2.1
|
||||
if fadeDelay < 0.5 then fadeDelay = 0.5 end
|
||||
|
||||
MTH_FX_TitleFS:SetTextColor(1, 0.82, 0)
|
||||
if style == "starfall" then
|
||||
MTH_FX_TitleFS:SetTextColor(0.45, 0.80, 1.00)
|
||||
else
|
||||
MTH_FX_TitleFS:SetTextColor(1, 0.82, 0)
|
||||
end
|
||||
MTH_FX_TitleFS:SetText(title or "")
|
||||
MTH_FX_SubFS:SetText(subtitle or "")
|
||||
|
||||
Sauce:Stop(MTH_FX_MsgFrame)
|
||||
MTH_FX_MsgFrame:SetAlpha(0)
|
||||
MTH_FX_MsgFrame:Show()
|
||||
Sauce:Sequence({
|
||||
Sauce:Group({
|
||||
Sauce:FadeTo(MTH_FX_MsgFrame, 1, 0.25),
|
||||
Sauce:Pulse(MTH_FX_MsgFrame, 1.22, 0.5),
|
||||
}),
|
||||
Sauce:FadeOut(MTH_FX_MsgFrame, 0.6, "inQuad", { delay = 2.2 }),
|
||||
})
|
||||
if style == "victory" or style == "fanfare" then
|
||||
Sauce:Sequence({
|
||||
Sauce:Group({
|
||||
Sauce:SlideIn(MTH_FX_MsgFrame, "UP", 80, 0.36, "outBack", { defer = true }),
|
||||
Sauce:Pulse(MTH_FX_MsgFrame, style == "victory" and 1.32 or 1.18, 0.52, { defer = true }),
|
||||
}),
|
||||
Sauce:FadeOut(MTH_FX_MsgFrame, 0.6, "inQuad", { delay = fadeDelay }),
|
||||
})
|
||||
else
|
||||
Sauce:Sequence({
|
||||
Sauce:Group({
|
||||
Sauce:FadeTo(MTH_FX_MsgFrame, 1, 0.25),
|
||||
Sauce:Pulse(MTH_FX_MsgFrame, 1.22, 0.5),
|
||||
}),
|
||||
Sauce:FadeOut(MTH_FX_MsgFrame, 0.6, "inQuad", { delay = fadeDelay }),
|
||||
})
|
||||
end
|
||||
|
||||
MTH_FX_Container:Show()
|
||||
local colorCount = table.getn(MTH_FX_Colors)
|
||||
@@ -371,34 +451,70 @@ function MTH_Celebrate(title, subtitle)
|
||||
local i = 1
|
||||
while i <= MTH_FX_NUM_SPARKS do
|
||||
local spark = MTH_FX_Sparks[i]
|
||||
spark:SetTexture("Interface\\Cooldown\\star4")
|
||||
spark:SetTexCoord(0, 1, 0, 1)
|
||||
local startX, startY = 0, 0
|
||||
local delay, gravity = 0, 300
|
||||
local angle = (i / MTH_FX_NUM_SPARKS) * 2 * math.pi + (math.random() - 0.5) * 0.4
|
||||
local speed = 150 + math.random() * 130
|
||||
local vx = math.cos(angle) * speed
|
||||
local vy = math.sin(angle) * speed + 70
|
||||
local dur = 0.9 + math.random() * 0.4
|
||||
|
||||
if style == "victory" then
|
||||
startX = ((i - math.floor(i / 2) * 2) == 0) and -130 or 130
|
||||
speed = 100 + math.random() * 100
|
||||
vx = math.cos(angle) * speed
|
||||
vy = math.sin(angle) * speed + 105
|
||||
elseif style == "starfall" then
|
||||
startX = -230 + math.random() * 460
|
||||
startY = 200 + math.random() * 100
|
||||
vx = -35 + math.random() * 70
|
||||
vy = -10 + math.random() * 25
|
||||
gravity = 235
|
||||
dur = 1.1 + math.random() * 0.5
|
||||
elseif style == "fanfare" then
|
||||
delay = ((i - 1) - math.floor((i - 1) / 3) * 3) * 0.18
|
||||
speed = 110 + math.random() * 90
|
||||
vx = math.cos(angle) * speed
|
||||
vy = math.sin(angle) * speed + 90
|
||||
elseif style == "comet" then
|
||||
if i == 1 then
|
||||
startY = -180
|
||||
vx, vy, gravity, dur = 0, 430, 0, 0.52
|
||||
else
|
||||
delay = 0.5
|
||||
startY = 45
|
||||
speed = 130 + math.random() * 120
|
||||
vx = math.cos(angle) * speed
|
||||
vy = math.sin(angle) * speed + 70
|
||||
end
|
||||
end
|
||||
|
||||
colorIdx = colorIdx + 1
|
||||
if colorIdx > colorCount then colorIdx = 1 end
|
||||
local color = MTH_FX_Colors[colorIdx]
|
||||
spark:SetVertexColor(color[1], color[2], color[3])
|
||||
spark:SetAlpha(1)
|
||||
spark:SetAlpha(delay > 0 and 0 or 1)
|
||||
spark:Show()
|
||||
|
||||
Sauce:Stop(spark)
|
||||
Sauce:Group({
|
||||
Sauce:Tween(spark, {
|
||||
type = "mth_ballistic",
|
||||
from = { 0, 0 },
|
||||
from = { startX, startY },
|
||||
duration = dur,
|
||||
easing = "linear",
|
||||
delay = delay,
|
||||
anchor = MTH_FX_Container,
|
||||
dur = dur,
|
||||
vx = vx,
|
||||
vy = vy,
|
||||
gravity = 300,
|
||||
gravity = gravity,
|
||||
}),
|
||||
Sauce:FadeTo(spark, 1, 0.05, "outQuad", { delay = delay }),
|
||||
Sauce:FadeTo(spark, 0, 0.45, "inQuad", {
|
||||
delay = dur - 0.45,
|
||||
delay = delay + dur - 0.45,
|
||||
onFinish = function()
|
||||
spark:Hide()
|
||||
end,
|
||||
@@ -408,6 +524,203 @@ function MTH_Celebrate(title, subtitle)
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_FX_ShowPreviewBanner(title, subtitle, r, g, b)
|
||||
MTH_FX_TitleFS:SetTextColor(r, g, b)
|
||||
MTH_FX_TitleFS:SetText(title)
|
||||
MTH_FX_SubFS:SetText(subtitle)
|
||||
Sauce:Stop(MTH_FX_MsgFrame)
|
||||
MTH_FX_MsgFrame:SetAlpha(0)
|
||||
MTH_FX_MsgFrame:Show()
|
||||
Sauce:Sequence({
|
||||
Sauce:Group({
|
||||
Sauce:FadeTo(MTH_FX_MsgFrame, 1, 0.18),
|
||||
Sauce:Pulse(MTH_FX_MsgFrame, 1.18, 0.45),
|
||||
}),
|
||||
Sauce:FadeOut(MTH_FX_MsgFrame, 0.55, "inQuad", { delay = 2.1 }),
|
||||
})
|
||||
end
|
||||
|
||||
function MTH_CelebrateConfetti(title, subtitle)
|
||||
if not Sauce or not MTH_FX_EnsureFrames() then return end
|
||||
MTH_FX_ShowPreviewBanner(title or "Celebration!", subtitle or "Confetti", 1, 0.82, 0.15)
|
||||
MTH_FX_Container:Show()
|
||||
local count = table.getn(MTH_FX_Colors)
|
||||
local i = 1
|
||||
while i <= MTH_FX_NUM_CONFETTI do
|
||||
local bit = MTH_FX_Confetti[i]
|
||||
local color = MTH_FX_Colors[((i - 1) - math.floor((i - 1) / count) * count) + 1]
|
||||
local size = 3 + math.random() * 5
|
||||
local angle = math.random() * 2 * math.pi
|
||||
local speed = 90 + math.random() * 210
|
||||
local dur = 1.25 + math.random() * 0.7
|
||||
bit:SetWidth(size)
|
||||
bit:SetHeight(size * 1.6)
|
||||
bit:SetVertexColor(color[1], color[2], color[3])
|
||||
bit:SetAlpha(1)
|
||||
bit:Show()
|
||||
Sauce:Stop(bit)
|
||||
Sauce:Group({
|
||||
Sauce:Tween(bit, {
|
||||
type = "mth_ballistic", from = { 0, 0 }, duration = dur,
|
||||
easing = "linear", anchor = MTH_FX_Container, dur = dur,
|
||||
vx = math.cos(angle) * speed, vy = math.sin(angle) * speed + 100, gravity = 255,
|
||||
}),
|
||||
Sauce:FadeTo(bit, 0, 0.35, "inQuad", {
|
||||
delay = dur - 0.35, onFinish = function() bit:Hide() end,
|
||||
}),
|
||||
})
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_CelebrateBlackHole(title, subtitle)
|
||||
if not Sauce or not MTH_FX_EnsureFrames() then return end
|
||||
MTH_FX_ShowPreviewBanner(title or "Gravity Well", subtitle or "Black Hole", 0.68, 0.45, 1.00)
|
||||
MTH_FX_Container:Show()
|
||||
local colors = { { 0.45, 0.25, 1 }, { 0.30, 0.50, 1 }, { 0.80, 0.55, 1 } }
|
||||
local i = 1
|
||||
while i <= MTH_FX_NUM_BLACKHOLE do
|
||||
local particle = MTH_FX_BlackHole[i]
|
||||
local color = colors[((i - 1) - math.floor((i - 1) / 3) * 3) + 1]
|
||||
local startAngle = math.random() * 2 * math.pi
|
||||
local radius = 95 + math.random() * 145
|
||||
local duration = 1.5 + math.random() * 1.0
|
||||
local size = 7 + math.random() * 8
|
||||
particle:SetWidth(size)
|
||||
particle:SetHeight(size)
|
||||
particle:SetVertexColor(color[1], color[2], color[3])
|
||||
particle:SetAlpha(1)
|
||||
particle:Show()
|
||||
Sauce:Stop(particle)
|
||||
Sauce:Group({
|
||||
Sauce:Tween(particle, {
|
||||
type = "mth_spiral", from = 0, to = 1, duration = duration,
|
||||
easing = "inQuad", anchor = MTH_FX_Container, cx = 0, cy = 0,
|
||||
radius = radius, startAngle = startAngle, rotations = 1.5 + math.random() * 2.0,
|
||||
}),
|
||||
Sauce:Size(particle, { size, size }, { 2, 2 }, duration, "inQuad"),
|
||||
Sauce:FadeTo(particle, 0, duration * 0.3, "inQuad", {
|
||||
delay = duration * 0.7, onFinish = function() particle:Hide() end,
|
||||
}),
|
||||
})
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_CelebrateShockwave(title, subtitle)
|
||||
if not Sauce or not MTH_FX_EnsureFrames() then return end
|
||||
MTH_FX_ShowPreviewBanner(title or "Impact!", subtitle or "Shockwave", 1, 0.62, 0.15)
|
||||
MTH_FX_Container:Show()
|
||||
local dotsPerWave = 16
|
||||
local i = 1
|
||||
while i <= MTH_FX_NUM_CONFETTI do
|
||||
local dot = MTH_FX_Confetti[i]
|
||||
local wave = math.floor((i - 1) / dotsPerWave) + 1
|
||||
local pos = (i - 1) - math.floor((i - 1) / dotsPerWave) * dotsPerWave
|
||||
local angle = pos / dotsPerWave * 2 * math.pi
|
||||
local duration = 1.1 + wave * 0.18
|
||||
local radius = 115 + wave * 52
|
||||
local delay = (wave - 1) * 0.18
|
||||
local intensity = 1 - (wave - 1) * 0.20
|
||||
dot:SetWidth(5)
|
||||
dot:SetHeight(5)
|
||||
dot:SetVertexColor(1 * intensity, 0.65 * intensity, 0.18 * intensity)
|
||||
dot:SetAlpha(0)
|
||||
dot:Show()
|
||||
Sauce:Stop(dot)
|
||||
Sauce:Group({
|
||||
Sauce:Tween(dot, {
|
||||
type = "mth_ballistic", from = { 0, 0 }, duration = duration,
|
||||
easing = "outQuad", delay = delay, anchor = MTH_FX_Container, dur = duration,
|
||||
vx = math.cos(angle) * radius / duration, vy = math.sin(angle) * radius / duration, gravity = 0,
|
||||
}),
|
||||
Sauce:FadeTo(dot, 1, 0.04, "outQuad", { delay = delay }),
|
||||
Sauce:FadeTo(dot, 0, 0.4, "inQuad", {
|
||||
delay = delay + duration - 0.4, onFinish = function() dot:Hide() end,
|
||||
}),
|
||||
})
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_CelebrateShatter(title, subtitle)
|
||||
if not Sauce or not MTH_FX_EnsureFrames() then return end
|
||||
MTH_FX_ShowPreviewBanner(title or "Shattered!", subtitle or "Shatter", 0.70, 0.85, 1.00)
|
||||
MTH_FX_Container:Show()
|
||||
local grid = 4
|
||||
local iconSize = 80
|
||||
local fragmentSize = iconSize / grid
|
||||
local i = 1
|
||||
while i <= MTH_FX_NUM_SPARKS do
|
||||
local spark = MTH_FX_Sparks[i]
|
||||
if i <= grid * grid then
|
||||
local row = math.floor((i - 1) / grid)
|
||||
local col = (i - 1) - row * grid
|
||||
local x = (col - grid / 2 + 0.5) * fragmentSize
|
||||
local y = ((grid - 1 - row) - grid / 2 + 0.5) * fragmentSize
|
||||
local duration = 1.1 + math.random() * 0.45
|
||||
spark:SetTexture("Interface\\Icons\\Ability_Hunter_SniperShot")
|
||||
spark:SetTexCoord(col / grid, (col + 1) / grid, row / grid, (row + 1) / grid)
|
||||
spark:SetWidth(fragmentSize)
|
||||
spark:SetHeight(fragmentSize)
|
||||
spark:SetVertexColor(1, 1, 1)
|
||||
spark:SetAlpha(1)
|
||||
spark:Show()
|
||||
Sauce:Stop(spark)
|
||||
Sauce:Group({
|
||||
Sauce:Tween(spark, {
|
||||
type = "mth_ballistic", from = { x, y }, duration = duration,
|
||||
easing = "linear", anchor = MTH_FX_Container, dur = duration,
|
||||
vx = x * 4 + (math.random() - 0.5) * 90,
|
||||
vy = y * 4 + 95 + math.random() * 80, gravity = 265,
|
||||
}),
|
||||
Sauce:FadeTo(spark, 0, 0.35, "inQuad", {
|
||||
delay = duration - 0.35, onFinish = function() spark:Hide() end,
|
||||
}),
|
||||
})
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_CelebratePreview(style)
|
||||
style = string.lower(tostring(style or "firework"))
|
||||
if style == "all" then
|
||||
if not Sauce then return end
|
||||
local styles = { "firework", "blackhole", "shockwave", "shatter" }
|
||||
local steps = {}
|
||||
local i = 1
|
||||
while i <= table.getn(styles) do
|
||||
local previewStyle = styles[i]
|
||||
table.insert(steps, Sauce:Run(function()
|
||||
MTH_CelebratePreview(previewStyle)
|
||||
end, { defer = true }))
|
||||
table.insert(steps, Sauce:Delay(3.7))
|
||||
i = i + 1
|
||||
end
|
||||
Sauce:Sequence(steps)
|
||||
return true
|
||||
end
|
||||
|
||||
if style == "blackhole" then
|
||||
MTH_CelebrateBlackHole("Celebration Preview", "Black Hole")
|
||||
return true
|
||||
elseif style == "shockwave" then
|
||||
MTH_CelebrateShockwave("Celebration Preview", "Shockwave")
|
||||
return true
|
||||
elseif style == "shatter" then
|
||||
MTH_CelebrateShatter("Celebration Preview", "Shatter")
|
||||
return true
|
||||
end
|
||||
|
||||
local labels = {
|
||||
firework = "Firework Burst",
|
||||
}
|
||||
if not labels[style] then return false end
|
||||
MTH_Celebrate("Celebration Preview", labels[style], style)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Convenience wrapper used when the hunter's pet learns a new ability/rank.
|
||||
function MTH_CelebratePetAbility(label)
|
||||
if not MTH_FX_IsEnabled("pet.celebrate") then
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end
|
||||
|
||||
local trainerColumns = {
|
||||
{ x = 28, width = 150, align = "LEFT" },
|
||||
{ x = 180, width = 62, align = "LEFT" },
|
||||
{ x = 244, width = 43, align = "LEFT" },
|
||||
{ x = 289, width = 80, align = "LEFT" },
|
||||
{ x = 371, width = 105, align = "LEFT" },
|
||||
{ x = 478, width = 74, align = "LEFT" },
|
||||
}
|
||||
|
||||
MTH_HUNTERBOOK_TABS.hunterspells = {
|
||||
headerLabel = "Hunter Spells",
|
||||
columnLabels = { "Spell", "Rank", "Level", "Price", "Branch", "Status" },
|
||||
columnLayout = trainerColumns,
|
||||
}
|
||||
|
||||
MTH_HUNTERBOOK_TABS.petspells = {
|
||||
headerLabel = "Pet Spells",
|
||||
columnLabels = { "Spell", "Rank", "Level", "Price", "Branch", "Status" },
|
||||
columnLayout = trainerColumns,
|
||||
}
|
||||
|
||||
function MTH_BOOKTAB_IsTrainerSpellMode(mode)
|
||||
return mode == "hunterspells" or mode == "petspells"
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_GetTrainerKind()
|
||||
if MTH_BOOK_STATE and MTH_BOOK_STATE.mode == "petspells" then return "pet" end
|
||||
return "hunter"
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_FormatTrainerCost(copper)
|
||||
local value = tonumber(copper) or 0
|
||||
local gold = math.floor(value / 10000)
|
||||
value = value - gold * 10000
|
||||
local silver = math.floor(value / 100)
|
||||
local bronze = value - silver * 100
|
||||
local parts = {}
|
||||
if gold > 0 then table.insert(parts, "|cffffd100" .. tostring(gold) .. "g|r") end
|
||||
if silver > 0 or gold > 0 then table.insert(parts, "|cffc7c7cf" .. tostring(silver) .. "s|r") end
|
||||
table.insert(parts, "|cffeda55f" .. tostring(bronze) .. "c|r")
|
||||
return table.concat(parts, " ")
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_GetTrainerDisplayCost(copper)
|
||||
if type(MTH_TrainerPlan_GetPrice) == "function" then
|
||||
return MTH_TrainerPlan_GetPrice({ trainerCost = copper }, MTH_BOOK_STATE and MTH_BOOK_STATE.trainerHonoredDiscount)
|
||||
end
|
||||
return tonumber(copper) or 0
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_GetTrainerSpellStatus(row)
|
||||
local kind = MTH_BOOKTAB_GetTrainerKind()
|
||||
local playerLevel = MTH_BOOK_GetPlayerLevelValue and MTH_BOOK_GetPlayerLevelValue() or nil
|
||||
local status = type(MTH_TrainerPlan_GetStatus) == "function" and MTH_TrainerPlan_GetStatus(kind, row, playerLevel) or "notyet"
|
||||
if status == "known" then return "Known", "|cff40ff40" end
|
||||
if status == "notlearned" then return "Not learned", "|cffffa500" end
|
||||
return "Not yet learnable", "|cffff4040"
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_BuildTrainerSpellResults()
|
||||
local results = {}
|
||||
local kind = MTH_BOOKTAB_GetTrainerKind()
|
||||
local plan = type(MTH_TrainerPlan_Build) == "function" and MTH_TrainerPlan_Build(kind, {
|
||||
playerLevel = MTH_BOOK_GetPlayerLevelValue and MTH_BOOK_GetPlayerLevelValue() or nil,
|
||||
honored = MTH_BOOK_STATE and MTH_BOOK_STATE.trainerHonoredDiscount,
|
||||
}) or nil
|
||||
if type(plan) ~= "table" or type(plan.rows) ~= "table" then return results end
|
||||
local search = MTH_BOOK_SafeLower(MTH_BOOK_STATE.search or "")
|
||||
for _, planned in ipairs(plan.rows) do
|
||||
local row = planned.row
|
||||
local level = tonumber(row.requiredLevel) or 0
|
||||
local haystack = tostring(row.name or "") .. " " .. tostring(row.rank or "") .. " " .. tostring(row.section or "") .. " " .. tostring(row.description or "")
|
||||
if type(row.prerequisites) == "table" then
|
||||
for _, prerequisite in ipairs(row.prerequisites) do
|
||||
haystack = haystack .. " " .. tostring(prerequisite)
|
||||
end
|
||||
end
|
||||
haystack = MTH_BOOK_SafeLower(haystack)
|
||||
if (search == "" or string.find(haystack, search, 1, true) ~= nil)
|
||||
and (not MTH_BOOK_STATE.minLevel or level >= MTH_BOOK_STATE.minLevel)
|
||||
and (not MTH_BOOK_STATE.maxLevel or level <= MTH_BOOK_STATE.maxLevel)
|
||||
and (type(MTH_TrainerPlan_MatchesQuick) ~= "function" or MTH_TrainerPlan_MatchesQuick(planned.status, MTH_BOOK_STATE.quick))
|
||||
then
|
||||
table.insert(results, row)
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_UpdateTrainerPlanPanel(detail)
|
||||
local parent = detail and detail:GetParent()
|
||||
if not parent then return end
|
||||
if not parent.mthTrainerPlan then
|
||||
local panel = CreateFrame("Frame", nil, parent)
|
||||
panel:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, -238)
|
||||
panel:SetWidth(136)
|
||||
panel:SetHeight(128)
|
||||
panel:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 12, insets = { left = 3, right = 3, top = 3, bottom = 3 } })
|
||||
panel:SetBackdropColor(0.06, 0.06, 0.06, 1)
|
||||
panel:SetBackdropBorderColor(0.28, 0.28, 0.28, 1)
|
||||
panel.text = panel:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
panel.text:SetPoint("TOPLEFT", panel, "TOPLEFT", 7, -7)
|
||||
panel.text:SetWidth(122)
|
||||
panel.text:SetJustifyH("LEFT")
|
||||
panel.text:SetJustifyV("TOP")
|
||||
parent.mthTrainerPlan = panel
|
||||
end
|
||||
local plan = type(MTH_TrainerPlan_Build) == "function" and MTH_TrainerPlan_Build(MTH_BOOKTAB_GetTrainerKind(), {
|
||||
playerLevel = MTH_BOOK_GetPlayerLevelValue and MTH_BOOK_GetPlayerLevelValue() or nil,
|
||||
honored = MTH_BOOK_STATE.trainerHonoredDiscount,
|
||||
}) or nil
|
||||
if not plan then return end
|
||||
local nextLevel = plan.nextLevel.level
|
||||
local nextLabel = "Next"
|
||||
if nextLevel then nextLabel = "At level " .. tostring(nextLevel) end
|
||||
local function formatBlock(label, summary)
|
||||
return "|cffffffff" .. label .. "|r\n"
|
||||
.. "|cff4da6ff" .. tostring(summary.count) .. "|r |cff9a9a9aspells|r |cffffffff| |r"
|
||||
.. MTH_BOOKTAB_FormatTrainerCost(summary.cost)
|
||||
end
|
||||
parent.mthTrainerPlan.text:SetText("|cffffd100Training Plan|r\n"
|
||||
.. formatBlock("Now", plan.now)
|
||||
.. "\n\n" .. formatBlock(nextLabel, plan.nextLevel)
|
||||
.. "\n\n" .. formatBlock("Remaining", plan.remaining))
|
||||
parent.mthTrainerPlan:Show()
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_GetTrainerSpellTotal()
|
||||
local catalog = MTH_DS_TrainerCatalog
|
||||
local rows = catalog and catalog[MTH_BOOKTAB_GetTrainerKind()] or nil
|
||||
return type(rows) == "table" and table.getn(rows) or 0
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_TrainerSpellSort(a, b)
|
||||
local al = tonumber(a and a.requiredLevel) or 0
|
||||
local bl = tonumber(b and b.requiredLevel) or 0
|
||||
if al ~= bl then return al < bl end
|
||||
local an = MTH_BOOK_SafeLower(a and a.name)
|
||||
local bn = MTH_BOOK_SafeLower(b and b.name)
|
||||
if an ~= bn then return an < bn end
|
||||
return MTH_BOOK_SafeLower(a and a.rank) < MTH_BOOK_SafeLower(b and b.rank)
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_GetTrainerSpellSortKey(row, col)
|
||||
if col == 1 then return MTH_BOOK_SafeLower(row and row.name) end
|
||||
if col == 2 then return MTH_BOOK_SafeLower(row and row.rank) end
|
||||
if col == 3 then return tonumber(row and row.requiredLevel) or 0 end
|
||||
if col == 4 then return tonumber(row and row.trainerCost) or 0 end
|
||||
if col == 5 then return MTH_BOOK_SafeLower(row and row.section) end
|
||||
if col == 6 then
|
||||
local status = MTH_BOOKTAB_GetTrainerSpellStatus(row)
|
||||
return MTH_BOOK_SafeLower(status)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_GetTrainerSpellRowValues(row)
|
||||
local requiredLevel = tonumber(row and row.requiredLevel) or 0
|
||||
local playerLevel = MTH_BOOK_GetPlayerLevelValue and MTH_BOOK_GetPlayerLevelValue() or nil
|
||||
local levelColor = (playerLevel and playerLevel >= requiredLevel) and "|cff40ff40" or "|cffff4040"
|
||||
local status, statusColor = MTH_BOOKTAB_GetTrainerSpellStatus(row)
|
||||
return {
|
||||
tostring(row and row.name or "-"),
|
||||
tostring(row and row.rank or "-"),
|
||||
levelColor .. tostring(row and row.requiredLevel or "-") .. "|r",
|
||||
MTH_BOOKTAB_FormatTrainerCost(MTH_BOOKTAB_GetTrainerDisplayCost(row and row.trainerCost)),
|
||||
tostring(row and row.section or "-"),
|
||||
statusColor .. status .. "|r",
|
||||
}
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_UpdateTrainerSpellDetail(detail, selected)
|
||||
if not MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE and MTH_BOOK_STATE.mode) then return false end
|
||||
MTH_BOOKTAB_UpdateTrainerPlanPanel(detail)
|
||||
if not selected then
|
||||
MTH_BOOK_SetDetailText(detail, "|cFFFFD100Trainer Spells|r\n\nSelect a spell rank to inspect its trainer level, undiscounted cost, and prerequisites.")
|
||||
return true
|
||||
end
|
||||
local lines = {}
|
||||
table.insert(lines, "|cffffff00" .. tostring(selected.name or "Unknown") .. "|r")
|
||||
if selected.rank and selected.rank ~= "" then table.insert(lines, "|cffaaaaaa" .. tostring(selected.rank) .. "|r") end
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, "|cff9a9a9aBranch:|r |cffffffff" .. tostring(selected.section or "-") .. "|r")
|
||||
local requiredLevel = tonumber(selected.requiredLevel) or 0
|
||||
local playerLevel = MTH_BOOK_GetPlayerLevelValue and MTH_BOOK_GetPlayerLevelValue() or nil
|
||||
local levelColor = (playerLevel and playerLevel >= requiredLevel) and "|cff40ff40" or "|cffff4040"
|
||||
table.insert(lines, "|cff9a9a9aRequired Level:|r " .. levelColor .. tostring(selected.requiredLevel or "-") .. "|r")
|
||||
local status, statusColor = MTH_BOOKTAB_GetTrainerSpellStatus(selected)
|
||||
table.insert(lines, "|cff9a9a9aStatus:|r " .. statusColor .. status .. "|r")
|
||||
table.insert(lines, "|cff9a9a9aBase Price:|r " .. MTH_BOOKTAB_FormatTrainerCost(selected.trainerCost))
|
||||
table.insert(lines, "|cff9a9a9aHonored Price:|r " .. MTH_BOOKTAB_FormatTrainerCost(math.floor((tonumber(selected.trainerCost) or 0) * 0.90)))
|
||||
if type(selected.prerequisites) == "table" and table.getn(selected.prerequisites) > 0 then
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, "|cffffd100Prerequisites|r")
|
||||
for _, prerequisite in ipairs(selected.prerequisites) do
|
||||
table.insert(lines, " " .. tostring(prerequisite))
|
||||
end
|
||||
end
|
||||
if selected.description and selected.description ~= "" then
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, "|cffffd100Description|r")
|
||||
table.insert(lines, "|cffffffff " .. tostring(selected.description) .. "|r")
|
||||
end
|
||||
local detailFrame = detail.mthTrainerDetailParent or detail:GetParent()
|
||||
if not detail.mthTrainerIcon then
|
||||
detail.mthTrainerIcon = detail:CreateTexture(nil, "ARTWORK")
|
||||
detail.mthTrainerIcon:SetWidth(28)
|
||||
detail.mthTrainerIcon:SetHeight(28)
|
||||
end
|
||||
detail.mthTrainerIcon:SetParent(detail)
|
||||
detail.mthTrainerIcon:ClearAllPoints()
|
||||
detail.mthTrainerIcon:SetPoint("TOPLEFT", detail, "TOPLEFT", 0, 0)
|
||||
detail.mthTrainerIcon:SetTexture(MTH_BOOK_ResolveIconPath(selected.icon) or "Interface\\Icons\\INV_Misc_QuestionMark")
|
||||
detail.mthTrainerIcon:Show()
|
||||
if detail.SetTextInsets then detail:SetTextInsets(4, 4, 36, 4) end
|
||||
if not detail.mthTrainerDetailScroll then
|
||||
detail:ClearAllPoints()
|
||||
detail:SetPoint("TOPLEFT", detailFrame, "TOPLEFT", 8, -42)
|
||||
detail:SetWidth(detailFrame:GetWidth() - 16)
|
||||
end
|
||||
MTH_BOOK_SetDetailText(detail, table.concat(lines, "\n"))
|
||||
return true
|
||||
end
|
||||
+356
-38
@@ -147,6 +147,7 @@ local MTH_BOOK_STATE = {
|
||||
familyStatsPercent = false,
|
||||
petHideNoAbilities = true,
|
||||
petHideUnknown = true,
|
||||
trainerHonoredDiscount = false,
|
||||
forcedBeastId = nil,
|
||||
page = 1,
|
||||
pageSize = 16,
|
||||
@@ -170,6 +171,8 @@ local MTH_BOOK_STATE = {
|
||||
npcs = { col = nil, asc = true },
|
||||
stable = { col = nil, asc = true },
|
||||
pethistory = { col = 5, asc = false },
|
||||
hunterspells = { col = 3, asc = true },
|
||||
petspells = { col = 3, asc = true },
|
||||
},
|
||||
sliderDragging = false,
|
||||
}
|
||||
@@ -308,6 +311,17 @@ local MTH_BOOK_CONTENT_LAYOUT_ITEMS = {
|
||||
detailH = 366,
|
||||
}
|
||||
|
||||
local MTH_BOOK_CONTENT_LAYOUT_TRAINERS = {
|
||||
listX = 16,
|
||||
listY = -146,
|
||||
listW = 564,
|
||||
listH = 374,
|
||||
detailX = 608,
|
||||
detailY = -146,
|
||||
detailW = 136,
|
||||
detailH = 230,
|
||||
}
|
||||
|
||||
local MTH_BOOK_CONTENT_LAYOUT_PETABILITIES = {
|
||||
listX = 16,
|
||||
listY = -106,
|
||||
@@ -363,7 +377,52 @@ local MTH_BOOK_CONTENT_LAYOUT_HISTORY = {
|
||||
detailH = 446,
|
||||
}
|
||||
|
||||
local function MTH_BOOK_ApplyContentLayoutForMode()
|
||||
function MTH_BOOK_ConfigureTrainerDetailScroll(isTrainerMode, detailParent, detailText)
|
||||
if not (detailParent and detailText) then return end
|
||||
if isTrainerMode then
|
||||
if not detailParent.mthTrainerDetailScroll then
|
||||
local scroll = CreateFrame("ScrollFrame", "MTH_BOOK_TrainerDetailScroll", detailParent, "UIPanelScrollFrameTemplate")
|
||||
scroll:SetPoint("TOPLEFT", detailParent, "TOPLEFT", 4, -4)
|
||||
scroll:SetPoint("BOTTOMRIGHT", detailParent, "BOTTOMRIGHT", -24, 4)
|
||||
detailParent.mthTrainerDetailScroll = scroll
|
||||
detailText:EnableMouseWheel(true)
|
||||
detailText:SetScript("OnMouseWheel", function()
|
||||
local current = scroll:GetVerticalScroll()
|
||||
if arg1 and arg1 > 0 then
|
||||
scroll:SetVerticalScroll(current - 24)
|
||||
else
|
||||
scroll:SetVerticalScroll(current + 24)
|
||||
end
|
||||
end)
|
||||
end
|
||||
local scroll = detailParent.mthTrainerDetailScroll
|
||||
detailText.mthTrainerDetailParent = detailParent
|
||||
detailText.mthTrainerDetailScroll = scroll
|
||||
if detailText:GetParent() ~= scroll then detailText:SetParent(scroll) end
|
||||
detailText:ClearAllPoints()
|
||||
detailText:SetPoint("TOPLEFT", scroll, "TOPLEFT", 0, 0)
|
||||
scroll:SetScrollChild(detailText)
|
||||
scroll:SetWidth(detailParent:GetWidth() - 28)
|
||||
scroll:SetHeight(detailParent:GetHeight() - 8)
|
||||
detailText:SetWidth(detailParent:GetWidth() - 28)
|
||||
detailText:SetHeight(scroll:GetHeight())
|
||||
scroll:Show()
|
||||
else
|
||||
local scroll = detailParent.mthTrainerDetailScroll
|
||||
if scroll then scroll:Hide() end
|
||||
if detailText.mthTrainerDetailParent then
|
||||
detailText:SetParent(detailText.mthTrainerDetailParent)
|
||||
detailText:ClearAllPoints()
|
||||
detailText:SetPoint("TOPLEFT", detailParent, "TOPLEFT", 6, -6)
|
||||
detailText:SetWidth(detailParent:GetWidth() - 12)
|
||||
detailText:SetHeight(detailParent:GetHeight() - 12)
|
||||
if detailText.SetTextInsets then detailText:SetTextInsets(4, 4, 4, 4) end
|
||||
detailText.mthTrainerDetailScroll = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_BOOK_ApplyContentLayoutForMode()
|
||||
local listParent = getglobal("MTH_BOOK_ListBackdrop")
|
||||
local detailParent = getglobal("MTH_BOOK_DetailBackdrop")
|
||||
local detailText = getglobal("MTH_BOOK_DetailBackdropDetailText")
|
||||
@@ -374,6 +433,9 @@ local function MTH_BOOK_ApplyContentLayoutForMode()
|
||||
local layout = MTH_BOOK_CONTENT_LAYOUT_DEFAULT
|
||||
if MTH_BOOK_STATE.mode == "petabilities" then
|
||||
layout = MTH_BOOK_CONTENT_LAYOUT_PETABILITIES
|
||||
elseif type(MTH_BOOKTAB_IsTrainerSpellMode) == "function"
|
||||
and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
layout = MTH_BOOK_CONTENT_LAYOUT_TRAINERS
|
||||
elseif MTH_BOOK_IsItemMode() then
|
||||
layout = MTH_BOOK_CONTENT_LAYOUT_ITEMS
|
||||
elseif MTH_BOOK_STATE.mode == "npcs" then
|
||||
@@ -411,6 +473,9 @@ local function MTH_BOOK_ApplyContentLayoutForMode()
|
||||
if listSlider then
|
||||
listSlider:SetHeight(sliderHeight)
|
||||
end
|
||||
local isTrainerMode = type(MTH_BOOKTAB_IsTrainerSpellMode) == "function"
|
||||
and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode)
|
||||
MTH_BOOK_ConfigureTrainerDetailScroll(isTrainerMode, detailParent, detailText)
|
||||
|
||||
if MTH_BOOK_STATE.petUI then
|
||||
local paneHeight = layout.listH - 8
|
||||
@@ -477,6 +542,45 @@ local function MTH_BOOK_DropdownCreateInfo()
|
||||
return {}
|
||||
end
|
||||
|
||||
function MTH_BOOK_EnsureTrainerStatusDropdown()
|
||||
local dropdown = getglobal("MTH_BOOK_TrainerStatusDropdown")
|
||||
if not dropdown then
|
||||
local filter = getglobal("MTH_BOOK_Filter1")
|
||||
local parent = filter and filter:GetParent()
|
||||
if not parent then return nil end
|
||||
dropdown = CreateFrame("Frame", "MTH_BOOK_TrainerStatusDropdown", parent, "UIDropDownMenuTemplate")
|
||||
dropdown:SetPoint("TOPLEFT", parent, "TOPLEFT", 150, -116)
|
||||
UIDropDownMenu_Initialize(dropdown, function()
|
||||
local options = {
|
||||
{ value = "all", label = "All" },
|
||||
{ value = "known", label = "Known" },
|
||||
{ value = "notlearned", label = "Not learned" },
|
||||
{ value = "notyet", label = "Not yet learnable" },
|
||||
}
|
||||
for i = 1, table.getn(options) do
|
||||
local option = options[i]
|
||||
local value = option.value
|
||||
local label = option.label
|
||||
local info = MTH_BOOK_DropdownCreateInfo()
|
||||
info.text = label
|
||||
info.checked = MTH_BOOK_STATE.quick == value
|
||||
info.func = function()
|
||||
MTH_BOOK_STATE.quick = value
|
||||
MTH_BOOK_STATE.page = 1
|
||||
MTH_BOOK_STATE.selectedEntry = nil
|
||||
MTH_BOOK_DropdownSetText(dropdown, label)
|
||||
MTH_BOOK_RefreshFilter()
|
||||
end
|
||||
UIDropDownMenu_AddButton(info)
|
||||
end
|
||||
end)
|
||||
UIDropDownMenu_SetWidth(136, dropdown)
|
||||
UIDropDownMenu_JustifyText("LEFT", dropdown)
|
||||
dropdown:Hide()
|
||||
end
|
||||
return dropdown
|
||||
end
|
||||
|
||||
local function MTH_BOOK_SetSliderFromCursor(slider)
|
||||
if not slider then return end
|
||||
local minValue, maxValue = slider:GetMinMaxValues()
|
||||
@@ -1459,6 +1563,12 @@ local function MTH_BOOK_GetSortKey(entry, col)
|
||||
if col == 6 then return MTH_BOOK_SafeLower(tostring(row.abandonReason or "")) end
|
||||
end
|
||||
|
||||
if type(MTH_BOOKTAB_IsTrainerSpellMode) == "function"
|
||||
and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
return type(MTH_BOOKTAB_GetTrainerSpellSortKey) == "function"
|
||||
and MTH_BOOKTAB_GetTrainerSpellSortKey(entry, col) or nil
|
||||
end
|
||||
|
||||
local items = MTH_BOOK_GetItemsTable()
|
||||
local item = items and items[entry]
|
||||
if not item then return nil end
|
||||
@@ -1514,6 +1624,10 @@ local function MTH_BOOK_DefaultCompare(a, b)
|
||||
if MTH_BOOK_STATE.mode == "npcs" then return MTH_BOOK_NPCSort(a, b) end
|
||||
if MTH_BOOK_STATE.mode == "stable" then return MTH_BOOK_StableSort(a, b) end
|
||||
if MTH_BOOK_STATE.mode == "pethistory" then return MTH_BOOK_PetHistorySort(a, b) end
|
||||
if type(MTH_BOOKTAB_IsTrainerSpellMode) == "function"
|
||||
and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
return type(MTH_BOOKTAB_TrainerSpellSort) == "function" and MTH_BOOKTAB_TrainerSpellSort(a, b) or false
|
||||
end
|
||||
return MTH_BOOK_ItemSort(a, b)
|
||||
end
|
||||
|
||||
@@ -1782,6 +1896,15 @@ local function MTH_BOOK_BuildResults()
|
||||
return results
|
||||
end
|
||||
|
||||
if type(MTH_BOOKTAB_IsTrainerSpellMode) == "function"
|
||||
and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
if type(MTH_BOOKTAB_BuildTrainerSpellResults) == "function" then
|
||||
results = MTH_BOOKTAB_BuildTrainerSpellResults()
|
||||
end
|
||||
MTH_BOOK_ApplyActiveSort(results)
|
||||
return results
|
||||
end
|
||||
|
||||
local items = MTH_BOOK_GetItemsTable()
|
||||
if not items then return results end
|
||||
for itemId, item in pairs(items) do
|
||||
@@ -1825,6 +1948,10 @@ local function MTH_BOOK_GetTotalCount()
|
||||
local store = MTH_BOOK_GetPetDatastore()
|
||||
return (type(store) == "table" and type(store.historyById) == "table") and MTH_BOOK_CountMap(store.historyById) or 0
|
||||
end
|
||||
if type(MTH_BOOKTAB_IsTrainerSpellMode) == "function"
|
||||
and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
return type(MTH_BOOKTAB_GetTrainerSpellTotal) == "function" and MTH_BOOKTAB_GetTrainerSpellTotal() or 0
|
||||
end
|
||||
local items = MTH_BOOK_GetItemsTable()
|
||||
return items and MTH_BOOK_CountMap(items) or 0
|
||||
end
|
||||
@@ -2328,7 +2455,9 @@ end
|
||||
|
||||
local function MTH_BOOK_UpdateQuickFilterControls()
|
||||
MTH_BOOK_UpdateCheckboxLayoutByMode()
|
||||
if MTH_BOOK_IsItemMode() then
|
||||
if type(MTH_BOOKTAB_IsTrainerSpellMode) == "function" and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
MTH_BOOK_STATE.pageSize = 17
|
||||
elseif MTH_BOOK_IsItemMode() then
|
||||
MTH_BOOK_STATE.pageSize = 18
|
||||
elseif MTH_BOOK_STATE.mode == "families" then
|
||||
MTH_BOOK_STATE.pageSize = MTH_BOOK_MAX_ROWS
|
||||
@@ -2350,6 +2479,7 @@ local function MTH_BOOK_UpdateQuickFilterControls()
|
||||
local itemSubtypeDropdown = getglobal("MTH_BOOK_ItemSubtypeDropdown")
|
||||
local npcFunctionDropdown = getglobal("MTH_BOOK_NPCFunctionDropdown")
|
||||
local npcZoneDropdown = getglobal("MTH_BOOK_NPCZoneDropdown")
|
||||
local trainerStatusDropdown = MTH_BOOK_EnsureTrainerStatusDropdown()
|
||||
local searchLabel = getglobal("MTH_BOOK_SearchLabel")
|
||||
local minLabel = getglobal("MTH_BOOK_MinLabel")
|
||||
local maxLabel = getglobal("MTH_BOOK_MaxLabel")
|
||||
@@ -2359,6 +2489,7 @@ local function MTH_BOOK_UpdateQuickFilterControls()
|
||||
local hideNoAbilities = getglobal("MTH_BOOK_HideNoAbilities")
|
||||
local hideUnknown = getglobal("MTH_BOOK_HideUnknown")
|
||||
local petOnlyMyLevel = getglobal("MTH_BOOK_PetOnlyMyLevel")
|
||||
local trainerHonoredDiscount = getglobal("MTH_BOOK_TrainerHonoredDiscount")
|
||||
local petInZoneOnly = getglobal("MTH_BOOK_PetInZoneOnly")
|
||||
local npcInZoneOnly = getglobal("MTH_BOOK_NPCInZoneOnly")
|
||||
local showAllOnMapButton = getglobal("MTH_BOOK_ShowAllOnMapButton")
|
||||
@@ -2369,6 +2500,7 @@ local function MTH_BOOK_UpdateQuickFilterControls()
|
||||
local nextButton = getglobal("MTH_BOOK_NextButton")
|
||||
local listSlider = getglobal("MTH_BOOK_ListSlider")
|
||||
local sliderBackdrop = getglobal("MTH_BOOK_ListSliderBackdrop")
|
||||
if trainerStatusDropdown then trainerStatusDropdown:Hide() end
|
||||
|
||||
if MTH_BOOK_STATE.mode == "petabilities" or MTH_BOOK_STATE.mode == "stable" then
|
||||
if stats then stats:Hide() end
|
||||
@@ -2543,6 +2675,59 @@ local function MTH_BOOK_UpdateQuickFilterControls()
|
||||
resetButton:ClearAllPoints()
|
||||
resetButton:SetPoint("TOPLEFT", resetButton:GetParent(), "TOPLEFT", 546, -80)
|
||||
end
|
||||
elseif type(MTH_BOOKTAB_IsTrainerSpellMode) == "function"
|
||||
and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
if searchLabel then searchLabel:Show() end
|
||||
if minLabel then minLabel:Show() end
|
||||
if maxLabel then maxLabel:Show() end
|
||||
if search then search:Show(); search:ClearAllPoints(); search:SetPoint("TOPLEFT", search:GetParent(), "TOPLEFT", 66, -80); search:SetWidth(160) end
|
||||
if minLevel then minLevel:Show(); minLevel:ClearAllPoints(); minLevel:SetPoint("TOPLEFT", minLevel:GetParent(), "TOPLEFT", 302, -80) end
|
||||
if maxLevel then maxLevel:Show(); maxLevel:ClearAllPoints(); maxLevel:SetPoint("TOPLEFT", maxLevel:GetParent(), "TOPLEFT", 414, -80) end
|
||||
if applyButton then applyButton:Show(); applyButton:ClearAllPoints(); applyButton:SetPoint("TOPLEFT", applyButton:GetParent(), "TOPLEFT", 478, -80) end
|
||||
local resetButton = getglobal("MTH_BOOK_ResetButton")
|
||||
if resetButton then resetButton:Show(); resetButton:ClearAllPoints(); resetButton:SetPoint("TOPLEFT", resetButton:GetParent(), "TOPLEFT", 546, -80) end
|
||||
if scanButton then scanButton:Hide() end
|
||||
for i = 1, 6 do
|
||||
local btn = getglobal("MTH_BOOK_Filter" .. i)
|
||||
if btn then btn:Hide() end
|
||||
end
|
||||
if trainerStatusDropdown then
|
||||
local labels = { all = "All", known = "Known", notlearned = "Not learned", notyet = "Not yet learnable" }
|
||||
trainerStatusDropdown:ClearAllPoints()
|
||||
trainerStatusDropdown:SetPoint("TOPLEFT", trainerStatusDropdown:GetParent(), "TOPLEFT", 150, -116)
|
||||
MTH_BOOK_DropdownSetText(trainerStatusDropdown, labels[MTH_BOOK_STATE.quick] or "All")
|
||||
trainerStatusDropdown:Show()
|
||||
end
|
||||
if familyDropdown then familyDropdown:Hide() end
|
||||
if abilityDropdown then abilityDropdown:Hide() end
|
||||
if rankDropdown then rankDropdown:Hide() end
|
||||
if petLearnSourceDropdown then petLearnSourceDropdown:Hide() end
|
||||
if beastZoneLabel then beastZoneLabel:Hide() end
|
||||
if beastContinentDropdown then beastContinentDropdown:Hide() end
|
||||
if beastZoneDropdown then beastZoneDropdown:Hide() end
|
||||
if itemSubtypeDropdown then itemSubtypeDropdown:Hide() end
|
||||
if npcFunctionDropdown then npcFunctionDropdown:Hide() end
|
||||
if npcZoneDropdown then npcZoneDropdown:Hide() end
|
||||
if hideNoAbilities then hideNoAbilities:Hide() end
|
||||
if hideUnknown then hideUnknown:Hide() end
|
||||
if petOnlyMyLevel then petOnlyMyLevel:Hide() end
|
||||
if trainerHonoredDiscount then
|
||||
trainerHonoredDiscount:ClearAllPoints()
|
||||
trainerHonoredDiscount:SetPoint("TOPLEFT", trainerHonoredDiscount:GetParent(), "TOPLEFT", 18, -110)
|
||||
trainerHonoredDiscount:SetChecked(MTH_BOOK_STATE.trainerHonoredDiscount and 1 or nil)
|
||||
local trainerHonoredDiscountText = getglobal("MTH_BOOK_TrainerHonoredDiscountText")
|
||||
if trainerHonoredDiscountText then trainerHonoredDiscountText:SetText("Honored prices") end
|
||||
trainerHonoredDiscount:Show()
|
||||
end
|
||||
if petInZoneOnly then petInZoneOnly:Hide() end
|
||||
if npcInZoneOnly then npcInZoneOnly:Hide() end
|
||||
if showAllOnMapButton then showAllOnMapButton:Hide() end
|
||||
local requireVendor = getglobal("MTH_BOOK_RequireVendor")
|
||||
local requireDrop = getglobal("MTH_BOOK_RequireDrop")
|
||||
local requireObject = getglobal("MTH_BOOK_RequireObject")
|
||||
if requireVendor then requireVendor:Hide() end
|
||||
if requireDrop then requireDrop:Hide() end
|
||||
if requireObject then requireObject:Hide() end
|
||||
elseif MTH_BOOK_STATE.mode == "npcs" then
|
||||
if searchLabel then searchLabel:Show() end
|
||||
if minLabel then minLabel:Hide() end
|
||||
@@ -2768,6 +2953,9 @@ local function MTH_BOOK_UpdateQuickFilterControls()
|
||||
if MTH_BOOK_STATE.mode ~= "petabilities" and not MTH_BOOK_IsItemMode() and petOnlyMyLevel then
|
||||
petOnlyMyLevel:Hide()
|
||||
end
|
||||
if (type(MTH_BOOKTAB_IsTrainerSpellMode) ~= "function" or not MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode)) and trainerHonoredDiscount then
|
||||
trainerHonoredDiscount:Hide()
|
||||
end
|
||||
if MTH_BOOK_STATE.mode ~= "pets" and petInZoneOnly then
|
||||
petInZoneOnly:Hide()
|
||||
end
|
||||
@@ -3270,13 +3458,20 @@ end
|
||||
function MTH_BOOK_SetDetailText(detail, text)
|
||||
if not detail then return end
|
||||
local wrapped = tostring(text or "")
|
||||
if MTH_BOOK_STATE.mode ~= "pethistory" and MTH_BOOK_STATE.mode ~= "pets" and MTH_BOOK_STATE.mode ~= "abilities" then
|
||||
if MTH_BOOK_STATE.mode ~= "pethistory" and MTH_BOOK_STATE.mode ~= "pets" and MTH_BOOK_STATE.mode ~= "abilities"
|
||||
and (type(MTH_BOOKTAB_IsTrainerSpellMode) ~= "function" or not MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode)) then
|
||||
wrapped = MTH_BOOK_WrapDetailText(wrapped)
|
||||
end
|
||||
detail._mthLocking = true
|
||||
detail:SetText(wrapped)
|
||||
detail._mthLockedText = wrapped
|
||||
detail._mthLocking = false
|
||||
local scroll = detail.mthTrainerDetailScroll
|
||||
if scroll then
|
||||
local textHeight = detail.GetStringHeight and detail:GetStringHeight() or 0
|
||||
detail:SetHeight(math.max(scroll:GetHeight(), textHeight + 12))
|
||||
scroll:SetVerticalScroll(0)
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_BOOK_ConfigureReadOnlyEditBox(detail)
|
||||
@@ -3323,6 +3518,9 @@ end
|
||||
function MTH_BOOK_UpdateDetail()
|
||||
local detail = getglobal("MTH_BOOK_DetailBackdropDetailText")
|
||||
if not detail then return end
|
||||
if detail.mthTrainerIcon then detail.mthTrainerIcon:Hide() end
|
||||
local detailParent = detail:GetParent()
|
||||
if detailParent and detailParent.mthTrainerPlan then detailParent.mthTrainerPlan:Hide() end
|
||||
MTH_BOOK_HideDetailTitle()
|
||||
-- Hide model skin preview when not in a beast-list mode
|
||||
if MTH_BOOK_STATE.mode ~= "pets" and MTH_BOOK_STATE.mode ~= "abilities" then
|
||||
@@ -3330,6 +3528,11 @@ function MTH_BOOK_UpdateDetail()
|
||||
end
|
||||
local petTop = MTH_BOOK_STATE.petUI and MTH_BOOK_STATE.petUI.detailTop
|
||||
local petBottom = MTH_BOOK_STATE.petUI and MTH_BOOK_STATE.petUI.detailBottom
|
||||
if type(MTH_BOOKTAB_UpdateTrainerSpellDetail) == "function"
|
||||
and MTH_BOOKTAB_UpdateTrainerSpellDetail(detail, MTH_BOOK_STATE.selectedEntry) then
|
||||
MTH_BOOK_UpdateOpenMapButton()
|
||||
return
|
||||
end
|
||||
if not MTH_BOOK_STATE.selectedEntry then
|
||||
if MTH_BOOK_ModelViewer_Clear then MTH_BOOK_ModelViewer_Clear() end
|
||||
if MTH_BOOK_STATE.mode == "petabilities" then
|
||||
@@ -4031,6 +4234,12 @@ local function MTH_BOOK_ApplyColumnLayout(parent)
|
||||
end
|
||||
|
||||
local function MTH_BOOK_GetRowValues(entry)
|
||||
if type(MTH_BOOKTAB_IsTrainerSpellMode) == "function"
|
||||
and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
return type(MTH_BOOKTAB_GetTrainerSpellRowValues) == "function"
|
||||
and MTH_BOOKTAB_GetTrainerSpellRowValues(entry) or { "", "", "", "", "" }
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.mode == "pets" then
|
||||
-- MetaHunt DB entry
|
||||
local beast = MTH_DS_Beasts and MTH_DS_Beasts[entry]
|
||||
@@ -4317,6 +4526,9 @@ local function MTH_BOOK_AssignEntry(btn, entry)
|
||||
btn.entry = entry
|
||||
elseif MTH_BOOK_STATE.mode == "pethistory" then
|
||||
btn.entry = { petId = entry }
|
||||
elseif type(MTH_BOOKTAB_IsTrainerSpellMode) == "function"
|
||||
and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
btn.entry = entry
|
||||
else
|
||||
btn.entry = { itemId = entry }
|
||||
end
|
||||
@@ -4482,11 +4694,18 @@ MTH_BOOK_UpdateResults = function()
|
||||
btn.itemIcon:SetTexture(nil)
|
||||
btn.itemIcon:Hide()
|
||||
end
|
||||
elseif type(MTH_BOOKTAB_IsTrainerSpellMode) == "function" and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
local iconPath = MTH_BOOK_ResolveIconPath(entry.icon)
|
||||
btn.itemIcon:ClearAllPoints()
|
||||
btn.itemIcon:SetPoint("LEFT", btn, "LEFT", 8, 0)
|
||||
btn.itemIcon:SetTexture(iconPath or "Interface\\Icons\\INV_Misc_QuestionMark")
|
||||
btn.itemIcon:Show()
|
||||
else
|
||||
btn.itemIcon:SetTexture(nil)
|
||||
btn.itemIcon:Hide()
|
||||
end
|
||||
end
|
||||
if btn.trainerCoinIcon then btn.trainerCoinIcon:Hide() end
|
||||
if btn.familyAbilityButtons then
|
||||
local abilityEntries = nil
|
||||
local abilityColIndex = nil
|
||||
@@ -4572,6 +4791,7 @@ MTH_BOOK_UpdateResults = function()
|
||||
btn.itemIcon:SetTexture(nil)
|
||||
btn.itemIcon:Hide()
|
||||
end
|
||||
if btn.trainerCoinIcon then btn.trainerCoinIcon:Hide() end
|
||||
if btn.familyAbilityButtons then
|
||||
for chipIndex = 1, table.getn(btn.familyAbilityButtons) do
|
||||
local chip = btn.familyAbilityButtons[chipIndex]
|
||||
@@ -4628,6 +4848,11 @@ local function MTH_BOOK_UpdateModeLabels()
|
||||
check1 = ""
|
||||
check2 = ""
|
||||
check3 = ""
|
||||
elseif type(MTH_BOOKTAB_IsTrainerSpellMode) == "function" and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
filterNames = { "All", "Known", "Not learned", "Not yet learnable", "", "" }
|
||||
check1 = ""
|
||||
check2 = ""
|
||||
check3 = ""
|
||||
elseif MTH_BOOK_STATE.mode == "npcs" then
|
||||
filterNames = { "", "", "", "", "", "" }
|
||||
check1 = "Alliance"
|
||||
@@ -4769,6 +4994,9 @@ local function MTH_BOOK_SetQuickFilter(index)
|
||||
MTH_BOOK_STATE.quick = "all"
|
||||
elseif MTH_BOOK_STATE.mode == "petabilities" then
|
||||
MTH_BOOK_STATE.quick = "all"
|
||||
elseif type(MTH_BOOKTAB_IsTrainerSpellMode) == "function" and MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE.mode) then
|
||||
local map = { [1] = "all", [2] = "known", [3] = "notlearned", [4] = "notyet" }
|
||||
MTH_BOOK_STATE.quick = map[index] or "all"
|
||||
elseif MTH_BOOK_STATE.mode == "npcs" then
|
||||
MTH_BOOK_STATE.quick = "all"
|
||||
elseif MTH_BOOK_STATE.mode == "abilities" then
|
||||
@@ -4786,10 +5014,94 @@ end
|
||||
|
||||
local function MTH_BOOK_StyleSectionTab(button)
|
||||
if not button then return end
|
||||
button:SetHeight(24)
|
||||
button:SetHeight(26)
|
||||
button:SetNormalTexture(nil)
|
||||
button:SetPushedTexture(nil)
|
||||
button:SetHighlightTexture(nil)
|
||||
button:SetDisabledTexture(nil)
|
||||
if not button.mthMenuBackground then
|
||||
button.mthMenuBackground = button:CreateTexture(nil, "BACKGROUND")
|
||||
button.mthMenuBackground:SetAllPoints(button)
|
||||
button.mthMenuBackground:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
end
|
||||
local regions = { button:GetRegions() }
|
||||
for _, region in ipairs(regions) do
|
||||
if region ~= button.mthMenuBackground and region.GetObjectType
|
||||
and region:GetObjectType() == "Texture" then
|
||||
region:Hide()
|
||||
end
|
||||
end
|
||||
button:SetScript("OnEnter", function()
|
||||
if not this.mthSelected then
|
||||
this.mthMenuBackground:SetVertexColor(0.20, 0.17, 0.06, 1.00)
|
||||
end
|
||||
end)
|
||||
button:SetScript("OnLeave", function()
|
||||
if not this.mthSelected then
|
||||
this.mthMenuBackground:SetVertexColor(0.10, 0.10, 0.10, 1.00)
|
||||
end
|
||||
end)
|
||||
button:SetScript("OnShow", nil)
|
||||
end
|
||||
|
||||
function MTH_BOOK_EnsureVerticalNavigation(frame)
|
||||
if not frame then return end
|
||||
local contentPane = getglobal("MTH_BOOK_ContentPane")
|
||||
if not contentPane then
|
||||
contentPane = CreateFrame("Frame", "MTH_BOOK_ContentPane", frame)
|
||||
contentPane:SetPoint("TOPLEFT", frame, "TOPLEFT", 112, 0)
|
||||
contentPane:SetWidth(760)
|
||||
contentPane:SetHeight(560)
|
||||
|
||||
local navBackdrop = CreateFrame("Frame", "MTH_BOOK_NavBackdrop", frame)
|
||||
navBackdrop:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -42)
|
||||
navBackdrop:SetWidth(100)
|
||||
navBackdrop:SetHeight(476)
|
||||
navBackdrop:SetBackdrop({
|
||||
bgFile = "Interface\\Buttons\\WHITE8X8",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true,
|
||||
tileSize = 16,
|
||||
edgeSize = 16,
|
||||
insets = { left = 3, right = 3, top = 3, bottom = 3 },
|
||||
})
|
||||
navBackdrop:SetBackdropColor(0.07, 0.07, 0.07, 1.00)
|
||||
navBackdrop:SetBackdropBorderColor(0.28, 0.28, 0.28, 1.00)
|
||||
navBackdrop:SetFrameLevel(1)
|
||||
|
||||
local contentChildren = {
|
||||
"StatsText", "SearchLabel", "MinLabel", "MaxLabel", "BeastZoneLabel",
|
||||
"ListBackdrop", "DetailBackdrop", "OpenMapButton", "TopAreaBackdrop",
|
||||
"Search", "MinLevel", "MaxLevel", "ApplyButton", "PetLearnSourceDropdown",
|
||||
"ItemSubtypeDropdown", "PetOnlyMyLevel", "PetBookScanButton", "ResetButton", "TrainerHonoredDiscount",
|
||||
"Filter1", "Filter2", "Filter3", "Filter4", "Filter5", "Filter6",
|
||||
"FamilyDropdown", "AbilityDropdown", "RankDropdown", "PetInZoneOnly",
|
||||
"ShowAllOnMapButton", "NPCFunctionDropdown", "NPCInZoneOnly", "RequireVendor",
|
||||
"RequireDrop", "RequireObject", "HideNoAbilities", "HideUnknown",
|
||||
"BeastContinentDropdown", "BeastZoneDropdown", "ListSliderBackdrop", "ListSlider",
|
||||
"PrevButton", "NextButton",
|
||||
}
|
||||
for _, suffix in ipairs(contentChildren) do
|
||||
local child = getglobal("MTH_BOOK_" .. suffix)
|
||||
if child and child:GetParent() == frame then
|
||||
local point, relativeTo, relativePoint, x, y = child:GetPoint(1)
|
||||
child:SetParent(contentPane)
|
||||
if relativeTo == frame then
|
||||
child:ClearAllPoints()
|
||||
child:SetPoint(point, contentPane, relativePoint, x, y)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local topAreaBackdrop = getglobal("MTH_BOOK_TopAreaBackdrop")
|
||||
if topAreaBackdrop then
|
||||
topAreaBackdrop:ClearAllPoints()
|
||||
topAreaBackdrop:SetPoint("TOPLEFT", contentPane, "TOPLEFT", 0, -44)
|
||||
topAreaBackdrop:SetWidth(760)
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_BOOK_UpdateSectionTabs()
|
||||
local sectionPets = getglobal("MTH_BOOK_SectionPets")
|
||||
local sectionFamilies = getglobal("MTH_BOOK_SectionFamilies")
|
||||
@@ -4800,13 +5112,18 @@ local function MTH_BOOK_UpdateSectionTabs()
|
||||
local sectionNPCs = getglobal("MTH_BOOK_SectionNPCs")
|
||||
local sectionStable = getglobal("MTH_BOOK_SectionStable")
|
||||
local sectionPetHistory = getglobal("MTH_BOOK_SectionPetHistory")
|
||||
local sectionHunterSpells = getglobal("MTH_BOOK_SectionHunterSpells")
|
||||
local sectionPetSpells = getglobal("MTH_BOOK_SectionPetSpells")
|
||||
|
||||
local function setState(button, selected)
|
||||
if not button then return end
|
||||
button.mthSelected = selected and true or false
|
||||
if selected then
|
||||
PanelTemplates_SelectTab(button)
|
||||
button.mthMenuBackground:SetVertexColor(0.38, 0.28, 0.05, 1.00)
|
||||
button:SetTextColor(1.00, 0.82, 0.00)
|
||||
else
|
||||
PanelTemplates_DeselectTab(button)
|
||||
button.mthMenuBackground:SetVertexColor(0.10, 0.10, 0.10, 1.00)
|
||||
button:SetTextColor(0.90, 0.90, 0.90)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4819,6 +5136,8 @@ local function MTH_BOOK_UpdateSectionTabs()
|
||||
setState(sectionNPCs, MTH_BOOK_STATE.mode == "npcs")
|
||||
setState(sectionStable, MTH_BOOK_STATE.mode == "stable")
|
||||
setState(sectionPetHistory, MTH_BOOK_STATE.mode == "pethistory")
|
||||
setState(sectionHunterSpells, MTH_BOOK_STATE.mode == "hunterspells")
|
||||
setState(sectionPetSpells, MTH_BOOK_STATE.mode == "petspells")
|
||||
end
|
||||
|
||||
local function MTH_BOOK_LayoutSectionTabs()
|
||||
@@ -4832,40 +5151,23 @@ local function MTH_BOOK_LayoutSectionTabs()
|
||||
local sectionNPCs = getglobal("MTH_BOOK_SectionNPCs")
|
||||
local sectionStable = getglobal("MTH_BOOK_SectionStable")
|
||||
local sectionPetHistory = getglobal("MTH_BOOK_SectionPetHistory")
|
||||
local sectionHunterSpells = getglobal("MTH_BOOK_SectionHunterSpells")
|
||||
local sectionPetSpells = getglobal("MTH_BOOK_SectionPetSpells")
|
||||
if not (tabBar and sectionPets and sectionFamilies and sectionPetAbilities and sectionItems and sectionNPCs and sectionProjectiles and sectionAmmoBags) then return end
|
||||
|
||||
sectionPets:ClearAllPoints()
|
||||
sectionPets:SetPoint("TOPLEFT", tabBar, "TOPLEFT", 0, 0)
|
||||
|
||||
sectionPetAbilities:ClearAllPoints()
|
||||
sectionPetAbilities:SetPoint("LEFT", sectionPets, "RIGHT", 2, 0)
|
||||
|
||||
sectionFamilies:ClearAllPoints()
|
||||
sectionFamilies:SetPoint("LEFT", sectionPetAbilities, "RIGHT", 2, 0)
|
||||
|
||||
sectionNPCs:ClearAllPoints()
|
||||
sectionNPCs:SetPoint("LEFT", sectionFamilies, "RIGHT", 2, 0)
|
||||
|
||||
sectionItems:ClearAllPoints()
|
||||
sectionItems:SetPoint("LEFT", sectionNPCs, "RIGHT", 2, 0)
|
||||
|
||||
sectionProjectiles:ClearAllPoints()
|
||||
sectionProjectiles:SetPoint("LEFT", sectionItems, "RIGHT", 2, 0)
|
||||
|
||||
sectionAmmoBags:ClearAllPoints()
|
||||
sectionAmmoBags:SetPoint("LEFT", sectionProjectiles, "RIGHT", 2, 0)
|
||||
|
||||
if sectionStable then
|
||||
sectionStable:ClearAllPoints()
|
||||
sectionStable:SetPoint("LEFT", sectionAmmoBags, "RIGHT", 2, 0)
|
||||
end
|
||||
|
||||
if sectionPetHistory then
|
||||
sectionPetHistory:ClearAllPoints()
|
||||
if sectionStable then
|
||||
sectionPetHistory:SetPoint("LEFT", sectionStable, "RIGHT", 2, 0)
|
||||
else
|
||||
sectionPetHistory:SetPoint("LEFT", sectionAmmoBags, "RIGHT", 2, 0)
|
||||
local sections = {
|
||||
sectionPets, sectionPetAbilities, sectionFamilies, sectionNPCs, sectionItems,
|
||||
sectionProjectiles, sectionAmmoBags, sectionStable, sectionPetHistory,
|
||||
sectionHunterSpells, sectionPetSpells,
|
||||
}
|
||||
local y = 0
|
||||
for _, section in ipairs(sections) do
|
||||
if section then
|
||||
section:ClearAllPoints()
|
||||
section:SetPoint("TOPLEFT", tabBar, "TOPLEFT", 0, y)
|
||||
section:SetWidth(96)
|
||||
section:SetHeight(26)
|
||||
y = y - 28
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -5304,6 +5606,7 @@ local function MTH_BOOK_WireUI(frame)
|
||||
frame:SetBackdropColor(0.05, 0.05, 0.05, 1.00)
|
||||
frame:SetBackdropBorderColor(0.35, 0.35, 0.35, 1.00)
|
||||
end
|
||||
MTH_BOOK_EnsureVerticalNavigation(frame)
|
||||
|
||||
local closeButton = getglobal("MTH_BOOK_CloseButton")
|
||||
local applyButton = getglobal("MTH_BOOK_ApplyButton")
|
||||
@@ -5317,6 +5620,9 @@ local function MTH_BOOK_WireUI(frame)
|
||||
local sectionNPCs = getglobal("MTH_BOOK_SectionNPCs")
|
||||
local sectionStable = getglobal("MTH_BOOK_SectionStable")
|
||||
local sectionPetHistory = getglobal("MTH_BOOK_SectionPetHistory")
|
||||
local sectionHunterSpells = getglobal("MTH_BOOK_SectionHunterSpells")
|
||||
local sectionPetSpells = getglobal("MTH_BOOK_SectionPetSpells")
|
||||
local trainerHonoredDiscount = getglobal("MTH_BOOK_TrainerHonoredDiscount")
|
||||
local requireVendor = getglobal("MTH_BOOK_RequireVendor")
|
||||
local requireDrop = getglobal("MTH_BOOK_RequireDrop")
|
||||
local requireObject = getglobal("MTH_BOOK_RequireObject")
|
||||
@@ -5358,6 +5664,14 @@ local function MTH_BOOK_WireUI(frame)
|
||||
if sectionNPCs then sectionNPCs:SetScript("OnClick", function() MTH_BOOK_SetMode("npcs") end) end
|
||||
if sectionStable then sectionStable:SetScript("OnClick", function() MTH_BOOK_SetMode("stable") end) end
|
||||
if sectionPetHistory then sectionPetHistory:SetScript("OnClick", function() MTH_BOOK_SetMode("pethistory") end) end
|
||||
if sectionHunterSpells then sectionHunterSpells:SetScript("OnClick", function() MTH_BOOK_SetMode("hunterspells") end) end
|
||||
if sectionPetSpells then sectionPetSpells:SetScript("OnClick", function() MTH_BOOK_SetMode("petspells") end) end
|
||||
if trainerHonoredDiscount then
|
||||
trainerHonoredDiscount:SetScript("OnClick", function()
|
||||
MTH_BOOK_STATE.trainerHonoredDiscount = this:GetChecked() == 1
|
||||
MTH_BOOK_UpdateResults()
|
||||
end)
|
||||
end
|
||||
|
||||
MTH_BOOK_StyleSectionTab(sectionPets)
|
||||
MTH_BOOK_StyleSectionTab(sectionFamilies)
|
||||
@@ -5368,7 +5682,10 @@ local function MTH_BOOK_WireUI(frame)
|
||||
MTH_BOOK_StyleSectionTab(sectionNPCs)
|
||||
MTH_BOOK_StyleSectionTab(sectionStable)
|
||||
MTH_BOOK_StyleSectionTab(sectionPetHistory)
|
||||
MTH_BOOK_StyleSectionTab(sectionHunterSpells)
|
||||
MTH_BOOK_StyleSectionTab(sectionPetSpells)
|
||||
MTH_BOOK_LayoutSectionTabs()
|
||||
MTH_BOOK_ApplyContentLayoutForMode()
|
||||
|
||||
for i = 1, 6 do
|
||||
local filterBtn = getglobal("MTH_BOOK_Filter" .. i)
|
||||
@@ -5384,6 +5701,7 @@ local function MTH_BOOK_WireUI(frame)
|
||||
MTH_BOOK_InitPetLearnSourceDropdown()
|
||||
MTH_BOOK_InitItemSubtypeDropdown()
|
||||
MTH_BOOK_InitNPCFunctionDropdown()
|
||||
MTH_BOOK_EnsureTrainerStatusDropdown()
|
||||
|
||||
if petBookScanButton then
|
||||
petBookScanButton:SetScript("OnClick", function()
|
||||
|
||||
+20
-2
@@ -2,7 +2,7 @@
|
||||
..\FrameXML\UI.xsd">
|
||||
<Frame name="MTH_BOOK_Template" toplevel="true" frameStrata="DIALOG" movable="true" enableMouse="true" hidden="true" parent="UIParent" virtual="true">
|
||||
<Size>
|
||||
<AbsDimension x="760" y="560"/>
|
||||
<AbsDimension x="880" y="560"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
@@ -128,7 +128,7 @@
|
||||
</Frame>
|
||||
|
||||
<Frame name="$parentTabBar">
|
||||
<Size><AbsDimension x="728" y="24"/></Size>
|
||||
<Size><AbsDimension x="96" y="472"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="18" y="-44"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
@@ -188,6 +188,18 @@
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionAmmoBags" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionHunterSpells" inherits="TabButtonTemplate" text="Hunter Spells">
|
||||
<Size><AbsDimension x="82" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionPetHistory" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionPetSpells" inherits="TabButtonTemplate" text="Pet Spells">
|
||||
<Size><AbsDimension x="82" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionHunterSpells" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
|
||||
<EditBox name="$parentSearch" inherits="InputBoxTemplate">
|
||||
<Size><AbsDimension x="180" y="20"/></Size>
|
||||
@@ -235,6 +247,12 @@
|
||||
</Anchors>
|
||||
</CheckButton>
|
||||
|
||||
<CheckButton name="$parentTrainerHonoredDiscount" inherits="UICheckButtonTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="618" y="-80"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</CheckButton>
|
||||
|
||||
<Button name="$parentPetBookScanButton" inherits="UIPanelButtonTemplate" text="Scan from Pet Book" hidden="true">
|
||||
<Size><AbsDimension x="130" y="20"/></Size>
|
||||
<Anchors>
|
||||
|
||||
@@ -1126,7 +1126,7 @@ function MTH_SetupMessagesOptions()
|
||||
if not section then return end
|
||||
section:SetPoint("TOPLEFT", container, "TOPLEFT", 8, -8)
|
||||
section:SetPoint("TOPRIGHT", container, "TOPRIGHT", -8, -8)
|
||||
section:SetHeight(268)
|
||||
section:SetHeight(380)
|
||||
|
||||
local titleFrame = getglobal("MetaHuntMessagesSectionTitle")
|
||||
if titleFrame then
|
||||
@@ -1197,7 +1197,18 @@ function MTH_SetupMessagesOptions()
|
||||
end)
|
||||
end
|
||||
|
||||
local petRanAwayToggle = MTH_CreateCheckbox(section, "MetaHuntMessagesPetRanAway", "Pet ran away", -176)
|
||||
local trainerScanToggle = MTH_CreateCheckbox(section, "MetaHuntMessagesTrainerScan", "Trainer scan", -176)
|
||||
if trainerScanToggle then
|
||||
trainerScanToggle:SetChecked(msgSettings.trainerScan ~= false)
|
||||
trainerScanToggle:SetScript("OnClick", function()
|
||||
if not this then return end
|
||||
if MTH and MTH.SetMessageEnabled then
|
||||
MTH:SetMessageEnabled("trainerScan", MTH_ZB_IsChecked(this))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local petRanAwayToggle = MTH_CreateCheckbox(section, "MetaHuntMessagesPetRanAway", "Pet ran away", -204)
|
||||
if petRanAwayToggle then
|
||||
petRanAwayToggle:SetChecked(msgSettings.petRanAway ~= false)
|
||||
petRanAwayToggle:SetScript("OnClick", function()
|
||||
@@ -1208,7 +1219,7 @@ function MTH_SetupMessagesOptions()
|
||||
end)
|
||||
end
|
||||
|
||||
local mapMarkersToggle = MTH_CreateCheckbox(section, "MetaHuntMessagesMapMarkers", "Map markers", -204)
|
||||
local mapMarkersToggle = MTH_CreateCheckbox(section, "MetaHuntMessagesMapMarkers", "Map markers", -232)
|
||||
if mapMarkersToggle then
|
||||
mapMarkersToggle:SetChecked(msgSettings.mapMarkers ~= false)
|
||||
mapMarkersToggle:SetScript("OnClick", function()
|
||||
@@ -1219,6 +1230,38 @@ function MTH_SetupMessagesOptions()
|
||||
end)
|
||||
end
|
||||
|
||||
local hunterLevelToggle = MTH_CreateCheckbox(section, "MetaHuntMessagesHunterLevel", "Hunter level training summary", -260)
|
||||
if hunterLevelToggle then
|
||||
hunterLevelToggle:SetChecked(msgSettings.hunterLevelUp ~= false)
|
||||
hunterLevelToggle:SetScript("OnClick", function()
|
||||
if MTH and MTH.SetMessageEnabled then MTH:SetMessageEnabled("hunterLevelUp", MTH_ZB_IsChecked(this)) end
|
||||
end)
|
||||
end
|
||||
|
||||
local hunterQuietLevelToggle = MTH_CreateCheckbox(section, "MetaHuntMessagesHunterQuietLevel", "Hunter quiet-level message", -288)
|
||||
if hunterQuietLevelToggle then
|
||||
hunterQuietLevelToggle:SetChecked(msgSettings.hunterLevelQuiet and true or false)
|
||||
hunterQuietLevelToggle:SetScript("OnClick", function()
|
||||
if MTH and MTH.SetMessageEnabled then MTH:SetMessageEnabled("hunterLevelQuiet", MTH_ZB_IsChecked(this)) end
|
||||
end)
|
||||
end
|
||||
|
||||
local petLevelToggle = MTH_CreateCheckbox(section, "MetaHuntMessagesPetLevel", "Pet level training summary", -316)
|
||||
if petLevelToggle then
|
||||
petLevelToggle:SetChecked(msgSettings.petLevelUp ~= false)
|
||||
petLevelToggle:SetScript("OnClick", function()
|
||||
if MTH and MTH.SetMessageEnabled then MTH:SetMessageEnabled("petLevelUp", MTH_ZB_IsChecked(this)) end
|
||||
end)
|
||||
end
|
||||
|
||||
local petLoyaltyToggle = MTH_CreateCheckbox(section, "MetaHuntMessagesPetLoyalty", "Pet loyalty gain", -344)
|
||||
if petLoyaltyToggle then
|
||||
petLoyaltyToggle:SetChecked(msgSettings.petLoyaltyUp ~= false)
|
||||
petLoyaltyToggle:SetScript("OnClick", function()
|
||||
if MTH and MTH.SetMessageEnabled then MTH:SetMessageEnabled("petLoyaltyUp", MTH_ZB_IsChecked(this)) end
|
||||
end)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function MTH_SetupGeneralOptions()
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
-- MetaHunt shared trainer spell status and cost planner.
|
||||
-- UI, chat notifications, and future level-up messages consume these functions.
|
||||
|
||||
local function MTH_TP_Lower(value)
|
||||
return string.lower(tostring(value or ""))
|
||||
end
|
||||
|
||||
local function MTH_TP_RankNumber(rank)
|
||||
local _, _, value = string.find(tostring(rank or ""), "(%d+)")
|
||||
return tonumber(value) or 0
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_GetPrice(row, honored)
|
||||
local base = tonumber(row and row.trainerCost) or 0
|
||||
if honored then return math.floor(base * 0.90) end
|
||||
return base
|
||||
end
|
||||
|
||||
local function MTH_TP_IsKnownHunter(row)
|
||||
if type(MTH_HT_GetSpellInfo) ~= "function" then return false end
|
||||
local known = MTH_HT_GetSpellInfo(row and row.name)
|
||||
if type(known) ~= "table" then return false end
|
||||
local requiredRank = MTH_TP_RankNumber(row and row.rank)
|
||||
return requiredRank == 0 or (tonumber(known.rankNum) or 0) >= requiredRank
|
||||
end
|
||||
|
||||
local function MTH_TP_IsKnownPet(row)
|
||||
if type(MTH_PETS_GetRootStore) ~= "function" then return false end
|
||||
local pets = MTH_PETS_GetRootStore()
|
||||
local store = pets and pets.petStore
|
||||
local petId = store and store.activeCurrentId
|
||||
local pet = store and store.activeById and petId and store.activeById[tostring(petId)]
|
||||
local spells = pet and pet.petSpellbook and pet.petSpellbook.spells
|
||||
if type(spells) ~= "table" then return false end
|
||||
local requiredName = MTH_TP_Lower(row and row.name)
|
||||
local requiredRank = MTH_TP_RankNumber(row and row.rank)
|
||||
for _, spell in ipairs(spells) do
|
||||
if MTH_TP_Lower(spell and spell.name) == requiredName then
|
||||
local knownRank = tonumber(spell.rank) or 0
|
||||
if requiredRank == 0 or knownRank >= requiredRank then return true end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_IsKnown(kind, row)
|
||||
if kind == "hunter" then return MTH_TP_IsKnownHunter(row) end
|
||||
return MTH_TP_IsKnownPet(row)
|
||||
end
|
||||
|
||||
local function MTH_TP_HasMissingPrerequisite(kind, row)
|
||||
if type(row and row.prerequisites) ~= "table" then return false end
|
||||
for _, prerequisite in ipairs(row.prerequisites) do
|
||||
local _, _, name, rank = string.find(tostring(prerequisite), "^(.+)%s%([Rr]ank%s*(%d+)%)$")
|
||||
if name and rank and not MTH_TrainerPlan_IsKnown(kind, { name = name, rank = "Rank " .. rank }) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_GetStatus(kind, row, playerLevel)
|
||||
if MTH_TrainerPlan_IsKnown(kind, row) then return "known" end
|
||||
local observed = type(MTH_GetTrainerLiveStatus) == "function"
|
||||
and MTH_GetTrainerLiveStatus(kind, row and row.name, row and row.rank) or nil
|
||||
if observed and observed.status == "used" then return "known" end
|
||||
if observed and observed.status == "available" then return "notlearned" end
|
||||
if observed and observed.status == "unavailable" then return "notyet" end
|
||||
local requiredLevel = tonumber(row and row.requiredLevel) or 0
|
||||
if not playerLevel or playerLevel < requiredLevel or MTH_TP_HasMissingPrerequisite(kind, row) then
|
||||
return "notyet"
|
||||
end
|
||||
return "notlearned"
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_Build(kind, options)
|
||||
options = options or {}
|
||||
local catalog = MTH_DS_TrainerCatalog
|
||||
local rows = catalog and catalog[kind] or nil
|
||||
local playerLevel = tonumber(options.playerLevel)
|
||||
if not playerLevel and type(UnitLevel) == "function" then playerLevel = tonumber(UnitLevel("player")) end
|
||||
local honored = options.honored and true or false
|
||||
local plan = {
|
||||
kind = kind,
|
||||
playerLevel = playerLevel,
|
||||
honored = honored,
|
||||
rows = {},
|
||||
now = { count = 0, cost = 0 },
|
||||
nextLevel = { level = nil, count = 0, cost = 0 },
|
||||
remaining = { count = 0, cost = 0 },
|
||||
}
|
||||
if type(rows) ~= "table" then return plan end
|
||||
for _, row in ipairs(rows) do
|
||||
local status = MTH_TrainerPlan_GetStatus(kind, row, playerLevel)
|
||||
local price = MTH_TrainerPlan_GetPrice(row, honored)
|
||||
local entry = { row = row, status = status, price = price }
|
||||
table.insert(plan.rows, entry)
|
||||
if status ~= "known" then
|
||||
plan.remaining.count = plan.remaining.count + 1
|
||||
plan.remaining.cost = plan.remaining.cost + price
|
||||
if status == "notlearned" then
|
||||
plan.now.count = plan.now.count + 1
|
||||
plan.now.cost = plan.now.cost + price
|
||||
end
|
||||
local requiredLevel = tonumber(row.requiredLevel) or 0
|
||||
if playerLevel and requiredLevel > playerLevel then
|
||||
if not plan.nextLevel.level or requiredLevel < plan.nextLevel.level then
|
||||
plan.nextLevel.level = requiredLevel
|
||||
plan.nextLevel.count = 0
|
||||
plan.nextLevel.cost = 0
|
||||
end
|
||||
if requiredLevel == plan.nextLevel.level then
|
||||
plan.nextLevel.count = plan.nextLevel.count + 1
|
||||
plan.nextLevel.cost = plan.nextLevel.cost + price
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return plan
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_BuildLevel(kind, targetLevel, options)
|
||||
options = options or {}
|
||||
local level = tonumber(targetLevel)
|
||||
local catalog = MTH_DS_TrainerCatalog
|
||||
local rows = catalog and catalog[kind] or nil
|
||||
local honored = options.honored and true or false
|
||||
local plan = {
|
||||
kind = kind,
|
||||
level = level,
|
||||
honored = honored,
|
||||
rows = {},
|
||||
available = { count = 0, cost = 0 },
|
||||
unlearned = { count = 0, cost = 0 },
|
||||
}
|
||||
if not level or type(rows) ~= "table" then return plan end
|
||||
for _, row in ipairs(rows) do
|
||||
if tonumber(row.requiredLevel) == level and (options.includeKnown or not MTH_TrainerPlan_IsKnown(kind, row)) then
|
||||
local status = MTH_TrainerPlan_GetStatus(kind, row, level)
|
||||
local price = MTH_TrainerPlan_GetPrice(row, honored)
|
||||
local entry = { row = row, status = status, price = price }
|
||||
table.insert(plan.rows, entry)
|
||||
plan.unlearned.count = plan.unlearned.count + 1
|
||||
plan.unlearned.cost = plan.unlearned.cost + price
|
||||
if status == "notlearned" then
|
||||
plan.available.count = plan.available.count + 1
|
||||
plan.available.cost = plan.available.cost + price
|
||||
end
|
||||
end
|
||||
end
|
||||
return plan
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_MatchesQuick(status, quick)
|
||||
if quick == "known" then return status == "known" end
|
||||
if quick == "notlearned" then return status == "notlearned" end
|
||||
if quick == "notyet" then return status == "notyet" end
|
||||
return true
|
||||
end
|
||||
Reference in New Issue
Block a user