mirror of
https://codeberg.org/Duvelcorp/MetaHunt.git
synced 2026-09-29 19:16:04 +00:00
First version
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
local function AntiDaze_Print(msg)
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print(msg)
|
||||
elseif DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(tostring(msg))
|
||||
end
|
||||
end
|
||||
|
||||
local function AntiDaze_GetSaved()
|
||||
if type(MTH_CharSavedVariables) ~= "table" then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.antiDaze) ~= "table" then
|
||||
MTH_CharSavedVariables.antiDaze = {}
|
||||
end
|
||||
return MTH_CharSavedVariables.antiDaze
|
||||
end
|
||||
|
||||
local function AntiDaze_IsEnabled()
|
||||
local saved = AntiDaze_GetSaved()
|
||||
return saved["enabled"] and true or false
|
||||
end
|
||||
|
||||
local function AntiDaze_RegisterCombatEvents()
|
||||
if not AntiDazeFrame then
|
||||
return
|
||||
end
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE")
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE")
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
|
||||
AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PARTY_DAMAGE")
|
||||
end
|
||||
|
||||
local function AntiDaze_UnregisterCombatEvents()
|
||||
if not AntiDazeFrame then
|
||||
return
|
||||
end
|
||||
AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE")
|
||||
AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE")
|
||||
AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
|
||||
AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_PARTY_DAMAGE")
|
||||
end
|
||||
|
||||
function AntiDaze_SetEnabled(enabled, silent)
|
||||
local saved = AntiDaze_GetSaved()
|
||||
if enabled then
|
||||
saved["enabled"] = 1
|
||||
AntiDaze_RegisterCombatEvents()
|
||||
if not silent then
|
||||
AntiDaze_Print("AntiDaze enabled.")
|
||||
end
|
||||
else
|
||||
saved["enabled"] = nil
|
||||
AntiDaze_UnregisterCombatEvents()
|
||||
if not silent then
|
||||
AntiDaze_Print("AntiDaze disabled.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AntiDaze_GetEnabled()
|
||||
return AntiDaze_IsEnabled()
|
||||
end
|
||||
|
||||
local function AntiDaze_ResolvePlayerFromMessage(msg)
|
||||
local afflictedPattern = _G["ZHUNTER_AFFLICTED_DAZED"] or "(.+) (.+) afflicted by Dazed"
|
||||
local _, _, player = string.find(msg or "", afflictedPattern)
|
||||
if player then
|
||||
return player
|
||||
end
|
||||
|
||||
local lowerMsg = string.lower(msg or "")
|
||||
if string.find(lowerMsg, "dazed", 1, true) or string.find(lowerMsg, "daze", 1, true) then
|
||||
local playerName = UnitName("player")
|
||||
if string.find(msg or "", "You", 1, true) or (playerName and string.find(msg or "", playerName, 1, true)) then
|
||||
return _G["ZHUNTER_YOU"] or "You"
|
||||
end
|
||||
|
||||
for i = 1, GetNumPartyMembers() do
|
||||
local partyName = UnitName("party" .. i)
|
||||
if partyName and string.find(msg or "", partyName, 1, true) then
|
||||
return partyName
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function AntiDaze_CancelRelevantAspect(isSelfDazed)
|
||||
local packName = _G["ZHUNTER_ASPECT_PACK"] or "Aspect of the Pack"
|
||||
local cheetahName = _G["ZHUNTER_ASPECT_CHEETAH"] or "Aspect of the Cheetah"
|
||||
|
||||
for i = 0, 32 do
|
||||
if GetPlayerBuff(i, "HELPFUL") < 0 then
|
||||
break
|
||||
end
|
||||
|
||||
AntiDazeTooltip:SetOwner(UIParent, "ANCHOR_NONE")
|
||||
AntiDazeTooltip:SetPlayerBuff(i, "HELPFUL")
|
||||
local text = AntiDazeTooltipTextLeft1 and AntiDazeTooltipTextLeft1:GetText()
|
||||
if text == packName or (isSelfDazed and text == cheetahName) then
|
||||
CancelPlayerBuff(i)
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function AntiDaze_HandleSlash()
|
||||
AntiDaze_SetEnabled(not AntiDaze_IsEnabled())
|
||||
end
|
||||
|
||||
AntiDazeTooltip = CreateFrame("GameTooltip", "AntiDazeTooltip", nil, "GameTooltipTemplate")
|
||||
AntiDazeFrame = CreateFrame("Frame", "AntiDazeFrame")
|
||||
if AntiDazeFrame then
|
||||
AntiDazeFrame:RegisterEvent("VARIABLES_LOADED")
|
||||
AntiDazeFrame:SetScript("OnEvent", function()
|
||||
if event == "VARIABLES_LOADED" then
|
||||
if AntiDaze_IsEnabled() then
|
||||
AntiDaze_RegisterCombatEvents()
|
||||
else
|
||||
AntiDaze_UnregisterCombatEvents()
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local player = AntiDaze_ResolvePlayerFromMessage(arg1)
|
||||
if player then
|
||||
local you = _G["ZHUNTER_YOU"] or "You"
|
||||
local group = {}
|
||||
group[you] = 1
|
||||
for i = 1, GetNumPartyMembers() do
|
||||
group[UnitName("party" .. i)] = 1
|
||||
end
|
||||
if group[player] then
|
||||
AntiDaze_CancelRelevantAspect(player == you)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
SLASH_ANTIDAZE1 = "/antidaze"
|
||||
SlashCmdList["ANTIDAZE"] = AntiDaze_HandleSlash
|
||||
SLASH_ZANTIDAZE1 = "/zantidaze"
|
||||
SlashCmdList["ZANTIDAZE"] = AntiDaze_HandleSlash
|
||||
@@ -0,0 +1,189 @@
|
||||
local function AutoStrip_Print(msg)
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print(msg)
|
||||
elseif DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(tostring(msg))
|
||||
end
|
||||
end
|
||||
|
||||
function AutoStrip_GetSaved()
|
||||
if type(MTH_CharSavedVariables) ~= "table" then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.autoStrip) ~= "table" then
|
||||
MTH_CharSavedVariables.autoStrip = {}
|
||||
end
|
||||
return MTH_CharSavedVariables.autoStrip
|
||||
end
|
||||
|
||||
local AUTO_STRIP_ORDER = {16, 17, 18, 5, 7, 1, 3, 10, 8, 6, 9}
|
||||
AutoStrip_On = nil
|
||||
AutoStrip_WasInCombat = nil
|
||||
AutoStrip_Frame = nil
|
||||
|
||||
function AutoStrip_SetDisplayIcon()
|
||||
local icon = getglobal("AutoStripDisplayIcon") or getglobal("AutoStripDisplayIconTexture")
|
||||
if icon then
|
||||
icon:SetTexture("Interface\\Icons\\Ability_Creature_Cursed_02")
|
||||
icon:Show()
|
||||
end
|
||||
end
|
||||
|
||||
function AutoStrip_GetEnabled()
|
||||
return AutoStrip_On and true or false
|
||||
end
|
||||
|
||||
function AutoStrip_SetAutoStripToggle(enabled, silent)
|
||||
local saved = AutoStrip_GetSaved()
|
||||
if enabled then
|
||||
saved["autostrip"] = 1
|
||||
AutoStrip_On = 1
|
||||
if AutoStripDisplay and AutoStripDisplay:IsVisible() and AutoStripDisplayAutoCast then
|
||||
AutoStripDisplayAutoCast:Show()
|
||||
end
|
||||
if not silent then
|
||||
AutoStrip_Print("Auto-Strip enabled.")
|
||||
end
|
||||
else
|
||||
saved["autostrip"] = nil
|
||||
AutoStrip_On = nil
|
||||
if AutoStripDisplayAutoCast then
|
||||
AutoStripDisplayAutoCast:Hide()
|
||||
end
|
||||
if not silent then
|
||||
AutoStrip_Print("Auto-Strip disabled.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AutoStrip_SetDisplayToggle(enabled, silent)
|
||||
local saved = AutoStrip_GetSaved()
|
||||
if enabled then
|
||||
saved["display"] = 1
|
||||
if AutoStripDisplay then
|
||||
AutoStripDisplay:Show()
|
||||
AutoStrip_SetDisplayIcon()
|
||||
if AutoStrip_On and AutoStripDisplayAutoCast then
|
||||
AutoStripDisplayAutoCast:Show()
|
||||
end
|
||||
end
|
||||
if not silent then
|
||||
AutoStrip_Print("AutoStrip button is now visible.")
|
||||
end
|
||||
else
|
||||
saved["display"] = nil
|
||||
if AutoStripDisplay then
|
||||
AutoStripDisplay:Hide()
|
||||
end
|
||||
if not silent then
|
||||
AutoStrip_Print("AutoStrip button is now hidden.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AutoStrip_UnequipAll(weaponsOnly)
|
||||
AutoStrip_On = nil
|
||||
if AutoStripDisplayAutoCast then
|
||||
AutoStripDisplayAutoCast:Hide()
|
||||
end
|
||||
|
||||
local hasEmptySlot = nil
|
||||
for bag = 0, 4 do
|
||||
for slot = 1, GetContainerNumSlots(bag) do
|
||||
if not GetContainerItemLink(bag, slot) then
|
||||
hasEmptySlot = 1
|
||||
break
|
||||
end
|
||||
end
|
||||
if hasEmptySlot then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not hasEmptySlot then
|
||||
AutoStrip_Print("You need at least one empty bag slot to auto-strip.")
|
||||
return
|
||||
end
|
||||
|
||||
local startIndex = 1
|
||||
local finishIndex = weaponsOnly and 3 or table.getn(AUTO_STRIP_ORDER)
|
||||
for bag = 0, 4 do
|
||||
for slot = 1, GetContainerNumSlots(bag) do
|
||||
if not GetContainerItemLink(bag, slot) then
|
||||
for index = startIndex, finishIndex do
|
||||
if GetInventoryItemLink("player", AUTO_STRIP_ORDER[index]) then
|
||||
PickupInventoryItem(AUTO_STRIP_ORDER[index])
|
||||
PickupContainerItem(bag, slot)
|
||||
startIndex = index + 1
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function AutoStrip_OnEvent()
|
||||
if event == "VARIABLES_LOADED" then
|
||||
local saved = AutoStrip_GetSaved()
|
||||
AutoStrip_WasInCombat = UnitAffectingCombat("player") and 1 or nil
|
||||
AutoStrip_SetDisplayToggle(saved["display"] and true or false, 1)
|
||||
AutoStrip_SetAutoStripToggle(saved["autostrip"] and true or false, 1)
|
||||
return
|
||||
end
|
||||
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
AutoStrip_WasInCombat = UnitAffectingCombat("player") and 1 or nil
|
||||
return
|
||||
end
|
||||
|
||||
if event == "PLAYER_REGEN_DISABLED" then
|
||||
AutoStrip_WasInCombat = 1
|
||||
return
|
||||
end
|
||||
|
||||
if event == "PLAYER_REGEN_ENABLED" then
|
||||
if AutoStrip_On and AutoStrip_WasInCombat then
|
||||
AutoStrip_UnequipAll()
|
||||
end
|
||||
AutoStrip_WasInCombat = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function AutoStrip_HandleSlash(msg)
|
||||
local saved = AutoStrip_GetSaved()
|
||||
if msg == "display" then
|
||||
AutoStrip_SetDisplayToggle(not (saved["display"] and true or false))
|
||||
elseif msg == "toggle" then
|
||||
local enableAutoStrip = not AutoStrip_GetEnabled()
|
||||
if enableAutoStrip and not saved["display"] then
|
||||
AutoStrip_SetDisplayToggle(true, 1)
|
||||
end
|
||||
AutoStrip_SetAutoStripToggle(enableAutoStrip, 1)
|
||||
if enableAutoStrip then
|
||||
AutoStrip_Print("You will strip when you leave combat.")
|
||||
else
|
||||
AutoStrip_Print("You will no longer strip when you leave combat.")
|
||||
end
|
||||
else
|
||||
AutoStrip_Print("Possible Commands: \"display\", \"toggle\"")
|
||||
end
|
||||
end
|
||||
|
||||
AutoStrip_Frame = CreateFrame("Frame", "AutoStripFrame")
|
||||
if AutoStrip_Frame then
|
||||
AutoStrip_Frame:RegisterEvent("VARIABLES_LOADED")
|
||||
AutoStrip_Frame:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||
AutoStrip_Frame:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||||
AutoStrip_Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
AutoStrip_Frame:SetScript("OnEvent", AutoStrip_OnEvent)
|
||||
end
|
||||
|
||||
if AutoStripDisplay then
|
||||
AutoStrip_SetDisplayIcon()
|
||||
end
|
||||
|
||||
SLASH_AUTOSTRIP1 = "/autostrip"
|
||||
SlashCmdList["AUTOSTRIP"] = AutoStrip_HandleSlash
|
||||
SLASH_ZSTRIP1 = "/zstrip"
|
||||
SlashCmdList["ZSTRIP"] = AutoStrip_HandleSlash
|
||||
@@ -0,0 +1,97 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Button name="AutoStripDisplay" parent="UIParent" enableMouse="true" movable="true" inherits="ActionButtonTemplate" frameStrata="DIALOG" hidden="true">
|
||||
<Size>
|
||||
<AbsDimension x="22.5" y="22.5"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="0"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="OVERLAY">
|
||||
<Texture name="$parentAutoCastable" file="Interface\Buttons\UI-AutoCastableOverlay">
|
||||
<Size>
|
||||
<AbsDimension x="43.5" y="43.5"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="0"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Model name="$parentAutoCast" file="Interface\Buttons\UI-AutoCastButton.mdx" scale="0.9" hidden="true" setAllPoints="true">
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
this:SetSequence(0);
|
||||
this:SetSequenceTime(0, 0);
|
||||
</OnLoad>
|
||||
</Scripts>
|
||||
</Model>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
this:RegisterForDrag("LeftButton");
|
||||
this:RegisterForClicks("LeftButtonUp", "RightButtonUp");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
if arg1 == "LeftButton" then
|
||||
if not UnitAffectingCombat("player") then
|
||||
AutoStrip_UnequipAll()
|
||||
else
|
||||
AutoStrip_UnequipAll(1)
|
||||
end
|
||||
elseif arg1 == "RightButton" then
|
||||
AutoStrip_SetAutoStripToggle(not AutoStrip_GetEnabled())
|
||||
end
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
GameTooltip:SetOwner(this, "ANCHOR_TOPLEFT");
|
||||
GameTooltip:SetText("AutoStrip", 1, 1, 1)
|
||||
GameTooltip:AddLine("Left-Click to strip now.")
|
||||
GameTooltip:AddLine("Right-Click to toggle auto-strip")
|
||||
GameTooltip:AddLine("Alt-Drag to move")
|
||||
GameTooltip:Show()
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
GameTooltip:Hide()
|
||||
</OnLeave>
|
||||
<OnMouseUp>
|
||||
if ( this.isMoving ) then
|
||||
this:StopMovingOrSizing()
|
||||
this.isMoving = false
|
||||
end
|
||||
</OnMouseUp>
|
||||
<OnDragStart>
|
||||
if IsAltKeyDown() then
|
||||
this:StartMoving()
|
||||
this.isMoving = true
|
||||
end
|
||||
</OnDragStart>
|
||||
<OnDragStop>
|
||||
this:StopMovingOrSizing()
|
||||
this.isMoving = false
|
||||
</OnDragStop>
|
||||
</Scripts>
|
||||
<NormalTexture name="$parentNormalTexture" file="Interface\Buttons\UI-Quickslot2">
|
||||
<Size>
|
||||
<AbsDimension x="40.5" y="40.5"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="-1"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</NormalTexture>
|
||||
</Button>
|
||||
</Ui>
|
||||
@@ -0,0 +1,58 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt Configuration System
|
||||
------------------------------------------------------
|
||||
|
||||
-- Global config management for modules
|
||||
MTH.config = {}
|
||||
|
||||
local function MTH_ConfigEnsureStore(module_name)
|
||||
if type(module_name) ~= "string" or module_name == "" then
|
||||
module_name = "core"
|
||||
end
|
||||
|
||||
local root = _G and _G.MTH_SavedVariables or MTH_SavedVariables
|
||||
if type(root) ~= "table" then
|
||||
if MTH and MTH.InitSavedVariables then
|
||||
MTH:InitSavedVariables()
|
||||
end
|
||||
root = _G and _G.MTH_SavedVariables or MTH_SavedVariables
|
||||
end
|
||||
|
||||
if type(root) ~= "table" then
|
||||
root = { modules = {} }
|
||||
if _G then
|
||||
_G.MTH_SavedVariables = root
|
||||
end
|
||||
MTH_SavedVariables = root
|
||||
end
|
||||
|
||||
if type(root.modules) ~= "table" then
|
||||
root.modules = {}
|
||||
end
|
||||
|
||||
if type(root.modules[module_name]) ~= "table" then
|
||||
local legacy = root[module_name]
|
||||
if type(legacy) ~= "table" then
|
||||
legacy = {}
|
||||
end
|
||||
root.modules[module_name] = legacy
|
||||
end
|
||||
|
||||
root[module_name] = root.modules[module_name]
|
||||
MTH_SavedVariables = root
|
||||
return root.modules[module_name]
|
||||
end
|
||||
|
||||
function MTH:GetConfig(module_name, key, default)
|
||||
local store = MTH_ConfigEnsureStore(module_name)
|
||||
local value = store[key]
|
||||
if value == nil then
|
||||
return default
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
function MTH:SetConfig(module_name, key, value)
|
||||
local store = MTH_ConfigEnsureStore(module_name)
|
||||
store[key] = value
|
||||
end
|
||||
@@ -0,0 +1,152 @@
|
||||
MTH_CONST = MTH_CONST or {}
|
||||
MTH_CONST.version = MTH_CONST.version or "1.0.3"
|
||||
|
||||
MTH_CONST.WEAPON_TYPES = {
|
||||
BOWS = "Bows",
|
||||
CROSSBOWS = "Crossbows",
|
||||
GUNS = "Guns",
|
||||
}
|
||||
|
||||
MTH_CONST.AMMO = {
|
||||
BULLETS = {
|
||||
LIGHT = "Light Shot",
|
||||
CRAFTLIGHT = "Crafted Light Shot",
|
||||
FLASH = "Flash Pellet",
|
||||
HEAVY = "Heavy Shot",
|
||||
PEBBLE = "Smooth Pebble",
|
||||
CRAFTHEAVY = "Crafted Heavy Shot",
|
||||
SOLID = "Solid Shot",
|
||||
CRAFTSOLID = "Crafted Solid Shot",
|
||||
EXPLODING = "Exploding Shot",
|
||||
MITHRILSLUG = "Hi-impact Mithril Slugs",
|
||||
ACCURATE = "Accurate Slugs",
|
||||
GYROSHOT = "Mithril Gyro-Shot",
|
||||
ICETHREADED = "Ice Threaded Bullet",
|
||||
THORIUM = "Thorium Shells",
|
||||
ROCKSHARD = "Rockshard Pellets",
|
||||
CANNONBALL = "Miniature Cannon Balls",
|
||||
},
|
||||
ARROWS = {
|
||||
ROUGH = "Rough Arrow",
|
||||
SHARP = "Sharp Arrow",
|
||||
RAZOR = "Razor Arrow",
|
||||
FEATHER = "Feathered Arrow",
|
||||
PRECISION = "Precision Arrow",
|
||||
JAGGED = "Jagged Arrow",
|
||||
ICETHREADED = "Ice Threaded Arrow",
|
||||
THORIUM = "Thorium Headed Arrow",
|
||||
DOOMSHOT = "Doomshot",
|
||||
},
|
||||
JUNKSHOTS = {
|
||||
SERPENT = "Serpent Sting",
|
||||
SCORPID = "Scorpid Sting",
|
||||
VIPER = "Viper Sting",
|
||||
ARCANE = "Arcane Shot",
|
||||
CONCUSSIVE = "Concussive Shot",
|
||||
DISTRACTING = "Distracting Shot",
|
||||
BAITED = "Baited Shot",
|
||||
TRANQ = "Tranquilizing Shot",
|
||||
},
|
||||
}
|
||||
|
||||
MTH_WEAPON_BOWS = MTH_CONST.WEAPON_TYPES.BOWS
|
||||
MTH_WEAPON_CROSSBOWS = MTH_CONST.WEAPON_TYPES.CROSSBOWS
|
||||
MTH_WEAPON_GUNS = MTH_CONST.WEAPON_TYPES.GUNS
|
||||
|
||||
BULLETS_LIGHT = MTH_CONST.AMMO.BULLETS.LIGHT
|
||||
BULLETS_CRAFTLIGHT = MTH_CONST.AMMO.BULLETS.CRAFTLIGHT
|
||||
BULLETS_FLASH = MTH_CONST.AMMO.BULLETS.FLASH
|
||||
BULLETS_HEAVY = MTH_CONST.AMMO.BULLETS.HEAVY
|
||||
BULLETS_PEBBLE = MTH_CONST.AMMO.BULLETS.PEBBLE
|
||||
BULLETS_CRAFTHEAVY = MTH_CONST.AMMO.BULLETS.CRAFTHEAVY
|
||||
BULLETS_SOLID = MTH_CONST.AMMO.BULLETS.SOLID
|
||||
BULLETS_CRAFTSOLID = MTH_CONST.AMMO.BULLETS.CRAFTSOLID
|
||||
BULLETS_EXPLODING = MTH_CONST.AMMO.BULLETS.EXPLODING
|
||||
BULLETS_MITHRILSLUG = MTH_CONST.AMMO.BULLETS.MITHRILSLUG
|
||||
BULLETS_ACCURATE = MTH_CONST.AMMO.BULLETS.ACCURATE
|
||||
BULLETS_GYROSHOT = MTH_CONST.AMMO.BULLETS.GYROSHOT
|
||||
BULLETS_ICETHREADED = MTH_CONST.AMMO.BULLETS.ICETHREADED
|
||||
BULLETS_THORIUM = MTH_CONST.AMMO.BULLETS.THORIUM
|
||||
BULLETS_ROCKSHARD = MTH_CONST.AMMO.BULLETS.ROCKSHARD
|
||||
BULLETS_CANNONBALL = MTH_CONST.AMMO.BULLETS.CANNONBALL
|
||||
|
||||
ARROWS_ROUGH = MTH_CONST.AMMO.ARROWS.ROUGH
|
||||
ARROWS_SHARP = MTH_CONST.AMMO.ARROWS.SHARP
|
||||
ARROWS_RAZOR = MTH_CONST.AMMO.ARROWS.RAZOR
|
||||
ARROWS_FEATHER = MTH_CONST.AMMO.ARROWS.FEATHER
|
||||
ARROWS_PRECISION = MTH_CONST.AMMO.ARROWS.PRECISION
|
||||
ARROWS_JAGGED = MTH_CONST.AMMO.ARROWS.JAGGED
|
||||
ARROWS_ICETHREADED = MTH_CONST.AMMO.ARROWS.ICETHREADED
|
||||
ARROWS_THORIUM = MTH_CONST.AMMO.ARROWS.THORIUM
|
||||
ARROWS_DOOMSHOT = MTH_CONST.AMMO.ARROWS.DOOMSHOT
|
||||
|
||||
JUNKSHOT_SERPENT = MTH_CONST.AMMO.JUNKSHOTS.SERPENT
|
||||
JUNKSHOT_SCORPID = MTH_CONST.AMMO.JUNKSHOTS.SCORPID
|
||||
JUNKSHOT_VIPER = MTH_CONST.AMMO.JUNKSHOTS.VIPER
|
||||
JUNKSHOT_ARCANE = MTH_CONST.AMMO.JUNKSHOTS.ARCANE
|
||||
JUNKSHOT_CONCUSSIVE = MTH_CONST.AMMO.JUNKSHOTS.CONCUSSIVE
|
||||
JUNKSHOT_DISTRACTING = MTH_CONST.AMMO.JUNKSHOTS.DISTRACTING
|
||||
|
||||
MTH_AMMO_BULLETS = {
|
||||
BULLETS_LIGHT,
|
||||
BULLETS_CRAFTLIGHT,
|
||||
BULLETS_FLASH,
|
||||
BULLETS_HEAVY,
|
||||
BULLETS_PEBBLE,
|
||||
BULLETS_CRAFTHEAVY,
|
||||
BULLETS_SOLID,
|
||||
BULLETS_CRAFTSOLID,
|
||||
BULLETS_EXPLODING,
|
||||
BULLETS_MITHRILSLUG,
|
||||
BULLETS_ACCURATE,
|
||||
BULLETS_GYROSHOT,
|
||||
BULLETS_ICETHREADED,
|
||||
BULLETS_THORIUM,
|
||||
BULLETS_ROCKSHARD,
|
||||
BULLETS_CANNONBALL,
|
||||
}
|
||||
|
||||
MTH_AMMO_ARROWS = {
|
||||
ARROWS_ROUGH,
|
||||
ARROWS_SHARP,
|
||||
ARROWS_RAZOR,
|
||||
ARROWS_FEATHER,
|
||||
ARROWS_PRECISION,
|
||||
ARROWS_JAGGED,
|
||||
ARROWS_ICETHREADED,
|
||||
ARROWS_THORIUM,
|
||||
ARROWS_DOOMSHOT,
|
||||
}
|
||||
|
||||
MTH_AMMO_JUNKSHOTS = {
|
||||
JUNKSHOT_SERPENT,
|
||||
JUNKSHOT_SCORPID,
|
||||
JUNKSHOT_VIPER,
|
||||
JUNKSHOT_ARCANE,
|
||||
JUNKSHOT_CONCUSSIVE,
|
||||
JUNKSHOT_DISTRACTING,
|
||||
}
|
||||
|
||||
MTH_AMMO_BULLET_RANK = {}
|
||||
for i = 1, table.getn(MTH_AMMO_BULLETS) do
|
||||
local ammoName = MTH_AMMO_BULLETS[i]
|
||||
if ammoName then
|
||||
MTH_AMMO_BULLET_RANK[ammoName] = i
|
||||
end
|
||||
end
|
||||
|
||||
MTH_AMMO_ARROW_RANK = {}
|
||||
for i = 1, table.getn(MTH_AMMO_ARROWS) do
|
||||
local ammoName = MTH_AMMO_ARROWS[i]
|
||||
if ammoName then
|
||||
MTH_AMMO_ARROW_RANK[ammoName] = i
|
||||
end
|
||||
end
|
||||
|
||||
MTH_AMMO_JUNKSHOT_SET = {}
|
||||
for i = 1, table.getn(MTH_AMMO_JUNKSHOTS) do
|
||||
local spellName = MTH_AMMO_JUNKSHOTS[i]
|
||||
if spellName then
|
||||
MTH_AMMO_JUNKSHOT_SET[spellName] = 1
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,912 @@
|
||||
if not MTH then
|
||||
error("MetaHunt core framework missing: api/core-framework.lua must load before api/core-scan-beasttraining.lua")
|
||||
end
|
||||
|
||||
local function MTH_PT_SafeLower(text)
|
||||
if text == nil then return "" end
|
||||
return string.lower(tostring(text))
|
||||
end
|
||||
|
||||
local function MTH_PT_GetGlobal(name)
|
||||
if type(getglobal) == "function" then
|
||||
return getglobal(name)
|
||||
end
|
||||
if _G then
|
||||
return _G[name]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local MTH_PT_ApplyScan
|
||||
local MTH_PT_AbilityHasPositiveRanks
|
||||
local MTH_PT_PromoteUnrankedKnownAliases
|
||||
|
||||
local MTH_PT_VERBOSE_SCAN_LOGS = false
|
||||
|
||||
local function MTH_PT_GetStore()
|
||||
if not MTH_CharSavedVariables then MTH_CharSavedVariables = {} end
|
||||
if not MTH_CharSavedVariables.trainScan and MTH_CharSavedVariables.petTraining then
|
||||
MTH_CharSavedVariables.trainScan = MTH_CharSavedVariables.petTraining
|
||||
end
|
||||
if not MTH_CharSavedVariables.trainScan then
|
||||
MTH_CharSavedVariables.trainScan = {
|
||||
spellMap = {},
|
||||
hunterKnownMap = {},
|
||||
lastScan = 0,
|
||||
lastPrompt = 0,
|
||||
hasCompletedPetScan = nil,
|
||||
}
|
||||
end
|
||||
if not MTH_CharSavedVariables.trainScan.spellMap then
|
||||
MTH_CharSavedVariables.trainScan.spellMap = {}
|
||||
end
|
||||
if not MTH_CharSavedVariables.trainScan.hunterKnownMap then
|
||||
MTH_CharSavedVariables.trainScan.hunterKnownMap = {}
|
||||
end
|
||||
if MTH_CharSavedVariables.trainScan.hasCompletedPetScan == nil then
|
||||
if tonumber(MTH_CharSavedVariables.trainScan.lastScan) and tonumber(MTH_CharSavedVariables.trainScan.lastScan) > 0 then
|
||||
MTH_CharSavedVariables.trainScan.hasCompletedPetScan = 1
|
||||
elseif MTH_CharSavedVariables.trainScan.spellMap then
|
||||
for _ in pairs(MTH_CharSavedVariables.trainScan.spellMap) do
|
||||
MTH_CharSavedVariables.trainScan.hasCompletedPetScan = 1
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
MTH_CharSavedVariables.petTraining = MTH_CharSavedVariables.trainScan
|
||||
MTH_PT_PromoteUnrankedKnownAliases(MTH_CharSavedVariables.trainScan)
|
||||
return MTH_CharSavedVariables.trainScan
|
||||
end
|
||||
|
||||
function MTH_GetPetTrainingSnapshot()
|
||||
return MTH_PT_GetStore()
|
||||
end
|
||||
|
||||
function MTH_GetKnownPetSpellMap()
|
||||
local store = MTH_PT_GetStore()
|
||||
if type(store) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
return store.spellMap
|
||||
end
|
||||
|
||||
local function MTH_PT_ParseRank(name, subText)
|
||||
local rankNumber = nil
|
||||
local sub = tostring(subText or "")
|
||||
if sub ~= "" then
|
||||
local _, _, subRank = string.find(sub, "(%d+)")
|
||||
rankNumber = tonumber(subRank)
|
||||
end
|
||||
if not rankNumber then
|
||||
local nm = tostring(name or "")
|
||||
local _, _, nameRank = string.find(nm, "(%d+)")
|
||||
rankNumber = tonumber(nameRank)
|
||||
end
|
||||
if not rankNumber then
|
||||
local full = tostring(name or "") .. " " .. tostring(subText or "")
|
||||
local _, _, fullRank = string.find(full, "(%d+)")
|
||||
rankNumber = tonumber(fullRank)
|
||||
end
|
||||
if rankNumber then
|
||||
return "Rank " .. tostring(rankNumber), rankNumber
|
||||
end
|
||||
return "", nil
|
||||
end
|
||||
|
||||
local function MTH_PT_NormalizeName(name)
|
||||
local label = tostring(name or "")
|
||||
label = string.gsub(label, "^%s+", "")
|
||||
label = string.gsub(label, "%s+$", "")
|
||||
return label
|
||||
end
|
||||
|
||||
local function MTH_PT_BaseAbility(name)
|
||||
local base = MTH_PT_NormalizeName(name)
|
||||
base = string.gsub(base, "%s*%([^%)]*%d+[^%)]*%)", "")
|
||||
base = string.gsub(base, "%s*%([Rr]ank[^%)]*%)", "")
|
||||
base = string.gsub(base, "%s+[Rr]ank%s+%d+", "")
|
||||
base = string.gsub(base, "%s+%d+$", "")
|
||||
base = string.gsub(base, "%s+$", "")
|
||||
return base
|
||||
end
|
||||
|
||||
local function MTH_PT_MakeToken(name, subText)
|
||||
local base = MTH_PT_BaseAbility(name)
|
||||
local rankText, rankNumber = MTH_PT_ParseRank(name, subText)
|
||||
if rankNumber and rankNumber > 0 and not MTH_PT_AbilityHasPositiveRanks(base) then
|
||||
rankText = ""
|
||||
rankNumber = nil
|
||||
end
|
||||
if rankText ~= "" then
|
||||
return MTH_PT_SafeLower(base) .. "#" .. tostring(rankNumber), base, rankText, rankNumber
|
||||
end
|
||||
return MTH_PT_SafeLower(base), base, "", nil
|
||||
end
|
||||
|
||||
local function MTH_PT_HasAnyLearnedSpells()
|
||||
local store = MTH_PT_GetStore()
|
||||
local spellMap = store.spellMap
|
||||
if not spellMap then return false end
|
||||
for _ in pairs(spellMap) do
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function MTH_PT_IsBeastTrainingContextActive()
|
||||
local frameNames = { "BeastTrainingFrame", "PetTrainingFrame", "CraftFrame" }
|
||||
for i = 1, table.getn(frameNames) do
|
||||
local frameName = frameNames[i]
|
||||
local frame = (type(getglobal) == "function" and getglobal(frameName)) or (_G and _G[frameName])
|
||||
if frame then
|
||||
if frame.IsVisible and frame:IsVisible() then
|
||||
return true
|
||||
end
|
||||
if frame.IsShown and frame:IsShown() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function MTH_PT_GetBeastTrainingRowCount()
|
||||
if not MTH_PT_IsBeastTrainingContextActive() then
|
||||
return 0
|
||||
end
|
||||
|
||||
local getNumCrafts = MTH_PT_GetGlobal("GetNumCrafts")
|
||||
if type(getNumCrafts) ~= "function" then
|
||||
return 0
|
||||
end
|
||||
return tonumber(getNumCrafts()) or 0
|
||||
end
|
||||
|
||||
function MTH_PT_HasBeastTrainingRows()
|
||||
return MTH_PT_GetBeastTrainingRowCount() > 0
|
||||
end
|
||||
|
||||
local function MTH_PT_DebugLog(line)
|
||||
if not MTH_PT_VERBOSE_SCAN_LOGS then return end
|
||||
local text = "[TRAINSCAN] " .. tostring(line or "")
|
||||
MTH:Print(text, "debug")
|
||||
end
|
||||
|
||||
local function MTH_PT_MakeTokenFromAbilityRank(abilityName, rankNumber)
|
||||
local ability = MTH_PT_NormalizeName(abilityName)
|
||||
local key = MTH_PT_SafeLower(ability)
|
||||
if key == "" then return "", ability, "", nil end
|
||||
local rank = tonumber(rankNumber)
|
||||
if rank and rank > 0 and not MTH_PT_AbilityHasPositiveRanks(ability) then
|
||||
rank = nil
|
||||
end
|
||||
if rank and rank > 0 then
|
||||
return key .. "#" .. tostring(rank), ability, "Rank " .. tostring(rank), rank
|
||||
end
|
||||
return key, ability, "", nil
|
||||
end
|
||||
|
||||
local function MTH_PT_AbilityExistsInData(baseAbility)
|
||||
local ability = MTH_PT_NormalizeName(baseAbility)
|
||||
if ability == "" then return false end
|
||||
if not MTH_DS_PetSpells or not MTH_DS_PetSpells.byAbility then
|
||||
return false
|
||||
end
|
||||
if MTH_DS_PetSpells.byAbility[ability] then
|
||||
return true
|
||||
end
|
||||
for abilityName in pairs(MTH_DS_PetSpells.byAbility) do
|
||||
if MTH_PT_SafeLower(abilityName) == MTH_PT_SafeLower(ability) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
MTH_PT_AbilityHasPositiveRanks = function(baseAbility)
|
||||
local ability = MTH_PT_NormalizeName(baseAbility)
|
||||
if ability == "" then return false end
|
||||
if not MTH_DS_PetSpells or not MTH_DS_PetSpells.byAbility then
|
||||
return false
|
||||
end
|
||||
|
||||
local bucket = MTH_DS_PetSpells.byAbility[ability]
|
||||
if not bucket then
|
||||
for abilityName, row in pairs(MTH_DS_PetSpells.byAbility) do
|
||||
if MTH_PT_SafeLower(abilityName) == MTH_PT_SafeLower(ability) then
|
||||
bucket = row
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not (bucket and bucket.spells) then
|
||||
return false
|
||||
end
|
||||
|
||||
for i = 1, table.getn(bucket.spells) do
|
||||
local spell = bucket.spells[i]
|
||||
if spell then
|
||||
local rank = tonumber(spell.rankNumber)
|
||||
if not rank and spell.rank then
|
||||
local _, _, parsed = string.find(tostring(spell.rank), "(%d+)")
|
||||
rank = tonumber(parsed)
|
||||
end
|
||||
if rank and rank > 0 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
MTH_PT_PromoteUnrankedKnownAliases = function(store)
|
||||
if not store or not store.hunterKnownMap or not store.spellMap then
|
||||
return
|
||||
end
|
||||
|
||||
for token, isKnown in pairs(store.hunterKnownMap) do
|
||||
if isKnown == true then
|
||||
local _, _, baseToken = string.find(tostring(token), "^(.-)#%d+$")
|
||||
if baseToken and baseToken ~= "" then
|
||||
local baseRow = store.spellMap[baseToken]
|
||||
if baseRow and ((tonumber(baseRow.rankNumber) or 0) <= 0) then
|
||||
store.hunterKnownMap[baseToken] = true
|
||||
if baseRow.isKnown ~= true then
|
||||
baseRow.isKnown = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_PT_MarkHunterKnownToken(token)
|
||||
if token == nil or token == "" then
|
||||
return false
|
||||
end
|
||||
|
||||
local store = MTH_PT_GetStore()
|
||||
local markToken = token
|
||||
local _, _, baseToken = string.find(tostring(token), "^(.-)#%d+$")
|
||||
if baseToken and baseToken ~= "" then
|
||||
local baseRow = store.spellMap and store.spellMap[baseToken]
|
||||
if baseRow and ((tonumber(baseRow.rankNumber) or 0) <= 0) then
|
||||
markToken = baseToken
|
||||
end
|
||||
end
|
||||
local changed = false
|
||||
if store.hunterKnownMap[markToken] ~= true then
|
||||
store.hunterKnownMap[markToken] = true
|
||||
changed = true
|
||||
end
|
||||
if markToken ~= token and store.hunterKnownMap[token] ~= true then
|
||||
store.hunterKnownMap[token] = true
|
||||
changed = true
|
||||
end
|
||||
|
||||
if store.spellMap and store.spellMap[markToken] and store.spellMap[markToken].isKnown ~= true then
|
||||
store.spellMap[markToken].isKnown = true
|
||||
changed = true
|
||||
end
|
||||
if markToken ~= token and store.spellMap and store.spellMap[token] and store.spellMap[token].isKnown ~= true then
|
||||
store.spellMap[token].isKnown = true
|
||||
changed = true
|
||||
end
|
||||
|
||||
if changed and MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("hunter-known token promoted: " .. tostring(token))
|
||||
end
|
||||
|
||||
return changed
|
||||
end
|
||||
|
||||
local function MTH_PT_IsHunterTokenKnown(token)
|
||||
if token == nil or token == "" then
|
||||
return false
|
||||
end
|
||||
|
||||
local store = MTH_PT_GetStore()
|
||||
if store.hunterKnownMap and store.hunterKnownMap[token] == true then
|
||||
return true
|
||||
end
|
||||
|
||||
if store.spellMap and store.spellMap[token] and store.spellMap[token].isKnown == true then
|
||||
return true
|
||||
end
|
||||
|
||||
local _, _, baseToken = string.find(tostring(token), "^(.-)#%d+$")
|
||||
if baseToken and baseToken ~= "" then
|
||||
if store.hunterKnownMap and store.hunterKnownMap[baseToken] == true then
|
||||
return true
|
||||
end
|
||||
if store.spellMap and store.spellMap[baseToken] and store.spellMap[baseToken].isKnown == true then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local function MTH_PT_ProcessLearnSystemMessage(rawMessage)
|
||||
local message = tostring(rawMessage or "")
|
||||
if message == "" then return false end
|
||||
message = string.gsub(message, "|c%x%x%x%x%x%x%x%x", "")
|
||||
message = string.gsub(message, "|r", "")
|
||||
message = string.gsub(message, "^%s+", "")
|
||||
message = string.gsub(message, "%s+$", "")
|
||||
|
||||
local _, _, abilityName, rankText = string.find(message, "^[Yy]ou have learned a new spell:%s*(.-)%s*%([Rr]ank%s*(%d+)%)%.?$")
|
||||
if not abilityName or not rankText then
|
||||
_, _, abilityName, rankText = string.find(message, "^[Yy]ou have learned a new spell:%s*(.-)%s*[Rr]ank%s*(%d+)%.?$")
|
||||
end
|
||||
if not abilityName or not rankText then
|
||||
return false
|
||||
end
|
||||
|
||||
local rankNumber = tonumber(rankText)
|
||||
if not rankNumber then
|
||||
return false
|
||||
end
|
||||
|
||||
abilityName = MTH_PT_NormalizeName(abilityName)
|
||||
if abilityName == "" then return false end
|
||||
if not MTH_PT_AbilityExistsInData(abilityName) then
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("learn-msg ignored (not pet ability dataset): '" .. tostring(abilityName) .. "'")
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local token = MTH_PT_MakeTokenFromAbilityRank(abilityName, rankNumber)
|
||||
if token == "" then return false end
|
||||
|
||||
local alreadyKnown = MTH_PT_IsHunterTokenKnown(token)
|
||||
local changed = false
|
||||
if not alreadyKnown then
|
||||
changed = MTH_PT_MarkHunterKnownToken(token)
|
||||
end
|
||||
|
||||
local petRecorded = false
|
||||
if type(MTH_PETS_RecordCurrentPetLearnedAbility) == "function" then
|
||||
petRecorded = MTH_PETS_RecordCurrentPetLearnedAbility(abilityName, rankNumber, "beasttraining:learn-msg") and true or false
|
||||
end
|
||||
|
||||
if changed or petRecorded then
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("learn-msg captured: source='you have learned a new spell' ability='" .. tostring(abilityName) .. "' rank=" .. tostring(rankNumber) .. " token='" .. tostring(token) .. "' changed=" .. tostring(changed) .. " petRecorded=" .. tostring(petRecorded))
|
||||
end
|
||||
local label = tostring(abilityName)
|
||||
if rankNumber and rankNumber > 0 then
|
||||
label = label .. " (Rank " .. tostring(rankNumber) .. ")"
|
||||
end
|
||||
if MTH and MTH.Print then
|
||||
if changed then
|
||||
MTH:Print("Pet ability learned and recorded: " .. label .. ". It is now marked as known in MetaHunt.")
|
||||
else
|
||||
MTH:Print("Pet ability learned for current pet: " .. label .. ".")
|
||||
end
|
||||
end
|
||||
if changed then
|
||||
MTH_PT_ApplyScan("learn-msg")
|
||||
end
|
||||
if type(MTH_PSP_RequestScan) == "function" then
|
||||
MTH_PSP_RequestScan("train-learn-msg")
|
||||
end
|
||||
elseif alreadyKnown and MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("learn-msg ignored (already known and no active pet row update): token='" .. tostring(token) .. "'")
|
||||
end
|
||||
|
||||
return changed or petRecorded
|
||||
end
|
||||
|
||||
local function MTH_PT_CollectKnownBeastTrainingMap()
|
||||
if not MTH_PT_IsBeastTrainingContextActive() then
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("collect beast-training known skipped: explicit beast training context not active")
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
local getNumCrafts = MTH_PT_GetGlobal("GetNumCrafts")
|
||||
local getCraftInfo = MTH_PT_GetGlobal("GetCraftInfo")
|
||||
if type(getNumCrafts) ~= "function" or type(getCraftInfo) ~= "function" then
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("collect beast-training known unavailable: GetNumCrafts=" .. tostring(type(getNumCrafts)) .. ", GetCraftInfo=" .. tostring(type(getCraftInfo)))
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
local totalCrafts = tonumber(getNumCrafts()) or 0
|
||||
local knownMap = {}
|
||||
if totalCrafts <= 0 then
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("collect beast-training known empty: totalRows=0")
|
||||
end
|
||||
return knownMap
|
||||
end
|
||||
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("collect beast-training known start: totalRows=" .. tostring(totalCrafts))
|
||||
end
|
||||
for i = 1, totalCrafts do
|
||||
local craftName, craftSubSpellName, craftType = getCraftInfo(i)
|
||||
local typeText = tostring(craftType or "")
|
||||
local token, base, parsedRankText, rankNumber = MTH_PT_MakeToken(craftName, craftSubSpellName)
|
||||
|
||||
if token ~= "" then
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("beast-training row=" .. tostring(i) .. " token='" .. tostring(token) .. "' name='" .. tostring(base or "") .. "' rank='" .. tostring(parsedRankText or "") .. "' rankNumber=" .. tostring(rankNumber) .. " type='" .. typeText .. "'")
|
||||
end
|
||||
knownMap[token] = {
|
||||
name = MTH_PT_NormalizeName(base),
|
||||
rank = parsedRankText,
|
||||
rankNumber = rankNumber,
|
||||
fullName = MTH_PT_NormalizeName(craftName),
|
||||
subText = tostring(craftSubSpellName or ""),
|
||||
isKnown = true,
|
||||
scannedAt = time(),
|
||||
rowType = typeText,
|
||||
}
|
||||
end
|
||||
end
|
||||
if MTH_PT_DebugLog then
|
||||
local total = 0
|
||||
for _ in pairs(knownMap) do total = total + 1 end
|
||||
MTH_PT_DebugLog("collect beast-training known end: tokens=" .. tostring(total))
|
||||
end
|
||||
return knownMap
|
||||
end
|
||||
|
||||
function MTH_PT_ApplyScan(triggerReason)
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("scan start: trigger=" .. tostring(triggerReason or ""))
|
||||
end
|
||||
|
||||
local store = MTH_PT_GetStore()
|
||||
local oldMap = store.spellMap
|
||||
if not oldMap then oldMap = {} end
|
||||
local hunterKnownMap = store.hunterKnownMap or {}
|
||||
|
||||
local knownMap = MTH_PT_CollectKnownBeastTrainingMap()
|
||||
local knownCount = 0
|
||||
for _ in pairs(knownMap) do knownCount = knownCount + 1 end
|
||||
if knownCount <= 0 then
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("scan abort: beast-training known count=0")
|
||||
end
|
||||
return false, 0, 0, 0
|
||||
end
|
||||
for token in pairs(knownMap) do
|
||||
hunterKnownMap[token] = true
|
||||
end
|
||||
for token, row in pairs(oldMap) do
|
||||
if row and row.isKnown then
|
||||
hunterKnownMap[token] = true
|
||||
end
|
||||
end
|
||||
for token in pairs(knownMap) do
|
||||
if hunterKnownMap[token] == nil then
|
||||
hunterKnownMap[token] = false
|
||||
end
|
||||
end
|
||||
store.hunterKnownMap = hunterKnownMap
|
||||
|
||||
local newMap = {}
|
||||
if MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility then
|
||||
for abilityName, bucket in pairs(MTH_DS_PetSpells.byAbility) do
|
||||
if bucket and bucket.spells then
|
||||
for i = 1, table.getn(bucket.spells) do
|
||||
local spell = bucket.spells[i]
|
||||
if spell then
|
||||
local rankNumber = tonumber(spell.rankNumber)
|
||||
if not rankNumber and spell.rank then
|
||||
local _, _, parsed = string.find(tostring(spell.rank), "(%d+)")
|
||||
rankNumber = tonumber(parsed)
|
||||
end
|
||||
local token, baseName, rankText, parsedRankNumber = MTH_PT_MakeTokenFromAbilityRank(abilityName, rankNumber)
|
||||
if token ~= "" then
|
||||
local tpCost = tonumber(spell.trainingPointCost)
|
||||
if not tpCost and spell.cost then
|
||||
local _, _, parsedCost = string.find(tostring(spell.cost), "(%d+)")
|
||||
tpCost = tonumber(parsedCost)
|
||||
end
|
||||
newMap[token] = {
|
||||
name = baseName,
|
||||
rank = rankText,
|
||||
rankNumber = parsedRankNumber,
|
||||
fullName = rankText ~= "" and (baseName .. " " .. rankText) or baseName,
|
||||
subText = rankText,
|
||||
serviceType = "datastore",
|
||||
reqLevel = tonumber(spell.trainLevel),
|
||||
isKnown = hunterKnownMap[token] and true or false,
|
||||
isKnownPet = knownMap[token] and true or false,
|
||||
scannedAt = time(),
|
||||
trainingPointCost = tpCost,
|
||||
learnMethod = spell.learnMethod,
|
||||
icon = spell.icon,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for token, row in pairs(knownMap) do
|
||||
if newMap[token] then
|
||||
newMap[token].isKnownPet = true
|
||||
newMap[token].scannedAt = time()
|
||||
else
|
||||
newMap[token] = {
|
||||
name = row.name,
|
||||
rank = row.rank,
|
||||
rankNumber = row.rankNumber,
|
||||
fullName = row.fullName,
|
||||
subText = row.subText,
|
||||
serviceType = "beast-training",
|
||||
reqLevel = nil,
|
||||
isKnown = hunterKnownMap[token] and true or false,
|
||||
isKnownPet = true,
|
||||
scannedAt = time(),
|
||||
trainingPointCost = nil,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local discovered = 0
|
||||
for _ in pairs(newMap) do discovered = discovered + 1 end
|
||||
if discovered <= 0 then
|
||||
return false, 0, 0, 0
|
||||
end
|
||||
|
||||
local added = 0
|
||||
local removed = 0
|
||||
local addedDetails = {}
|
||||
for token in pairs(newMap) do
|
||||
if not oldMap[token] then
|
||||
added = added + 1
|
||||
local row = newMap[token]
|
||||
local spellName = MTH_PT_NormalizeName((row and row.name) or "")
|
||||
local rankText = tostring((row and row.rank) or "")
|
||||
if spellName == "" then
|
||||
spellName = MTH_PT_NormalizeName((row and row.fullName) or "")
|
||||
end
|
||||
if spellName ~= "" then
|
||||
local label = spellName
|
||||
if rankText ~= "" then
|
||||
label = label .. " " .. rankText
|
||||
end
|
||||
table.insert(addedDetails, label)
|
||||
end
|
||||
end
|
||||
end
|
||||
for token in pairs(oldMap) do
|
||||
if not newMap[token] then
|
||||
removed = removed + 1
|
||||
end
|
||||
end
|
||||
|
||||
store.spellMap = newMap
|
||||
store.lastScan = time()
|
||||
store.hasCompletedPetScan = 1
|
||||
MTH_CharSavedVariables.trainScan = store
|
||||
MTH_CharSavedVariables.petTraining = store
|
||||
|
||||
local totalRanks = 0
|
||||
local baseSet = {}
|
||||
for _, row in pairs(newMap) do
|
||||
totalRanks = totalRanks + 1
|
||||
local base = MTH_PT_SafeLower(row and row.name)
|
||||
if base ~= "" then baseSet[base] = true end
|
||||
end
|
||||
local totalBaselines = 0
|
||||
for _ in pairs(baseSet) do totalBaselines = totalBaselines + 1 end
|
||||
|
||||
if triggerReason == "initial" then
|
||||
MTH:Print("Initial train scan complete: " .. tostring(totalBaselines) .. " baselines / " .. tostring(totalRanks) .. " ranks recorded for this character.")
|
||||
elseif added > 0 then
|
||||
table.sort(addedDetails)
|
||||
local detailsText = table.concat(addedDetails, ", ")
|
||||
if detailsText ~= "" then
|
||||
local msg = "New pet data recorded (" .. tostring(added) .. " new rank(s)): " .. detailsText .. ". Known totals for this character: " .. tostring(totalBaselines) .. " ability type(s), " .. tostring(totalRanks) .. " rank(s)."
|
||||
MTH:Print(msg)
|
||||
MTH:Print("INFO: " .. msg, "debug")
|
||||
else
|
||||
local msg = "New pet data recorded (" .. tostring(added) .. " new rank(s)). Known totals for this character: " .. tostring(totalBaselines) .. " ability type(s), " .. tostring(totalRanks) .. " rank(s)."
|
||||
MTH:Print(msg)
|
||||
MTH:Print("INFO: " .. msg, "debug")
|
||||
end
|
||||
end
|
||||
|
||||
return true, added, totalBaselines, totalRanks
|
||||
end
|
||||
|
||||
local function MTH_PT_ShouldScanNow()
|
||||
return MTH_PT_HasBeastTrainingRows()
|
||||
end
|
||||
|
||||
MTH_PT_IsPetBookVisible = function()
|
||||
local frameNames = { "BeastTrainingFrame", "PetTrainingFrame", "CraftFrame" }
|
||||
for i = 1, table.getn(frameNames) do
|
||||
local frameName = frameNames[i]
|
||||
local frame = (type(getglobal) == "function" and getglobal(frameName)) or (_G and _G[frameName])
|
||||
if frame then
|
||||
if frame.IsVisible and frame:IsVisible() then
|
||||
return true
|
||||
end
|
||||
if frame.IsShown and frame:IsShown() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function MTH_PT_RunDoubleScan(triggerBase)
|
||||
local triggerText = tostring(triggerBase or "")
|
||||
local feedbackOnOpen = (
|
||||
triggerText == "petbook-open"
|
||||
or triggerText == "craft-open"
|
||||
or triggerText == "craft-update"
|
||||
or triggerText == "beast-training-rows-open"
|
||||
or triggerText == "petbook-visible-poll"
|
||||
)
|
||||
if not MTH_PT_ShouldScanNow() then
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("scan skipped: no beast-training rows, trigger=" .. tostring(triggerBase or ""))
|
||||
end
|
||||
if feedbackOnOpen and MTH and MTH.Print then
|
||||
MTH:Print("Beast Training opened, but scan did not run yet (no scannable rows detected).")
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local t0 = time()
|
||||
local ok, added, baselines, ranks = MTH_PT_ApplyScan(triggerText .. ":single")
|
||||
local t1 = time()
|
||||
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("scan complete: trigger=" .. tostring(triggerBase or "") .. " ok=" .. tostring(ok and true or false) .. " dt=" .. tostring((t1 or 0) - (t0 or 0)) .. "s")
|
||||
end
|
||||
if feedbackOnOpen and MTH and MTH.Print then
|
||||
if ok then
|
||||
local addedCount = tonumber(added) or 0
|
||||
local msg = "Beast Training scan complete: " .. tostring(baselines or 0) .. " baselines / " .. tostring(ranks or 0) .. " ranks."
|
||||
if addedCount > 0 then
|
||||
msg = msg .. " Added +" .. tostring(addedCount) .. " new rank(s)."
|
||||
end
|
||||
MTH:Print(msg)
|
||||
else
|
||||
MTH:Print("Beast Training scan attempted, but no data was recorded.")
|
||||
end
|
||||
end
|
||||
|
||||
return ok and true or false
|
||||
end
|
||||
|
||||
local MTH_PetTrainingFrame = nil
|
||||
local MTH_PT_PetBookWasVisible = false
|
||||
local MTH_PT_PetBookPollElapsed = 0
|
||||
local MTH_PT_TrainingRowsWerePresent = false
|
||||
local MTH_PT_LastTrainingRowCount = 0
|
||||
local MTH_PT_LastAutoDoubleScanAt = 0
|
||||
|
||||
local function MTH_PT_RunDoubleScanThrottled(triggerBase, minIntervalSeconds)
|
||||
local now = tonumber(time()) or 0
|
||||
local minInterval = tonumber(minIntervalSeconds) or 0
|
||||
if minInterval > 0 and (now - (MTH_PT_LastAutoDoubleScanAt or 0)) < minInterval then
|
||||
return false
|
||||
end
|
||||
MTH_PT_LastAutoDoubleScanAt = now
|
||||
return MTH_PT_RunDoubleScan(triggerBase)
|
||||
end
|
||||
|
||||
local function MTH_PT_OnEvent(_, evt, eventArg1)
|
||||
evt = evt or event
|
||||
eventArg1 = eventArg1 or arg1
|
||||
if not evt then
|
||||
return
|
||||
end
|
||||
|
||||
if evt == "PLAYER_ENTERING_WORLD" then
|
||||
if UnitClass then
|
||||
local _, classToken = UnitClass("player")
|
||||
if classToken ~= "HUNTER" then return end
|
||||
end
|
||||
if not MTH_PT_HasAnyLearnedSpells() then
|
||||
local store = MTH_PT_GetStore()
|
||||
local now = time()
|
||||
if not store.lastPrompt or (now - store.lastPrompt) > 60 then
|
||||
store.lastPrompt = now
|
||||
MTH:Print("No Beast Training scan found for this character. Open Beast Training (right panel) to record pet spell ranks.")
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if evt == "LEARNED_SPELL_IN_TAB" then
|
||||
if MTH_PT_ShouldScanNow() then
|
||||
MTH_PT_RunDoubleScan("petbook-visible:" .. tostring(evt))
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if evt == "PET_TRAINING_SHOW" then
|
||||
MTH_PT_RunDoubleScan("petbook-open")
|
||||
return
|
||||
end
|
||||
|
||||
if evt == "CRAFT_SHOW" then
|
||||
MTH_PT_RunDoubleScanThrottled("craft-open", 1)
|
||||
return
|
||||
end
|
||||
|
||||
if evt == "CRAFT_UPDATE" then
|
||||
if MTH_PT_ShouldScanNow() then
|
||||
MTH_PT_RunDoubleScanThrottled("craft-update", 1)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if evt == "SPELLS_CHANGED" then
|
||||
if MTH_PT_ShouldScanNow() then
|
||||
MTH_PT_RunDoubleScan("petbook-visible:" .. tostring(evt))
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if evt == "UNIT_PET" or evt == "PET_BAR_UPDATE" then
|
||||
if evt == "UNIT_PET" then
|
||||
local unit = eventArg1
|
||||
if unit ~= "player" then return end
|
||||
end
|
||||
if MTH_PT_ShouldScanNow() then
|
||||
MTH_PT_RunDoubleScan("petbook-visible:" .. tostring(evt))
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if evt == "CHAT_MSG_SYSTEM" then
|
||||
MTH_PT_ProcessLearnSystemMessage(eventArg1)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_PT_InitService()
|
||||
if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate("trainscan-init") then
|
||||
return
|
||||
end
|
||||
|
||||
if MTH_PetTrainingFrame then
|
||||
return
|
||||
end
|
||||
MTH_PetTrainingFrame = CreateFrame("Frame", "MTHPetTrainingScanFrame")
|
||||
MTH_PetTrainingFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_PetTrainingFrame:RegisterEvent("LEARNED_SPELL_IN_TAB")
|
||||
MTH_PetTrainingFrame:RegisterEvent("PET_TRAINING_SHOW")
|
||||
MTH_PetTrainingFrame:RegisterEvent("SPELLS_CHANGED")
|
||||
MTH_PetTrainingFrame:RegisterEvent("UNIT_PET")
|
||||
MTH_PetTrainingFrame:RegisterEvent("PET_BAR_UPDATE")
|
||||
MTH_PetTrainingFrame:RegisterEvent("CHAT_MSG_SYSTEM")
|
||||
MTH_PetTrainingFrame:RegisterEvent("CRAFT_SHOW")
|
||||
MTH_PetTrainingFrame:RegisterEvent("CRAFT_UPDATE")
|
||||
MTH_PetTrainingFrame:SetScript("OnEvent", MTH_PT_OnEvent)
|
||||
MTH_PetTrainingFrame:SetScript("OnUpdate", function(_, elapsed)
|
||||
elapsed = elapsed or arg1
|
||||
MTH_PT_PetBookPollElapsed = (MTH_PT_PetBookPollElapsed or 0) + (elapsed or 0)
|
||||
if MTH_PT_PetBookPollElapsed < 0.20 then
|
||||
return
|
||||
end
|
||||
MTH_PT_PetBookPollElapsed = 0
|
||||
|
||||
local rowCount = MTH_PT_GetBeastTrainingRowCount()
|
||||
local hasRows = rowCount > 0
|
||||
if hasRows and not MTH_PT_TrainingRowsWerePresent then
|
||||
MTH_PT_RunDoubleScanThrottled("beast-training-rows-open", 1)
|
||||
end
|
||||
|
||||
if hasRows and rowCount ~= (MTH_PT_LastTrainingRowCount or 0) then
|
||||
MTH_PT_RunDoubleScanThrottled("beast-training-rows-changed", 1)
|
||||
end
|
||||
|
||||
if hasRows then
|
||||
MTH_PT_RunDoubleScanThrottled("beast-training-rows-present", 8)
|
||||
end
|
||||
|
||||
MTH_PT_TrainingRowsWerePresent = hasRows
|
||||
MTH_PT_LastTrainingRowCount = rowCount
|
||||
|
||||
local visible = MTH_PT_IsPetBookVisible()
|
||||
if visible and not MTH_PT_PetBookWasVisible then
|
||||
MTH_PT_RunDoubleScanThrottled("petbook-visible-poll", 1)
|
||||
end
|
||||
|
||||
MTH_PT_PetBookWasVisible = visible and true or false
|
||||
end)
|
||||
end
|
||||
|
||||
function MTH_PT_ShutdownService(_reason)
|
||||
if not MTH_PetTrainingFrame then
|
||||
return
|
||||
end
|
||||
|
||||
MTH_PetTrainingFrame:UnregisterAllEvents()
|
||||
MTH_PetTrainingFrame:SetScript("OnEvent", nil)
|
||||
MTH_PetTrainingFrame:SetScript("OnUpdate", nil)
|
||||
MTH_PetTrainingFrame = nil
|
||||
|
||||
MTH_PT_PetBookWasVisible = false
|
||||
MTH_PT_PetBookPollElapsed = 0
|
||||
MTH_PT_TrainingRowsWerePresent = false
|
||||
MTH_PT_LastTrainingRowCount = 0
|
||||
MTH_PT_LastAutoDoubleScanAt = 0
|
||||
end
|
||||
|
||||
function MTH_CommandPetScan()
|
||||
local function requestBeastTrainingOpen()
|
||||
if MTH_PT_IsPetBookVisible and MTH_PT_IsPetBookVisible() then
|
||||
return true
|
||||
end
|
||||
|
||||
local bookTypePet = MTH_PT_GetGlobal("BOOKTYPE_PET")
|
||||
local openSpellBook = MTH_PT_GetGlobal("OpenSpellBook")
|
||||
if type(openSpellBook) == "function" and bookTypePet then
|
||||
openSpellBook(bookTypePet)
|
||||
end
|
||||
|
||||
if MTH_PT_IsPetBookVisible and MTH_PT_IsPetBookVisible() then
|
||||
return true
|
||||
end
|
||||
|
||||
local toggleSpellBook = MTH_PT_GetGlobal("ToggleSpellBook")
|
||||
if type(toggleSpellBook) == "function" then
|
||||
if bookTypePet then
|
||||
toggleSpellBook(bookTypePet)
|
||||
else
|
||||
toggleSpellBook()
|
||||
end
|
||||
end
|
||||
|
||||
return MTH_PT_IsPetBookVisible and MTH_PT_IsPetBookVisible() or false
|
||||
end
|
||||
|
||||
if UnitClass then
|
||||
local _, classToken = UnitClass("player")
|
||||
if classToken ~= "HUNTER" then
|
||||
MTH:Print("Train scan is hunter-only.")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if not MTH_PT_ShouldScanNow() then
|
||||
local opened = requestBeastTrainingOpen()
|
||||
if not MTH_PT_ShouldScanNow() then
|
||||
if opened then
|
||||
MTH:Print("Beast Training is open, but no scannable rows were detected yet. Try once more after the list fully loads.")
|
||||
else
|
||||
MTH:Print("Could not detect Beast Training rows. Open Beast Training (right panel) and run /mth trainscan again.")
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local ok, added, baselines, ranks = MTH_PT_ApplyScan("manual")
|
||||
if not ok then
|
||||
MTH:Print("Train scan failed. Keep Beast Training open and try /mth trainscan again.")
|
||||
return
|
||||
end
|
||||
|
||||
MTH:Print("Train scan saved for this character: " .. tostring(baselines) .. " baselines / " .. tostring(ranks) .. " ranks.")
|
||||
if (tonumber(added) or 0) > 0 then
|
||||
MTH:Print("Newly recorded ranks: +" .. tostring(added))
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_CommandTrainScan()
|
||||
return MTH_CommandPetScan()
|
||||
end
|
||||
|
||||
MTH_TR_InitService = MTH_PT_InitService
|
||||
MTH_TR_ShutdownService = MTH_PT_ShutdownService
|
||||
@@ -0,0 +1,295 @@
|
||||
if not MTH then
|
||||
error("MetaHunt core framework missing: api/core-framework.lua must load before api/core-scan-spellbookpet.lua")
|
||||
end
|
||||
|
||||
local MTH_PS_Frame = nil
|
||||
local MTH_PS_LastScanAt = 0
|
||||
local MTH_PS_VERBOSE_LOGS = false
|
||||
local MTH_PS_ForceScanUntil = 0
|
||||
local MTH_PS_ForceScanBudget = 0
|
||||
|
||||
local function MTH_PS_ClearForceWindow()
|
||||
MTH_PS_ForceScanUntil = 0
|
||||
MTH_PS_ForceScanBudget = 0
|
||||
end
|
||||
|
||||
local function MTH_PS_ArmForceWindow(durationSeconds, maxScans)
|
||||
local now = tonumber(time()) or 0
|
||||
local duration = tonumber(durationSeconds) or 4
|
||||
local budget = tonumber(maxScans) or 6
|
||||
if duration < 1 then duration = 1 end
|
||||
if budget < 1 then budget = 1 end
|
||||
MTH_PS_ForceScanUntil = now + duration
|
||||
MTH_PS_ForceScanBudget = budget
|
||||
end
|
||||
|
||||
local function MTH_PS_IsFollowupEventTrigger(trigger)
|
||||
local t = tostring(trigger or "")
|
||||
return t == "event:UNIT_PET"
|
||||
or t == "event:PET_BAR_UPDATE"
|
||||
or t == "event:SPELLS_CHANGED"
|
||||
or t == "event:LEARNED_SPELL_IN_TAB"
|
||||
end
|
||||
|
||||
local function MTH_PS_IsPriorityTrigger(trigger)
|
||||
local t = tostring(trigger or "")
|
||||
return t == "unit-pet-current-change" or t == "refresh-current-change"
|
||||
end
|
||||
|
||||
local function MTH_PS_DebugPrint(message, force)
|
||||
return
|
||||
end
|
||||
|
||||
local function MTH_PS_TraceTrigger(trigger, accepted, reason)
|
||||
return
|
||||
end
|
||||
|
||||
local function MTH_PS_SafeLower(text)
|
||||
if text == nil then return "" end
|
||||
return string.lower(tostring(text))
|
||||
end
|
||||
|
||||
local function MTH_PS_GetGlobal(name)
|
||||
if type(getglobal) == "function" then
|
||||
return getglobal(name)
|
||||
end
|
||||
if _G then
|
||||
return _G[name]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_PS_GetStore()
|
||||
if not MTH_CharSavedVariables then MTH_CharSavedVariables = {} end
|
||||
if type(MTH_CharSavedVariables.petSpellScan) ~= "table" then
|
||||
MTH_CharSavedVariables.petSpellScan = {
|
||||
lastScan = 0,
|
||||
lastTrigger = "",
|
||||
lastCount = 0,
|
||||
lastHash = "",
|
||||
lastChangeAt = 0,
|
||||
}
|
||||
end
|
||||
return MTH_CharSavedVariables.petSpellScan
|
||||
end
|
||||
|
||||
local function MTH_PS_ParseRank(name, subText)
|
||||
local rankNumber = nil
|
||||
local sub = tostring(subText or "")
|
||||
if sub ~= "" then
|
||||
local _, _, subRank = string.find(sub, "(%d+)")
|
||||
rankNumber = tonumber(subRank)
|
||||
end
|
||||
if not rankNumber then
|
||||
local _, _, nameRank = string.find(tostring(name or ""), "(%d+)")
|
||||
rankNumber = tonumber(nameRank)
|
||||
end
|
||||
if rankNumber and rankNumber > 0 then
|
||||
return rankNumber
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_PS_MakeToken(name, rankNumber)
|
||||
local base = MTH_PS_SafeLower(tostring(name or ""))
|
||||
if base == "" then return "" end
|
||||
local rank = tonumber(rankNumber)
|
||||
if rank and rank > 0 then
|
||||
return base .. "#" .. tostring(rank)
|
||||
end
|
||||
return base
|
||||
end
|
||||
|
||||
local function MTH_PS_CollectPetSpellbookRows()
|
||||
local getSpellName = MTH_PS_GetGlobal("GetSpellName")
|
||||
local getSpellTexture = MTH_PS_GetGlobal("GetSpellTexture")
|
||||
local isPassiveSpell = MTH_PS_GetGlobal("IsPassiveSpell")
|
||||
local bookTypePet = MTH_PS_GetGlobal("BOOKTYPE_PET")
|
||||
if type(getSpellName) ~= "function" or not bookTypePet then
|
||||
MTH_PS_DebugPrint("Collect rows aborted: GetSpellName=" .. tostring(type(getSpellName)) .. " BOOKTYPE_PET=" .. tostring(bookTypePet), true)
|
||||
return {}
|
||||
end
|
||||
|
||||
local rows = {}
|
||||
for slot = 1, 200 do
|
||||
local name, subText = getSpellName(slot, bookTypePet)
|
||||
if not name or name == "" then
|
||||
if slot == 1 then
|
||||
MTH_PS_DebugPrint("Slot 1 returned empty name (pet spellbook not ready/visible in this client context)", true)
|
||||
end
|
||||
break
|
||||
end
|
||||
local rankNumber = MTH_PS_ParseRank(name, subText)
|
||||
local token = MTH_PS_MakeToken(name, rankNumber)
|
||||
table.insert(rows, {
|
||||
slot = slot,
|
||||
name = tostring(name or ""),
|
||||
rank = rankNumber,
|
||||
icon = (type(getSpellTexture) == "function") and getSpellTexture(slot, bookTypePet) or nil,
|
||||
isPassive = (type(isPassiveSpell) == "function") and (isPassiveSpell(slot, bookTypePet) and true or false) or false,
|
||||
token = token,
|
||||
})
|
||||
end
|
||||
MTH_PS_DebugPrint("Collected rows: " .. tostring(table.getn(rows)), true)
|
||||
return rows
|
||||
end
|
||||
|
||||
local function MTH_PS_HashRows(rows)
|
||||
if type(rows) ~= "table" or table.getn(rows) == 0 then
|
||||
return ""
|
||||
end
|
||||
local parts = {}
|
||||
for i = 1, table.getn(rows) do
|
||||
local row = rows[i]
|
||||
parts[i] = tostring(row.token or "") .. ":" .. tostring(row.slot or "")
|
||||
end
|
||||
return table.concat(parts, "|")
|
||||
end
|
||||
|
||||
function MTH_PS_ScanNow(trigger)
|
||||
if UnitClass then
|
||||
local _, classToken = UnitClass("player")
|
||||
if classToken ~= "HUNTER" then
|
||||
return false, 0, false
|
||||
end
|
||||
end
|
||||
|
||||
local rows = MTH_PS_CollectPetSpellbookRows()
|
||||
local count = table.getn(rows)
|
||||
local hash = MTH_PS_HashRows(rows)
|
||||
local store = MTH_PS_GetStore()
|
||||
local changed = (hash ~= tostring(store.lastHash or ""))
|
||||
MTH_PS_DebugPrint("Scan trigger=" .. tostring(trigger or "auto") .. " count=" .. tostring(count) .. " changed=" .. tostring(changed and true or false), true)
|
||||
|
||||
store.lastScan = time()
|
||||
store.lastTrigger = tostring(trigger or "auto")
|
||||
store.lastCount = count
|
||||
if changed then
|
||||
store.lastHash = hash
|
||||
store.lastChangeAt = store.lastScan
|
||||
end
|
||||
|
||||
if type(MTH_PETS_RecordCurrentPetSpellbookSnapshot) == "function" then
|
||||
local persistedCount, updated = MTH_PETS_RecordCurrentPetSpellbookSnapshot(rows, "petspellscan:" .. tostring(trigger or "auto"))
|
||||
if updated == false then
|
||||
MTH_PS_DebugPrint("Snapshot skipped; preserved existing count=" .. tostring(persistedCount or 0), true)
|
||||
else
|
||||
MTH_PS_DebugPrint("Snapshot persisted to current pet row", true)
|
||||
end
|
||||
else
|
||||
MTH_PS_DebugPrint("Snapshot function missing: MTH_PETS_RecordCurrentPetSpellbookSnapshot", true)
|
||||
end
|
||||
|
||||
MTH_PS_LastScanAt = store.lastScan
|
||||
return true, count, changed
|
||||
end
|
||||
|
||||
function MTH_PSP_RequestScan(trigger, minIntervalSeconds)
|
||||
local triggerText = tostring(trigger or "request")
|
||||
local now = tonumber(time()) or 0
|
||||
local minInterval = tonumber(minIntervalSeconds) or 1
|
||||
local bypassThrottle = false
|
||||
local bypassReason = ""
|
||||
|
||||
if MTH_PS_IsPriorityTrigger(triggerText) then
|
||||
bypassThrottle = true
|
||||
bypassReason = "priority"
|
||||
MTH_PS_ArmForceWindow(4, 6)
|
||||
elseif MTH_PS_IsFollowupEventTrigger(triggerText)
|
||||
and MTH_PS_ForceScanBudget > 0
|
||||
and now <= (tonumber(MTH_PS_ForceScanUntil) or 0)
|
||||
then
|
||||
bypassThrottle = true
|
||||
bypassReason = "followup"
|
||||
MTH_PS_ForceScanBudget = MTH_PS_ForceScanBudget - 1
|
||||
end
|
||||
|
||||
if (not bypassThrottle) and minInterval > 0 and (now - (MTH_PS_LastScanAt or 0)) < minInterval then
|
||||
MTH_PS_TraceTrigger(triggerText, false, "throttled")
|
||||
return false
|
||||
end
|
||||
|
||||
if bypassThrottle then
|
||||
MTH_PS_TraceTrigger(triggerText, true, bypassReason)
|
||||
else
|
||||
MTH_PS_TraceTrigger(triggerText, true, "queued")
|
||||
end
|
||||
|
||||
local ok, count = MTH_PS_ScanNow(triggerText)
|
||||
if ok and tonumber(count) and tonumber(count) > 0 then
|
||||
MTH_PS_ClearForceWindow()
|
||||
elseif MTH_PS_IsPriorityTrigger(triggerText) and type(UnitExists) == "function" and UnitExists("pet") then
|
||||
MTH_PS_ArmForceWindow(4, 6)
|
||||
end
|
||||
if not ok then
|
||||
MTH_PS_TraceTrigger(triggerText, false, "scan-failed")
|
||||
end
|
||||
return ok and true or false
|
||||
end
|
||||
|
||||
local function MTH_PS_OnEvent(_, evt, eventArg1)
|
||||
evt = evt or event
|
||||
eventArg1 = eventArg1 or arg1
|
||||
if not evt then return end
|
||||
|
||||
if evt == "UNIT_PET" and eventArg1 ~= "player" then
|
||||
return
|
||||
end
|
||||
|
||||
if evt == "PLAYER_ENTERING_WORLD" or evt == "UNIT_PET" or evt == "PET_BAR_UPDATE" or evt == "SPELLS_CHANGED" or evt == "LEARNED_SPELL_IN_TAB" then
|
||||
MTH_PSP_RequestScan("event:" .. tostring(evt), 1)
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_PS_InitService()
|
||||
if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate("petspellscan-init") then
|
||||
return
|
||||
end
|
||||
if MTH_PS_Frame then
|
||||
return
|
||||
end
|
||||
|
||||
MTH_PS_Frame = CreateFrame("Frame", "MTHPetSpellScanFrame")
|
||||
MTH_PS_Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_PS_Frame:RegisterEvent("UNIT_PET")
|
||||
MTH_PS_Frame:RegisterEvent("PET_BAR_UPDATE")
|
||||
MTH_PS_Frame:RegisterEvent("SPELLS_CHANGED")
|
||||
MTH_PS_Frame:RegisterEvent("LEARNED_SPELL_IN_TAB")
|
||||
MTH_PS_Frame:SetScript("OnEvent", MTH_PS_OnEvent)
|
||||
end
|
||||
|
||||
function MTH_PS_ShutdownService(_reason)
|
||||
if not MTH_PS_Frame then
|
||||
return
|
||||
end
|
||||
MTH_PS_Frame:UnregisterAllEvents()
|
||||
MTH_PS_Frame:SetScript("OnEvent", nil)
|
||||
MTH_PS_Frame = nil
|
||||
MTH_PS_LastScanAt = 0
|
||||
MTH_PS_ClearForceWindow()
|
||||
end
|
||||
|
||||
function MTH_CommandPetSpellScan()
|
||||
MTH_PS_VERBOSE_LOGS = true
|
||||
local petName = UnitName and UnitName("pet") or nil
|
||||
local hasPet = petName and petName ~= ""
|
||||
local _, playerClass = UnitClass and UnitClass("player") or nil, nil
|
||||
if UnitClass then
|
||||
_, playerClass = UnitClass("player")
|
||||
end
|
||||
MTH_PS_DebugPrint("Manual command start: class=" .. tostring(playerClass) .. " pet=" .. tostring(petName or "<none>") .. " hasPet=" .. tostring(hasPet and true or false), true)
|
||||
local bookTypePet = MTH_PS_GetGlobal("BOOKTYPE_PET")
|
||||
local getSpellName = MTH_PS_GetGlobal("GetSpellName")
|
||||
MTH_PS_DebugPrint("API check: BOOKTYPE_PET=" .. tostring(bookTypePet) .. " GetSpellNameType=" .. tostring(type(getSpellName)), true)
|
||||
|
||||
local ok, count, changed = MTH_PS_ScanNow("manual")
|
||||
if not ok then
|
||||
MTH:Print("Pet spellbook scan failed.")
|
||||
MTH_PS_VERBOSE_LOGS = false
|
||||
return
|
||||
end
|
||||
MTH:Print("Pet spellbook scan: " .. tostring(count or 0) .. " spell(s). changed=" .. tostring(changed and true or false))
|
||||
local store = MTH_PS_GetStore()
|
||||
MTH_PS_DebugPrint("Store status: lastScan=" .. tostring(store.lastScan or 0) .. " lastCount=" .. tostring(store.lastCount or 0) .. " lastTrigger=" .. tostring(store.lastTrigger or "") .. " lastChangeAt=" .. tostring(store.lastChangeAt or 0), true)
|
||||
MTH_PS_VERBOSE_LOGS = false
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
+264
@@ -0,0 +1,264 @@
|
||||
if not MTH then
|
||||
error("MetaHunt core framework missing: api/core-framework.lua must load before api/core.lua")
|
||||
end
|
||||
|
||||
local function MTH_PT_GetGlobal(name)
|
||||
if type(getglobal) == "function" then
|
||||
return getglobal(name)
|
||||
end
|
||||
if _G then
|
||||
return _G[name]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local MTH_ItemLinkCacheTooltip = nil
|
||||
|
||||
local function MTH_GetQualityHexColor(quality)
|
||||
local q = tonumber(quality)
|
||||
if type(GetItemQualityColor) == "function" and q then
|
||||
local _, _, _, colorCode = GetItemQualityColor(q)
|
||||
if colorCode and colorCode ~= "" then
|
||||
local hex = string.gsub(tostring(colorCode), "|c", "")
|
||||
hex = string.gsub(hex, "|r", "")
|
||||
hex = string.gsub(hex, "^#", "")
|
||||
if string.len(hex) == 8 then return hex end
|
||||
if string.len(hex) == 6 then return "ff" .. hex end
|
||||
end
|
||||
end
|
||||
return "ffffffff"
|
||||
end
|
||||
|
||||
function MTH_PrimeItemCache(itemId)
|
||||
local id = tonumber(itemId)
|
||||
if not id or id <= 0 then return false end
|
||||
if type(GetItemInfo) == "function" and GetItemInfo(id) then return true end
|
||||
|
||||
if not MTH_ItemLinkCacheTooltip then
|
||||
MTH_ItemLinkCacheTooltip = CreateFrame("GameTooltip", "MTH_ItemLinkCacheTooltip", UIParent, "GameTooltipTemplate")
|
||||
if not MTH_ItemLinkCacheTooltip then return false end
|
||||
MTH_ItemLinkCacheTooltip:SetOwner(UIParent, "ANCHOR_NONE")
|
||||
end
|
||||
|
||||
MTH_ItemLinkCacheTooltip:ClearLines()
|
||||
MTH_ItemLinkCacheTooltip:SetHyperlink("item:" .. tostring(id) .. ":0:0:0")
|
||||
return type(GetItemInfo) == "function" and (GetItemInfo(id) ~= nil) or false
|
||||
end
|
||||
|
||||
function MTH_GetClickableItemLink(itemId, fallbackName, tryPrimeCache)
|
||||
local id = tonumber(itemId)
|
||||
if not id then return nil end
|
||||
|
||||
local itemName = nil
|
||||
local itemLink = nil
|
||||
local itemQuality = nil
|
||||
if type(GetItemInfo) == "function" then
|
||||
itemName, itemLink, itemQuality = GetItemInfo(id)
|
||||
if (not itemName or itemName == "") and tryPrimeCache then
|
||||
MTH_PrimeItemCache(id)
|
||||
itemName, itemLink, itemQuality = GetItemInfo(id)
|
||||
end
|
||||
end
|
||||
|
||||
local displayName = tostring(itemName or fallbackName or ("Item " .. tostring(id)))
|
||||
local coreLink = nil
|
||||
if itemLink and itemLink ~= "" then
|
||||
local _, _, parsedCore = string.find(tostring(itemLink), "|H(item:[^|]+)|h")
|
||||
if parsedCore and parsedCore ~= "" then
|
||||
coreLink = parsedCore
|
||||
elseif string.find(tostring(itemLink), "^item:", 1, true) then
|
||||
coreLink = tostring(itemLink)
|
||||
end
|
||||
end
|
||||
if not coreLink then
|
||||
coreLink = "item:" .. tostring(id) .. ":0:0:0"
|
||||
end
|
||||
|
||||
local colorHex = MTH_GetQualityHexColor(itemQuality)
|
||||
return "|c" .. tostring(colorHex) .. "|H" .. tostring(coreLink) .. "|h[" .. tostring(displayName) .. "]|h|r"
|
||||
end
|
||||
|
||||
function MTH_InsertLinkToChat(link)
|
||||
if type(link) ~= "string" or link == "" then return false end
|
||||
|
||||
local wimEditBox = MTH_PT_GetGlobal("WIM_EditBoxInFocus")
|
||||
if wimEditBox and type(wimEditBox.Insert) == "function" then
|
||||
wimEditBox:Insert(link)
|
||||
return true
|
||||
end
|
||||
|
||||
local chatEditBox = MTH_PT_GetGlobal("ChatFrameEditBox")
|
||||
if not chatEditBox then return false end
|
||||
|
||||
if type(chatEditBox.IsVisible) == "function" and chatEditBox:IsVisible() and type(chatEditBox.Insert) == "function" then
|
||||
chatEditBox:Insert(link)
|
||||
return true
|
||||
end
|
||||
|
||||
if type(chatEditBox.Show) == "function" then chatEditBox:Show() end
|
||||
if type(chatEditBox.Insert) == "function" then
|
||||
chatEditBox:Insert(link)
|
||||
elseif type(chatEditBox.SetText) == "function" then
|
||||
chatEditBox:SetText(link)
|
||||
if type(chatEditBox.HighlightText) == "function" then
|
||||
chatEditBox:HighlightText()
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Slash command handler (original, from backup)
|
||||
MTH:RegisterSlashAliases("MTH")
|
||||
|
||||
local function MTH_CommandBook()
|
||||
if type(MTH_OpenHunterBook) == "function" then
|
||||
MTH_OpenHunterBook()
|
||||
elseif type(MTH_ToggleHunterBook) == "function" then
|
||||
MTH_ToggleHunterBook()
|
||||
else
|
||||
MTH:Print("Hunter Book is not available yet")
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_CommandOptions()
|
||||
if MTH_OpenOptions then
|
||||
MTH_OpenOptions("General")
|
||||
else
|
||||
MTH:Print("Options window is not available yet")
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_CommandPetSpellScanFallback()
|
||||
local getSpellName = (type(getglobal) == "function" and getglobal("GetSpellName")) or (_G and _G["GetSpellName"])
|
||||
local getSpellTexture = (type(getglobal) == "function" and getglobal("GetSpellTexture")) or (_G and _G["GetSpellTexture"])
|
||||
local isPassiveSpell = (type(getglobal) == "function" and getglobal("IsPassiveSpell")) or (_G and _G["IsPassiveSpell"])
|
||||
local bookTypePet = (type(getglobal) == "function" and getglobal("BOOKTYPE_PET")) or (_G and _G["BOOKTYPE_PET"])
|
||||
|
||||
local petName = UnitName and UnitName("pet") or nil
|
||||
local pets = (type(MTH_PETS_GetRootStore) == "function") and MTH_PETS_GetRootStore() or nil
|
||||
local currentPetId = (type(pets) == "table" and type(pets.currentPet) == "table") and pets.currentPet.id or nil
|
||||
|
||||
if type(getSpellName) ~= "function" or not bookTypePet then
|
||||
return false, 0
|
||||
end
|
||||
|
||||
local rows = {}
|
||||
for slot = 1, 200 do
|
||||
local name, subText = getSpellName(slot, bookTypePet)
|
||||
if not name or name == "" then
|
||||
break
|
||||
end
|
||||
local rankNumber = nil
|
||||
if tostring(subText or "") ~= "" then
|
||||
local _, _, r = string.find(tostring(subText), "(%d+)")
|
||||
rankNumber = tonumber(r)
|
||||
end
|
||||
if not rankNumber then
|
||||
local _, _, r2 = string.find(tostring(name), "(%d+)")
|
||||
rankNumber = tonumber(r2)
|
||||
end
|
||||
local token = string.lower(tostring(name or ""))
|
||||
if rankNumber and rankNumber > 0 then
|
||||
token = token .. "#" .. tostring(rankNumber)
|
||||
end
|
||||
table.insert(rows, {
|
||||
slot = slot,
|
||||
name = tostring(name or ""),
|
||||
rank = rankNumber,
|
||||
icon = (type(getSpellTexture) == "function") and getSpellTexture(slot, bookTypePet) or nil,
|
||||
isPassive = (type(isPassiveSpell) == "function") and (isPassiveSpell(slot, bookTypePet) and true or false) or false,
|
||||
token = token,
|
||||
})
|
||||
end
|
||||
|
||||
local count = table.getn(rows)
|
||||
if count > 0 then
|
||||
local preview = {}
|
||||
local maxPreview = math.min(6, count)
|
||||
for i = 1, maxPreview do
|
||||
local row = rows[i]
|
||||
if type(row) == "table" then
|
||||
local line = tostring(row.name or "?")
|
||||
local rank = tonumber(row.rank)
|
||||
if rank and rank > 0 then
|
||||
line = line .. " R" .. tostring(rank)
|
||||
end
|
||||
table.insert(preview, line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if type(MTH_PETS_RecordCurrentPetSpellbookSnapshot) == "function" then
|
||||
local persisted, updated = MTH_PETS_RecordCurrentPetSpellbookSnapshot(rows, "petspellscan:core-fallback")
|
||||
return true, tonumber(persisted) or count
|
||||
else
|
||||
return false, count
|
||||
end
|
||||
end
|
||||
|
||||
if type(MTH_PS_ScanNow) ~= "function" then
|
||||
function MTH_PS_ScanNow(trigger)
|
||||
local ok, count = MTH_CommandPetSpellScanFallback()
|
||||
return ok and true or false, tonumber(count) or 0, false
|
||||
end
|
||||
end
|
||||
|
||||
if type(MTH_PSP_RequestScan) ~= "function" then
|
||||
local mthPsFallbackLastScan = 0
|
||||
function MTH_PSP_RequestScan(trigger, minIntervalSeconds)
|
||||
local now = tonumber(time()) or 0
|
||||
local minInterval = tonumber(minIntervalSeconds) or 1
|
||||
local triggerText = tostring(trigger or "request")
|
||||
if minInterval > 0 and (now - mthPsFallbackLastScan) < minInterval then
|
||||
return false
|
||||
end
|
||||
local ok = MTH_PS_ScanNow(triggerText)
|
||||
if ok then
|
||||
mthPsFallbackLastScan = now
|
||||
end
|
||||
return ok and true or false
|
||||
end
|
||||
end
|
||||
|
||||
if type(MTH_CommandPetSpellScan) ~= "function" then
|
||||
function MTH_CommandPetSpellScan()
|
||||
local ok, count = MTH_PS_ScanNow("manual-core-fallback")
|
||||
MTH:Print("Pet spellbook scan: " .. tostring(count or 0) .. " spell(s). ok=" .. tostring(ok and true or false))
|
||||
end
|
||||
end
|
||||
|
||||
function SlashCmdList.MTH(msg, editbox)
|
||||
if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate("slash") then
|
||||
return
|
||||
end
|
||||
|
||||
msg = msg or ""
|
||||
msg = string.gsub(tostring(msg), "^%s+", "")
|
||||
msg = string.gsub(msg, "%s+$", "")
|
||||
local lowerMsg = string.lower(msg)
|
||||
if msg == "" then
|
||||
MTH:Print("Available: /mth options, /mth book")
|
||||
elseif lowerMsg == "options" then
|
||||
MTH_CommandOptions()
|
||||
elseif lowerMsg == "book" or lowerMsg == "hunterbook" then
|
||||
MTH_CommandBook()
|
||||
else
|
||||
MTH:Print("Unknown command: " .. tostring(msg))
|
||||
MTH:Print("Available: /mth options, /mth book")
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize on load
|
||||
MTH:InitSavedVariables()
|
||||
if type(MTH_ST_InitService) == "function" then
|
||||
MTH_ST_InitService()
|
||||
end
|
||||
if type(MTH_TR_InitService) == "function" then
|
||||
MTH_TR_InitService()
|
||||
elseif type(MTH_PT_InitService) == "function" then
|
||||
MTH_PT_InitService()
|
||||
end
|
||||
if type(MTH_PS_InitService) == "function" then
|
||||
MTH_PS_InitService()
|
||||
end
|
||||
@@ -0,0 +1,374 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt: Debug Frame
|
||||
-- Displays errors and debug messages in a scrollable frame
|
||||
------------------------------------------------------
|
||||
|
||||
MTH_DebugFrame = {
|
||||
frame = nil,
|
||||
scrollFrame = nil,
|
||||
scrollText = nil,
|
||||
errors = {},
|
||||
maxErrors = 500,
|
||||
isVisible = false,
|
||||
initialized = false,
|
||||
captureCount = 0,
|
||||
infoCount = 0
|
||||
}
|
||||
|
||||
function MTH_DebugFrame:Initialize()
|
||||
if self.initialized then
|
||||
return
|
||||
end
|
||||
self.initialized = true
|
||||
|
||||
-- Create main frame
|
||||
self.frame = CreateFrame("Frame", "MTH_DebugFrameWindow", UIParent)
|
||||
self.frame:SetWidth(800)
|
||||
self.frame:SetHeight(400)
|
||||
self.frame:SetPoint("CENTER", UIParent, "CENTER")
|
||||
self.frame:SetFrameStrata("DIALOG")
|
||||
self.frame:SetBackdrop({
|
||||
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
|
||||
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
||||
tile = true,
|
||||
tileSize = 32,
|
||||
edgeSize = 32,
|
||||
insets = { left = 11, right = 12, top = 12, bottom = 11 }
|
||||
})
|
||||
self.frame:SetBackdropColor(0, 0, 0, 0.8)
|
||||
self.frame:SetMovable(true)
|
||||
self.frame:EnableMouse(true)
|
||||
self.frame:RegisterForDrag("LeftButton")
|
||||
self.frame:SetScript("OnDragStart", function() self.frame:StartMoving() end)
|
||||
self.frame:SetScript("OnDragStop", function() self.frame:StopMovingOrSizing() end)
|
||||
if UISpecialFrames then
|
||||
local already = false
|
||||
for i = 1, table.getn(UISpecialFrames) do
|
||||
if UISpecialFrames[i] == "MTH_DebugFrameWindow" then
|
||||
already = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not already then
|
||||
table.insert(UISpecialFrames, "MTH_DebugFrameWindow")
|
||||
end
|
||||
end
|
||||
|
||||
-- Title
|
||||
local title = self.frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
|
||||
title:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 20, -15)
|
||||
title:SetText("MetaHunt Debug - Errors & Messages")
|
||||
|
||||
-- Close button
|
||||
local closeButton = CreateFrame("Button", nil, self.frame, "UIPanelButtonTemplate")
|
||||
closeButton:SetPoint("TOPRIGHT", self.frame, "TOPRIGHT", -8, -8)
|
||||
closeButton:SetWidth(22)
|
||||
closeButton:SetHeight(22)
|
||||
closeButton:SetText("X")
|
||||
closeButton:SetScript("OnClick", function() MTH_DebugFrame:Hide() end)
|
||||
|
||||
-- Clear button
|
||||
local clearButton = CreateFrame("Button", nil, self.frame, "GameMenuButtonTemplate")
|
||||
clearButton:SetPoint("BOTTOMLEFT", self.frame, "BOTTOMLEFT", 10, 10)
|
||||
clearButton:SetWidth(100)
|
||||
clearButton:SetHeight(25)
|
||||
clearButton:SetText("Clear")
|
||||
clearButton:SetScript("OnClick", function() MTH_DebugFrame:Clear() end)
|
||||
|
||||
-- Select-all button
|
||||
local copyButton = CreateFrame("Button", nil, self.frame, "GameMenuButtonTemplate")
|
||||
copyButton:SetPoint("LEFT", clearButton, "RIGHT", 10, 0)
|
||||
copyButton:SetWidth(100)
|
||||
copyButton:SetHeight(25)
|
||||
copyButton:SetText("Select All")
|
||||
copyButton:SetScript("OnClick", function() MTH_DebugFrame:SelectAllText() end)
|
||||
|
||||
-- Scroll frame + text content with proper vertical scrollbar
|
||||
self.scrollFrame = CreateFrame("ScrollFrame", "MTH_DebugScrollFrame", self.frame, "UIPanelScrollFrameTemplate")
|
||||
self.scrollFrame:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 15, -50)
|
||||
self.scrollFrame:SetPoint("BOTTOMRIGHT", self.frame, "BOTTOMRIGHT", -35, 45)
|
||||
|
||||
self.scrollText = CreateFrame("EditBox", "MTH_DebugScrollText", self.scrollFrame)
|
||||
self.scrollText:SetMultiLine(true)
|
||||
self.scrollText:SetMaxLetters(999999)
|
||||
self.scrollText:SetFont("Fonts\\FRIZQT__.TTF", 11, "")
|
||||
self.scrollText:SetTextColor(0.8, 0.8, 0.8, 1)
|
||||
self.scrollText:SetWidth(730)
|
||||
self.scrollText:SetHeight(1)
|
||||
self.scrollText:EnableMouse(true)
|
||||
self.scrollText:SetScript("OnCursorChanged", function()
|
||||
if not MTH_DebugFrame.scrollFrame then
|
||||
return
|
||||
end
|
||||
local width = MTH_DebugFrame.scrollFrame:GetWidth()
|
||||
if width and width > 20 then
|
||||
MTH_DebugFrame.scrollText:SetWidth(width - 20)
|
||||
end
|
||||
end)
|
||||
self.scrollText:SetScript("OnEscapePressed", function() self.scrollText:ClearFocus() end)
|
||||
self.scrollText:SetAutoFocus(false)
|
||||
self.scrollText:EnableMouseWheel(true)
|
||||
self.scrollText:SetScript("OnMouseWheel", function()
|
||||
if not MTH_DebugFrame.scrollFrame then
|
||||
return
|
||||
end
|
||||
local curr = MTH_DebugFrame.scrollFrame:GetVerticalScroll()
|
||||
local step = 24
|
||||
if arg1 and arg1 > 0 then
|
||||
MTH_DebugFrame.scrollFrame:SetVerticalScroll(curr - step)
|
||||
else
|
||||
MTH_DebugFrame.scrollFrame:SetVerticalScroll(curr + step)
|
||||
end
|
||||
end)
|
||||
self.scrollText:SetScript("OnTextChanged", nil)
|
||||
|
||||
self.scrollFrame:SetScrollChild(self.scrollText)
|
||||
|
||||
-- Hide by default
|
||||
self.frame:Hide()
|
||||
self.isVisible = false
|
||||
end
|
||||
|
||||
function MTH_DebugFrame:AddError(message)
|
||||
if not message then return end
|
||||
|
||||
self.captureCount = self.captureCount + 1
|
||||
|
||||
if string.len(message) > 1000 then
|
||||
message = string.sub(message, 1, 1000) .. "..."
|
||||
end
|
||||
|
||||
table.insert(self.errors, 1, {
|
||||
time = date("%H:%M:%S"),
|
||||
message = message,
|
||||
level = "error"
|
||||
})
|
||||
|
||||
-- Limit stored errors
|
||||
if table.getn(self.errors) > self.maxErrors then
|
||||
table.remove(self.errors, self.maxErrors + 1)
|
||||
end
|
||||
|
||||
self:UpdateDisplay()
|
||||
end
|
||||
|
||||
function MTH_DebugFrame:AddInfo(message)
|
||||
if not message then return end
|
||||
|
||||
self.infoCount = self.infoCount + 1
|
||||
|
||||
if string.len(message) > 1000 then
|
||||
message = string.sub(message, 1, 1000) .. "..."
|
||||
end
|
||||
|
||||
table.insert(self.errors, 1, {
|
||||
time = date("%H:%M:%S"),
|
||||
message = message,
|
||||
level = "info"
|
||||
})
|
||||
|
||||
if table.getn(self.errors) > self.maxErrors then
|
||||
table.remove(self.errors, self.maxErrors + 1)
|
||||
end
|
||||
|
||||
self:UpdateDisplay()
|
||||
end
|
||||
|
||||
function MTH_DebugFrame:UpdateDisplay()
|
||||
if not self.scrollText or not self.initialized then
|
||||
return
|
||||
end
|
||||
|
||||
local text = "=== Errors Captured: " .. self.captureCount .. " ===\n"
|
||||
text = text .. "=== Info Captured: " .. (self.infoCount or 0) .. " ===\n"
|
||||
text = text .. "=== Total in queue: " .. table.getn(self.errors) .. " ===\n\n"
|
||||
|
||||
for i = 1, table.getn(self.errors) do
|
||||
local err = self.errors[i]
|
||||
text = text .. "[" .. err.time .. "] " .. err.message .. "\n"
|
||||
end
|
||||
|
||||
local _, lineCount = string.gsub(text, "\n", "\n")
|
||||
local lineHeight = 13
|
||||
local contentHeight = (lineCount + 1) * lineHeight + 8
|
||||
if contentHeight < 1 then
|
||||
contentHeight = 1
|
||||
end
|
||||
self.scrollText:SetHeight(contentHeight)
|
||||
self.scrollText:SetText(text)
|
||||
if self.scrollFrame then
|
||||
self.scrollFrame:SetVerticalScroll(0)
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_DebugFrame:Toggle()
|
||||
if self.isVisible then
|
||||
self.frame:Hide()
|
||||
self.isVisible = false
|
||||
else
|
||||
if not self.initialized then
|
||||
self:Initialize()
|
||||
end
|
||||
self.frame:Show()
|
||||
self.isVisible = true
|
||||
self:UpdateDisplay()
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_DebugFrame:Show()
|
||||
if not self.initialized then
|
||||
self:Initialize()
|
||||
end
|
||||
self.frame:Show()
|
||||
self.isVisible = true
|
||||
self:UpdateDisplay()
|
||||
end
|
||||
|
||||
function MTH_DebugFrame:Hide()
|
||||
if self.frame then
|
||||
self.frame:Hide()
|
||||
end
|
||||
self.isVisible = false
|
||||
end
|
||||
|
||||
function MTH_DebugFrame:Clear()
|
||||
self.errors = {}
|
||||
self.captureCount = 0
|
||||
self.infoCount = 0
|
||||
self:UpdateDisplay()
|
||||
if self.initialized and self.isVisible then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Debug frame cleared")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_DebugFrame:SelectAllText()
|
||||
if not self.scrollText or not self.initialized then
|
||||
return
|
||||
end
|
||||
-- Select all text
|
||||
self.scrollText:HighlightText(0, -1)
|
||||
self.scrollText:SetFocus()
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("All text selected - press Ctrl+C to copy")
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------
|
||||
-- Error Capture Hooks
|
||||
------------------------------------------------------
|
||||
local function MTH_DF_AddMessageCapture(msg)
|
||||
if msg then
|
||||
local msgStr = tostring(msg)
|
||||
if string.find(msgStr, "%.lua:%d+:") then
|
||||
MTH_DebugFrame:AddError(msgStr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_DF_IsGlobalCaptureEnabled()
|
||||
if MTH and MTH.GetConfig then
|
||||
return MTH:GetConfig("debug", "globalErrorCapture", false) and true or false
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function MTH_DF_SetGlobalCaptureEnabled(enabled)
|
||||
if MTH and MTH.SetConfig then
|
||||
MTH:SetConfig("debug", "globalErrorCapture", enabled and true or false)
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_DF_InstallHooks()
|
||||
if MTH_DebugFrame.hooksInstalled then
|
||||
return
|
||||
end
|
||||
if not MTH_DF_IsGlobalCaptureEnabled() then
|
||||
return
|
||||
end
|
||||
|
||||
if DEFAULT_CHAT_FRAME and type(DEFAULT_CHAT_FRAME.AddMessage) == "function" then
|
||||
MTH_DebugFrame.originalAddMessage = DEFAULT_CHAT_FRAME.AddMessage
|
||||
MTH_DebugFrame.addMessageHook = function(self, msg, arg1, arg2, arg3, arg4, arg5)
|
||||
MTH_DF_AddMessageCapture(msg)
|
||||
if MTH_DebugFrame.originalAddMessage then
|
||||
return MTH_DebugFrame.originalAddMessage(self, msg, arg1, arg2, arg3, arg4, arg5)
|
||||
end
|
||||
end
|
||||
DEFAULT_CHAT_FRAME.AddMessage = MTH_DebugFrame.addMessageHook
|
||||
end
|
||||
|
||||
if _G and type(_G["ScriptErrors"]) == "function" then
|
||||
MTH_DebugFrame.originalScriptErrors = _G["ScriptErrors"]
|
||||
MTH_DebugFrame.scriptErrorsHook = function(msg)
|
||||
if msg then
|
||||
MTH_DebugFrame:AddError(tostring(msg))
|
||||
end
|
||||
if MTH_DebugFrame.originalScriptErrors then
|
||||
return MTH_DebugFrame.originalScriptErrors(msg)
|
||||
end
|
||||
end
|
||||
_G["ScriptErrors"] = MTH_DebugFrame.scriptErrorsHook
|
||||
end
|
||||
|
||||
MTH_DebugFrame.hooksInstalled = true
|
||||
end
|
||||
|
||||
local function MTH_DF_UninstallHooks()
|
||||
if not MTH_DebugFrame.hooksInstalled then
|
||||
return
|
||||
end
|
||||
|
||||
if DEFAULT_CHAT_FRAME
|
||||
and MTH_DebugFrame.addMessageHook
|
||||
and DEFAULT_CHAT_FRAME.AddMessage == MTH_DebugFrame.addMessageHook
|
||||
and MTH_DebugFrame.originalAddMessage
|
||||
then
|
||||
DEFAULT_CHAT_FRAME.AddMessage = MTH_DebugFrame.originalAddMessage
|
||||
end
|
||||
|
||||
if _G
|
||||
and MTH_DebugFrame.scriptErrorsHook
|
||||
and _G["ScriptErrors"] == MTH_DebugFrame.scriptErrorsHook
|
||||
and MTH_DebugFrame.originalScriptErrors
|
||||
then
|
||||
_G["ScriptErrors"] = MTH_DebugFrame.originalScriptErrors
|
||||
end
|
||||
|
||||
MTH_DebugFrame.hooksInstalled = false
|
||||
end
|
||||
|
||||
function MTH_DebugFrame:IsGlobalCaptureEnabled()
|
||||
return MTH_DF_IsGlobalCaptureEnabled()
|
||||
end
|
||||
|
||||
function MTH_DebugFrame:SetGlobalCaptureEnabled(enabled)
|
||||
local want = enabled and true or false
|
||||
MTH_DF_SetGlobalCaptureEnabled(want)
|
||||
if want then
|
||||
MTH_DF_InstallHooks()
|
||||
else
|
||||
MTH_DF_UninstallHooks()
|
||||
end
|
||||
end
|
||||
|
||||
MTH_DebugFrame.InstallHooks = MTH_DF_InstallHooks
|
||||
MTH_DebugFrame.UninstallHooks = MTH_DF_UninstallHooks
|
||||
|
||||
-- Event listener for addon load
|
||||
local eventFrame = CreateFrame("Frame")
|
||||
eventFrame:RegisterEvent("ADDON_LOADED")
|
||||
eventFrame:SetScript("OnEvent", function(self, event, addonName)
|
||||
if event == "ADDON_LOADED" and addonName == "MetaHunt" then
|
||||
if MTH and MTH.GetConfig then
|
||||
MTH_DF_SetGlobalCaptureEnabled(MTH:GetConfig("debug", "globalErrorCapture", false))
|
||||
end
|
||||
MTH_DF_InstallHooks()
|
||||
MTH_DebugFrame:Initialize()
|
||||
if MTH and MTH.Print then
|
||||
local mode = MTH_DF_IsGlobalCaptureEnabled() and "legacy-global" or "safe-local"
|
||||
MTH:Print("=== MetaHunt Debug Frame Initialized (" .. mode .. ") ===", "debug")
|
||||
end
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,390 @@
|
||||
if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end
|
||||
|
||||
MTH_HUNTERBOOK_TABS.pets = {
|
||||
headerLabel = "Beasts",
|
||||
columnLabels = { "ID", "Lvl", "Family", "Name", "Abilities", "Zone", "R", "E", "U" },
|
||||
columnLayout = {
|
||||
{ x = 8, width = 28, align = "LEFT" },
|
||||
{ x = 38, width = 30, align = "LEFT" },
|
||||
{ x = 68, width = 74, align = "LEFT" },
|
||||
{ x = 142, width = 130, align = "LEFT" },
|
||||
{ x = 272, width = 180, align = "LEFT" },
|
||||
{ x = 452, width = 68, align = "LEFT" },
|
||||
{ x = 520, width = 10, align = "CENTER" },
|
||||
{ x = 532, width = 10, align = "CENTER" },
|
||||
{ x = 544, width = 10, align = "CENTER" },
|
||||
},
|
||||
}
|
||||
|
||||
function MTH_BOOK_GetBeastAbilitiesSummary(beast)
|
||||
if not beast or not beast.abilities or beast.abilities == "" or beast.abilities == "None" then
|
||||
return "-"
|
||||
end
|
||||
return tostring(beast.abilities)
|
||||
end
|
||||
|
||||
function MTH_BOOK_GetBeastZoneSummary(beast)
|
||||
if not beast or not beast.coords or table.getn(beast.coords) == 0 then
|
||||
return "-"
|
||||
end
|
||||
|
||||
local seen = {}
|
||||
local zoneNames = {}
|
||||
for i = 1, table.getn(beast.coords) do
|
||||
local c = beast.coords[i]
|
||||
if c and c[3] and not seen[c[3]] then
|
||||
seen[c[3]] = true
|
||||
table.insert(zoneNames, MTH_BOOK_GetZoneName(c[3]))
|
||||
end
|
||||
end
|
||||
|
||||
if table.getn(zoneNames) == 0 then return "-" end
|
||||
if table.getn(zoneNames) == 1 then return zoneNames[1] end
|
||||
return zoneNames[1] .. " +" .. tostring(table.getn(zoneNames) - 1)
|
||||
end
|
||||
|
||||
function MTH_BOOK_SplitAbilities(text)
|
||||
local out = {}
|
||||
if not text or text == "" or text == "None" then return out end
|
||||
local startPos = 1
|
||||
local textLen = string.len(text)
|
||||
while startPos <= textLen do
|
||||
local commaPos = string.find(text, ",", startPos, true)
|
||||
local token
|
||||
if commaPos then
|
||||
token = string.sub(text, startPos, commaPos - 1)
|
||||
startPos = commaPos + 1
|
||||
else
|
||||
token = string.sub(text, startPos)
|
||||
startPos = textLen + 1
|
||||
end
|
||||
token = string.gsub(token, "^%s+", "")
|
||||
token = string.gsub(token, "%s+$", "")
|
||||
if token ~= "" then
|
||||
table.insert(out, token)
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
function MTH_BOOK_HasUsableAbilities(beast)
|
||||
if not beast then return false end
|
||||
local abilities = MTH_BOOK_SplitAbilities(beast.abilities)
|
||||
if table.getn(abilities) == 0 then return false end
|
||||
for i = 1, table.getn(abilities) do
|
||||
local token = MTH_BOOK_SafeLower(abilities[i])
|
||||
if token ~= "" and token ~= "none" and token ~= "unknown" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_BOOK_HasKnownLevel(beast)
|
||||
if not beast then return false end
|
||||
if MTH_BOOK_IsUnknownText(beast.lvl) then return false end
|
||||
return MTH_BOOK_ParseLevel(beast.lvl) ~= nil
|
||||
end
|
||||
|
||||
function MTH_BOOK_HasKnownZone(beast)
|
||||
if not beast or not beast.coords or table.getn(beast.coords) == 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
for i = 1, table.getn(beast.coords) do
|
||||
local c = beast.coords[i]
|
||||
if c and c[3] then
|
||||
local zoneName = MTH_BOOK_SafeLower(MTH_BOOK_GetZoneName(c[3]))
|
||||
if zoneName ~= "" and zoneName ~= "unknown" and string.sub(zoneName, 1, 5) ~= "zone " then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_BOOK_ParseAbilityToken(token)
|
||||
local text = tostring(token or "")
|
||||
text = string.gsub(text, "^%s+", "")
|
||||
text = string.gsub(text, "%s+$", "")
|
||||
if text == "" then return "", "", nil end
|
||||
|
||||
local _, _, ability, rank = string.find(text, "^(.-)%s*(%d+)$")
|
||||
if ability and ability ~= "" then
|
||||
ability = string.gsub(ability, "^%s+", "")
|
||||
ability = string.gsub(ability, "%s+$", "")
|
||||
if ability ~= "" then
|
||||
return MTH_BOOK_SafeLower(ability), ability, tonumber(rank)
|
||||
end
|
||||
end
|
||||
|
||||
return MTH_BOOK_SafeLower(text), text, nil
|
||||
end
|
||||
|
||||
function MTH_BOOK_BeastHasAbilityRank(beast, abilityValue, rankValue)
|
||||
if not beast then return false end
|
||||
local wantedAbility = MTH_BOOK_SafeLower(abilityValue)
|
||||
if wantedAbility == "" or wantedAbility == "all" then return true end
|
||||
|
||||
local wantedRank = tonumber(rankValue)
|
||||
local abilities = MTH_BOOK_SplitAbilities(beast.abilities)
|
||||
for i = 1, table.getn(abilities) do
|
||||
local abilityLower, _, rank = MTH_BOOK_ParseAbilityToken(abilities[i])
|
||||
if abilityLower == wantedAbility then
|
||||
if not wantedRank then
|
||||
return true
|
||||
end
|
||||
if rank and rank == wantedRank then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_BOOK_ParseBeastTraits(beast)
|
||||
local traits = {
|
||||
rare = beast and beast.rare and true or false,
|
||||
elite = beast and (beast.elite == true or beast.isElite == true) or false,
|
||||
unique = beast and (beast.unique == true or beast.isUnique == true) or false,
|
||||
}
|
||||
|
||||
if not beast then return traits end
|
||||
|
||||
local rank = MTH_BOOK_SafeLower(beast.rank)
|
||||
if rank ~= "" then
|
||||
if string.find(rank, "rare", 1, true) then traits.rare = true end
|
||||
if string.find(rank, "elite", 1, true) then traits.elite = true end
|
||||
if string.find(rank, "unique", 1, true) then traits.unique = true end
|
||||
end
|
||||
|
||||
local cls = MTH_BOOK_SafeLower(beast.classification)
|
||||
if cls ~= "" then
|
||||
if string.find(cls, "rare", 1, true) then traits.rare = true end
|
||||
if string.find(cls, "elite", 1, true) then traits.elite = true end
|
||||
if string.find(cls, "unique", 1, true) then traits.unique = true end
|
||||
end
|
||||
|
||||
local name = MTH_BOOK_SafeLower(beast.name)
|
||||
if name ~= "" then
|
||||
local scanPos = 1
|
||||
while true do
|
||||
local startPos, endPos = string.find(name, "%b()", scanPos)
|
||||
if not startPos then break end
|
||||
local token = string.sub(name, startPos, endPos)
|
||||
scanPos = endPos + 1
|
||||
|
||||
local inner = string.sub(token, 2, -2)
|
||||
inner = string.gsub(inner, "^%s+", "")
|
||||
inner = string.gsub(inner, "%s+$", "")
|
||||
local normalized = string.gsub(inner, "[%s%-_/]", "")
|
||||
|
||||
if inner == "rare" or normalized == "r" then traits.rare = true end
|
||||
if inner == "elite" or normalized == "e" then traits.elite = true end
|
||||
if inner == "unique" or normalized == "u" then traits.unique = true end
|
||||
|
||||
if string.find(inner, "rare", 1, true) then traits.rare = true end
|
||||
if string.find(inner, "elite", 1, true) then traits.elite = true end
|
||||
if string.find(inner, "unique", 1, true) then traits.unique = true end
|
||||
|
||||
if normalized ~= "" and string.find(normalized, "[^reu]", 1) == nil then
|
||||
if string.find(normalized, "r", 1, true) then traits.rare = true end
|
||||
if string.find(normalized, "e", 1, true) then traits.elite = true end
|
||||
if string.find(normalized, "u", 1, true) then traits.unique = true end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return traits
|
||||
end
|
||||
|
||||
function MTH_BOOK_BeastMatches(beastId, beast)
|
||||
if not beast then return false end
|
||||
local forcedBeastId = MTH_BOOK_STATE and tonumber(MTH_BOOK_STATE.forcedBeastId) or nil
|
||||
if forcedBeastId and tonumber(beastId) ~= forcedBeastId then
|
||||
return false
|
||||
end
|
||||
local beastLevel = MTH_BOOK_ParseLevel(beast.lvl)
|
||||
local traits = MTH_BOOK_ParseBeastTraits(beast)
|
||||
local familyLower = MTH_BOOK_SafeLower(beast.family or "")
|
||||
|
||||
if MTH_BOOK_STATE.minLevel and (not beastLevel or beastLevel < MTH_BOOK_STATE.minLevel) then return false end
|
||||
if MTH_BOOK_STATE.maxLevel and (not beastLevel or beastLevel > MTH_BOOK_STATE.maxLevel) then return false end
|
||||
|
||||
if MTH_BOOK_STATE.flag1 and not traits.rare then return false end
|
||||
if MTH_BOOK_STATE.flag2 and not traits.elite then return false end
|
||||
if MTH_BOOK_STATE.flag3 and not traits.unique then return false end
|
||||
if not MTH_BOOK_BeastHasAbilityRank(beast, MTH_BOOK_STATE.petAbility, MTH_BOOK_STATE.petRank) then return false end
|
||||
if MTH_BOOK_STATE.petHideNoAbilities and not MTH_BOOK_HasUsableAbilities(beast) then return false end
|
||||
if MTH_BOOK_STATE.petHideUnknown then
|
||||
if familyLower == "unknown" then return false end
|
||||
if not MTH_BOOK_HasKnownLevel(beast) then return false end
|
||||
if not MTH_BOOK_HasKnownZone(beast) then return false end
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.quick ~= "all" then
|
||||
if familyLower ~= MTH_BOOK_STATE.quick then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.petInZoneOnly then
|
||||
local currentZoneId = MTH_BOOK_GetCurrentZoneId()
|
||||
if currentZoneId and not MTH_BOOK_BeastHasZoneId(beast, currentZoneId) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.search ~= "" then
|
||||
local name = MTH_BOOK_SafeLower(beast.name)
|
||||
local family = MTH_BOOK_SafeLower(beast.family)
|
||||
local idText = tostring(beastId)
|
||||
if string.find(name, MTH_BOOK_STATE.search, 1, true) == nil
|
||||
and string.find(family, MTH_BOOK_STATE.search, 1, true) == nil
|
||||
and string.find(idText, MTH_BOOK_STATE.search, 1, true) == nil
|
||||
then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_BOOK_BeastSort(a, b)
|
||||
local beasts = MTH_DS_Beasts
|
||||
local ba = beasts and beasts[a]
|
||||
local bb = beasts and beasts[b]
|
||||
if not ba or not bb then return a < b end
|
||||
local la = MTH_BOOK_ParseLevel(ba.lvl) or 0
|
||||
local lb = MTH_BOOK_ParseLevel(bb.lvl) or 0
|
||||
if la ~= lb then return la < lb end
|
||||
local na = ba.name or ""
|
||||
local nb = bb.name or ""
|
||||
if na ~= nb then return na < nb end
|
||||
return a < b
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_BeastLoreTrim(value)
|
||||
local text = tostring(value or "")
|
||||
text = string.gsub(text, "^%s+", "")
|
||||
text = string.gsub(text, "%s+$", "")
|
||||
return text
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_BeastLoreFindAbilityBundle(abilityName)
|
||||
if not (MTH_DS_PetSpells and type(MTH_DS_PetSpells.byAbility) == "table") then
|
||||
return nil
|
||||
end
|
||||
local direct = MTH_DS_PetSpells.byAbility[abilityName]
|
||||
if type(direct) == "table" then
|
||||
return direct
|
||||
end
|
||||
local wanted = MTH_BOOK_SafeLower(abilityName)
|
||||
for key, bundle in pairs(MTH_DS_PetSpells.byAbility) do
|
||||
if MTH_BOOK_SafeLower(key) == wanted then
|
||||
return bundle
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_BuildBeastAbilityEntries(beast)
|
||||
local entries = {}
|
||||
if type(beast) ~= "table" then
|
||||
return entries
|
||||
end
|
||||
|
||||
local tokens = MTH_BOOK_SplitAbilities(beast.abilities)
|
||||
local seen = {}
|
||||
for i = 1, table.getn(tokens) do
|
||||
local rawToken = MTH_BOOKTAB_BeastLoreTrim(tokens[i])
|
||||
if rawToken ~= "" then
|
||||
local tokenKey = MTH_BOOK_SafeLower(rawToken)
|
||||
if tokenKey ~= "" and not seen[tokenKey] then
|
||||
seen[tokenKey] = true
|
||||
|
||||
local _, abilityLabel, requestedRank = MTH_BOOK_ParseAbilityToken(rawToken)
|
||||
local bundle = MTH_BOOKTAB_BeastLoreFindAbilityBundle(abilityLabel)
|
||||
local canonical = abilityLabel
|
||||
if type(bundle) == "table" and type(bundle.ability) == "string" and bundle.ability ~= "" then
|
||||
canonical = bundle.ability
|
||||
end
|
||||
|
||||
local spellRows = (type(bundle) == "table" and type(bundle.spells) == "table") and bundle.spells or {}
|
||||
local preferredSpell = nil
|
||||
local firstBeastSpell = nil
|
||||
local rankMin = nil
|
||||
local rankMax = nil
|
||||
local rankMap = {}
|
||||
for s = 1, table.getn(spellRows) do
|
||||
local spell = spellRows[s]
|
||||
if type(spell) == "table" and MTH_BOOK_SafeLower(spell.learnMethod or "beast") ~= "trainer" then
|
||||
if not firstBeastSpell then
|
||||
firstBeastSpell = spell
|
||||
end
|
||||
local rank = tonumber(spell.rankNumber)
|
||||
if rank and rank > 0 then
|
||||
rankMap[rank] = true
|
||||
if not rankMin or rank < rankMin then rankMin = rank end
|
||||
if not rankMax or rank > rankMax then rankMax = rank end
|
||||
if requestedRank and requestedRank == rank then
|
||||
preferredSpell = spell
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local rankCount = 0
|
||||
for _ in pairs(rankMap) do
|
||||
rankCount = rankCount + 1
|
||||
end
|
||||
|
||||
if not preferredSpell then
|
||||
preferredSpell = firstBeastSpell
|
||||
end
|
||||
|
||||
local displayName = canonical
|
||||
if requestedRank then
|
||||
displayName = canonical .. " " .. tostring(requestedRank)
|
||||
end
|
||||
|
||||
table.insert(entries, {
|
||||
name = displayName,
|
||||
tooltipName = canonical,
|
||||
requestedRank = requestedRank,
|
||||
icon = preferredSpell and preferredSpell.icon or nil,
|
||||
description = preferredSpell and preferredSpell.description or "",
|
||||
rankMin = rankMin,
|
||||
rankMax = rankMax,
|
||||
rankCount = rankCount,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return entries
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_ShowBeastAbilityTooltip(anchor, abilityEntry)
|
||||
if not anchor or type(abilityEntry) ~= "table" then return end
|
||||
local tooltip = MTH_BOOK_GetSpellTooltip and MTH_BOOK_GetSpellTooltip() or GameTooltip
|
||||
if not tooltip then return end
|
||||
|
||||
tooltip:SetOwner(anchor, "ANCHOR_RIGHT")
|
||||
tooltip:ClearLines()
|
||||
if MTH_BOOK_SetSpellTooltipIcon then
|
||||
MTH_BOOK_SetSpellTooltipIcon(tooltip, abilityEntry.icon)
|
||||
end
|
||||
local title = tostring(abilityEntry.tooltipName or abilityEntry.name or "Unknown")
|
||||
if abilityEntry.requestedRank then
|
||||
title = title .. " " .. tostring(abilityEntry.requestedRank)
|
||||
end
|
||||
tooltip:AddLine(title, 1.00, 0.82, 0.00)
|
||||
if abilityEntry.description and abilityEntry.description ~= "" then
|
||||
tooltip:AddLine(" ")
|
||||
tooltip:AddLine(tostring(abilityEntry.description), 0.90, 0.90, 0.90, true)
|
||||
end
|
||||
tooltip:Show()
|
||||
end
|
||||
@@ -0,0 +1,398 @@
|
||||
if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end
|
||||
|
||||
MTH_HUNTERBOOK_TABS.families = {
|
||||
headerLabel = "Families",
|
||||
columnLabels = { "Family", "Named", "Coords", "Abilities", "Diet" },
|
||||
columnLayout = {
|
||||
{ x = 10, width = 106, align = "LEFT" },
|
||||
{ x = 122, width = 52, align = "LEFT" },
|
||||
{ x = 176, width = 52, align = "LEFT" },
|
||||
{ x = 232, width = 292, align = "LEFT" },
|
||||
{ x = 460, width = 184, align = "LEFT" },
|
||||
},
|
||||
}
|
||||
|
||||
local function MTH_BOOKTAB_FamiliesTrace(message)
|
||||
return
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_FamiliesSafeLower(value)
|
||||
return string.lower(tostring(value or ""))
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_FamiliesTrim(value)
|
||||
local text = tostring(value or "")
|
||||
text = string.gsub(text, "^%s+", "")
|
||||
text = string.gsub(text, "%s+$", "")
|
||||
return text
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_FindAbilityBundle(abilityName)
|
||||
if not (MTH_DS_PetSpells and type(MTH_DS_PetSpells.byAbility) == "table") then
|
||||
return nil
|
||||
end
|
||||
local direct = MTH_DS_PetSpells.byAbility[abilityName]
|
||||
if type(direct) == "table" then
|
||||
return direct
|
||||
end
|
||||
local wanted = MTH_BOOKTAB_FamiliesSafeLower(abilityName)
|
||||
for key, bundle in pairs(MTH_DS_PetSpells.byAbility) do
|
||||
if MTH_BOOKTAB_FamiliesSafeLower(key) == wanted then
|
||||
return bundle
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_BuildFamiliesRows()
|
||||
local results = {}
|
||||
local families = MTH_DS_Families
|
||||
if type(families) ~= "table" then
|
||||
return results
|
||||
end
|
||||
|
||||
local abilityCounts = {}
|
||||
local canonicalMap = {}
|
||||
|
||||
for _, familyRow in pairs(families) do
|
||||
if type(familyRow) == "table" and type(familyRow.abilities) == "table" then
|
||||
local seenInFamily = {}
|
||||
for i = 1, table.getn(familyRow.abilities) do
|
||||
local rawName = MTH_BOOKTAB_FamiliesTrim(familyRow.abilities[i])
|
||||
if rawName ~= "" then
|
||||
local bundle = MTH_BOOKTAB_FindAbilityBundle(rawName)
|
||||
local canonical = rawName
|
||||
if type(bundle) == "table" and type(bundle.ability) == "string" and bundle.ability ~= "" then
|
||||
canonical = bundle.ability
|
||||
end
|
||||
local token = MTH_BOOKTAB_FamiliesSafeLower(canonical)
|
||||
if token ~= "" and not seenInFamily[token] then
|
||||
seenInFamily[token] = true
|
||||
abilityCounts[token] = (abilityCounts[token] or 0) + 1
|
||||
canonicalMap[token] = canonical
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for familyName, familyRow in pairs(families) do
|
||||
if type(familyRow) == "table" then
|
||||
local abilities = {}
|
||||
local seen = {}
|
||||
|
||||
if type(familyRow.abilities) == "table" then
|
||||
for i = 1, table.getn(familyRow.abilities) do
|
||||
local rawName = MTH_BOOKTAB_FamiliesTrim(familyRow.abilities[i])
|
||||
if rawName ~= "" then
|
||||
local bundle = MTH_BOOKTAB_FindAbilityBundle(rawName)
|
||||
local canonical = rawName
|
||||
if type(bundle) == "table" and type(bundle.ability) == "string" and bundle.ability ~= "" then
|
||||
canonical = bundle.ability
|
||||
end
|
||||
|
||||
local token = MTH_BOOKTAB_FamiliesSafeLower(canonical)
|
||||
if token ~= "" and not seen[token] then
|
||||
seen[token] = true
|
||||
local spellRows = (type(bundle) == "table" and type(bundle.spells) == "table") and bundle.spells or {}
|
||||
local firstBeastSpell = nil
|
||||
local rankMin = nil
|
||||
local rankMax = nil
|
||||
local rankMap = {}
|
||||
for s = 1, table.getn(spellRows) do
|
||||
local spell = spellRows[s]
|
||||
if type(spell) == "table" and MTH_BOOKTAB_FamiliesSafeLower(spell.learnMethod or "beast") ~= "trainer" then
|
||||
if not firstBeastSpell then
|
||||
firstBeastSpell = spell
|
||||
end
|
||||
local rank = tonumber(spell.rankNumber)
|
||||
if rank and rank > 0 then
|
||||
rankMap[rank] = true
|
||||
if not rankMin or rank < rankMin then rankMin = rank end
|
||||
if not rankMax or rank > rankMax then rankMax = rank end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local rankCount = 0
|
||||
for _ in pairs(rankMap) do
|
||||
rankCount = rankCount + 1
|
||||
end
|
||||
|
||||
table.insert(abilities, {
|
||||
name = canonicalMap[token] or canonical,
|
||||
token = token,
|
||||
isUnique = (abilityCounts[token] or 0) == 1,
|
||||
icon = firstBeastSpell and firstBeastSpell.icon or nil,
|
||||
description = firstBeastSpell and firstBeastSpell.description or "",
|
||||
rankMin = rankMin,
|
||||
rankMax = rankMax,
|
||||
rankCount = rankCount,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(abilities, function(a, b)
|
||||
if a.isUnique ~= b.isUnique then
|
||||
return a.isUnique and true or false
|
||||
end
|
||||
return MTH_BOOKTAB_FamiliesSafeLower(a.name) < MTH_BOOKTAB_FamiliesSafeLower(b.name)
|
||||
end)
|
||||
|
||||
local dietItems = {}
|
||||
if type(familyRow.food) == "table" then
|
||||
for i = 1, table.getn(familyRow.food) do
|
||||
local food = MTH_BOOKTAB_FamiliesTrim(familyRow.food[i])
|
||||
if food ~= "" then
|
||||
table.insert(dietItems, string.upper(string.sub(food, 1, 1)) .. string.sub(food, 2))
|
||||
end
|
||||
end
|
||||
table.sort(dietItems, function(a, b)
|
||||
return MTH_BOOKTAB_FamiliesSafeLower(a) < MTH_BOOKTAB_FamiliesSafeLower(b)
|
||||
end)
|
||||
end
|
||||
|
||||
table.insert(results, {
|
||||
family = tostring(familyName),
|
||||
named = tonumber(familyRow.named) or 0,
|
||||
coords = tonumber(familyRow.coords) or 0,
|
||||
abilities = abilities,
|
||||
dietText = table.concat(dietItems, ", "),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(results, function(a, b)
|
||||
return MTH_BOOKTAB_FamiliesSafeLower(a.family) < MTH_BOOKTAB_FamiliesSafeLower(b.family)
|
||||
end)
|
||||
|
||||
return results
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_ShowFamilyAbilityTooltip(anchor, abilityEntry)
|
||||
if not anchor or type(abilityEntry) ~= "table" then return end
|
||||
local tooltip = MTH_BOOK_GetSpellTooltip and MTH_BOOK_GetSpellTooltip() or GameTooltip
|
||||
if not tooltip then return end
|
||||
|
||||
tooltip:SetOwner(anchor, "ANCHOR_RIGHT")
|
||||
tooltip:ClearLines()
|
||||
if MTH_BOOK_SetSpellTooltipIcon then
|
||||
MTH_BOOK_SetSpellTooltipIcon(tooltip, abilityEntry.icon)
|
||||
end
|
||||
tooltip:AddLine(tostring(abilityEntry.name or "Unknown"), 1.00, 0.82, 0.00)
|
||||
tooltip:AddLine(" ")
|
||||
tooltip:AddLine("Baseline", 1.00, 0.82, 0.00)
|
||||
if abilityEntry.rankCount and abilityEntry.rankCount > 0 then
|
||||
if abilityEntry.rankMin and abilityEntry.rankMax then
|
||||
if abilityEntry.rankMin == abilityEntry.rankMax then
|
||||
tooltip:AddDoubleLine("Ranks", tostring(abilityEntry.rankMin), 0.85, 0.85, 0.85, 1, 1, 1)
|
||||
else
|
||||
tooltip:AddDoubleLine("Ranks", tostring(abilityEntry.rankMin) .. "-" .. tostring(abilityEntry.rankMax), 0.85, 0.85, 0.85, 1, 1, 1)
|
||||
end
|
||||
end
|
||||
else
|
||||
tooltip:AddDoubleLine("Ranks", "Unranked", 0.85, 0.85, 0.85, 1, 1, 1)
|
||||
end
|
||||
tooltip:AddDoubleLine("Family-Unique", abilityEntry.isUnique and "Yes" or "No", 0.85, 0.85, 0.85, 1, 1, 1)
|
||||
if abilityEntry.description and abilityEntry.description ~= "" then
|
||||
tooltip:AddLine(" ")
|
||||
tooltip:AddLine(tostring(abilityEntry.description), 0.90, 0.90, 0.90, true)
|
||||
end
|
||||
tooltip:Show()
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_EnsureFamiliesUI()
|
||||
if MTH_BOOK_STATE.familiesUI then return end
|
||||
local listParent = getglobal("MTH_BOOK_ListBackdrop")
|
||||
if not listParent then
|
||||
MTH_BOOKTAB_FamiliesTrace("EnsureFamiliesUI failed: missing MTH_BOOK_ListBackdrop")
|
||||
return
|
||||
end
|
||||
|
||||
local ui = {}
|
||||
ui.frame = CreateFrame("Frame", nil, listParent)
|
||||
ui.frame:SetPoint("TOPLEFT", listParent, "TOPLEFT", 6, -6)
|
||||
ui.frame:SetPoint("BOTTOMRIGHT", listParent, "BOTTOMRIGHT", -6, 6)
|
||||
ui.frame:Hide()
|
||||
|
||||
ui.headerFamily = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerFamily:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 4, -2)
|
||||
ui.headerFamily:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerFamily:SetText("Family")
|
||||
|
||||
ui.headerNamed = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerNamed:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 122, -2)
|
||||
ui.headerNamed:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerNamed:SetText("Named")
|
||||
|
||||
ui.headerCoords = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerCoords:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 176, -2)
|
||||
ui.headerCoords:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerCoords:SetText("Coords")
|
||||
|
||||
ui.headerAbilities = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerAbilities:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 232, -2)
|
||||
ui.headerAbilities:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerAbilities:SetText("Abilities")
|
||||
|
||||
ui.headerDiet = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerDiet:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 460, -2)
|
||||
ui.headerDiet:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerDiet:SetText("Diet")
|
||||
|
||||
ui.rows = {}
|
||||
for i = 1, 28 do
|
||||
local row = CreateFrame("Frame", nil, ui.frame)
|
||||
row:SetHeight(18)
|
||||
if i == 1 then
|
||||
row:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 0, -18)
|
||||
row:SetPoint("TOPRIGHT", ui.frame, "TOPRIGHT", 0, -18)
|
||||
else
|
||||
row:SetPoint("TOPLEFT", ui.rows[i - 1], "BOTTOMLEFT", 0, -1)
|
||||
row:SetPoint("TOPRIGHT", ui.rows[i - 1], "BOTTOMRIGHT", 0, -1)
|
||||
end
|
||||
|
||||
row.family = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.family:SetPoint("LEFT", row, "LEFT", 4, 0)
|
||||
row.family:SetWidth(106)
|
||||
row.family:SetJustifyH("LEFT")
|
||||
|
||||
row.named = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.named:SetPoint("LEFT", row, "LEFT", 122, 0)
|
||||
row.named:SetWidth(52)
|
||||
row.named:SetJustifyH("LEFT")
|
||||
|
||||
row.coords = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.coords:SetPoint("LEFT", row, "LEFT", 176, 0)
|
||||
row.coords:SetWidth(52)
|
||||
row.coords:SetJustifyH("LEFT")
|
||||
|
||||
row.diet = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.diet:SetPoint("LEFT", row, "LEFT", 460, 0)
|
||||
row.diet:SetWidth(184)
|
||||
row.diet:SetJustifyH("LEFT")
|
||||
|
||||
row.abilityButtons = {}
|
||||
for b = 1, 16 do
|
||||
local button = CreateFrame("Button", nil, row)
|
||||
button:SetHeight(14)
|
||||
button.icon = button:CreateTexture(nil, "ARTWORK")
|
||||
button.icon:SetPoint("LEFT", button, "LEFT", 0, 0)
|
||||
button.icon:SetWidth(12)
|
||||
button.icon:SetHeight(12)
|
||||
button.text = button:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
button.text:SetPoint("LEFT", button.icon, "RIGHT", 2, 0)
|
||||
button.text:SetJustifyH("LEFT")
|
||||
button.text:SetTextColor(0.90, 0.90, 0.90)
|
||||
button.entry = nil
|
||||
button:SetScript("OnEnter", function()
|
||||
if this and this.entry then
|
||||
MTH_BOOKTAB_ShowFamilyAbilityTooltip(this, this.entry)
|
||||
end
|
||||
end)
|
||||
button:SetScript("OnLeave", function()
|
||||
if type(MTH_BOOK_HideSpellTooltip) == "function" then
|
||||
MTH_BOOK_HideSpellTooltip()
|
||||
end
|
||||
end)
|
||||
button:Hide()
|
||||
row.abilityButtons[b] = button
|
||||
end
|
||||
|
||||
row.divider = row:CreateTexture(nil, "BORDER")
|
||||
row.divider:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
row.divider:SetVertexColor(0.28, 0.28, 0.28, 0.45)
|
||||
row.divider:SetPoint("BOTTOMLEFT", row, "BOTTOMLEFT", 0, 0)
|
||||
row.divider:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", 0, 0)
|
||||
row.divider:SetHeight(1)
|
||||
|
||||
ui.rows[i] = row
|
||||
end
|
||||
|
||||
MTH_BOOK_STATE.familiesUI = ui
|
||||
MTH_BOOKTAB_FamiliesTrace("EnsureFamiliesUI created rows=" .. tostring(table.getn(ui.rows or {})))
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_SetFamiliesUIVisible(visible)
|
||||
local ui = MTH_BOOK_STATE.familiesUI
|
||||
if visible then
|
||||
if not ui then
|
||||
MTH_BOOKTAB_EnsureFamiliesUI()
|
||||
ui = MTH_BOOK_STATE.familiesUI
|
||||
end
|
||||
if not ui then return end
|
||||
else
|
||||
if not ui then return end
|
||||
end
|
||||
|
||||
local listParent = getglobal("MTH_BOOK_ListBackdrop")
|
||||
local detailParent = getglobal("MTH_BOOK_DetailBackdrop")
|
||||
local detailText = getglobal("MTH_BOOK_DetailBackdropDetailText")
|
||||
local openMapButton = getglobal("MTH_BOOK_OpenMapButton")
|
||||
|
||||
if visible then
|
||||
if listParent then listParent:Show() end
|
||||
if detailParent then detailParent:Hide() end
|
||||
if detailText then detailText:Hide() end
|
||||
if openMapButton then openMapButton:Hide() end
|
||||
ui.frame:Show()
|
||||
else
|
||||
ui.frame:Hide()
|
||||
if listParent then listParent:Show() end
|
||||
if detailParent then detailParent:Show() end
|
||||
if detailText then detailText:Show() end
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_RenderFamiliesList()
|
||||
local ui = MTH_BOOK_STATE.familiesUI
|
||||
if not ui then return end
|
||||
local rows = MTH_BOOK_STATE.results or {}
|
||||
MTH_BOOKTAB_FamiliesTrace("RenderFamiliesList rows=" .. tostring(table.getn(rows)))
|
||||
|
||||
for i = 1, table.getn(ui.rows) do
|
||||
local rowFrame = ui.rows[i]
|
||||
local row = rows[i]
|
||||
if row then
|
||||
rowFrame:Show()
|
||||
rowFrame.family:SetText(tostring(row.family or "-"))
|
||||
rowFrame.named:SetText(tostring(row.named or 0))
|
||||
rowFrame.coords:SetText(tostring(row.coords or 0))
|
||||
rowFrame.diet:SetText(tostring(row.dietText or "-"))
|
||||
|
||||
local offsetX = 232
|
||||
for b = 1, table.getn(rowFrame.abilityButtons) do
|
||||
local button = rowFrame.abilityButtons[b]
|
||||
local ability = row.abilities and row.abilities[b] or nil
|
||||
if ability then
|
||||
local nameText = tostring(ability.name or "")
|
||||
if b < table.getn(row.abilities) then
|
||||
nameText = nameText .. ","
|
||||
end
|
||||
button.entry = ability
|
||||
button.text:SetText(nameText)
|
||||
local textW = button.text:GetStringWidth() or 40
|
||||
if textW < 24 then textW = 24 end
|
||||
button:SetWidth(14 + textW)
|
||||
button:ClearAllPoints()
|
||||
button:SetPoint("LEFT", rowFrame, "LEFT", offsetX, 0)
|
||||
local iconPath = ability.icon and MTH_BOOK_ResolveIconPath and MTH_BOOK_ResolveIconPath(ability.icon) or nil
|
||||
if iconPath then
|
||||
button.icon:SetTexture(iconPath)
|
||||
else
|
||||
button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
|
||||
end
|
||||
button:Show()
|
||||
offsetX = offsetX + button:GetWidth() + 6
|
||||
else
|
||||
button.entry = nil
|
||||
button:Hide()
|
||||
end
|
||||
end
|
||||
else
|
||||
rowFrame:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,189 @@
|
||||
if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end
|
||||
|
||||
MTH_HUNTERBOOK_TABS.npcs = {
|
||||
headerLabel = "NPC Finder",
|
||||
columnLabels = { "ID", "Name", "React", "Function", "Zone" },
|
||||
columnLayout = {
|
||||
{ x = 8, width = 52, align = "LEFT" },
|
||||
{ x = 60, width = 140, align = "LEFT" },
|
||||
{ x = 200, width = 40, align = "LEFT" },
|
||||
{ x = 242, width = 160, align = "LEFT" },
|
||||
{ x = 404, width = 148, align = "LEFT" },
|
||||
},
|
||||
}
|
||||
|
||||
function MTH_BOOKTAB_GetNPCReactBucket(fac)
|
||||
local f = tostring(fac or "")
|
||||
if f == "A" then return "A" end
|
||||
if f == "H" then return "H" end
|
||||
return "AH"
|
||||
end
|
||||
MTH_BOOK_GetNPCReactBucket = MTH_BOOKTAB_GetNPCReactBucket
|
||||
|
||||
function MTH_BOOKTAB_GetNPCFunctionLabel(key)
|
||||
local map = {
|
||||
vendor = "Ammo Vendor",
|
||||
repair = "Repair",
|
||||
stablemaster = "Stable Master",
|
||||
huntertrainer = "Hunter Trainer",
|
||||
pettrainer = "Pet Trainer",
|
||||
}
|
||||
return map[key] or (string.upper(string.sub(key, 1, 1)) .. string.sub(key, 2))
|
||||
end
|
||||
MTH_BOOK_GetNPCFunctionLabel = MTH_BOOKTAB_GetNPCFunctionLabel
|
||||
|
||||
function MTH_BOOKTAB_GetNPCFunctions(vendor)
|
||||
local out = {}
|
||||
local seen = {}
|
||||
local meta = vendor and vendor.meta
|
||||
if meta then
|
||||
for key, value in pairs(meta) do
|
||||
if type(key) == "string" and value and tostring(value) ~= "" then
|
||||
local normalizedKey = MTH_BOOK_SafeLower(key)
|
||||
if normalizedKey ~= "" and not seen[normalizedKey] then
|
||||
seen[normalizedKey] = true
|
||||
table.insert(out, normalizedKey)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
table.sort(out, function(a, b)
|
||||
return MTH_BOOK_SafeLower(MTH_BOOKTAB_GetNPCFunctionLabel(a)) < MTH_BOOK_SafeLower(MTH_BOOKTAB_GetNPCFunctionLabel(b))
|
||||
end)
|
||||
return out
|
||||
end
|
||||
MTH_BOOK_GetNPCFunctions = MTH_BOOKTAB_GetNPCFunctions
|
||||
|
||||
function MTH_BOOKTAB_GetNPCFunctionSummary(vendor)
|
||||
local keys = MTH_BOOKTAB_GetNPCFunctions(vendor)
|
||||
if table.getn(keys) == 0 then return "-", {} end
|
||||
local labels = {}
|
||||
for i = 1, table.getn(keys) do
|
||||
table.insert(labels, MTH_BOOKTAB_GetNPCFunctionLabel(keys[i]))
|
||||
end
|
||||
return table.concat(labels, ", "), keys
|
||||
end
|
||||
MTH_BOOK_GetNPCFunctionSummary = MTH_BOOKTAB_GetNPCFunctionSummary
|
||||
|
||||
local function MTH_BOOKTAB_GetZoneAndSubzone(zoneId)
|
||||
local zid = tonumber(zoneId)
|
||||
if not zid then return "-", "-" end
|
||||
local zoneName = MTH_BOOK_GetZoneName(zid)
|
||||
local row = MTH_DS_Zones and (MTH_DS_Zones[zid] or MTH_DS_Zones[tostring(zid)])
|
||||
if row and row.parent and tonumber(row.parent) and tonumber(row.parent) ~= zid then
|
||||
local parentName = MTH_BOOK_GetZoneName(row.parent)
|
||||
if parentName and string.sub(parentName, 1, 5) ~= "Zone " then
|
||||
return parentName, zoneName
|
||||
end
|
||||
end
|
||||
return zoneName, "-"
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_GetNPCZoneSummary(vendor)
|
||||
if not vendor or not vendor.coords or table.getn(vendor.coords) == 0 then
|
||||
return "-", "-", {}
|
||||
end
|
||||
local zones = {}
|
||||
local zoneSet = {}
|
||||
local firstZone = "-"
|
||||
local firstSubzone = "-"
|
||||
for i = 1, table.getn(vendor.coords) do
|
||||
local c = vendor.coords[i]
|
||||
if c and c[3] then
|
||||
local zoneName, subzoneName = MTH_BOOKTAB_GetZoneAndSubzone(c[3])
|
||||
if firstZone == "-" then
|
||||
firstZone = zoneName
|
||||
firstSubzone = subzoneName
|
||||
end
|
||||
if zoneName ~= "-" and not zoneSet[zoneName] then
|
||||
zoneSet[zoneName] = true
|
||||
table.insert(zones, zoneName)
|
||||
end
|
||||
end
|
||||
end
|
||||
return firstZone, firstSubzone, zones
|
||||
end
|
||||
MTH_BOOK_GetNPCZoneSummary = MTH_BOOKTAB_GetNPCZoneSummary
|
||||
|
||||
function MTH_BOOK_NPCHasZoneId(vendor, zoneId)
|
||||
local zid = tonumber(zoneId)
|
||||
if not zid or not vendor or not vendor.coords then
|
||||
return false
|
||||
end
|
||||
for i = 1, table.getn(vendor.coords) do
|
||||
local c = vendor.coords[i]
|
||||
if c and tonumber(c[3]) == zid then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_BOOK_NPCMatches(npcId, vendor)
|
||||
if not vendor then return false end
|
||||
local react = MTH_BOOK_GetNPCReactBucket(vendor.fac)
|
||||
if react == "A" and not MTH_BOOK_STATE.flag1 then return false end
|
||||
if react == "H" and not MTH_BOOK_STATE.flag2 then return false end
|
||||
if react == "AH" and not MTH_BOOK_STATE.flag3 then return false end
|
||||
|
||||
if MTH_BOOK_STATE.npcHideNoZone then
|
||||
local firstZone = MTH_BOOK_GetNPCZoneSummary(vendor)
|
||||
local zoneText = MTH_BOOK_SafeLower(firstZone)
|
||||
if firstZone == "-" or zoneText == "" or zoneText == "unknown" or string.sub(zoneText, 1, 5) == "zone " then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.npcFunction and MTH_BOOK_STATE.npcFunction ~= "all" then
|
||||
local hasFunction = false
|
||||
local functions = MTH_BOOK_GetNPCFunctions(vendor)
|
||||
for i = 1, table.getn(functions) do
|
||||
if functions[i] == MTH_BOOK_STATE.npcFunction then
|
||||
hasFunction = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not hasFunction then return false end
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.npcZone and MTH_BOOK_STATE.npcZone ~= "all" then
|
||||
local _, _, zones = MTH_BOOK_GetNPCZoneSummary(vendor)
|
||||
local hasZone = false
|
||||
for i = 1, table.getn(zones) do
|
||||
if zones[i] == MTH_BOOK_STATE.npcZone then
|
||||
hasZone = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not hasZone then return false end
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.npcInZoneOnly then
|
||||
local currentZoneId = MTH_BOOK_GetCurrentZoneId()
|
||||
if currentZoneId and not MTH_BOOK_NPCHasZoneId(vendor, currentZoneId) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.search ~= "" then
|
||||
local functionSummary = MTH_BOOK_GetNPCFunctionSummary(vendor)
|
||||
local firstZone, firstSubzone = MTH_BOOK_GetNPCZoneSummary(vendor)
|
||||
local hay = MTH_BOOK_SafeLower(tostring(vendor.name or "") .. " " .. tostring(functionSummary or "") .. " " .. tostring(firstZone or "") .. " " .. tostring(firstSubzone or "") .. " " .. tostring(npcId))
|
||||
if string.find(hay, MTH_BOOK_STATE.search, 1, true) == nil then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_BOOK_NPCSort(a, b)
|
||||
local vendors = MTH_DS_Vendors
|
||||
local va = vendors and vendors[a]
|
||||
local vb = vendors and vendors[b]
|
||||
if not va or not vb then return a < b end
|
||||
local na = MTH_BOOK_SafeLower(va.name)
|
||||
local nb = MTH_BOOK_SafeLower(vb.name)
|
||||
if na ~= nb then return na < nb end
|
||||
return a < b
|
||||
end
|
||||
@@ -0,0 +1,903 @@
|
||||
if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end
|
||||
|
||||
MTH_HUNTERBOOK_TABS.petabilities = {
|
||||
headerLabel = "Pet Abilities",
|
||||
columnLabels = { "Ability Name", "Ranks", "Families" },
|
||||
columnLayout = {
|
||||
{ x = 8, width = 160, align = "LEFT" },
|
||||
{ x = 170, width = 52, align = "LEFT" },
|
||||
{ x = 224, width = 328, align = "LEFT" },
|
||||
},
|
||||
}
|
||||
|
||||
MTH_HUNTERBOOK_TABS.abilities = {
|
||||
headerLabel = "Pet Abilities",
|
||||
columnLabels = { "Ability", "Lvl", "Family", "Beast", "Rare" },
|
||||
columnLayout = {
|
||||
{ x = 8, width = 112, align = "LEFT" },
|
||||
{ x = 120, width = 40, align = "LEFT" },
|
||||
{ x = 160, width = 78, align = "LEFT" },
|
||||
{ x = 238, width = 142, align = "LEFT" },
|
||||
{ x = 380, width = 38, align = "LEFT" },
|
||||
},
|
||||
}
|
||||
|
||||
function MTH_BOOK_AbilitySort(a, b)
|
||||
if a.ability ~= b.ability then return a.ability < b.ability end
|
||||
if a.level ~= b.level then return a.level < b.level end
|
||||
if a.name ~= b.name then return a.name < b.name end
|
||||
return a.beastId < b.beastId
|
||||
end
|
||||
|
||||
function MTH_BOOK_PetAbilitySort(a, b)
|
||||
local aa = MTH_BOOK_SafeLower(a and a.ability)
|
||||
local ab = MTH_BOOK_SafeLower(b and b.ability)
|
||||
if aa ~= ab then return aa < ab end
|
||||
local ra = tonumber(a and a.rankCount) or 0
|
||||
local rb = tonumber(b and b.rankCount) or 0
|
||||
if ra ~= rb then return ra > rb end
|
||||
local fa = tonumber(a and a.familyCount) or 0
|
||||
local fb = tonumber(b and b.familyCount) or 0
|
||||
if fa ~= fb then return fa > fb end
|
||||
return tostring(a and a.familyList or "") < tostring(b and b.familyList or "")
|
||||
end
|
||||
|
||||
function MTH_BOOK_GetSpellRowsForAbility(abilityName)
|
||||
if not abilityName or abilityName == "" then return 0 end
|
||||
if not (MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility) then return 0 end
|
||||
local row = MTH_DS_PetSpells.byAbility[abilityName]
|
||||
if not row or not row.spells then return 0 end
|
||||
return table.getn(row.spells)
|
||||
end
|
||||
|
||||
function MTH_BOOK_GetSpellCoverageForAbility(abilityName)
|
||||
local coverage = {
|
||||
ability = abilityName,
|
||||
icon = nil,
|
||||
spellRows = 0,
|
||||
hasBeastLearned = false,
|
||||
hasTrainerLearned = false,
|
||||
rankCount = 0,
|
||||
minRank = nil,
|
||||
maxRank = nil,
|
||||
minTrainLevel = nil,
|
||||
maxTrainLevel = nil,
|
||||
}
|
||||
|
||||
if not abilityName or abilityName == "" then return coverage end
|
||||
if not (MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility) then return coverage end
|
||||
|
||||
local row = MTH_DS_PetSpells.byAbility[abilityName]
|
||||
if not row or not row.spells then return coverage end
|
||||
|
||||
local rankMap = {}
|
||||
local hasPositiveRank = false
|
||||
for i = 1, table.getn(row.spells) do
|
||||
local spell = row.spells[i]
|
||||
if spell then
|
||||
local rankNumber = tonumber(spell.rankNumber)
|
||||
if rankNumber and rankNumber > 0 then
|
||||
hasPositiveRank = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
coverage.spellRows = 0
|
||||
for i = 1, table.getn(row.spells) do
|
||||
local spell = row.spells[i]
|
||||
if spell then
|
||||
local rankNumber = tonumber(spell.rankNumber)
|
||||
if hasPositiveRank and ((not rankNumber) or rankNumber <= 0) then
|
||||
-- Ignore legacy/unranked rows when proper ranked variants exist.
|
||||
else
|
||||
coverage.spellRows = coverage.spellRows + 1
|
||||
local learnMethod = MTH_BOOK_SafeLower(spell.learnMethod)
|
||||
if learnMethod == "trainer" then
|
||||
coverage.hasTrainerLearned = true
|
||||
else
|
||||
coverage.hasBeastLearned = true
|
||||
end
|
||||
if not coverage.icon and spell.icon and spell.icon ~= "" then
|
||||
coverage.icon = spell.icon
|
||||
end
|
||||
if rankNumber and rankNumber > 0 then
|
||||
rankMap[rankNumber] = true
|
||||
if not coverage.minRank or rankNumber < coverage.minRank then coverage.minRank = rankNumber end
|
||||
if not coverage.maxRank or rankNumber > coverage.maxRank then coverage.maxRank = rankNumber end
|
||||
end
|
||||
|
||||
local trainLevel = tonumber(spell.trainLevel)
|
||||
if trainLevel then
|
||||
if not coverage.minTrainLevel or trainLevel < coverage.minTrainLevel then coverage.minTrainLevel = trainLevel end
|
||||
if not coverage.maxTrainLevel or trainLevel > coverage.maxTrainLevel then coverage.maxTrainLevel = trainLevel end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _ in pairs(rankMap) do
|
||||
coverage.rankCount = coverage.rankCount + 1
|
||||
end
|
||||
if coverage.rankCount == 0 and coverage.spellRows > 0 then
|
||||
coverage.rankCount = 1
|
||||
end
|
||||
|
||||
return coverage
|
||||
end
|
||||
|
||||
function MTH_BOOK_GetKnownSpellMap()
|
||||
if not MTH_CharSavedVariables then return nil end
|
||||
if not MTH_CharSavedVariables.petTraining then return nil end
|
||||
local petTraining = MTH_CharSavedVariables.petTraining
|
||||
if petTraining.hunterKnownMap and petTraining.spellMap then
|
||||
for token, isKnown in pairs(petTraining.hunterKnownMap) do
|
||||
if isKnown == true then
|
||||
local _, _, baseToken = string.find(tostring(token), "^(.-)#%d+$")
|
||||
if baseToken and baseToken ~= "" then
|
||||
local baseRow = petTraining.spellMap[baseToken]
|
||||
if baseRow and ((tonumber(baseRow.rankNumber) or 0) <= 0) then
|
||||
baseRow.isKnown = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return MTH_CharSavedVariables.petTraining.spellMap
|
||||
end
|
||||
|
||||
function MTH_BOOK_GetKnownRankCountForAbility(abilityName)
|
||||
local spellMap = MTH_BOOK_GetKnownSpellMap()
|
||||
if not spellMap or not abilityName or abilityName == "" then return 0 end
|
||||
|
||||
local abilityLower = MTH_BOOK_SafeLower(abilityName)
|
||||
if abilityLower == "" then return 0 end
|
||||
|
||||
local knownRanks = {}
|
||||
for _, row in pairs(spellMap) do
|
||||
if row and row.name and (row.isKnown ~= false) then
|
||||
if MTH_BOOK_SafeLower(row.name) == abilityLower then
|
||||
local rankNumber = tonumber(row.rankNumber) or 0
|
||||
knownRanks[rankNumber] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local count = 0
|
||||
for _ in pairs(knownRanks) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
function MTH_BOOK_IsKnownRankForAbility(abilityName, rankNumber)
|
||||
local spellMap = MTH_BOOK_GetKnownSpellMap()
|
||||
if not spellMap then return false end
|
||||
if not abilityName or abilityName == "" then return false end
|
||||
local rankValue = tonumber(rankNumber)
|
||||
|
||||
local abilityLower = MTH_BOOK_SafeLower(abilityName)
|
||||
for _, row in pairs(spellMap) do
|
||||
if row and row.name and (row.isKnown ~= false) then
|
||||
if MTH_BOOK_SafeLower(row.name) == abilityLower then
|
||||
local rowRank = tonumber(row.rankNumber)
|
||||
if rankValue and rankValue > 0 then
|
||||
if rowRank == rankValue then
|
||||
return true
|
||||
end
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_BOOK_BuildPetAbilitiesRows(ignoreSearch)
|
||||
local spellRows = {}
|
||||
|
||||
if MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility then
|
||||
for abilityName in pairs(MTH_DS_PetSpells.byAbility) do
|
||||
local ability = tostring(abilityName or "")
|
||||
if ability ~= "" then
|
||||
local key = MTH_BOOK_SafeLower(ability)
|
||||
if key ~= "" then
|
||||
spellRows[key] = {
|
||||
ability = ability,
|
||||
familiesMap = {},
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function ensureRow(abilityName)
|
||||
local ability = tostring(abilityName or "")
|
||||
if ability == "" then return nil end
|
||||
local key = MTH_BOOK_SafeLower(ability)
|
||||
if key == "" then return nil end
|
||||
return spellRows[key], key
|
||||
end
|
||||
|
||||
if MTH_DS_Families then
|
||||
for familyName, row in pairs(MTH_DS_Families) do
|
||||
if familyName and familyName ~= "" and row and row.abilities then
|
||||
for i = 1, table.getn(row.abilities) do
|
||||
local ability = row.abilities[i]
|
||||
local spellRow = ensureRow(ability)
|
||||
if spellRow then
|
||||
spellRow.familiesMap[familyName] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if MTH_DS_Beasts then
|
||||
for _, beast in pairs(MTH_DS_Beasts) do
|
||||
if beast and beast.family and beast.family ~= "" then
|
||||
local tokens = MTH_BOOK_SplitAbilities(beast.abilities)
|
||||
for i = 1, table.getn(tokens) do
|
||||
local _, abilityName = MTH_BOOK_ParseAbilityToken(tokens[i])
|
||||
local spellRow = ensureRow(abilityName)
|
||||
if spellRow then
|
||||
spellRow.familiesMap[beast.family] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local results = {}
|
||||
for _, row in pairs(spellRows) do
|
||||
local coverage = MTH_BOOK_GetSpellCoverageForAbility(row.ability)
|
||||
local families = {}
|
||||
for familyName in pairs(row.familiesMap or {}) do
|
||||
table.insert(families, familyName)
|
||||
end
|
||||
table.sort(families, function(a, b)
|
||||
return MTH_BOOK_SafeLower(a) < MTH_BOOK_SafeLower(b)
|
||||
end)
|
||||
|
||||
local rankCount = tonumber(coverage.rankCount) or 0
|
||||
local knownRankCount = MTH_BOOK_GetKnownRankCountForAbility(row.ability)
|
||||
local minRank = coverage.minRank
|
||||
local maxRank = coverage.maxRank
|
||||
local minTrainLevel = coverage.minTrainLevel
|
||||
local maxTrainLevel = coverage.maxTrainLevel
|
||||
if MTH_BOOK_STATE.petOnlyMyLevel then
|
||||
knownRankCount, rankCount, minRank, maxRank, minTrainLevel, maxTrainLevel = MTH_BOOK_GetScopedRankSummary(row.ability)
|
||||
end
|
||||
if knownRankCount > rankCount then knownRankCount = rankCount end
|
||||
local rankText = tostring(knownRankCount) .. "/" .. tostring(rankCount)
|
||||
|
||||
local sourceFilter = tostring(MTH_BOOK_STATE.petLearnSource or "all")
|
||||
local sourceMatch = true
|
||||
if sourceFilter == "beast" then
|
||||
sourceMatch = coverage.hasBeastLearned and true or false
|
||||
elseif sourceFilter == "trainer" then
|
||||
sourceMatch = coverage.hasTrainerLearned and true or false
|
||||
end
|
||||
|
||||
local familyList = table.concat(families, ", ")
|
||||
local searchable = MTH_BOOK_SafeLower(tostring(row.ability or "") .. " " .. familyList)
|
||||
if sourceMatch and rankCount > 0 and (ignoreSearch or MTH_BOOK_STATE.search == "" or string.find(searchable, MTH_BOOK_STATE.search, 1, true) ~= nil) then
|
||||
table.insert(results, {
|
||||
ability = row.ability,
|
||||
icon = coverage.icon,
|
||||
knownRankCount = knownRankCount,
|
||||
rankCount = rankCount,
|
||||
rankText = rankText,
|
||||
families = families,
|
||||
familyCount = table.getn(families),
|
||||
familyList = familyList,
|
||||
spellRows = coverage.spellRows or 0,
|
||||
minRank = minRank,
|
||||
maxRank = maxRank,
|
||||
minTrainLevel = minTrainLevel,
|
||||
maxTrainLevel = maxTrainLevel,
|
||||
hasBeastLearned = coverage.hasBeastLearned,
|
||||
hasTrainerLearned = coverage.hasTrainerLearned,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(results, MTH_BOOK_PetAbilitySort)
|
||||
return results
|
||||
end
|
||||
|
||||
function MTH_BOOK_BuildPetAbilityRankRows(entry)
|
||||
local rows = {}
|
||||
if not entry or not entry.ability then return rows end
|
||||
if not (MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility) then return rows end
|
||||
|
||||
local spellBucket = MTH_DS_PetSpells.byAbility[entry.ability]
|
||||
if not spellBucket or not spellBucket.spells then return rows end
|
||||
|
||||
local hasPositiveRank = false
|
||||
for i = 1, table.getn(spellBucket.spells) do
|
||||
local spell = spellBucket.spells[i]
|
||||
if spell then
|
||||
local rankNumber = tonumber(spell.rankNumber)
|
||||
if rankNumber and rankNumber > 0 then
|
||||
hasPositiveRank = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, table.getn(spellBucket.spells) do
|
||||
local spell = spellBucket.spells[i]
|
||||
if spell then
|
||||
local rankNumber = tonumber(spell.rankNumber)
|
||||
if not rankNumber and spell.rank then
|
||||
local _, _, parsed = string.find(tostring(spell.rank), "(%d+)")
|
||||
rankNumber = tonumber(parsed)
|
||||
end
|
||||
if hasPositiveRank and ((not rankNumber) or rankNumber <= 0) then
|
||||
-- Ignore legacy/unranked rows when proper ranked variants exist.
|
||||
elseif MTH_BOOK_IsSpellInLevelScope(tonumber(spell.trainLevel)) then
|
||||
table.insert(rows, {
|
||||
id = spell.id,
|
||||
ability = entry.ability,
|
||||
icon = spell.icon or spell.iconName or spell.texture or spell.iconTexture or entry.icon,
|
||||
rankNumber = rankNumber,
|
||||
isKnown = MTH_BOOK_IsKnownRankForAbility(entry.ability, rankNumber),
|
||||
trainLevel = tonumber(spell.trainLevel),
|
||||
description = spell.description,
|
||||
cost = spell.cost,
|
||||
castTime = spell.castTime,
|
||||
range = spell.range,
|
||||
school = spell.school,
|
||||
effects = spell.effects,
|
||||
sourceUrl = spell.sourceUrl,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(rows, function(a, b)
|
||||
local ra = a.rankNumber or 0
|
||||
local rb = b.rankNumber or 0
|
||||
if ra ~= rb then return ra < rb end
|
||||
local la = a.trainLevel or 0
|
||||
local lb = b.trainLevel or 0
|
||||
if la ~= lb then return la < lb end
|
||||
return (tonumber(a.id) or 0) < (tonumber(b.id) or 0)
|
||||
end)
|
||||
|
||||
return rows
|
||||
end
|
||||
|
||||
function MTH_BOOK_GetRankLabel(rankNumber, includePrefix)
|
||||
local rankValue = tonumber(rankNumber)
|
||||
if rankValue and rankValue > 0 then
|
||||
if includePrefix then
|
||||
return "Rank " .. tostring(rankValue)
|
||||
end
|
||||
return tostring(rankValue)
|
||||
end
|
||||
if includePrefix then
|
||||
return "Unranked"
|
||||
end
|
||||
return "None"
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_EnsurePetAbilitiesUI(listParent, detailParent)
|
||||
if MTH_BOOK_STATE.petUI then return end
|
||||
if not listParent or not detailParent then return end
|
||||
|
||||
local ui = {}
|
||||
|
||||
ui.leftPane = CreateFrame("Frame", nil, listParent)
|
||||
ui.leftPane:SetPoint("TOPLEFT", listParent, "TOPLEFT", 4, -4)
|
||||
ui.leftPane:SetWidth(162)
|
||||
ui.leftPane:SetHeight(346)
|
||||
|
||||
ui.rightPane = CreateFrame("Frame", nil, listParent)
|
||||
ui.rightPane:SetPoint("TOPLEFT", ui.leftPane, "TOPRIGHT", 8, 0)
|
||||
ui.rightPane:SetWidth(390)
|
||||
ui.rightPane:SetHeight(346)
|
||||
|
||||
ui.vSplit = listParent:CreateTexture(nil, "BORDER")
|
||||
ui.vSplit:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
ui.vSplit:SetVertexColor(0.30, 0.30, 0.30, 0.60)
|
||||
ui.vSplit:SetPoint("TOPLEFT", ui.leftPane, "TOPRIGHT", 4, -2)
|
||||
ui.vSplit:SetPoint("BOTTOMLEFT", ui.leftPane, "BOTTOMRIGHT", 4, 2)
|
||||
ui.vSplit:SetWidth(1)
|
||||
|
||||
ui.leftHeaderAbility = ui.leftPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.leftHeaderAbility:SetPoint("TOPLEFT", ui.leftPane, "TOPLEFT", 24, -6)
|
||||
ui.leftHeaderAbility:SetWidth(102)
|
||||
ui.leftHeaderAbility:SetJustifyH("LEFT")
|
||||
ui.leftHeaderAbility:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.leftHeaderAbility:SetText("Ability")
|
||||
|
||||
ui.leftHeaderRank = ui.leftPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.leftHeaderRank:SetPoint("TOPLEFT", ui.leftPane, "TOPLEFT", 126, -6)
|
||||
ui.leftHeaderRank:SetWidth(28)
|
||||
ui.leftHeaderRank:SetJustifyH("LEFT")
|
||||
ui.leftHeaderRank:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.leftHeaderRank:SetText("K/T")
|
||||
|
||||
ui.rightHeaderRank = ui.rightPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.rightHeaderRank:SetPoint("TOPLEFT", ui.rightPane, "TOPLEFT", 2, -6)
|
||||
ui.rightHeaderRank:SetWidth(36)
|
||||
ui.rightHeaderRank:SetJustifyH("LEFT")
|
||||
ui.rightHeaderRank:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.rightHeaderRank:SetText("Rank")
|
||||
|
||||
ui.rightHeaderLevel = ui.rightPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.rightHeaderKnown = ui.rightPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.rightHeaderKnown:SetPoint("TOPLEFT", ui.rightPane, "TOPLEFT", 34, -6)
|
||||
ui.rightHeaderKnown:SetWidth(22)
|
||||
ui.rightHeaderKnown:SetJustifyH("LEFT")
|
||||
ui.rightHeaderKnown:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.rightHeaderKnown:SetText("K?")
|
||||
|
||||
ui.rightHeaderLevel:SetPoint("TOPLEFT", ui.rightPane, "TOPLEFT", 58, -6)
|
||||
ui.rightHeaderLevel:SetWidth(40)
|
||||
ui.rightHeaderLevel:SetJustifyH("LEFT")
|
||||
ui.rightHeaderLevel:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.rightHeaderLevel:SetText("Lvl")
|
||||
|
||||
ui.rightHeaderDesc = ui.rightPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.rightHeaderDesc:SetPoint("TOPLEFT", ui.rightPane, "TOPLEFT", 98, -6)
|
||||
ui.rightHeaderDesc:SetWidth(286)
|
||||
ui.rightHeaderDesc:SetJustifyH("LEFT")
|
||||
ui.rightHeaderDesc:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.rightHeaderDesc:SetText("Description")
|
||||
|
||||
ui.leftRowsVisible = 20
|
||||
|
||||
ui.leftScroll = CreateFrame("Slider", "MTH_BOOK_PetLeftScroll", ui.leftPane, "UIPanelScrollBarTemplate")
|
||||
ui.leftScroll:SetPoint("TOPRIGHT", ui.leftPane, "TOPRIGHT", -2, -22)
|
||||
ui.leftScroll:SetPoint("BOTTOMRIGHT", ui.leftPane, "BOTTOMRIGHT", -2, 20)
|
||||
ui.leftScroll:SetWidth(16)
|
||||
if not getglobal("MTH_BOOK_PetLeftScrollScrollFrame") then
|
||||
local leftScrollShim = CreateFrame("ScrollFrame", "MTH_BOOK_PetLeftScrollScrollFrame", ui.leftPane)
|
||||
leftScrollShim:SetPoint("TOPLEFT", ui.leftPane, "TOPLEFT", 0, 0)
|
||||
leftScrollShim:SetWidth(1)
|
||||
leftScrollShim:SetHeight(1)
|
||||
leftScrollShim:Hide()
|
||||
end
|
||||
ui.leftScroll:SetScript("OnValueChanged", function()
|
||||
local val = this:GetValue() or 0
|
||||
val = math.floor(val + 0.5)
|
||||
if val ~= MTH_BOOK_STATE.petLeftOffset then
|
||||
MTH_BOOK_STATE.petLeftOffset = val
|
||||
MTH_BOOK_UpdatePetAbilitiesLists()
|
||||
end
|
||||
end)
|
||||
ui.leftScroll:SetMinMaxValues(0, 0)
|
||||
ui.leftScroll:SetValueStep(1)
|
||||
ui.leftScroll:SetValue(0)
|
||||
|
||||
ui.leftPane:EnableMouseWheel(true)
|
||||
ui.leftPane:SetScript("OnMouseWheel", function()
|
||||
local delta = arg1 or 0
|
||||
if delta == 0 then return end
|
||||
local current = tonumber(MTH_BOOK_STATE.petLeftOffset or 0) or 0
|
||||
if delta < 0 then
|
||||
current = current + 1
|
||||
else
|
||||
current = current - 1
|
||||
end
|
||||
if current < 0 then current = 0 end
|
||||
local maxOffset = 0
|
||||
if MTH_BOOK_STATE.results then
|
||||
maxOffset = math.max(0, table.getn(MTH_BOOK_STATE.results) - table.getn(ui.leftButtons or {}))
|
||||
end
|
||||
if current > maxOffset then current = maxOffset end
|
||||
MTH_BOOK_STATE.petLeftOffset = current
|
||||
if ui.leftScroll then
|
||||
ui.leftScroll:SetValue(current)
|
||||
end
|
||||
MTH_BOOK_UpdatePetAbilitiesLists()
|
||||
end)
|
||||
|
||||
ui.leftButtons = {}
|
||||
local leftY = -24
|
||||
for i = 1, ui.leftRowsVisible do
|
||||
local btn = CreateFrame("Button", nil, ui.leftPane)
|
||||
btn:SetPoint("TOPLEFT", ui.leftPane, "TOPLEFT", 0, leftY)
|
||||
btn:SetWidth(144)
|
||||
btn:SetHeight(19)
|
||||
btn.entry = nil
|
||||
|
||||
btn.selected = btn:CreateTexture(nil, "BACKGROUND")
|
||||
btn.selected:SetAllPoints(btn)
|
||||
btn.selected:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
btn.selected:SetVertexColor(0.18, 0.35, 0.55, 0.35)
|
||||
btn.selected:Hide()
|
||||
|
||||
local hl = btn:CreateTexture(nil, "HIGHLIGHT")
|
||||
hl:SetAllPoints(btn)
|
||||
hl:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
||||
hl:SetBlendMode("ADD")
|
||||
hl:SetAlpha(0.30)
|
||||
|
||||
btn.ability = btn:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
btn.ability:SetPoint("LEFT", btn, "LEFT", 24, 0)
|
||||
btn.ability:SetWidth(100)
|
||||
btn.ability:SetJustifyH("LEFT")
|
||||
|
||||
btn.rank = btn:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
btn.rank:SetPoint("LEFT", btn, "LEFT", 126, 0)
|
||||
btn.rank:SetWidth(26)
|
||||
btn.rank:SetJustifyH("LEFT")
|
||||
|
||||
btn.icon = btn:CreateTexture(nil, "ARTWORK")
|
||||
btn.icon:SetPoint("LEFT", btn, "LEFT", 2, 0)
|
||||
btn.icon:SetWidth(16)
|
||||
btn.icon:SetHeight(16)
|
||||
|
||||
btn:SetScript("OnClick", function()
|
||||
if not this.entry then return end
|
||||
MTH_BOOK_STATE.selectedEntry = this.entry
|
||||
MTH_BOOK_STATE.selectedPetRankEntry = nil
|
||||
MTH_BOOK_STATE.petRankRows = MTH_BOOK_BuildPetAbilityRankRows(this.entry)
|
||||
MTH_BOOK_UpdateResults()
|
||||
end)
|
||||
btn:SetScript("OnEnter", function()
|
||||
if this and this.entry then
|
||||
MTH_BOOK_ShowBaselineTooltip(this, this.entry)
|
||||
end
|
||||
end)
|
||||
btn:SetScript("OnLeave", function()
|
||||
MTH_BOOK_HideSpellTooltip()
|
||||
end)
|
||||
|
||||
ui.leftButtons[i] = btn
|
||||
leftY = leftY - 20
|
||||
end
|
||||
|
||||
local footerParent = listParent:GetParent() or listParent
|
||||
ui.recapText = footerParent:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.recapText:SetPoint("TOPLEFT", footerParent, "TOPLEFT", 20, -530)
|
||||
ui.recapText:SetWidth(190)
|
||||
ui.recapText:SetJustifyH("LEFT")
|
||||
ui.recapText:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.recapText:SetText("Abilities 0 | Known 0/0")
|
||||
|
||||
ui.progressBackdrop = CreateFrame("Frame", nil, footerParent)
|
||||
ui.progressBackdrop:SetPoint("LEFT", ui.recapText, "RIGHT", 8, 0)
|
||||
ui.progressBackdrop:SetWidth(320)
|
||||
ui.progressBackdrop:SetHeight(14)
|
||||
ui.progressBackdrop:SetFrameLevel(footerParent:GetFrameLevel() + 3)
|
||||
ui.progressBackdrop:SetBackdrop({
|
||||
bgFile = "Interface\\Buttons\\WHITE8X8",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true,
|
||||
tileSize = 8,
|
||||
edgeSize = 8,
|
||||
insets = { left = 2, right = 2, top = 2, bottom = 2 },
|
||||
})
|
||||
ui.progressBackdrop:SetBackdropColor(0.08, 0.08, 0.08, 0.95)
|
||||
ui.progressBackdrop:SetBackdropBorderColor(0.24, 0.24, 0.24, 0.90)
|
||||
|
||||
ui.progressBar = CreateFrame("StatusBar", nil, ui.progressBackdrop)
|
||||
ui.progressBar:SetPoint("TOPLEFT", ui.progressBackdrop, "TOPLEFT", 2, -2)
|
||||
ui.progressBar:SetPoint("BOTTOMRIGHT", ui.progressBackdrop, "BOTTOMRIGHT", -2, 2)
|
||||
ui.progressBar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8")
|
||||
ui.progressBar:SetStatusBarColor(0.18, 0.75, 0.22, 0.9)
|
||||
ui.progressBar:SetMinMaxValues(0, 1)
|
||||
ui.progressBar:SetValue(0)
|
||||
|
||||
ui.progressText = ui.progressBackdrop:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
ui.progressText:SetPoint("CENTER", ui.progressBackdrop, "CENTER", 0, 0)
|
||||
ui.progressText:SetTextColor(1.00, 1.00, 1.00)
|
||||
ui.progressText:SetText("0%")
|
||||
|
||||
ui.rightButtons = {}
|
||||
local rightRowsVisible = 10
|
||||
local rightRowHeight = 36
|
||||
local rightRowStep = 37
|
||||
local rightY = -24
|
||||
for i = 1, rightRowsVisible do
|
||||
local btn = CreateFrame("Button", nil, ui.rightPane)
|
||||
btn:SetPoint("TOPLEFT", ui.rightPane, "TOPLEFT", 0, rightY)
|
||||
btn:SetWidth(388)
|
||||
btn:SetHeight(rightRowHeight)
|
||||
btn.entry = nil
|
||||
|
||||
btn.selected = btn:CreateTexture(nil, "BACKGROUND")
|
||||
btn.selected:SetAllPoints(btn)
|
||||
btn.selected:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
btn.selected:SetVertexColor(0.18, 0.35, 0.55, 0.35)
|
||||
btn.selected:Hide()
|
||||
|
||||
local hl = btn:CreateTexture(nil, "HIGHLIGHT")
|
||||
hl:SetAllPoints(btn)
|
||||
hl:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
||||
hl:SetBlendMode("ADD")
|
||||
hl:SetAlpha(0.28)
|
||||
|
||||
btn.rank = btn:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
btn.rank:SetPoint("LEFT", btn, "LEFT", 2, 1)
|
||||
btn.rank:SetWidth(30)
|
||||
btn.rank:SetJustifyH("LEFT")
|
||||
|
||||
btn.knownIcon = btn:CreateTexture(nil, "ARTWORK")
|
||||
btn.knownIcon:SetPoint("LEFT", btn, "LEFT", 38, 1)
|
||||
btn.knownIcon:SetWidth(12)
|
||||
btn.knownIcon:SetHeight(12)
|
||||
btn.knownIcon:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
|
||||
btn.knownIcon:SetVertexColor(0.18, 0.90, 0.18, 1.00)
|
||||
btn.knownIcon:Hide()
|
||||
|
||||
btn.level = btn:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
btn.level:SetPoint("LEFT", btn, "LEFT", 58, 1)
|
||||
btn.level:SetWidth(40)
|
||||
btn.level:SetJustifyH("LEFT")
|
||||
|
||||
btn.desc = btn:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
btn.desc:SetPoint("LEFT", btn, "LEFT", 98, 1)
|
||||
btn.desc:SetWidth(286)
|
||||
btn.desc:SetJustifyH("LEFT")
|
||||
|
||||
btn:SetScript("OnClick", function()
|
||||
if not this.entry then return end
|
||||
MTH_BOOK_STATE.selectedPetRankEntry = this.entry
|
||||
MTH_BOOK_UpdateDetail()
|
||||
MTH_BOOK_UpdateResults()
|
||||
end)
|
||||
btn:SetScript("OnEnter", function()
|
||||
if this and this.entry then
|
||||
MTH_BOOK_ShowRankTooltip(this, this.entry)
|
||||
end
|
||||
end)
|
||||
btn:SetScript("OnLeave", function()
|
||||
MTH_BOOK_HideSpellTooltip()
|
||||
end)
|
||||
|
||||
ui.rightButtons[i] = btn
|
||||
rightY = rightY - rightRowStep
|
||||
end
|
||||
|
||||
ui.detailTitleTop = detailParent:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.detailTitleTop:SetPoint("TOPLEFT", detailParent, "TOPLEFT", 8, -8)
|
||||
ui.detailTitleTop:SetWidth(120)
|
||||
ui.detailTitleTop:SetJustifyH("LEFT")
|
||||
ui.detailTitleTop:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.detailTitleTop:SetText("Spell baseline")
|
||||
|
||||
ui.detailTop = CreateFrame("EditBox", nil, detailParent)
|
||||
ui.detailTop:SetMultiLine(true)
|
||||
ui.detailTop:SetAutoFocus(false)
|
||||
ui.detailTop:SetPoint("TOPLEFT", ui.detailTitleTop, "BOTTOMLEFT", 0, -4)
|
||||
ui.detailTop:SetWidth(120)
|
||||
ui.detailTop:SetHeight(132)
|
||||
|
||||
ui.detailTopIcon = detailParent:CreateTexture(nil, "ARTWORK")
|
||||
ui.detailTopIcon:SetPoint("TOPRIGHT", ui.detailTop, "TOPRIGHT", -4, -4)
|
||||
ui.detailTopIcon:SetWidth(16)
|
||||
ui.detailTopIcon:SetHeight(16)
|
||||
ui.detailTopIcon:Hide()
|
||||
|
||||
ui.detailTitleBottom = detailParent:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.detailTitleBottom:SetPoint("TOPLEFT", ui.detailTop, "BOTTOMLEFT", 0, -8)
|
||||
ui.detailTitleBottom:SetWidth(120)
|
||||
ui.detailTitleBottom:SetJustifyH("LEFT")
|
||||
ui.detailTitleBottom:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.detailTitleBottom:SetText("Current Rank")
|
||||
|
||||
ui.detailBottom = CreateFrame("EditBox", nil, detailParent)
|
||||
ui.detailBottom:SetMultiLine(true)
|
||||
ui.detailBottom:SetAutoFocus(false)
|
||||
ui.detailBottom:SetPoint("TOPLEFT", ui.detailTitleBottom, "BOTTOMLEFT", 0, -4)
|
||||
ui.detailBottom:SetWidth(120)
|
||||
ui.detailBottom:SetHeight(116)
|
||||
|
||||
MTH_BOOK_ConfigureReadOnlyEditBox(ui.detailTop)
|
||||
MTH_BOOK_ConfigureReadOnlyEditBox(ui.detailBottom)
|
||||
if ui.detailTop.SetTextColor then ui.detailTop:SetTextColor(1.00, 1.00, 1.00) end
|
||||
if ui.detailBottom.SetTextColor then ui.detailBottom:SetTextColor(1.00, 1.00, 1.00) end
|
||||
|
||||
MTH_BOOK_STATE.petUI = ui
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_SetPetAbilitiesUIVisible(visible)
|
||||
local ui = MTH_BOOK_STATE.petUI
|
||||
if not ui then return end
|
||||
|
||||
local detail = getglobal("MTH_BOOK_DetailBackdropDetailText")
|
||||
if detail then
|
||||
if visible then detail:Hide() else detail:Show() end
|
||||
end
|
||||
|
||||
local widgets = {
|
||||
ui.leftPane,
|
||||
ui.leftScroll,
|
||||
ui.rightPane,
|
||||
ui.vSplit,
|
||||
ui.leftHeaderAbility,
|
||||
ui.leftHeaderRank,
|
||||
ui.rightHeaderRank,
|
||||
ui.rightHeaderKnown,
|
||||
ui.rightHeaderLevel,
|
||||
ui.rightHeaderDesc,
|
||||
ui.recapText,
|
||||
ui.progressBackdrop,
|
||||
ui.detailTop,
|
||||
ui.detailBottom,
|
||||
ui.detailTitleTop,
|
||||
ui.detailTitleBottom,
|
||||
}
|
||||
|
||||
for i = 1, table.getn(widgets) do
|
||||
local widget = widgets[i]
|
||||
if widget and widget.Show and widget.Hide then
|
||||
if visible then widget:Show() else widget:Hide() end
|
||||
end
|
||||
end
|
||||
|
||||
if ui.detailTopIcon and not visible then
|
||||
ui.detailTopIcon:Hide()
|
||||
end
|
||||
|
||||
if not visible then
|
||||
MTH_BOOK_HideSpellTooltip()
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_UpdatePetAbilitiesLists()
|
||||
local ui = MTH_BOOK_STATE.petUI
|
||||
if not ui then return end
|
||||
|
||||
local resultCount = table.getn(MTH_BOOK_STATE.results)
|
||||
local visibleRows = table.getn(ui.leftButtons)
|
||||
local maxOffset = math.max(0, resultCount - visibleRows)
|
||||
if MTH_BOOK_STATE.petLeftOffset < 0 then MTH_BOOK_STATE.petLeftOffset = 0 end
|
||||
if MTH_BOOK_STATE.petLeftOffset > maxOffset then MTH_BOOK_STATE.petLeftOffset = maxOffset end
|
||||
if ui.leftScroll then
|
||||
ui.leftScroll:SetMinMaxValues(0, maxOffset)
|
||||
ui.leftScroll:SetValueStep(1)
|
||||
ui.leftScroll:SetValue(MTH_BOOK_STATE.petLeftOffset)
|
||||
local upButton = getglobal("MTH_BOOK_PetLeftScrollScrollUpButton")
|
||||
local downButton = getglobal("MTH_BOOK_PetLeftScrollScrollDownButton")
|
||||
if upButton then
|
||||
if MTH_BOOK_STATE.petLeftOffset > 0 then
|
||||
upButton:EnableMouse(true)
|
||||
upButton:SetAlpha(1)
|
||||
else
|
||||
upButton:EnableMouse(false)
|
||||
upButton:SetAlpha(0.35)
|
||||
end
|
||||
end
|
||||
if downButton then
|
||||
if MTH_BOOK_STATE.petLeftOffset < maxOffset then
|
||||
downButton:EnableMouse(true)
|
||||
downButton:SetAlpha(1)
|
||||
else
|
||||
downButton:EnableMouse(false)
|
||||
downButton:SetAlpha(0.35)
|
||||
end
|
||||
end
|
||||
if maxOffset > 0 then
|
||||
ui.leftScroll:SetAlpha(1)
|
||||
ui.leftScroll:Show()
|
||||
else
|
||||
ui.leftScroll:SetAlpha(1)
|
||||
ui.leftScroll:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local startIndex = MTH_BOOK_STATE.petLeftOffset + 1
|
||||
for i = 1, visibleRows do
|
||||
local btn = ui.leftButtons[i]
|
||||
if btn then
|
||||
local rowIndex = startIndex + i - 1
|
||||
local entry = MTH_BOOK_STATE.results[rowIndex]
|
||||
btn.entry = entry
|
||||
if entry then
|
||||
if btn.icon then
|
||||
if entry.icon and entry.icon ~= "" then
|
||||
if string.find(entry.icon, "\\", 1, true) then
|
||||
btn.icon:SetTexture(entry.icon)
|
||||
else
|
||||
btn.icon:SetTexture("Interface\\Icons\\" .. tostring(entry.icon))
|
||||
end
|
||||
btn.icon:Show()
|
||||
else
|
||||
btn.icon:Hide()
|
||||
end
|
||||
end
|
||||
btn.ability:SetText(tostring(entry.ability or "?"))
|
||||
btn.rank:SetText(tostring(entry.rankText or ""))
|
||||
btn.ability:SetTextColor(0.92, 0.92, 0.92)
|
||||
btn.rank:SetTextColor(0.92, 0.92, 0.92)
|
||||
if btn.icon then btn.icon:SetVertexColor(1, 1, 1, 1) end
|
||||
if MTH_BOOK_STATE.selectedEntry and MTH_BOOK_STATE.selectedEntry.ability == entry.ability then
|
||||
btn.selected:Show()
|
||||
else
|
||||
btn.selected:Hide()
|
||||
end
|
||||
btn:Show()
|
||||
else
|
||||
if btn.icon then btn.icon:Hide() end
|
||||
btn.ability:SetText("")
|
||||
btn.rank:SetText("")
|
||||
btn.selected:Hide()
|
||||
btn:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local totalKnown = 0
|
||||
local totalRanks = 0
|
||||
for i = 1, resultCount do
|
||||
local entry = MTH_BOOK_STATE.results[i]
|
||||
if entry then
|
||||
totalKnown = totalKnown + (tonumber(entry.knownRankCount) or 0)
|
||||
totalRanks = totalRanks + (tonumber(entry.rankCount) or 0)
|
||||
end
|
||||
end
|
||||
if ui.recapText then
|
||||
ui.recapText:SetText("Abilities " .. tostring(resultCount) .. " | Known " .. tostring(totalKnown) .. "/" .. tostring(totalRanks))
|
||||
end
|
||||
if ui.progressBar then
|
||||
ui.progressBar:SetMinMaxValues(0, math.max(1, totalRanks))
|
||||
ui.progressBar:SetValue(totalKnown)
|
||||
end
|
||||
if ui.progressText then
|
||||
local pct = 0
|
||||
if totalRanks > 0 then
|
||||
pct = math.floor((totalKnown / totalRanks) * 100 + 0.5)
|
||||
end
|
||||
ui.progressText:SetText(tostring(pct) .. "%")
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.selectedEntry then
|
||||
local keep = false
|
||||
for i = 1, table.getn(MTH_BOOK_STATE.results) do
|
||||
local row = MTH_BOOK_STATE.results[i]
|
||||
if row and row.ability == MTH_BOOK_STATE.selectedEntry.ability then
|
||||
MTH_BOOK_STATE.selectedEntry = row
|
||||
keep = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not keep then
|
||||
MTH_BOOK_STATE.selectedEntry = nil
|
||||
MTH_BOOK_STATE.selectedPetRankEntry = nil
|
||||
end
|
||||
end
|
||||
|
||||
MTH_BOOK_STATE.petRankRows = MTH_BOOK_BuildPetAbilityRankRows(MTH_BOOK_STATE.selectedEntry)
|
||||
|
||||
for i = 1, table.getn(ui.rightButtons) do
|
||||
local btn = ui.rightButtons[i]
|
||||
if btn then
|
||||
local entry = MTH_BOOK_STATE.petRankRows[i]
|
||||
btn.entry = entry
|
||||
if entry then
|
||||
btn.rank:SetText(MTH_BOOK_GetRankLabel(entry.rankNumber, false))
|
||||
if btn.knownIcon then
|
||||
if entry.isKnown then
|
||||
btn.knownIcon:Show()
|
||||
else
|
||||
btn.knownIcon:Hide()
|
||||
end
|
||||
end
|
||||
btn.level:SetText(entry.trainLevel and tostring(entry.trainLevel) or "-")
|
||||
btn.desc:SetText(tostring(entry.description or "-"))
|
||||
if MTH_BOOK_STATE.selectedPetRankEntry and MTH_BOOK_STATE.selectedPetRankEntry.id == entry.id then
|
||||
btn.selected:Show()
|
||||
else
|
||||
btn.selected:Hide()
|
||||
end
|
||||
btn:Show()
|
||||
else
|
||||
btn.rank:SetText("")
|
||||
if btn.knownIcon then btn.knownIcon:Hide() end
|
||||
btn.level:SetText("")
|
||||
btn.desc:SetText("")
|
||||
btn.selected:Hide()
|
||||
btn:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end
|
||||
|
||||
MTH_HUNTERBOOK_TABS.pethistory = {
|
||||
headerLabel = "Pet History",
|
||||
columnLabels = { "Pet ID", "Name", "Family", "Lvl", "Lost Date/Time", "Lost Cause" },
|
||||
columnLayout = {
|
||||
{ x = 8, width = 66, align = "LEFT" },
|
||||
{ x = 76, width = 104, align = "LEFT" },
|
||||
{ x = 182, width = 84, align = "LEFT" },
|
||||
{ x = 268, width = 30, align = "LEFT" },
|
||||
{ x = 300, width = 128, align = "LEFT" },
|
||||
{ x = 430, width = 78, align = "LEFT" },
|
||||
},
|
||||
}
|
||||
|
||||
function MTH_BOOK_PetHistorySort(a, b)
|
||||
local ra = MTH_BOOK_GetPetStoreRow(a)
|
||||
local rb = MTH_BOOK_GetPetStoreRow(b)
|
||||
if not ra and not rb then return tostring(a) < tostring(b) end
|
||||
if not ra then return false end
|
||||
if not rb then return true end
|
||||
local ta = tonumber(ra.abandonedAt) or 0
|
||||
local tb = tonumber(rb.abandonedAt) or 0
|
||||
if ta ~= tb then return ta > tb end
|
||||
local nameA = MTH_BOOK_SafeLower(ra.name)
|
||||
local nameB = MTH_BOOK_SafeLower(rb.name)
|
||||
if nameA ~= nameB then return nameA < nameB end
|
||||
return tostring(a) < tostring(b)
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end
|
||||
|
||||
MTH_HUNTERBOOK_TABS.items = {
|
||||
headerLabel = "Ranged Weapons",
|
||||
columnLabels = { "ID", "Req", "Lvl", "Type", "Name", "DPS", "Speed", "Source" },
|
||||
columnLayout = {
|
||||
{ x = 8, width = 36, align = "LEFT" },
|
||||
{ x = 44, width = 30, align = "LEFT" },
|
||||
{ x = 74, width = 30, align = "LEFT" },
|
||||
{ x = 104, width = 62, align = "LEFT" },
|
||||
{ x = 166, width = 188, align = "LEFT" },
|
||||
{ x = 356, width = 44, align = "LEFT" },
|
||||
{ x = 402, width = 40, align = "LEFT" },
|
||||
{ x = 444, width = 108, align = "LEFT" },
|
||||
},
|
||||
}
|
||||
|
||||
function MTH_BOOK_NormalizeItemSubtype(value)
|
||||
local subtype = MTH_BOOK_SafeLower(value)
|
||||
if subtype == "arrows" then return "arrow" end
|
||||
if subtype == "bullets" then return "bullet" end
|
||||
if subtype == "bows" then return "bow" end
|
||||
if subtype == "rifle" or subtype == "rifles" then return "gun" end
|
||||
if subtype == "cross bow" or subtype == "cross-bow" or subtype == "crossbows" or subtype == "xbow" or subtype == "xbows" then return "crossbow" end
|
||||
return subtype
|
||||
end
|
||||
|
||||
function MTH_BOOK_IsRangedWeaponSubtype(subtype)
|
||||
return subtype == "bow" or subtype == "gun" or subtype == "crossbow"
|
||||
end
|
||||
|
||||
function MTH_BOOK_ItemMatches(itemId, item)
|
||||
if not item then return false end
|
||||
if item.notingame then return false end
|
||||
local itemSubtype = MTH_BOOK_NormalizeItemSubtype(item.subtype)
|
||||
if not MTH_BOOK_IsRangedWeaponSubtype(itemSubtype) then return false end
|
||||
local selectedSubtype = MTH_BOOK_NormalizeItemSubtype(MTH_BOOK_STATE.itemSubtype)
|
||||
if selectedSubtype ~= "all" and itemSubtype ~= selectedSubtype then return false end
|
||||
if MTH_BOOK_STATE.itemOnlyMyLevel then
|
||||
local playerLevel = MTH_BOOK_GetPlayerLevelValue()
|
||||
local reqLevel = tonumber(item.reqlevel)
|
||||
if playerLevel and reqLevel and reqLevel > playerLevel then
|
||||
return false
|
||||
end
|
||||
end
|
||||
if MTH_BOOK_STATE.flag1 and not MTH_BOOK_HasEntries(item.vendors) then return false end
|
||||
if MTH_BOOK_STATE.flag2 and not MTH_BOOK_HasEntries(item.drops) then return false end
|
||||
if MTH_BOOK_STATE.flag3 and not MTH_BOOK_HasEntries(item.objects) then return false end
|
||||
|
||||
if MTH_BOOK_STATE.search ~= "" then
|
||||
local name = MTH_BOOK_SafeLower(item.name)
|
||||
local idText = tostring(itemId)
|
||||
if string.find(name, MTH_BOOK_STATE.search, 1, true) == nil and string.find(idText, MTH_BOOK_STATE.search, 1, true) == nil then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_BOOK_ItemSort(a, b)
|
||||
local items = MTH_BOOK_GetItemsTable()
|
||||
local ia = items and items[a]
|
||||
local ib = items and items[b]
|
||||
if not ia or not ib then return a < b end
|
||||
local la = ia.level or 0
|
||||
local lb = ib.level or 0
|
||||
if la ~= lb then return la < lb end
|
||||
local na = ia.name or ""
|
||||
local nb = ib.name or ""
|
||||
if na ~= nb then return na < nb end
|
||||
return a < b
|
||||
end
|
||||
+4946
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,333 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\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"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
</Anchors>
|
||||
<Backdrop bgFile="Interface\Buttons\WHITE8X8" edgeFile="Interface\DialogFrame\UI-DialogBox-Border" tile="true">
|
||||
<BackgroundInsets>
|
||||
<AbsInset left="11" right="12" top="12" bottom="11"/>
|
||||
</BackgroundInsets>
|
||||
<TileSize><AbsValue val="32"/></TileSize>
|
||||
<EdgeSize><AbsValue val="32"/></EdgeSize>
|
||||
</Backdrop>
|
||||
|
||||
<Layers>
|
||||
<Layer level="ARTWORK">
|
||||
<FontString name="$parentHeaderText" inherits="GameFontNormal" text="The Book of Hunters">
|
||||
<Anchors>
|
||||
<Anchor point="TOP">
|
||||
<Offset><AbsDimension x="0" y="-18"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
|
||||
<FontString name="$parentStatsText" inherits="GameFontNormalSmall" text="Beasts: 0 | Filtered: 0 | Page 1/1">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset><AbsDimension x="20" y="-530"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
|
||||
<FontString name="$parentSearchLabel" inherits="GameFontNormalSmall" text="Search">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset><AbsDimension x="18" y="-82"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
|
||||
<FontString name="$parentMinLabel" inherits="GameFontNormalSmall" text="Min Lvl">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset><AbsDimension x="266" y="-82"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
|
||||
<FontString name="$parentMaxLabel" inherits="GameFontNormalSmall" text="Max Lvl">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset><AbsDimension x="374" y="-82"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
|
||||
</Layer>
|
||||
</Layers>
|
||||
|
||||
<Frames>
|
||||
<Frame name="$parentListBackdrop">
|
||||
<Size><AbsDimension x="564" y="354"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="16" y="-166"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Backdrop bgFile="Interface\Buttons\WHITE8X8" edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
|
||||
<BackgroundInsets><AbsInset left="3" right="3" top="3" bottom="3"/></BackgroundInsets>
|
||||
<TileSize><AbsValue val="16"/></TileSize>
|
||||
<EdgeSize><AbsValue val="16"/></EdgeSize>
|
||||
</Backdrop>
|
||||
</Frame>
|
||||
|
||||
<Frame name="$parentDetailBackdrop">
|
||||
<Size><AbsDimension x="136" y="324"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="608" y="-166"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Backdrop bgFile="Interface\Buttons\WHITE8X8" edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
|
||||
<BackgroundInsets><AbsInset left="3" right="3" top="3" bottom="3"/></BackgroundInsets>
|
||||
<TileSize><AbsValue val="16"/></TileSize>
|
||||
<EdgeSize><AbsValue val="16"/></EdgeSize>
|
||||
</Backdrop>
|
||||
<Frames>
|
||||
<EditBox name="$parentDetailText" autoFocus="false" multiLine="true" text="Select an entry to view details.">
|
||||
<Size><AbsDimension x="120" y="306"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="8" y="-8"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</EditBox>
|
||||
</Frames>
|
||||
</Frame>
|
||||
|
||||
<Button name="$parentOpenMapButton" inherits="UIPanelButtonTemplate" text="Open Map" hidden="true">
|
||||
<Size><AbsDimension x="136" y="22"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="608" y="-496"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
|
||||
<Button name="$parentCloseButton" inherits="UIPanelButtonTemplate" text="X">
|
||||
<Size><AbsDimension x="22" y="22"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-8" y="-8"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
|
||||
<Frame name="$parentTopAreaBackdrop">
|
||||
<Size><AbsDimension x="728" y="472"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentTabBar" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="-2" y="-2"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Backdrop bgFile="Interface\Buttons\WHITE8X8" edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
|
||||
<BackgroundInsets><AbsInset left="3" right="3" top="3" bottom="3"/></BackgroundInsets>
|
||||
<TileSize><AbsValue val="16"/></TileSize>
|
||||
<EdgeSize><AbsValue val="16"/></EdgeSize>
|
||||
</Backdrop>
|
||||
</Frame>
|
||||
|
||||
<Frame name="$parentTabBar">
|
||||
<Size><AbsDimension x="620" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="18" y="-44"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Frame>
|
||||
|
||||
<Button name="$parentSectionStable" inherits="TabButtonTemplate" text="Stable">
|
||||
<Size><AbsDimension x="86" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentTabBar"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionPetAbilities" inherits="TabButtonTemplate" text="Pet Abilities">
|
||||
<Size><AbsDimension x="108" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionStable" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionPets" inherits="TabButtonTemplate" text="Beast Lore">
|
||||
<Size><AbsDimension x="98" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionPetAbilities" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionFamilies" inherits="TabButtonTemplate" text="Families">
|
||||
<Size><AbsDimension x="92" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionPets" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionNPCs" inherits="TabButtonTemplate" text="NPC Finder">
|
||||
<Size><AbsDimension x="110" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionFamilies" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionItems" inherits="TabButtonTemplate" text="Weapons">
|
||||
<Size><AbsDimension x="104" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionNPCs" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionPetHistory" inherits="TabButtonTemplate" text="Pet History">
|
||||
<Size><AbsDimension x="104" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionItems" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
|
||||
<EditBox name="$parentSearch" inherits="InputBoxTemplate">
|
||||
<Size><AbsDimension x="180" y="20"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="54" y="-80"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</EditBox>
|
||||
|
||||
<EditBox name="$parentMinLevel" inherits="InputBoxTemplate">
|
||||
<Size><AbsDimension x="45" y="20"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="302" y="-80"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</EditBox>
|
||||
|
||||
<EditBox name="$parentMaxLevel" inherits="InputBoxTemplate">
|
||||
<Size><AbsDimension x="45" y="20"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="414" y="-80"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</EditBox>
|
||||
|
||||
<Button name="$parentApplyButton" inherits="UIPanelButtonTemplate" text="Apply">
|
||||
<Size><AbsDimension x="60" y="20"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="478" y="-80"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
|
||||
<Frame name="$parentPetLearnSourceDropdown" inherits="UIDropDownMenuTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="12" y="-80"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Frame>
|
||||
|
||||
<Frame name="$parentItemSubtypeDropdown" inherits="UIDropDownMenuTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="12" y="-108"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Frame>
|
||||
|
||||
<CheckButton name="$parentPetOnlyMyLevel" inherits="UICheckButtonTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="228" 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>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="614" y="-80"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
|
||||
<Button name="$parentResetButton" inherits="UIPanelButtonTemplate" text="Reset">
|
||||
<Size><AbsDimension x="60" y="20"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="546" y="-80"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
|
||||
<Button name="$parentFilter1" inherits="UIPanelButtonTemplate" text="All"><Size><AbsDimension x="70" y="20"/></Size><Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="18" y="-114"/></Offset></Anchor></Anchors></Button>
|
||||
<Button name="$parentFilter2" inherits="UIPanelButtonTemplate" text=""><Size><AbsDimension x="70" y="20"/></Size><Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="92" y="-114"/></Offset></Anchor></Anchors></Button>
|
||||
<Button name="$parentFilter3" inherits="UIPanelButtonTemplate" text=""><Size><AbsDimension x="70" y="20"/></Size><Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="166" y="-114"/></Offset></Anchor></Anchors></Button>
|
||||
<Button name="$parentFilter4" inherits="UIPanelButtonTemplate" text=""><Size><AbsDimension x="70" y="20"/></Size><Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="240" y="-114"/></Offset></Anchor></Anchors></Button>
|
||||
<Button name="$parentFilter5" inherits="UIPanelButtonTemplate" text=""><Size><AbsDimension x="70" y="20"/></Size><Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="314" y="-114"/></Offset></Anchor></Anchors></Button>
|
||||
<Button name="$parentFilter6" inherits="UIPanelButtonTemplate" text=""><Size><AbsDimension x="70" y="20"/></Size><Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="388" y="-114"/></Offset></Anchor></Anchors></Button>
|
||||
|
||||
<Frame name="$parentFamilyDropdown" inherits="UIDropDownMenuTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="10" y="-108"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Frame>
|
||||
<Frame name="$parentAbilityDropdown" inherits="UIDropDownMenuTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="180" y="-108"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Frame>
|
||||
<Frame name="$parentRankDropdown" inherits="UIDropDownMenuTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="350" y="-108"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Frame>
|
||||
<CheckButton name="$parentPetInZoneOnly" inherits="UICheckButtonTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="488" y="-112"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</CheckButton>
|
||||
<Button name="$parentShowAllOnMapButton" inherits="UIPanelButtonTemplate" text="Show all on map" hidden="true">
|
||||
<Size><AbsDimension x="122" y="20"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="602" y="-110"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Frame name="$parentNPCFunctionDropdown" inherits="UIDropDownMenuTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="242" y="-80"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Frame>
|
||||
<CheckButton name="$parentNPCInZoneOnly" inherits="UICheckButtonTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="404" y="-84"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</CheckButton>
|
||||
|
||||
<CheckButton name="$parentRequireVendor" inherits="UICheckButtonTemplate">
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="18" y="-140"/></Offset></Anchor></Anchors>
|
||||
</CheckButton>
|
||||
<CheckButton name="$parentRequireDrop" inherits="UICheckButtonTemplate">
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="86" y="-140"/></Offset></Anchor></Anchors>
|
||||
</CheckButton>
|
||||
<CheckButton name="$parentRequireObject" inherits="UICheckButtonTemplate">
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="154" y="-140"/></Offset></Anchor></Anchors>
|
||||
</CheckButton>
|
||||
<CheckButton name="$parentHideNoAbilities" inherits="UICheckButtonTemplate">
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="232" y="-140"/></Offset></Anchor></Anchors>
|
||||
</CheckButton>
|
||||
<CheckButton name="$parentHideUnknown" inherits="UICheckButtonTemplate">
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="372" y="-140"/></Offset></Anchor></Anchors>
|
||||
</CheckButton>
|
||||
|
||||
<Frame name="$parentListSliderBackdrop">
|
||||
<Size><AbsDimension x="22" y="322"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentListBackdrop" relativePoint="TOPRIGHT"><Offset><AbsDimension x="2" y="-20"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Backdrop bgFile="Interface\Buttons\WHITE8X8" edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
|
||||
<BackgroundInsets><AbsInset left="2" right="2" top="2" bottom="2"/></BackgroundInsets>
|
||||
<TileSize><AbsValue val="16"/></TileSize>
|
||||
<EdgeSize><AbsValue val="16"/></EdgeSize>
|
||||
</Backdrop>
|
||||
</Frame>
|
||||
|
||||
<Slider name="$parentListSlider" inherits="UISliderTemplate" orientation="VERTICAL" minValue="1" maxValue="1" defaultValue="1" valueStep="1">
|
||||
<Size><AbsDimension x="20" y="322"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER" relativeTo="$parentListSliderBackdrop"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Slider>
|
||||
|
||||
<Button name="$parentPrevButton" inherits="UIPanelButtonTemplate" text="Prev">
|
||||
<Size><AbsDimension x="80" y="22"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="280" y="-524"/></Offset></Anchor></Anchors>
|
||||
</Button>
|
||||
<Button name="$parentNextButton" inherits="UIPanelButtonTemplate" text="Next">
|
||||
<Size><AbsDimension x="80" y="22"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="368" y="-524"/></Offset></Anchor></Anchors>
|
||||
</Button>
|
||||
</Frames>
|
||||
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
this:RegisterForDrag("LeftButton")
|
||||
</OnLoad>
|
||||
<OnDragStart>
|
||||
this:StartMoving();
|
||||
</OnDragStart>
|
||||
<OnDragStop>
|
||||
this:StopMovingOrSizing();
|
||||
</OnDragStop>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
</Ui>
|
||||
@@ -0,0 +1,180 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt Localization System
|
||||
------------------------------------------------------
|
||||
|
||||
MTH.localization = {}
|
||||
MTH.currentLocale = GetLocale()
|
||||
|
||||
-- Binding Headers (must be defined early for keybindings)
|
||||
BINDING_HEADER_FEEDOMATIC = "[MetaHunt] Feed-O-Matic"
|
||||
BINDING_HEADER_ZAspectHeader = "[MetaHunt] ZAspect Buttons"
|
||||
BINDING_HEADER_ZTrackHeader = "[MetaHunt] ZTrack Buttons"
|
||||
BINDING_HEADER_ZTrapHeader = "[MetaHunt] ZTrap Buttons"
|
||||
BINDING_HEADER_ZPetHeader = "[MetaHunt] ZPet Buttons"
|
||||
BINDING_HEADER_ZAmmoHeader = "[MetaHunt] ZAmmo Buttons"
|
||||
|
||||
-- Localization table structure
|
||||
local L = {}
|
||||
|
||||
-- English (default)
|
||||
if MTH.currentLocale == "enUS" or MTH.currentLocale == "enGB" then
|
||||
|
||||
-- FeedOMatic
|
||||
L.FEEDOMATIC_NAME = "Feed-O-Matic"
|
||||
L.FEEDOMATIC_DESC = "Helps a Hunter keep his pets well fed"
|
||||
L.TOOLTIPS_OPTION_FOOD = "Add tooltips on food"
|
||||
L.TOOLTIPS_OPTION_FOOD_HELP = "Show your current pet's food preference directly in item tooltips."
|
||||
L.TOOLTIPS_OPTION_OWNPET = "Activate on my pet"
|
||||
L.TOOLTIPS_OPTION_OWNPET_HELP = "Show your own pet status details in tooltip when hovering your pet context."
|
||||
L.TOOLTIPS_FOOD_QUALITY_UNKNOWN = "%s can eat this, but hasn't tried it yet."
|
||||
L.TOOLTIPS_FOOD_QUALITY_UNDER = "%s doesn't like this anymore."
|
||||
L.TOOLTIPS_FOOD_QUALITY_MIGHT = "%s might eat this."
|
||||
L.TOOLTIPS_FOOD_QUALITY_WILL = "%s will eat this."
|
||||
L.TOOLTIPS_FOOD_QUALITY_LIKE = "%s likes to eat this."
|
||||
L.TOOLTIPS_FOOD_QUALITY_LOVE = "%s loves to eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_UNKNOWN = "can eat this, but hasn't tried it yet."
|
||||
L.TOOLTIPS_FOOD_MARKER_UNDER = "doesn't like this anymore."
|
||||
L.TOOLTIPS_FOOD_MARKER_MIGHT = "might eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_WILL = "will eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_LIKE = "likes to eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_LOVE = "loves to eat this."
|
||||
|
||||
-- zhunter
|
||||
L.ZHUNTER_NAME = "zhunter"
|
||||
L.ZHUNTER_DESC = "Various features to make a hunter's life easier"
|
||||
|
||||
-- German / Deutsch
|
||||
elseif MTH.currentLocale == "deDE" then
|
||||
|
||||
L.FEEDOMATIC_NAME = "Futter-O-Mat"
|
||||
L.ZHUNTER_NAME = "zhunter"
|
||||
L.TOOLTIPS_OPTION_FOOD = "Add tooltips on food"
|
||||
L.TOOLTIPS_OPTION_FOOD_HELP = "Show your current pet's food preference directly in item tooltips."
|
||||
L.TOOLTIPS_OPTION_OWNPET = "Activate on my pet"
|
||||
L.TOOLTIPS_OPTION_OWNPET_HELP = "Show your own pet status details in tooltip when hovering your pet context."
|
||||
L.TOOLTIPS_FOOD_QUALITY_UNKNOWN = "%s can eat this, but hasn't tried it yet."
|
||||
L.TOOLTIPS_FOOD_QUALITY_UNDER = "%s doesn't like this anymore."
|
||||
L.TOOLTIPS_FOOD_QUALITY_MIGHT = "%s might eat this."
|
||||
L.TOOLTIPS_FOOD_QUALITY_WILL = "%s will eat this."
|
||||
L.TOOLTIPS_FOOD_QUALITY_LIKE = "%s likes to eat this."
|
||||
L.TOOLTIPS_FOOD_QUALITY_LOVE = "%s loves to eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_UNKNOWN = "can eat this, but hasn't tried it yet."
|
||||
L.TOOLTIPS_FOOD_MARKER_UNDER = "doesn't like this anymore."
|
||||
L.TOOLTIPS_FOOD_MARKER_MIGHT = "might eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_WILL = "will eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_LIKE = "likes to eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_LOVE = "loves to eat this."
|
||||
|
||||
-- French / Français
|
||||
elseif MTH.currentLocale == "frFR" then
|
||||
|
||||
L.FEEDOMATIC_NAME = "Nourricier"
|
||||
L.ZHUNTER_NAME = "zhunter"
|
||||
L.TOOLTIPS_OPTION_FOOD = "Add tooltips on food"
|
||||
L.TOOLTIPS_OPTION_FOOD_HELP = "Show your current pet's food preference directly in item tooltips."
|
||||
L.TOOLTIPS_OPTION_OWNPET = "Activate on my pet"
|
||||
L.TOOLTIPS_OPTION_OWNPET_HELP = "Show your own pet status details in tooltip when hovering your pet context."
|
||||
L.TOOLTIPS_FOOD_QUALITY_UNKNOWN = "%s can eat this, but hasn't tried it yet."
|
||||
L.TOOLTIPS_FOOD_QUALITY_UNDER = "%s doesn't like this anymore."
|
||||
L.TOOLTIPS_FOOD_QUALITY_MIGHT = "%s might eat this."
|
||||
L.TOOLTIPS_FOOD_QUALITY_WILL = "%s will eat this."
|
||||
L.TOOLTIPS_FOOD_QUALITY_LIKE = "%s likes to eat this."
|
||||
L.TOOLTIPS_FOOD_QUALITY_LOVE = "%s loves to eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_UNKNOWN = "can eat this, but hasn't tried it yet."
|
||||
L.TOOLTIPS_FOOD_MARKER_UNDER = "doesn't like this anymore."
|
||||
L.TOOLTIPS_FOOD_MARKER_MIGHT = "might eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_WILL = "will eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_LIKE = "likes to eat this."
|
||||
L.TOOLTIPS_FOOD_MARKER_LOVE = "loves to eat this."
|
||||
|
||||
end
|
||||
|
||||
MTH.localization[MTH.currentLocale] = L
|
||||
|
||||
-- Get localized string
|
||||
function MTH:GetLocalization(key, default)
|
||||
if L[key] then
|
||||
return L[key]
|
||||
end
|
||||
return default or key
|
||||
end
|
||||
|
||||
-- Module localization helper
|
||||
function MTH:RegisterModuleLocalization(module_name, localizationTable)
|
||||
if not self.localization.modules then
|
||||
self.localization.modules = {}
|
||||
end
|
||||
self.localization.modules[module_name] = localizationTable
|
||||
end
|
||||
|
||||
function MTH:GetModuleLocalization(module_name, key, default)
|
||||
if self.localization.modules and self.localization.modules[module_name] then
|
||||
local L = self.localization.modules[module_name]
|
||||
if L[key] then
|
||||
return L[key]
|
||||
end
|
||||
end
|
||||
return default or key
|
||||
end
|
||||
|
||||
local MTH_SPELL_LOCALE = {
|
||||
enUS = {
|
||||
["Banish"] = "Banish",
|
||||
["Bestial Wrath"] = "Bestial Wrath",
|
||||
["Concussive Shot"] = "Concussive Shot",
|
||||
["Counterattack"] = "Counterattack",
|
||||
["Critical Mass"] = "Critical Mass",
|
||||
["Clever Traps"] = "Clever Traps",
|
||||
["Deterrence"] = "Deterrence",
|
||||
["Explosive Trap"] = "Explosive Trap",
|
||||
["Explosive Trap Effect"] = "Explosive Trap Effect",
|
||||
["Feed Pet"] = "Feed Pet",
|
||||
["Flare"] = "Flare",
|
||||
["Freezing Trap"] = "Freezing Trap",
|
||||
["Freezing Trap Effect"] = "Freezing Trap Effect",
|
||||
["Frost Trap"] = "Frost Trap",
|
||||
["Frost Trap Aura"] = "Frost Trap Aura",
|
||||
["Hunter's Mark"] = "Hunter's Mark",
|
||||
["Holy Strength"] = "Holy Strength",
|
||||
["Immolation Trap"] = "Immolation Trap",
|
||||
["Immolation Trap Effect"] = "Immolation Trap Effect",
|
||||
["Improved Concussive Shot"] = "Improved Concussive Shot",
|
||||
["Improved Wing Clip"] = "Improved Wing Clip",
|
||||
["Perception"] = "Perception",
|
||||
["Piercing Shot"] = "Piercing Shot",
|
||||
["Piercing Shots"] = "Piercing Shots",
|
||||
["Quick Shots"] = "Quick Shots",
|
||||
["Rapid Fire"] = "Rapid Fire",
|
||||
["Scare Beast"] = "Scare Beast",
|
||||
["Scatter Shot"] = "Scatter Shot",
|
||||
["Scorpid Poison"] = "Scorpid Poison",
|
||||
["Scorpid Sting"] = "Scorpid Sting",
|
||||
["Serpent Sting"] = "Serpent Sting",
|
||||
["Stoneform"] = "Stoneform",
|
||||
["Unstable Power"] = "Unstable Power",
|
||||
["Ephemeral Power"] = "Ephemeral Power",
|
||||
["Viper Sting"] = "Viper Sting",
|
||||
["War Stomp"] = "War Stomp",
|
||||
["Will of the Forsaken"] = "Will of the Forsaken",
|
||||
["Wing Clip"] = "Wing Clip",
|
||||
["Wyvern Sting"] = "Wyvern Sting",
|
||||
["Blood Fury"] = "Blood Fury",
|
||||
["Berserking"] = "Berserking",
|
||||
},
|
||||
}
|
||||
|
||||
MTH.Const = MTH.Const or {}
|
||||
MTH.Const.SpellLocaleMap = MTH_SPELL_LOCALE
|
||||
|
||||
function MTH:LocalizeSpell(spellToken)
|
||||
local key = tostring(spellToken or "")
|
||||
if key == "" then
|
||||
return key
|
||||
end
|
||||
local locale = tostring(self.currentLocale or GetLocale() or "enUS")
|
||||
local localeMap = MTH_SPELL_LOCALE[locale] or MTH_SPELL_LOCALE.enUS
|
||||
if localeMap and localeMap[key] then
|
||||
return localeMap[key]
|
||||
end
|
||||
return key
|
||||
end
|
||||
+1262
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,171 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt Minimap Button
|
||||
------------------------------------------------------
|
||||
|
||||
if not MTH_MinimapButton then
|
||||
MTH_MinimapButton = {}
|
||||
end
|
||||
|
||||
local function MTH_MB_GetStore()
|
||||
if not MTH_CharSavedVariables then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.minimapButton) ~= "table" then
|
||||
MTH_CharSavedVariables.minimapButton = {}
|
||||
end
|
||||
local store = MTH_CharSavedVariables.minimapButton
|
||||
if store.angle == nil then
|
||||
store.angle = 220
|
||||
end
|
||||
return store
|
||||
end
|
||||
|
||||
local function MTH_MB_GetRadius()
|
||||
return 80
|
||||
end
|
||||
|
||||
local function MTH_MB_UpdatePosition()
|
||||
if not MTH_MinimapButton.frame then
|
||||
return
|
||||
end
|
||||
local store = MTH_MB_GetStore()
|
||||
local angle = tonumber(store.angle) or 220
|
||||
local rad = math.rad(angle)
|
||||
local radius = MTH_MB_GetRadius()
|
||||
local x = math.cos(rad) * radius
|
||||
local y = math.sin(rad) * radius
|
||||
MTH_MinimapButton.frame:ClearAllPoints()
|
||||
MTH_MinimapButton.frame:SetPoint("CENTER", Minimap, "CENTER", x, y)
|
||||
end
|
||||
|
||||
local function MTH_MB_UpdateDragPosition()
|
||||
if not MTH_MinimapButton.frame then
|
||||
return
|
||||
end
|
||||
local mx, my = Minimap:GetCenter()
|
||||
local px, py = GetCursorPosition()
|
||||
local scale = Minimap:GetEffectiveScale()
|
||||
if scale and scale ~= 0 then
|
||||
px = px / scale
|
||||
py = py / scale
|
||||
end
|
||||
local dx = px - mx
|
||||
local dy = py - my
|
||||
local angle = math.deg(math.atan2(dy, dx))
|
||||
local store = MTH_MB_GetStore()
|
||||
store.angle = angle
|
||||
MTH_MB_UpdatePosition()
|
||||
end
|
||||
|
||||
local function MTH_MB_SetHunterIcon(texture)
|
||||
if not texture then
|
||||
return
|
||||
end
|
||||
texture:SetTexture("Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES")
|
||||
if CLASS_ICON_TCOORDS and CLASS_ICON_TCOORDS["HUNTER"] then
|
||||
local coords = CLASS_ICON_TCOORDS["HUNTER"]
|
||||
texture:SetTexCoord(coords[1], coords[2], coords[3], coords[4])
|
||||
else
|
||||
texture:SetTexCoord(0, 0.25, 0.25, 0.5)
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_MinimapButton:Initialize()
|
||||
if self.frame then
|
||||
MTH_MB_UpdatePosition()
|
||||
self.frame:Show()
|
||||
return
|
||||
end
|
||||
|
||||
local button = CreateFrame("Button", "MTH_MinimapButtonFrame", Minimap)
|
||||
if not button then
|
||||
return
|
||||
end
|
||||
|
||||
button:SetWidth(31)
|
||||
button:SetHeight(31)
|
||||
button:SetFrameStrata("MEDIUM")
|
||||
button:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
||||
button:RegisterForDrag("LeftButton")
|
||||
|
||||
local border = button:CreateTexture(nil, "OVERLAY")
|
||||
border:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
|
||||
border:SetWidth(54)
|
||||
border:SetHeight(54)
|
||||
border:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0)
|
||||
|
||||
local icon = button:CreateTexture(nil, "BACKGROUND")
|
||||
icon:SetWidth(20)
|
||||
icon:SetHeight(20)
|
||||
icon:SetPoint("CENTER", button, "CENTER", 0, 0)
|
||||
MTH_MB_SetHunterIcon(icon)
|
||||
|
||||
local highlight = button:CreateTexture(nil, "HIGHLIGHT")
|
||||
highlight:SetTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
|
||||
highlight:SetBlendMode("ADD")
|
||||
highlight:SetWidth(31)
|
||||
highlight:SetHeight(31)
|
||||
highlight:SetPoint("CENTER", button, "CENTER", 0, 0)
|
||||
|
||||
button:SetScript("OnEnter", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
GameTooltip:SetOwner(self, "ANCHOR_LEFT")
|
||||
GameTooltip:SetText("MetaHunt", 1, 0.82, 0)
|
||||
GameTooltip:AddLine("Left-Click: Open Hunter Book", 1, 1, 1)
|
||||
GameTooltip:AddLine("Right-Click: Open MetaHunt Options", 1, 1, 1)
|
||||
GameTooltip:AddLine("Drag: Move button", 0.8, 0.8, 0.8)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
|
||||
button:SetScript("OnLeave", function()
|
||||
GameTooltip:Hide()
|
||||
end)
|
||||
|
||||
button:SetScript("OnDragStart", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
self.dragging = 1
|
||||
self:SetScript("OnUpdate", function()
|
||||
MTH_MB_UpdateDragPosition()
|
||||
end)
|
||||
end)
|
||||
|
||||
button:SetScript("OnDragStop", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
self.dragging = nil
|
||||
self:SetScript("OnUpdate", nil)
|
||||
MTH_MB_UpdateDragPosition()
|
||||
end)
|
||||
|
||||
button:SetScript("OnClick", function(_, mouseButton)
|
||||
mouseButton = mouseButton or arg1
|
||||
if mouseButton == "RightButton" then
|
||||
if type(MTH_OpenOptions) == "function" then
|
||||
MTH_OpenOptions("General")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if type(MTH_OpenHunterBook) == "function" then
|
||||
MTH_OpenHunterBook()
|
||||
elseif type(MTH_ToggleHunterBook) == "function" then
|
||||
MTH_ToggleHunterBook()
|
||||
end
|
||||
end)
|
||||
|
||||
self.frame = button
|
||||
self.icon = icon
|
||||
MTH_MB_UpdatePosition()
|
||||
end
|
||||
|
||||
local loader = CreateFrame("Frame", "MTH_MinimapButtonLoader")
|
||||
loader:RegisterEvent("ADDON_LOADED")
|
||||
loader:SetScript("OnEvent", function(_, eventName, addonName)
|
||||
eventName = eventName or event
|
||||
addonName = addonName or arg1
|
||||
if eventName == "ADDON_LOADED" and addonName == "MetaHunt" then
|
||||
MTH_MinimapButton:Initialize()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,845 @@
|
||||
local MTH_AB_READY = MTH_OptionsRequire and MTH_OptionsRequire("options-autobuy", {
|
||||
"MTH_GetFrame",
|
||||
"MTH_ClearContainer",
|
||||
"MTH_CreateCheckbox",
|
||||
})
|
||||
|
||||
local MTH_AB_LAYOUT = {
|
||||
LEFT_SECTION_X = 0,
|
||||
RIGHT_SECTION_X = 290,
|
||||
ROW_CHECK_X = 0,
|
||||
ROW_CHECK_LABEL_X = 27,
|
||||
ROW_DROPDOWN_X = 33,
|
||||
ROW_DROPDOWN_WIDTH = 100,
|
||||
ROW_QTY_GAP = 6,
|
||||
ROW_SUFFIX_GAP = 6,
|
||||
ROW_STEP = 40,
|
||||
SECTION_TITLE_Y = -110,
|
||||
SECTION_ROWS_Y0 = -138,
|
||||
}
|
||||
|
||||
local MTH_AB_STATE = {
|
||||
container = nil,
|
||||
built = false,
|
||||
syncing = false,
|
||||
controls = {
|
||||
moduleEnabled = nil,
|
||||
projectilesEnabled = nil,
|
||||
petFoodEnabled = nil,
|
||||
petFoodQty = nil,
|
||||
petFoodQtyLabel = nil,
|
||||
petFoodQtySuffix = nil,
|
||||
petFoodScopeCurrent = nil,
|
||||
petFoodScopeAll = nil,
|
||||
petFoodTitle = nil,
|
||||
petFoodPetLines = {},
|
||||
title = nil,
|
||||
body = nil,
|
||||
sectionArrowTitle = nil,
|
||||
sectionBulletTitle = nil,
|
||||
rows = {
|
||||
arrow = {},
|
||||
bullet = {},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local function MTH_AB_SetCheckedNoClick(check, checked)
|
||||
if not check then return end
|
||||
MTH_AB_STATE.syncing = true
|
||||
check:SetChecked(checked and true or false)
|
||||
MTH_AB_STATE.syncing = false
|
||||
end
|
||||
|
||||
local function MTH_AB_GetEngine()
|
||||
return MTH_AutoBuyEngine
|
||||
end
|
||||
|
||||
local function MTH_AB_GetStore()
|
||||
local engine = MTH_AB_GetEngine()
|
||||
if engine and engine.EnsureDefaults then
|
||||
return engine:EnsureDefaults()
|
||||
end
|
||||
MTH_AutoBuy_Saved = MTH_AutoBuy_Saved or {}
|
||||
return MTH_AutoBuy_Saved
|
||||
end
|
||||
|
||||
local function MTH_AB_NormalizeRule(rule)
|
||||
if type(rule) ~= "table" then
|
||||
rule = {}
|
||||
end
|
||||
if rule.enabled == nil then
|
||||
rule.enabled = false
|
||||
end
|
||||
rule.enabled = rule.enabled and true or false
|
||||
rule.itemId = rule.itemId and (tonumber(rule.itemId) or rule.itemId) or nil
|
||||
|
||||
local stacks = tonumber(rule.stacks)
|
||||
if not stacks then
|
||||
stacks = rule.enabled and 1 or 0
|
||||
end
|
||||
stacks = math.floor(stacks)
|
||||
if stacks < 0 then
|
||||
stacks = 0
|
||||
end
|
||||
if rule.enabled and stacks < 1 then
|
||||
stacks = 1
|
||||
end
|
||||
rule.stacks = stacks
|
||||
return rule
|
||||
end
|
||||
|
||||
local function MTH_AB_EnsureConfig()
|
||||
local store = MTH_AB_GetStore()
|
||||
if store.enabled == nil then store.enabled = true end
|
||||
if type(store.projectiles) ~= "table" then store.projectiles = {} end
|
||||
if type(store.petFood) ~= "table" then store.petFood = {} end
|
||||
if store.projectiles.enabled == nil then store.projectiles.enabled = true end
|
||||
if store.petFood.enabled == nil then store.petFood.enabled = false end
|
||||
if store.petFood.stacks == nil then store.petFood.stacks = 1 end
|
||||
if store.petFood.scope == nil then store.petFood.scope = "current" end
|
||||
store.petFood.stacks = math.floor(tonumber(store.petFood.stacks) or 1)
|
||||
if store.petFood.stacks < 1 then store.petFood.stacks = 1 end
|
||||
if store.petFood.scope ~= "current" and store.petFood.scope ~= "all" then
|
||||
store.petFood.scope = "current"
|
||||
end
|
||||
if type(store.projectiles.arrows) ~= "table" then store.projectiles.arrows = {} end
|
||||
if type(store.projectiles.bullets) ~= "table" then store.projectiles.bullets = {} end
|
||||
if type(store.projectiles.arrows.rules) ~= "table" then store.projectiles.arrows.rules = {} end
|
||||
if type(store.projectiles.bullets.rules) ~= "table" then store.projectiles.bullets.rules = {} end
|
||||
|
||||
for i = 1, 3 do
|
||||
store.projectiles.arrows.rules[i] = MTH_AB_NormalizeRule(store.projectiles.arrows.rules[i])
|
||||
store.projectiles.bullets.rules[i] = MTH_AB_NormalizeRule(store.projectiles.bullets.rules[i])
|
||||
end
|
||||
|
||||
return store
|
||||
end
|
||||
|
||||
local function MTH_AB_SetPetFoodStacks(store, value)
|
||||
if type(store) ~= "table" or type(store.petFood) ~= "table" then
|
||||
return
|
||||
end
|
||||
local stacks = math.floor(tonumber(value) or 1)
|
||||
if stacks < 1 then stacks = 1 end
|
||||
store.petFood.stacks = stacks
|
||||
end
|
||||
|
||||
local function MTH_AB_TitleCase(text)
|
||||
local source = tostring(text or "")
|
||||
if source == "" then return "" end
|
||||
return string.upper(string.sub(source, 1, 1)) .. string.lower(string.sub(source, 2))
|
||||
end
|
||||
|
||||
local function MTH_AB_NormalizeFamilyToken(text)
|
||||
local value = string.lower(tostring(text or ""))
|
||||
value = string.gsub(value, "%b()", " ")
|
||||
value = string.gsub(value, "[^%a]", "")
|
||||
return value
|
||||
end
|
||||
|
||||
local function MTH_AB_SingularizeFamilyToken(token)
|
||||
local value = tostring(token or "")
|
||||
if string.len(value) <= 3 then
|
||||
return value
|
||||
end
|
||||
if string.sub(value, -3) == "ves" and string.len(value) > 4 then
|
||||
return string.sub(value, 1, -4) .. "f"
|
||||
end
|
||||
if string.sub(value, -3) == "ies" and string.len(value) > 4 then
|
||||
return string.sub(value, 1, -4) .. "y"
|
||||
end
|
||||
if string.sub(value, -3) == "xes" and string.len(value) > 4 then
|
||||
return string.sub(value, 1, -3)
|
||||
end
|
||||
if string.sub(value, -4) == "ches" and string.len(value) > 5 then
|
||||
return string.sub(value, 1, -3)
|
||||
end
|
||||
if string.sub(value, -4) == "shes" and string.len(value) > 5 then
|
||||
return string.sub(value, 1, -3)
|
||||
end
|
||||
if string.sub(value, -3) == "zes" and string.len(value) > 4 then
|
||||
return string.sub(value, 1, -3)
|
||||
end
|
||||
if string.sub(value, -3) == "ses" then
|
||||
return string.sub(value, 1, -3)
|
||||
end
|
||||
if string.sub(value, -1) == "s" then
|
||||
return string.sub(value, 1, -2)
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
local function MTH_AB_FindFamilyDietRow(familyName)
|
||||
if type(MTH_DS_Families) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local needle = MTH_AB_NormalizeFamilyToken(familyName)
|
||||
if needle == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
local needleSingular = MTH_AB_SingularizeFamilyToken(needle)
|
||||
for key, row in pairs(MTH_DS_Families) do
|
||||
local candidate = MTH_AB_NormalizeFamilyToken(key)
|
||||
if candidate ~= "" then
|
||||
if candidate == needle or candidate == needleSingular then
|
||||
return row
|
||||
end
|
||||
local candidateSingular = MTH_AB_SingularizeFamilyToken(candidate)
|
||||
if candidateSingular == needle or candidateSingular == needleSingular then
|
||||
return row
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_AB_GetPetDietText(familyName)
|
||||
local family = tostring(familyName or "")
|
||||
if family == "" then
|
||||
return "unknown diet"
|
||||
end
|
||||
local row = MTH_AB_FindFamilyDietRow(family)
|
||||
if type(row) ~= "table" or type(row.food) ~= "table" then
|
||||
return "unknown diet"
|
||||
end
|
||||
local diets = {}
|
||||
for i = 1, table.getn(row.food) do
|
||||
local diet = tostring(row.food[i] or "")
|
||||
if diet ~= "" then
|
||||
table.insert(diets, MTH_AB_TitleCase(diet))
|
||||
end
|
||||
end
|
||||
if table.getn(diets) == 0 then
|
||||
return "unknown diet"
|
||||
end
|
||||
return table.concat(diets, ", ")
|
||||
end
|
||||
|
||||
local function MTH_AB_GetPetFoodEntries()
|
||||
if type(MTH_PETS_RefreshCurrentPet) == "function" then
|
||||
pcall(MTH_PETS_RefreshCurrentPet)
|
||||
end
|
||||
|
||||
local entries = {}
|
||||
local defaultIcon = "Interface\\Icons\\INV_Misc_QuestionMark"
|
||||
local function buildEntryFromRow(row)
|
||||
if type(row) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
local name = tostring(row.name or "")
|
||||
if name == "" then
|
||||
return nil
|
||||
end
|
||||
local family = tostring(row.family or "")
|
||||
local icon = tostring((type(row.stableInfo) == "table" and row.stableInfo.icon) or row.icon or "")
|
||||
if icon == "" and type(row.stableRaw) == "table" then
|
||||
icon = tostring(row.stableRaw[1] or "")
|
||||
end
|
||||
if icon == "" then icon = defaultIcon end
|
||||
return {
|
||||
icon = icon,
|
||||
name = name,
|
||||
family = family,
|
||||
diets = MTH_AB_GetPetDietText(family),
|
||||
}
|
||||
end
|
||||
|
||||
local function setSlot(slot, data)
|
||||
entries[slot + 1] = data
|
||||
end
|
||||
|
||||
for slot = 0, 4 do
|
||||
setSlot(slot, {
|
||||
icon = defaultIcon,
|
||||
name = "",
|
||||
diets = "",
|
||||
placeholder = (slot == 0) and "NO CURRENT PET !" or "Free slot",
|
||||
})
|
||||
end
|
||||
|
||||
local petsRoot = type(MTH_PETS_GetRootStore) == "function" and MTH_PETS_GetRootStore() or nil
|
||||
local petStore = petsRoot and petsRoot.petStore or nil
|
||||
local activeById = petStore and petStore.activeById or nil
|
||||
local stableSlotIndex = petStore and petStore.stableSlotIndex or nil
|
||||
local currentInfo = type(MTH_GetCurrentPetInfo) == "function" and MTH_GetCurrentPetInfo() or nil
|
||||
if currentInfo and currentInfo.liveExists then
|
||||
local currentName = tostring(currentInfo.name or "")
|
||||
if currentName ~= "" then
|
||||
local currentFamily = tostring(currentInfo.family or "")
|
||||
local currentIcon = defaultIcon
|
||||
local currentId = currentInfo.id
|
||||
if currentId and type(activeById) == "table" and type(activeById[currentId]) == "table" then
|
||||
local currentRow = activeById[currentId]
|
||||
local iconFromRow = tostring((type(currentRow.stableInfo) == "table" and currentRow.stableInfo.icon) or currentRow.icon or "")
|
||||
if iconFromRow == "" and type(currentRow.stableRaw) == "table" then
|
||||
iconFromRow = tostring(currentRow.stableRaw[1] or "")
|
||||
end
|
||||
if iconFromRow ~= "" then
|
||||
currentIcon = iconFromRow
|
||||
end
|
||||
end
|
||||
setSlot(0, {
|
||||
icon = currentIcon,
|
||||
name = currentName,
|
||||
family = currentFamily,
|
||||
diets = MTH_AB_GetPetDietText(currentFamily),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
for slot = 1, 4 do
|
||||
local petId = nil
|
||||
if type(stableSlotIndex) == "table" then
|
||||
petId = stableSlotIndex[slot]
|
||||
end
|
||||
local row = (petId and type(activeById) == "table") and activeById[petId] or nil
|
||||
if type(row) ~= "table" and type(activeById) == "table" then
|
||||
for _, candidate in pairs(activeById) do
|
||||
if type(candidate) == "table" and tonumber(candidate.stableSlot) == slot then
|
||||
row = candidate
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
local entry = buildEntryFromRow(row)
|
||||
if entry then
|
||||
setSlot(slot, entry)
|
||||
end
|
||||
end
|
||||
|
||||
return entries
|
||||
end
|
||||
|
||||
local function MTH_AB_RefreshPetFoodListUI()
|
||||
local lines = MTH_AB_STATE.controls.petFoodPetLines or {}
|
||||
if table.getn(lines) == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local entries = MTH_AB_GetPetFoodEntries()
|
||||
for i = 1, table.getn(lines) do
|
||||
local line = lines[i]
|
||||
local entry = entries[i]
|
||||
if line and line.text and line.icon then
|
||||
if entry then
|
||||
line.icon:Show()
|
||||
line.icon:SetTexture(tostring(entry.icon or "Interface\\Icons\\INV_Misc_QuestionMark"))
|
||||
line.text:Show()
|
||||
if tostring(entry.name or "") ~= "" then
|
||||
line.text:SetText(tostring(entry.name) .. " likes " .. tostring(entry.diets or "unknown diet"))
|
||||
else
|
||||
line.text:SetText(tostring(entry.placeholder or ""))
|
||||
end
|
||||
else
|
||||
line.icon:Hide()
|
||||
line.text:SetText("")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_AB_GetBuyableItems(subtype)
|
||||
if type(MTH_AutoBuy_GetProjectileItems) == "function" then
|
||||
local result = MTH_AutoBuy_GetProjectileItems(subtype)
|
||||
if type(result) == "table" then
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
local wanted = string.lower(tostring(subtype or ""))
|
||||
local result = {}
|
||||
local source = MTH_DS_AmmoItems or {}
|
||||
for itemId, row in pairs(source) do
|
||||
if type(row) == "table" and string.lower(tostring(row.subtype or "")) == wanted and type(row.vendors) == "table" then
|
||||
for _ in pairs(row.vendors) do
|
||||
table.insert(result, {
|
||||
itemId = tonumber(itemId) or itemId,
|
||||
name = tostring(row.name or ("Item " .. tostring(itemId))),
|
||||
level = tonumber(row.level) or 0,
|
||||
reqlevel = tonumber(row.reqlevel) or 0,
|
||||
})
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(result, function(a, b)
|
||||
if (a.level or 0) ~= (b.level or 0) then
|
||||
return (a.level or 0) < (b.level or 0)
|
||||
end
|
||||
if tostring(a.name or "") ~= tostring(b.name or "") then
|
||||
return tostring(a.name or "") < tostring(b.name or "")
|
||||
end
|
||||
return tonumber(a.itemId or 0) < tonumber(b.itemId or 0)
|
||||
end)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
local function MTH_AB_GetItemLabel(item)
|
||||
if type(item) ~= "table" then return "Unknown" end
|
||||
return tostring(item.name or ("Item " .. tostring(item.itemId or "")))
|
||||
end
|
||||
|
||||
local function MTH_AB_DropdownSetText(frame, text)
|
||||
if UIDropDownMenu_SetText then
|
||||
UIDropDownMenu_SetText(tostring(text or ""), frame)
|
||||
return
|
||||
end
|
||||
if not frame then return end
|
||||
local textRegion = getglobal(frame:GetName() .. "Text")
|
||||
if textRegion and textRegion.SetText then
|
||||
textRegion:SetText(tostring(text or ""))
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_AB_FindItemById(list, itemId)
|
||||
itemId = tonumber(itemId)
|
||||
if not itemId or type(list) ~= "table" then return nil end
|
||||
for i = 1, table.getn(list) do
|
||||
if tonumber(list[i].itemId) == itemId then
|
||||
return list[i]
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_AB_GetRule(store, subtype, index)
|
||||
local bucket = subtype == "arrow" and store.projectiles.arrows or store.projectiles.bullets
|
||||
if type(bucket.rules[index]) ~= "table" then
|
||||
bucket.rules[index] = MTH_AB_NormalizeRule(nil)
|
||||
end
|
||||
bucket.rules[index] = MTH_AB_NormalizeRule(bucket.rules[index])
|
||||
return bucket.rules[index]
|
||||
end
|
||||
|
||||
local function MTH_AB_SetRuleEnabled(store, subtype, index, enabled)
|
||||
local rule = MTH_AB_GetRule(store, subtype, index)
|
||||
rule.enabled = enabled and true or false
|
||||
if not rule.enabled then
|
||||
rule.itemId = nil
|
||||
rule.stacks = 0
|
||||
elseif rule.stacks < 1 then
|
||||
rule.stacks = 1
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_AB_SetRuleItem(store, subtype, index, itemId)
|
||||
local rule = MTH_AB_GetRule(store, subtype, index)
|
||||
rule.itemId = itemId and (tonumber(itemId) or itemId) or nil
|
||||
end
|
||||
|
||||
local function MTH_AB_SetRuleStacks(store, subtype, index, value)
|
||||
local rule = MTH_AB_GetRule(store, subtype, index)
|
||||
local stacks = math.floor(tonumber(value) or 0)
|
||||
if stacks < 0 then stacks = 0 end
|
||||
if rule.enabled and stacks < 1 then stacks = 1 end
|
||||
rule.stacks = stacks
|
||||
end
|
||||
|
||||
local function MTH_AB_RefreshRowUI(row, store, moduleEnabled, projectilesEnabled)
|
||||
local rule = MTH_AB_GetRule(store, row.subtype, row.index)
|
||||
local rowEnabled = moduleEnabled and projectilesEnabled and rule.enabled
|
||||
local rowInteractive = moduleEnabled and projectilesEnabled
|
||||
local muted = rowInteractive and 1 or 0.5
|
||||
|
||||
MTH_AB_SetCheckedNoClick(row.check, rule.enabled)
|
||||
if rowInteractive then
|
||||
if row.check.Enable then row.check:Enable() end
|
||||
else
|
||||
if row.check.Disable then row.check:Disable() end
|
||||
end
|
||||
|
||||
if UIDropDownMenu_EnableDropDown and UIDropDownMenu_DisableDropDown then
|
||||
if rowEnabled then
|
||||
UIDropDownMenu_EnableDropDown(row.dropdown)
|
||||
else
|
||||
UIDropDownMenu_DisableDropDown(row.dropdown)
|
||||
end
|
||||
end
|
||||
|
||||
local selectedItem = MTH_AB_FindItemById(row.items, rule.itemId)
|
||||
if selectedItem then
|
||||
MTH_AB_DropdownSetText(row.dropdown, MTH_AB_GetItemLabel(selectedItem))
|
||||
else
|
||||
MTH_AB_DropdownSetText(row.dropdown, "Select item...")
|
||||
end
|
||||
|
||||
if row.qty then
|
||||
if rowEnabled then
|
||||
if row.qty.Enable then row.qty:Enable() end
|
||||
if row.qty.EnableMouse then row.qty:EnableMouse(true) end
|
||||
if row.qty.SetTextColor then row.qty:SetTextColor(1, 1, 1) end
|
||||
else
|
||||
if row.qty.Disable then row.qty:Disable() end
|
||||
if row.qty.EnableMouse then row.qty:EnableMouse(false) end
|
||||
if row.qty.ClearFocus then row.qty:ClearFocus() end
|
||||
if row.qty.SetTextColor then row.qty:SetTextColor(0.6, 0.6, 0.6) end
|
||||
end
|
||||
row.qty:SetText(tostring(rule.stacks or 0))
|
||||
end
|
||||
|
||||
if row.label and row.label.SetTextColor then
|
||||
row.label:SetTextColor(muted, muted, muted)
|
||||
end
|
||||
if row.suffix and row.suffix.SetTextColor then
|
||||
row.suffix:SetTextColor(muted, muted, muted)
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_AB_RefreshOptionsUI()
|
||||
if not MTH_AB_STATE.built then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
local moduleEnabled = true
|
||||
if MTH and MTH.IsModuleEnabled then
|
||||
moduleEnabled = MTH:IsModuleEnabled("autobuy", true) and true or false
|
||||
end
|
||||
|
||||
MTH_AB_SetCheckedNoClick(MTH_AB_STATE.controls.moduleEnabled, moduleEnabled)
|
||||
MTH_AB_SetCheckedNoClick(MTH_AB_STATE.controls.projectilesEnabled, store.projectiles.enabled and true or false)
|
||||
|
||||
if MTH_AB_STATE.controls.projectilesEnabled then
|
||||
local proj = MTH_AB_STATE.controls.projectilesEnabled
|
||||
if moduleEnabled then
|
||||
if proj.Enable then
|
||||
proj:Enable()
|
||||
end
|
||||
else
|
||||
if proj.Disable then
|
||||
proj:Disable()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if MTH_AB_STATE.controls.petFoodEnabled then
|
||||
local pf = MTH_AB_STATE.controls.petFoodEnabled
|
||||
MTH_AB_SetCheckedNoClick(pf, store.petFood.enabled and true or false)
|
||||
if moduleEnabled then
|
||||
if pf.Enable then pf:Enable() end
|
||||
else
|
||||
if pf.Disable then pf:Disable() end
|
||||
end
|
||||
end
|
||||
|
||||
local petFoodInteractive = moduleEnabled and store.petFood.enabled
|
||||
if MTH_AB_STATE.controls.petFoodQty then
|
||||
local qty = MTH_AB_STATE.controls.petFoodQty
|
||||
qty:SetText(tostring(store.petFood.stacks or 1))
|
||||
if petFoodInteractive then
|
||||
if qty.Enable then qty:Enable() end
|
||||
if qty.EnableMouse then qty:EnableMouse(true) end
|
||||
if qty.SetTextColor then qty:SetTextColor(1, 1, 1) end
|
||||
else
|
||||
if qty.Disable then qty:Disable() end
|
||||
if qty.EnableMouse then qty:EnableMouse(false) end
|
||||
if qty.ClearFocus then qty:ClearFocus() end
|
||||
if qty.SetTextColor then qty:SetTextColor(0.6, 0.6, 0.6) end
|
||||
end
|
||||
end
|
||||
|
||||
if MTH_AB_STATE.controls.petFoodScopeCurrent then
|
||||
MTH_AB_SetCheckedNoClick(MTH_AB_STATE.controls.petFoodScopeCurrent, store.petFood.scope == "current")
|
||||
if petFoodInteractive then
|
||||
if MTH_AB_STATE.controls.petFoodScopeCurrent.Enable then MTH_AB_STATE.controls.petFoodScopeCurrent:Enable() end
|
||||
else
|
||||
if MTH_AB_STATE.controls.petFoodScopeCurrent.Disable then MTH_AB_STATE.controls.petFoodScopeCurrent:Disable() end
|
||||
end
|
||||
end
|
||||
|
||||
if MTH_AB_STATE.controls.petFoodScopeAll then
|
||||
MTH_AB_SetCheckedNoClick(MTH_AB_STATE.controls.petFoodScopeAll, store.petFood.scope == "all")
|
||||
if petFoodInteractive then
|
||||
if MTH_AB_STATE.controls.petFoodScopeAll.Enable then MTH_AB_STATE.controls.petFoodScopeAll:Enable() end
|
||||
else
|
||||
if MTH_AB_STATE.controls.petFoodScopeAll.Disable then MTH_AB_STATE.controls.petFoodScopeAll:Disable() end
|
||||
end
|
||||
end
|
||||
|
||||
MTH_AB_RefreshPetFoodListUI()
|
||||
|
||||
for i = 1, 3 do
|
||||
MTH_AB_RefreshRowUI(MTH_AB_STATE.controls.rows.arrow[i], store, moduleEnabled, store.projectiles.enabled)
|
||||
MTH_AB_RefreshRowUI(MTH_AB_STATE.controls.rows.bullet[i], store, moduleEnabled, store.projectiles.enabled)
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_AB_CreateRow(container, subtype, index, xBase, yOffset)
|
||||
local row = {
|
||||
subtype = subtype,
|
||||
index = index,
|
||||
items = MTH_AB_GetBuyableItems(subtype),
|
||||
}
|
||||
local nameBase = "MetaHuntOptionsAutoBuy_" .. subtype .. "_" .. tostring(index)
|
||||
|
||||
row.check = MTH_CreateCheckbox(container, nameBase .. "Check", "", yOffset, xBase + MTH_AB_LAYOUT.ROW_CHECK_X)
|
||||
row.label = container:CreateFontString(nameBase .. "Label", "ARTWORK", "GameFontNormalSmall")
|
||||
row.label:SetPoint("TOPLEFT", container, "TOPLEFT", xBase + MTH_AB_LAYOUT.ROW_CHECK_LABEL_X, yOffset - 5)
|
||||
row.label:SetText("On?")
|
||||
|
||||
row.dropdown = CreateFrame("Frame", nameBase .. "Drop", container, "UIDropDownMenuTemplate")
|
||||
row.dropdown:ClearAllPoints()
|
||||
row.dropdown:SetPoint("TOPLEFT", container, "TOPLEFT", xBase + MTH_AB_LAYOUT.ROW_DROPDOWN_X, yOffset + 3)
|
||||
if UIDropDownMenu_SetWidth then
|
||||
UIDropDownMenu_SetWidth(MTH_AB_LAYOUT.ROW_DROPDOWN_WIDTH, row.dropdown)
|
||||
end
|
||||
if UIDropDownMenu_JustifyText then
|
||||
UIDropDownMenu_JustifyText("LEFT", row.dropdown)
|
||||
end
|
||||
|
||||
if UIDropDownMenu_Initialize then
|
||||
UIDropDownMenu_Initialize(row.dropdown, function()
|
||||
local infoFactory = UIDropDownMenu_CreateInfo
|
||||
local info = type(infoFactory) == "function" and infoFactory() or {}
|
||||
info.text = "Select item..."
|
||||
info.func = function()
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
MTH_AB_SetRuleItem(store, subtype, index, nil)
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end
|
||||
if UIDropDownMenu_AddButton then UIDropDownMenu_AddButton(info) end
|
||||
|
||||
for j = 1, table.getn(row.items) do
|
||||
local item = row.items[j]
|
||||
local label = MTH_AB_GetItemLabel(item)
|
||||
local itemId = item.itemId
|
||||
local entry = type(infoFactory) == "function" and infoFactory() or {}
|
||||
entry.text = label
|
||||
entry.func = function()
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
MTH_AB_SetRuleItem(store, subtype, index, itemId)
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end
|
||||
if UIDropDownMenu_AddButton then UIDropDownMenu_AddButton(entry) end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
row.qty = CreateFrame("EditBox", nameBase .. "Qty", container, "InputBoxTemplate")
|
||||
local dropButton = getglobal(nameBase .. "DropButton")
|
||||
if dropButton then
|
||||
row.qty:SetPoint("LEFT", dropButton, "RIGHT", MTH_AB_LAYOUT.ROW_QTY_GAP, -1)
|
||||
else
|
||||
row.qty:SetPoint("LEFT", row.dropdown, "RIGHT", MTH_AB_LAYOUT.ROW_QTY_GAP + 6, -3)
|
||||
end
|
||||
row.qty:SetWidth(40)
|
||||
row.qty:SetHeight(20)
|
||||
row.qty:SetNumeric(true)
|
||||
row.qty:SetAutoFocus(false)
|
||||
|
||||
row.suffix = container:CreateFontString(nameBase .. "Suffix", "ARTWORK", "GameFontNormalSmall")
|
||||
row.suffix:SetPoint("LEFT", row.qty, "RIGHT", MTH_AB_LAYOUT.ROW_SUFFIX_GAP, 0)
|
||||
row.suffix:SetText("Stacks")
|
||||
|
||||
if row.check then
|
||||
row.check:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self or MTH_AB_STATE.syncing then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
MTH_AB_SetRuleEnabled(store, subtype, index, self:GetChecked() and true or false)
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
end
|
||||
|
||||
row.qty:SetScript("OnEnterPressed", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
MTH_AB_SetRuleStacks(store, subtype, index, self:GetText())
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
self:ClearFocus()
|
||||
end)
|
||||
row.qty:SetScript("OnEditFocusLost", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
MTH_AB_SetRuleStacks(store, subtype, index, self:GetText())
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
|
||||
return row
|
||||
end
|
||||
|
||||
local function MTH_AB_BuildUI(container)
|
||||
if MTH_AB_STATE.built and MTH_AB_STATE.container == container then
|
||||
return
|
||||
end
|
||||
|
||||
MTH_ClearContainer(container)
|
||||
MTH_AB_STATE.container = container
|
||||
MTH_AB_STATE.built = true
|
||||
|
||||
local controls = MTH_AB_STATE.controls
|
||||
|
||||
controls.title = container:CreateFontString("MetaHuntOptionsAutoBuyTitle", "ARTWORK", "GameFontHighlight")
|
||||
if controls.title then
|
||||
if controls.title.SetPoint then controls.title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -10) end
|
||||
if controls.title.SetText then controls.title:SetText("Auto Buy") end
|
||||
end
|
||||
|
||||
controls.body = container:CreateFontString("MetaHuntOptionsAutoBuyBody", "ARTWORK", "GameFontNormalSmall")
|
||||
if controls.body then
|
||||
if controls.body.SetPoint then controls.body:SetPoint("TOPLEFT", container, "TOPLEFT", 176, -30) end
|
||||
if controls.body.SetWidth then controls.body:SetWidth(400) end
|
||||
if controls.body.SetJustifyH then controls.body:SetJustifyH("LEFT") end
|
||||
if controls.body.SetTextColor then controls.body:SetTextColor(1, 1, 1) end
|
||||
if controls.body.SetText then
|
||||
controls.body:SetText("Configure automatic buying rules that triggers when you open a vendor.\nThe quantity refers to how many STACKS of the item you want to have in your bags after your purchase")
|
||||
end
|
||||
end
|
||||
|
||||
controls.moduleEnabled = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyModuleEnabled", "Enable Auto Buy module", -30)
|
||||
if controls.moduleEnabled then
|
||||
controls.moduleEnabled:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self or MTH_AB_STATE.syncing then return end
|
||||
if MTH and MTH.SetModuleEnabled then
|
||||
local ok, err = MTH:SetModuleEnabled("autobuy", self:GetChecked() and true or false)
|
||||
if not ok and MTH and MTH.Print then
|
||||
MTH:Print("Failed to change Auto Buy state: " .. tostring(err), "error")
|
||||
end
|
||||
end
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
end
|
||||
|
||||
controls.projectilesEnabled = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyProjectilesEnabled", "Enable for Projectiles", -74)
|
||||
if controls.projectilesEnabled then
|
||||
controls.projectilesEnabled:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self or MTH_AB_STATE.syncing then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
store.projectiles.enabled = self:GetChecked() and true or false
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
end
|
||||
|
||||
controls.sectionArrowTitle = container:CreateFontString("MetaHuntOptionsAutoBuyArrowsTitle", "ARTWORK", "GameFontHighlight")
|
||||
if controls.sectionArrowTitle then
|
||||
if controls.sectionArrowTitle.SetPoint then
|
||||
controls.sectionArrowTitle:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X, MTH_AB_LAYOUT.SECTION_TITLE_Y)
|
||||
end
|
||||
if controls.sectionArrowTitle.SetText then controls.sectionArrowTitle:SetText("Arrows") end
|
||||
end
|
||||
|
||||
controls.sectionBulletTitle = container:CreateFontString("MetaHuntOptionsAutoBuyBulletsTitle", "ARTWORK", "GameFontHighlight")
|
||||
if controls.sectionBulletTitle then
|
||||
if controls.sectionBulletTitle.SetPoint then
|
||||
controls.sectionBulletTitle:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.RIGHT_SECTION_X, MTH_AB_LAYOUT.SECTION_TITLE_Y)
|
||||
end
|
||||
if controls.sectionBulletTitle.SetText then controls.sectionBulletTitle:SetText("Bullets") end
|
||||
end
|
||||
|
||||
for i = 1, 3 do
|
||||
local rowY = MTH_AB_LAYOUT.SECTION_ROWS_Y0 - ((i - 1) * MTH_AB_LAYOUT.ROW_STEP)
|
||||
controls.rows.arrow[i] = MTH_AB_CreateRow(container, "arrow", i, MTH_AB_LAYOUT.LEFT_SECTION_X, rowY)
|
||||
controls.rows.bullet[i] = MTH_AB_CreateRow(container, "bullet", i, MTH_AB_LAYOUT.RIGHT_SECTION_X, rowY)
|
||||
end
|
||||
|
||||
controls.petFoodTitle = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodTitle", "ARTWORK", "GameFontHighlight")
|
||||
if controls.petFoodTitle then
|
||||
controls.petFoodTitle:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X, -300)
|
||||
controls.petFoodTitle:SetText("Pet Food")
|
||||
end
|
||||
|
||||
controls.petFoodEnabled = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyPetFoodEnabled", "Enable for Pet Food", -322, MTH_AB_LAYOUT.LEFT_SECTION_X)
|
||||
if controls.petFoodEnabled then
|
||||
controls.petFoodEnabled:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self or MTH_AB_STATE.syncing then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
store.petFood.enabled = self:GetChecked() and true or false
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
end
|
||||
|
||||
controls.petFoodQtyLabel = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodQtyLabel", "ARTWORK", "GameFontNormalSmall")
|
||||
if controls.petFoodQtyLabel then
|
||||
controls.petFoodQtyLabel:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X + 28, -350)
|
||||
controls.petFoodQtyLabel:SetText("Quantity :")
|
||||
end
|
||||
|
||||
controls.petFoodQty = CreateFrame("EditBox", "MetaHuntOptionsAutoBuyPetFoodQty", container, "InputBoxTemplate")
|
||||
if controls.petFoodQty then
|
||||
controls.petFoodQty:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X + 95, -347)
|
||||
controls.petFoodQty:SetWidth(40)
|
||||
controls.petFoodQty:SetHeight(20)
|
||||
controls.petFoodQty:SetNumeric(true)
|
||||
controls.petFoodQty:SetAutoFocus(false)
|
||||
controls.petFoodQty:SetScript("OnEnterPressed", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
MTH_AB_SetPetFoodStacks(store, self:GetText())
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
self:ClearFocus()
|
||||
end)
|
||||
controls.petFoodQty:SetScript("OnEditFocusLost", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
MTH_AB_SetPetFoodStacks(store, self:GetText())
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
end
|
||||
|
||||
controls.petFoodQtySuffix = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodQtySuffix", "ARTWORK", "GameFontNormalSmall")
|
||||
if controls.petFoodQtySuffix then
|
||||
controls.petFoodQtySuffix:SetPoint("LEFT", controls.petFoodQty, "RIGHT", 8, 0)
|
||||
controls.petFoodQtySuffix:SetText("stacks")
|
||||
end
|
||||
|
||||
controls.petFoodScopeCurrent = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyPetFoodScopeCurrent", "Buy only for my current pet", -376, MTH_AB_LAYOUT.LEFT_SECTION_X + 28)
|
||||
if controls.petFoodScopeCurrent then
|
||||
controls.petFoodScopeCurrent:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self or MTH_AB_STATE.syncing then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
store.petFood.scope = "current"
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
end
|
||||
|
||||
controls.petFoodScopeAll = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyPetFoodScopeAll", "Buy for all my pets", -402, MTH_AB_LAYOUT.LEFT_SECTION_X + 28)
|
||||
if controls.petFoodScopeAll then
|
||||
controls.petFoodScopeAll:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self or MTH_AB_STATE.syncing then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
store.petFood.scope = "all"
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
end
|
||||
|
||||
controls.petFoodPetLines = {}
|
||||
for i = 1, 5 do
|
||||
local rowY = -434 - ((i - 1) * 22)
|
||||
local icon = container:CreateTexture("MetaHuntOptionsAutoBuyPetFoodPetIcon" .. tostring(i), "ARTWORK")
|
||||
icon:SetWidth(16)
|
||||
icon:SetHeight(16)
|
||||
icon:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X + 28, rowY)
|
||||
icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
|
||||
local text = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodPetText" .. tostring(i), "ARTWORK", "GameFontNormalSmall")
|
||||
text:SetPoint("LEFT", icon, "RIGHT", 6, 0)
|
||||
text:SetJustifyH("LEFT")
|
||||
text:SetTextColor(1, 1, 1)
|
||||
text:SetText("")
|
||||
controls.petFoodPetLines[i] = { icon = icon, text = text }
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_SetupAutoBuyOptions()
|
||||
if not MTH_AB_READY then return end
|
||||
local container = MTH_GetFrame("MetaHuntOptionsAutoBuy")
|
||||
if not container then return end
|
||||
|
||||
if CloseDropDownMenus then
|
||||
CloseDropDownMenus()
|
||||
end
|
||||
|
||||
MTH_AB_BuildUI(container)
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end
|
||||
@@ -0,0 +1,597 @@
|
||||
local function MTH_CHRON_GetGlobal(name)
|
||||
if type(getglobal) == "function" then
|
||||
return getglobal(name)
|
||||
end
|
||||
if _G then
|
||||
return _G[name]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_CHRON_IsReady()
|
||||
local required = {
|
||||
"MTH_GetFrame",
|
||||
"MTH_ClearContainer",
|
||||
"MTH_CreateCheckbox",
|
||||
"MTH_CreateSlider",
|
||||
"MTH_CreateActionButton",
|
||||
}
|
||||
|
||||
for i = 1, table.getn(required) do
|
||||
if MTH_CHRON_GetGlobal(required[i]) == nil then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local MTH_CHRON_STATE = {
|
||||
controls = {},
|
||||
}
|
||||
|
||||
local MTH_CHRON_DEFAULTS = {
|
||||
growup = false,
|
||||
reverse = false,
|
||||
fadeonkill = true,
|
||||
fadeonfade = true,
|
||||
barposition = {},
|
||||
ghost = 0,
|
||||
selfbars = true,
|
||||
barwidth = 220,
|
||||
barheight = 16,
|
||||
spacing = 0,
|
||||
barscale = 1,
|
||||
iconposition = "LEFT",
|
||||
textsize = 10,
|
||||
textcolor = "white",
|
||||
bgcolor = "teal",
|
||||
barcolor = "gray",
|
||||
bordercolor = "black",
|
||||
bordertex = "None",
|
||||
bgalpha = 0.5,
|
||||
text = "$t",
|
||||
onlyself = false,
|
||||
disabledSpells = {
|
||||
COMMON = {
|
||||
["Ephemeral Power"] = {},
|
||||
["Mind Quickening"] = {},
|
||||
["Unstable Power"] = {},
|
||||
},
|
||||
RACIAL = {},
|
||||
HUNTER = {},
|
||||
},
|
||||
}
|
||||
|
||||
local function MTH_CHRON_SeedDefaultCommonDisabled(commonBucket)
|
||||
if type(commonBucket) ~= "table" then
|
||||
return
|
||||
end
|
||||
local names = {
|
||||
"Ephemeral Power",
|
||||
"Mind Quickening",
|
||||
"Unstable Power",
|
||||
}
|
||||
for i = 1, table.getn(names) do
|
||||
local baseName = names[i]
|
||||
if commonBucket[baseName] == nil then
|
||||
commonBucket[baseName] = {}
|
||||
end
|
||||
if MTH and MTH.LocalizeSpell then
|
||||
local localized = MTH:LocalizeSpell(baseName)
|
||||
if localized and localized ~= "" and commonBucket[localized] == nil then
|
||||
commonBucket[localized] = {}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_CHRON_CopyTable(source)
|
||||
local target = {}
|
||||
for key, value in pairs(source) do
|
||||
if type(value) == "table" then
|
||||
target[key] = MTH_CHRON_CopyTable(value)
|
||||
else
|
||||
target[key] = value
|
||||
end
|
||||
end
|
||||
return target
|
||||
end
|
||||
|
||||
local function MTH_CHRON_GetProfile()
|
||||
if not (MTH and MTH.GetModuleSavedVariables) then
|
||||
return MTH_CHRON_CopyTable(MTH_CHRON_DEFAULTS)
|
||||
end
|
||||
|
||||
local store = MTH:GetModuleSavedVariables("chronometer")
|
||||
if type(store.profile) ~= "table" then
|
||||
store.profile = {}
|
||||
end
|
||||
local profile = store.profile
|
||||
|
||||
for key, value in pairs(MTH_CHRON_DEFAULTS) do
|
||||
if profile[key] == nil then
|
||||
if type(value) == "table" then
|
||||
profile[key] = MTH_CHRON_CopyTable(value)
|
||||
else
|
||||
profile[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if type(profile.disabledSpells) ~= "table" then
|
||||
profile.disabledSpells = {}
|
||||
end
|
||||
if type(profile.disabledSpells.COMMON) ~= "table" then
|
||||
profile.disabledSpells.COMMON = {}
|
||||
end
|
||||
MTH_CHRON_SeedDefaultCommonDisabled(profile.disabledSpells.COMMON)
|
||||
if type(profile.disabledSpells.RACIAL) ~= "table" then
|
||||
profile.disabledSpells.RACIAL = {}
|
||||
end
|
||||
|
||||
local _, class = UnitClass("player")
|
||||
class = class or "HUNTER"
|
||||
if type(profile.disabledSpells[class]) ~= "table" then
|
||||
profile.disabledSpells[class] = {}
|
||||
end
|
||||
|
||||
return profile
|
||||
end
|
||||
|
||||
local function MTH_CHRON_GetTimerDefinitions()
|
||||
local engine = MTH_ChronometerHunter
|
||||
if engine and engine.timers and (engine.timers[engine.SPELL] or engine.timers[engine.EVENT]) then
|
||||
return engine.timers, engine.SPELL, engine.EVENT
|
||||
end
|
||||
|
||||
if not engine then
|
||||
return nil, 1, 2
|
||||
end
|
||||
|
||||
local fake = {
|
||||
SPELL = 1,
|
||||
EVENT = 2,
|
||||
groups = {},
|
||||
timers = {},
|
||||
}
|
||||
function fake:AddGroup(id, forall, color)
|
||||
if color then
|
||||
self.groups[id] = { fa = forall, cr = color }
|
||||
else
|
||||
self.groups[id] = { fa = forall }
|
||||
end
|
||||
end
|
||||
function fake:AddTimer(kind, name, duration, targeted, isgain, selforselect, extra)
|
||||
if type(self.timers[kind]) ~= "table" then
|
||||
self.timers[kind] = {}
|
||||
end
|
||||
if type(extra) ~= "table" then
|
||||
extra = {}
|
||||
end
|
||||
self.timers[kind][name] = {
|
||||
d = duration,
|
||||
k = { t = targeted, g = isgain, s = selforselect },
|
||||
x = extra,
|
||||
}
|
||||
end
|
||||
|
||||
if type(engine.dataSetup) == "table" then
|
||||
for _, setupFunc in pairs(engine.dataSetup) do
|
||||
if type(setupFunc) == "function" then
|
||||
setupFunc(fake)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return fake.timers, fake.SPELL, fake.EVENT
|
||||
end
|
||||
|
||||
local function MTH_CHRON_SortKeys(list)
|
||||
table.sort(list, function(a, b)
|
||||
return tostring(a) < tostring(b)
|
||||
end)
|
||||
end
|
||||
|
||||
local function MTH_CHRON_BuildTimerLists()
|
||||
local timers, spellKind, eventKind = MTH_CHRON_GetTimerDefinitions()
|
||||
local classSpells, classEvents, racial = {}, {}, {}
|
||||
if type(timers) ~= "table" then
|
||||
return classSpells, classEvents, racial
|
||||
end
|
||||
|
||||
local function classify(timerRecord)
|
||||
if timerRecord and timerRecord.x and timerRecord.x.cl == "COMMON" then
|
||||
return "COMMON"
|
||||
elseif timerRecord and timerRecord.x and timerRecord.x.cl == "RACIAL" then
|
||||
return "RACIAL"
|
||||
end
|
||||
local _, class = UnitClass("player")
|
||||
return class or "HUNTER"
|
||||
end
|
||||
|
||||
for timerName, timerRecord in pairs(timers[spellKind] or {}) do
|
||||
local bucket = classify(timerRecord)
|
||||
if bucket == "RACIAL" then
|
||||
table.insert(racial, timerName)
|
||||
elseif bucket ~= "COMMON" then
|
||||
table.insert(classSpells, timerName)
|
||||
end
|
||||
end
|
||||
|
||||
for timerName, timerRecord in pairs(timers[eventKind] or {}) do
|
||||
local bucket = classify(timerRecord)
|
||||
if bucket == "RACIAL" then
|
||||
table.insert(racial, timerName)
|
||||
elseif bucket ~= "COMMON" then
|
||||
table.insert(classEvents, timerName)
|
||||
end
|
||||
end
|
||||
|
||||
MTH_CHRON_SortKeys(classSpells)
|
||||
MTH_CHRON_SortKeys(classEvents)
|
||||
MTH_CHRON_SortKeys(racial)
|
||||
return classSpells, classEvents, racial
|
||||
end
|
||||
|
||||
local function MTH_CHRON_ApplyLiveProfile(profile)
|
||||
if not profile then
|
||||
return
|
||||
end
|
||||
local engine = MTH_ChronometerHunter
|
||||
if not (engine and engine._mth_enabled) then
|
||||
return
|
||||
end
|
||||
|
||||
engine.profile = profile
|
||||
if engine.SetCandyBarGroupGrowth then
|
||||
engine:SetCandyBarGroupGrowth("MTHChronometer", profile.growup and true or false)
|
||||
end
|
||||
if engine.SetCandyBarGroupVerticalSpacing then
|
||||
engine:SetCandyBarGroupVerticalSpacing("MTHChronometer", profile.spacing or 0)
|
||||
end
|
||||
|
||||
if type(engine.bars) == "table" then
|
||||
for i = 1, 20 do
|
||||
local bar = engine.bars[i]
|
||||
if bar and bar.id then
|
||||
if profile.barscale and engine.SetCandyBarScale then
|
||||
engine:SetCandyBarScale(bar.id, profile.barscale)
|
||||
end
|
||||
if profile.barwidth and engine.SetCandyBarWidth then
|
||||
engine:SetCandyBarWidth(bar.id, profile.barwidth)
|
||||
end
|
||||
if profile.barheight and engine.SetCandyBarHeight then
|
||||
engine:SetCandyBarHeight(bar.id, profile.barheight)
|
||||
end
|
||||
if profile.iconposition and engine.SetCandyBarIconPosition then
|
||||
engine:SetCandyBarIconPosition(bar.id, profile.iconposition)
|
||||
end
|
||||
if profile.textsize and engine.SetCandyBarFontSize then
|
||||
engine:SetCandyBarFontSize(bar.id, profile.textsize)
|
||||
end
|
||||
if profile.text and engine.SetCandyBarText then
|
||||
local target = bar.target or "none"
|
||||
local text = target == "none" and (bar.name or "") or profile.text
|
||||
text = string.gsub(tostring(text or ""), "$t", tostring(target))
|
||||
text = string.gsub(tostring(text or ""), "$s", tostring(bar.name or ""))
|
||||
engine:SetCandyBarText(bar.id, text)
|
||||
end
|
||||
if engine.SetCandyBarReversed then
|
||||
engine:SetCandyBarReversed(bar.id, profile.reverse and true or false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_CHRON_MakeTitle(parent, name, text, x, y)
|
||||
local label = parent:CreateFontString(name, "ARTWORK", "GameFontHighlight")
|
||||
label:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
|
||||
label:SetText(text)
|
||||
return label
|
||||
end
|
||||
|
||||
local function MTH_CHRON_MakeSmall(parent, name, text, x, y)
|
||||
local label = parent:CreateFontString(name, "ARTWORK", "GameFontNormalSmall")
|
||||
label:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
|
||||
label:SetText(text)
|
||||
return label
|
||||
end
|
||||
|
||||
|
||||
local function MTH_CHRON_MakeInput(parent, name, x, y, width, height, value, onAccept)
|
||||
local edit = CreateFrame("EditBox", name, parent, "InputBoxTemplate")
|
||||
if not edit then
|
||||
return nil
|
||||
end
|
||||
edit:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
|
||||
edit:SetWidth(width or 200)
|
||||
edit:SetHeight(height or 20)
|
||||
edit:SetAutoFocus(false)
|
||||
edit:SetText(tostring(value or ""))
|
||||
edit:SetScript("OnEnterPressed", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
if type(onAccept) == "function" then
|
||||
onAccept(self:GetText() or "")
|
||||
end
|
||||
self:ClearFocus()
|
||||
end)
|
||||
return edit
|
||||
end
|
||||
|
||||
local function MTH_CHRON_NormalizeSection(tabKey)
|
||||
if not tabKey and _G then
|
||||
tabKey = _G.MTH_CHRON_OPTIONS_SECTION
|
||||
end
|
||||
if tabKey == "ChronometerBar" then
|
||||
return "bar"
|
||||
elseif tabKey == "ChronometerClassSpells" then
|
||||
return "classspells"
|
||||
elseif tabKey == "ChronometerClassEvents" then
|
||||
return "classevents"
|
||||
elseif tabKey == "ChronometerRacial" then
|
||||
return "racial"
|
||||
end
|
||||
return "general"
|
||||
end
|
||||
|
||||
local function MTH_CHRON_RenderTimerSection(container, profile, title, items, bucket)
|
||||
local timerX = 20
|
||||
local timerY = -84
|
||||
MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerTimersHeader", title, timerX, timerY)
|
||||
timerY = timerY - 24
|
||||
|
||||
if table.getn(items) == 0 then
|
||||
MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerSectionEmpty", "(none)", timerX + 8, timerY)
|
||||
return
|
||||
end
|
||||
|
||||
for i = 1, table.getn(items) do
|
||||
if timerY < -560 then
|
||||
MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerOverflow", "...more", timerX + 8, timerY)
|
||||
break
|
||||
end
|
||||
local timerName = items[i]
|
||||
local controlName = "MetaHuntOptionsChronometerTimer" .. tostring(bucket) .. tostring(i)
|
||||
local check = MTH_CreateCheckbox(container, controlName, tostring(timerName), timerY, timerX)
|
||||
if check then
|
||||
check:SetChecked(profile.disabledSpells[bucket][timerName] == nil and true or false)
|
||||
check:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
if self:GetChecked() then
|
||||
profile.disabledSpells[bucket][timerName] = nil
|
||||
else
|
||||
profile.disabledSpells[bucket][timerName] = {}
|
||||
end
|
||||
end)
|
||||
end
|
||||
timerY = timerY - 20
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_RefreshChronometerOptions(tabKey)
|
||||
if not MTH_CHRON_IsReady() then return end
|
||||
MTH_SetupChronometerOptions(tabKey)
|
||||
end
|
||||
|
||||
function MTH_SetupChronometerOptions(tabKey)
|
||||
if not MTH_CHRON_IsReady() then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Chronometer options dependencies missing", "error")
|
||||
end
|
||||
return
|
||||
end
|
||||
local section = MTH_CHRON_NormalizeSection(tabKey)
|
||||
local container = MTH_GetFrame("MetaHuntOptionsChronometer")
|
||||
if not container then return end
|
||||
|
||||
MTH_ClearContainer(container)
|
||||
MTH_CHRON_STATE.controls = {}
|
||||
|
||||
local profile = MTH_CHRON_GetProfile()
|
||||
local _, class = UnitClass("player")
|
||||
class = class or "HUNTER"
|
||||
|
||||
MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerTitle", "Chronometer", 20, -10)
|
||||
|
||||
local moduleCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerEnabled", "Enable Chronometer module", -54)
|
||||
if moduleCheck then
|
||||
local module = MTH and MTH:GetModule("chronometer")
|
||||
moduleCheck:SetChecked(module and module.enabled and true or false)
|
||||
moduleCheck:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
local enabled = self:GetChecked() and true or false
|
||||
if MTH and MTH.SetModuleEnabled then
|
||||
MTH:SetModuleEnabled("chronometer", enabled)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
if section == "general" then
|
||||
MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerGeneralHeader", "General", 20, -84)
|
||||
|
||||
local ghostSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerGhost", "Ghost Duration", 0, 30, 1, -112)
|
||||
if ghostSlider then
|
||||
ghostSlider:SetValue(tonumber(profile.ghost) or 0)
|
||||
ghostSlider.onChange = function(value)
|
||||
profile.ghost = tonumber(value) or 0
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end
|
||||
end
|
||||
|
||||
local killCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerFadeOnKill", "Fade on kill", -154)
|
||||
if killCheck then
|
||||
killCheck:SetChecked(profile.fadeonkill and true or false)
|
||||
killCheck:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
profile.fadeonkill = self:GetChecked() and true or false
|
||||
end)
|
||||
end
|
||||
|
||||
local fadeCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerFadeOnFade", "Fade on aura fade", -179)
|
||||
if fadeCheck then
|
||||
fadeCheck:SetChecked(profile.fadeonfade and true or false)
|
||||
fadeCheck:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
profile.fadeonfade = self:GetChecked() and true or false
|
||||
end)
|
||||
end
|
||||
|
||||
local selfCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerSelfBars", "Show self bars", -204)
|
||||
if selfCheck then
|
||||
selfCheck:SetChecked(profile.selfbars and true or false)
|
||||
selfCheck:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
profile.selfbars = self:GetChecked() and true or false
|
||||
end)
|
||||
end
|
||||
|
||||
local onlySelfCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerOnlySelf", "Only self target", -229)
|
||||
if onlySelfCheck then
|
||||
onlySelfCheck:SetChecked(profile.onlyself and true or false)
|
||||
onlySelfCheck:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
profile.onlyself = self:GetChecked() and true or false
|
||||
end)
|
||||
end
|
||||
elseif section == "bar" then
|
||||
MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerBarHeader", "Bars", 20, -84)
|
||||
|
||||
local runTestButton = MTH_CreateActionButton(container, "MetaHuntOptionsChronometerRunTest", "Run Test", 20, -112, 130, 22, function()
|
||||
local engine = MTH_ChronometerHunter
|
||||
if engine and engine.RunTest then
|
||||
engine:RunTest()
|
||||
end
|
||||
end)
|
||||
local anchorButton = MTH_CreateActionButton(container, "MetaHuntOptionsChronometerAnchor", "Toggle Anchor", 165, -112, 130, 22, function()
|
||||
local engine = MTH_ChronometerHunter
|
||||
if engine and engine.ToggleAnchor then
|
||||
engine:ToggleAnchor()
|
||||
end
|
||||
end)
|
||||
if runTestButton then runTestButton:Show() end
|
||||
if anchorButton then anchorButton:Show() end
|
||||
|
||||
local anchorX = tonumber(profile.barposition and profile.barposition.x) or 0
|
||||
local anchorY = tonumber(profile.barposition and profile.barposition.y) or 0
|
||||
MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerAnchorPos", "Anchor: X " .. tostring(anchorX) .. " Y " .. tostring(anchorY), 20, -142)
|
||||
|
||||
local widthSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarWidth", "Bar Width", 80, 320, 1, -180)
|
||||
if widthSlider then
|
||||
widthSlider:SetValue(tonumber(profile.barwidth) or 220)
|
||||
widthSlider.onChange = function(value)
|
||||
profile.barwidth = tonumber(value) or 220
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end
|
||||
end
|
||||
|
||||
local scaleSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarScale", "Bar Scale", 0.5, 1.5, 0.1, -222)
|
||||
if scaleSlider then
|
||||
scaleSlider:SetValue(tonumber(profile.barscale) or 1)
|
||||
scaleSlider.onChange = function(value)
|
||||
profile.barscale = tonumber(value) or 1
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end
|
||||
end
|
||||
|
||||
local heightSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarHeight", "Bar Height", 8, 30, 1, -264)
|
||||
if heightSlider then
|
||||
heightSlider:SetValue(tonumber(profile.barheight) or 16)
|
||||
heightSlider.onChange = function(value)
|
||||
profile.barheight = tonumber(value) or 16
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end
|
||||
end
|
||||
|
||||
local spacingSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarSpacing", "Bar Spacing", 0, 15, 1, -306)
|
||||
if spacingSlider then
|
||||
spacingSlider:SetValue(tonumber(profile.spacing) or 0)
|
||||
spacingSlider.onChange = function(value)
|
||||
profile.spacing = tonumber(value) or 0
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end
|
||||
end
|
||||
|
||||
local textSizeSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerTextSize", "Text Size", 8, 20, 1, -348)
|
||||
if textSizeSlider then
|
||||
textSizeSlider:SetValue(tonumber(profile.textsize) or 10)
|
||||
textSizeSlider.onChange = function(value)
|
||||
profile.textsize = tonumber(value) or 10
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end
|
||||
end
|
||||
|
||||
MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerTextPatternLabel", "Bar Text ($s spell, $t target)", 20, -390)
|
||||
MTH_CHRON_MakeInput(container, "MetaHuntOptionsChronometerTextPattern", 20, -408, 220, 20, profile.text or "$t", function(newText)
|
||||
if not newText or newText == "" then
|
||||
newText = "$t"
|
||||
end
|
||||
profile.text = tostring(newText)
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end)
|
||||
|
||||
local growCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerGrowUp", "Grow bars upward", -438)
|
||||
if growCheck then
|
||||
growCheck:SetChecked(profile.growup and true or false)
|
||||
growCheck:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
profile.growup = self:GetChecked() and true or false
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end)
|
||||
end
|
||||
|
||||
local reverseCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerReverse", "Reverse bars", -463)
|
||||
if reverseCheck then
|
||||
reverseCheck:SetChecked(profile.reverse and true or false)
|
||||
reverseCheck:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
profile.reverse = self:GetChecked() and true or false
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end)
|
||||
end
|
||||
|
||||
MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerIconPosLabel", "Icon Position", 20, -494)
|
||||
local iconLeft = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerIconLeft", "Left", -514)
|
||||
local iconRight = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerIconRight", "Right", -538)
|
||||
if iconLeft and iconRight then
|
||||
iconLeft:SetChecked(profile.iconposition ~= "RIGHT")
|
||||
iconRight:SetChecked(profile.iconposition == "RIGHT")
|
||||
iconLeft:SetScript("OnClick", function()
|
||||
profile.iconposition = "LEFT"
|
||||
iconLeft:SetChecked(true)
|
||||
iconRight:SetChecked(nil)
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end)
|
||||
iconRight:SetScript("OnClick", function()
|
||||
profile.iconposition = "RIGHT"
|
||||
iconRight:SetChecked(true)
|
||||
iconLeft:SetChecked(nil)
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end)
|
||||
end
|
||||
|
||||
else
|
||||
local classSpells, classEvents, racial = MTH_CHRON_BuildTimerLists()
|
||||
if section == "classspells" then
|
||||
MTH_CHRON_RenderTimerSection(container, profile, "Hunter Spells", classSpells, class)
|
||||
elseif section == "classevents" then
|
||||
MTH_CHRON_RenderTimerSection(container, profile, "Hunter Events", classEvents, class)
|
||||
elseif section == "racial" then
|
||||
MTH_CHRON_RenderTimerSection(container, profile, "Racial", racial, "RACIAL")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if _G then
|
||||
_G.MTH_SetupChronometerOptions = MTH_SetupChronometerOptions
|
||||
_G.MTH_RefreshChronometerOptions = MTH_RefreshChronometerOptions
|
||||
end
|
||||
@@ -0,0 +1,193 @@
|
||||
local MTH_CREDITS_READY = MTH_OptionsRequire and MTH_OptionsRequire("options-credits", {
|
||||
"MTH_GetFrame",
|
||||
"MTH_ClearContainer",
|
||||
})
|
||||
|
||||
local MTH_CREDITS_FONT_DELTA = 2
|
||||
|
||||
local function MTH_CreditsBumpFont(fs)
|
||||
if not fs or not fs.GetFont or not fs.SetFont then return end
|
||||
local path, size, flags = fs:GetFont()
|
||||
if path and size then
|
||||
fs:SetFont(path, size + MTH_CREDITS_FONT_DELTA, flags)
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_CreditsInsertLink(url)
|
||||
local link = tostring(url or "")
|
||||
if link == "" then return end
|
||||
if type(MTH_InsertLinkToChat) == "function" and MTH_InsertLinkToChat(link) then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Link inserted in chat: " .. link)
|
||||
end
|
||||
return
|
||||
end
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Link: " .. link)
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_CreditsClearFrame(frame)
|
||||
if not frame then return end
|
||||
local children = { frame:GetChildren() }
|
||||
for i = 1, table.getn(children) do
|
||||
if children[i] and children[i].Hide then
|
||||
children[i]:Hide()
|
||||
end
|
||||
end
|
||||
local regions = { frame:GetRegions() }
|
||||
for i = 1, table.getn(regions) do
|
||||
if regions[i] and regions[i].Hide then
|
||||
regions[i]:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_CreditsCreateLink(parent, label, url, anchorTo, y)
|
||||
local button = CreateFrame("Button", nil, parent)
|
||||
if not button then return nil end
|
||||
button:SetPoint("TOPLEFT", anchorTo, "BOTTOMLEFT", 0, y or -4)
|
||||
button:SetHeight(18)
|
||||
button:SetWidth(500)
|
||||
local text = button:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
text:SetPoint("LEFT", button, "LEFT", 0, 0)
|
||||
text:SetJustifyH("LEFT")
|
||||
text:SetText(label)
|
||||
text:SetTextColor(0.40, 0.75, 1.00)
|
||||
MTH_CreditsBumpFont(text)
|
||||
button:SetScript("OnClick", function()
|
||||
MTH_CreditsInsertLink(url)
|
||||
end)
|
||||
button:SetScript("OnEnter", function()
|
||||
text:SetTextColor(0.60, 0.90, 1.00)
|
||||
end)
|
||||
button:SetScript("OnLeave", function()
|
||||
text:SetTextColor(0.40, 0.75, 1.00)
|
||||
end)
|
||||
return button
|
||||
end
|
||||
|
||||
local function MTH_CreditsCreateText(parent, anchorTo, text, font, y, r, g, b)
|
||||
local fs = parent:CreateFontString(nil, "ARTWORK", font or "GameFontNormalSmall")
|
||||
if anchorTo == parent then
|
||||
fs:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, y or 0)
|
||||
else
|
||||
fs:SetPoint("TOPLEFT", anchorTo, "BOTTOMLEFT", 0, y or -8)
|
||||
end
|
||||
fs:SetWidth(500)
|
||||
fs:SetJustifyH("LEFT")
|
||||
fs:SetJustifyV("TOP")
|
||||
fs:SetTextColor(r or 0.93, g or 0.93, b or 0.93)
|
||||
fs:SetText(text)
|
||||
MTH_CreditsBumpFont(fs)
|
||||
return fs
|
||||
end
|
||||
|
||||
function MTH_SetupCreditsOptions()
|
||||
if not MTH_CREDITS_READY then return end
|
||||
|
||||
local container = MTH_GetFrame("MetaHuntOptionsCredits")
|
||||
if not container then return end
|
||||
|
||||
MTH_ClearContainer(container)
|
||||
|
||||
local title = container:CreateFontString("MetaHuntOptionsCreditsTitle", "ARTWORK", "GameFontNormal")
|
||||
title:SetPoint("TOPLEFT", container, "TOPLEFT", 10, -10)
|
||||
title:SetText("Credits")
|
||||
title:SetTextColor(1.00, 0.82, 0.00)
|
||||
|
||||
local scrollFrame = MTH_GetFrame("MetaHuntOptionsCreditsScroll")
|
||||
if not scrollFrame then
|
||||
scrollFrame = CreateFrame("ScrollFrame", "MetaHuntOptionsCreditsScroll", container, "UIPanelScrollFrameTemplate")
|
||||
end
|
||||
scrollFrame:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -10)
|
||||
scrollFrame:SetPoint("BOTTOMRIGHT", container, "BOTTOMRIGHT", -30, 10)
|
||||
scrollFrame:Show()
|
||||
|
||||
local content = MTH_GetFrame("MetaHuntOptionsCreditsContent")
|
||||
if not content then
|
||||
content = CreateFrame("Frame", "MetaHuntOptionsCreditsContent", scrollFrame)
|
||||
end
|
||||
content:SetWidth(520)
|
||||
content:SetHeight(2600)
|
||||
MTH_CreditsClearFrame(content)
|
||||
scrollFrame:SetScrollChild(content)
|
||||
|
||||
local cursor = MTH_CreditsCreateText(content, content, "About the code", "GameFontNormal", -2, 1.00, 0.82, 0.00)
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"This addon is, in others, a compilation of old vanilla addons that I loved and played with during years. But most of them had become very buggy with TwoW as new contain was added to the server. \n I fixed, reworked, enhanced, and melted them within a modern modular framework that I created.\n"
|
||||
.. "I want to make it crystal clear about what work is mine, and what is not and respectfully credit the original authors for the great stuff I've taken from them:",
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- zBars, Antidaze and Autostrip were core functionalities of Vanilla zHunterMod addon. And I kept the \"z\" naming of bars/buttons to always remember it. On top of this I have created myself zAmmo, zCompanions, zMount, zToys.\nAlso the SmartAmmo feature idea is coming from Zhuntermod: there was a file in it with that functionality, but was like a work-in-progress, totally unfunctional and even \"dangerous\" in its current state, and was not active in the addon. Since it was a fucking great idea, I recoded it mostly from scratch and made it work reliably and safely.",
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- Feed-o-Matic was entirely the work of Fizzwidget. I made it work for Twow, by melting it as a module calling Metahunt core to get pet's state and up-to-date data for new pet families and their diet.",
|
||||
"GameFontNormalSmall", -10)
|
||||
cursor = MTH_CreditsCreateLink(content,
|
||||
"https://www.wowinterface.com/downloads/info4160-FizzwidgetFeed-O-Matic.html",
|
||||
"https://www.wowinterface.com/downloads/info4160-FizzwidgetFeed-O-Matic.html",
|
||||
cursor,
|
||||
-4)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- Tooltips module is based on Hunter Helper, another vanilla addon of Fizzwidget. I kept the general idea, but there's not much original code left from it in Metahunt as I refactored everything so it plugs on MetaHunt controlled event handlers, can work with Turtle wow data, and is now usable on other things than Beasts.",
|
||||
"GameFontNormalSmall", -10)
|
||||
cursor = MTH_CreditsCreateLink(content,
|
||||
"https://legacy-wow.com/vanilla-addons/fizzwidget-hunter-helper/",
|
||||
"https://legacy-wow.com/vanilla-addons/fizzwidget-hunter-helper/",
|
||||
cursor,
|
||||
-4)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- Chronometer was an old and very popular vanilla addon working with ACE2 libraries. It has known uncountable iterations by various people over the years, and I can't credit them all of them here. The current version used by Metahunt is based on this Twow conversion made by \"wigan91\". I have reworked it to have it Hunter-focused-only: removed the non-hunter/race stuff, improved the definition for spells/effects config that were not correct, added missing hunter spells (not many), and implemented my own bar color scheme fitting better the hunter spells.",
|
||||
"GameFontNormalSmall", -10)
|
||||
cursor = MTH_CreditsCreateLink(content,
|
||||
"https://github.com/wigan91/Chronometer-TWoW",
|
||||
"https://github.com/wigan91/Chronometer-TWoW",
|
||||
cursor,
|
||||
-4)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- The Map/Marker system is the one of pfQuest from Master Shagu. Unfortunately he is no more reachable for some months and I could not have some talk with him about this. I mostly let it untouched, I just made slight modifications so it can integrate better within Metahunt, and doesn't conflict with pfQuest.",
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- The system allowing to fetch itemlinks in game (In the Weapon tab of the Hunter Book notably) is the one of Atlas Twow.",
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor, "About data sources", "GameFontNormal", -14, 1.00, 0.82, 0.00)
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"Metahunt ships with its own Turtle-WoW datastores, limited to hunter stuff only, and most of this data is up-to-date (Feb 2026).\n\n"
|
||||
.. "Sources used to build the data include:",
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- pfQuest for Beasts and Ammo vendors NPCs and their spawn points (and beasts respawn times if any). Thank you again Shagu.",
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- This invaluable and very accurate spreadsheet for beasts having pet abilities. A big thanks to all twow players that crafted this! Now you dont need that sheet anymore, you have Metahunt! And the Great Book of Huntards is awesome and accurate also because of YOU.",
|
||||
"GameFontNormalSmall", -10)
|
||||
cursor = MTH_CreditsCreateLink(content,
|
||||
"https://docs.google.com/spreadsheets/d/1u33knqlYXvY-jR6pvTd58gyDg8OYEuwYsSoEnIjeA8k/edit?gid=4136205#gid=4136205",
|
||||
"https://docs.google.com/spreadsheets/d/1u33knqlYXvY-jR6pvTd58gyDg8OYEuwYsSoEnIjeA8k/edit?gid=4136205#gid=4136205",
|
||||
cursor,
|
||||
-4)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- Atlas twow for ranged weapon sources\n\n"
|
||||
.. "- Twow wiki for pet diet (which was incomplete, missing Serpents and Foxes)",
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"With that being said, go back hunting fellas!",
|
||||
"GameFontNormalSmall", -10, 1.00, 0.82, 0.00)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"\n"
|
||||
.. "Metasploit <Atlantis>, of Nordaanar.",
|
||||
"GameFontNormalSmall", -10)
|
||||
end
|
||||
@@ -0,0 +1,373 @@
|
||||
local MTH_FOM_READY = MTH_OptionsRequire and MTH_OptionsRequire("options-feedomatic", {
|
||||
"MTH_GetFrame",
|
||||
"MTH_ClearContainer",
|
||||
"MTH_CreateCheckbox",
|
||||
})
|
||||
|
||||
local MTH_FOM_STATE = {
|
||||
enableCheck = nil,
|
||||
keepOpenEdit = nil,
|
||||
bindStatus = nil,
|
||||
bindButton = nil,
|
||||
bindClearButton = nil,
|
||||
bindingCapture = false,
|
||||
ctrl = {},
|
||||
}
|
||||
|
||||
local function MTH_EnsureFOMConfig()
|
||||
if FOM_Config then
|
||||
return
|
||||
end
|
||||
|
||||
if FOM_Config_Default then
|
||||
FOM_Config = {}
|
||||
for key, value in pairs(FOM_Config_Default) do
|
||||
FOM_Config[key] = value
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
FOM_Config = {
|
||||
Enabled = false,
|
||||
Alert = "emote",
|
||||
Level = "content",
|
||||
KeepOpenSlots = 8,
|
||||
AvoidUsefulFood = true,
|
||||
AvoidQuestFood = true,
|
||||
AvoidBonusFood = true,
|
||||
Fallback = false,
|
||||
SaveForCookingLevel = 1,
|
||||
PreferHigherQuality = true,
|
||||
AudioWarning = true,
|
||||
TextWarning = true,
|
||||
IconWarning = true,
|
||||
}
|
||||
end
|
||||
|
||||
local function MTH_FOM_SetAlert(mode)
|
||||
MTH_EnsureFOMConfig()
|
||||
if mode == "none" then
|
||||
FOM_Config.Alert = nil
|
||||
else
|
||||
FOM_Config.Alert = mode
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_FOM_SetLevel(level)
|
||||
MTH_EnsureFOMConfig()
|
||||
if level == "off" then
|
||||
FOM_Config.Level = nil
|
||||
else
|
||||
FOM_Config.Level = level
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_FOM_SetCookingLevel(level)
|
||||
MTH_EnsureFOMConfig()
|
||||
FOM_Config.SaveForCookingLevel = level
|
||||
end
|
||||
|
||||
local function MTH_FOM_GetBindingDisplayText()
|
||||
if not GetBindingKey then
|
||||
return "Unavailable"
|
||||
end
|
||||
local key1, key2 = GetBindingKey("FEEDOMATIC")
|
||||
if not key1 and not key2 then
|
||||
return "Unbound"
|
||||
end
|
||||
if key1 and key2 then
|
||||
return key1 .. " / " .. key2
|
||||
end
|
||||
return key1 or key2 or "Unbound"
|
||||
end
|
||||
|
||||
local function MTH_FOM_UpdateBindingStatus()
|
||||
local bindStatus = MTH_FOM_STATE.bindStatus
|
||||
if not bindStatus or not bindStatus.SetText then
|
||||
return
|
||||
end
|
||||
if MTH_FOM_STATE.bindingCapture then
|
||||
bindStatus:SetText("Feed key: press a key... (ESC to clear)")
|
||||
else
|
||||
bindStatus:SetText("Feed key: " .. MTH_FOM_GetBindingDisplayText())
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_FOM_StopBindingCapture()
|
||||
MTH_FOM_STATE.bindingCapture = false
|
||||
if MTH_FOM_STATE.bindButton then
|
||||
if MTH_FOM_STATE.bindButton.SetPropagateKeyboardInput then
|
||||
MTH_FOM_STATE.bindButton:SetPropagateKeyboardInput(true)
|
||||
end
|
||||
MTH_FOM_STATE.bindButton:EnableKeyboard(false)
|
||||
MTH_FOM_STATE.bindButton:SetText("Set Key")
|
||||
end
|
||||
MTH_FOM_UpdateBindingStatus()
|
||||
end
|
||||
|
||||
local function MTH_FOM_StartBindingCapture()
|
||||
MTH_FOM_STATE.bindingCapture = true
|
||||
if MTH_FOM_STATE.bindButton then
|
||||
MTH_FOM_STATE.bindButton:SetText("Press key...")
|
||||
MTH_FOM_STATE.bindButton:EnableKeyboard(true)
|
||||
if MTH_FOM_STATE.bindButton.SetPropagateKeyboardInput then
|
||||
MTH_FOM_STATE.bindButton:SetPropagateKeyboardInput(false)
|
||||
end
|
||||
end
|
||||
MTH_FOM_UpdateBindingStatus()
|
||||
end
|
||||
|
||||
local function MTH_FOM_BuildBindingChord(key)
|
||||
local chord = ""
|
||||
if IsControlKeyDown and IsControlKeyDown() and key ~= "LCTRL" and key ~= "RCTRL" then chord = chord .. "CTRL-" end
|
||||
if IsAltKeyDown and IsAltKeyDown() and key ~= "LALT" and key ~= "RALT" then chord = chord .. "ALT-" end
|
||||
if IsShiftKeyDown and IsShiftKeyDown() and key ~= "LSHIFT" and key ~= "RSHIFT" then chord = chord .. "SHIFT-" end
|
||||
return chord .. key
|
||||
end
|
||||
|
||||
local function MTH_FOM_SaveBinding(key)
|
||||
if not SetBinding or not SaveBindings or not GetBindingKey then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Keybinding APIs unavailable.")
|
||||
end
|
||||
return
|
||||
end
|
||||
if InCombatLockdown and InCombatLockdown() then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Cannot change keybindings during combat.")
|
||||
end
|
||||
MTH_FOM_StopBindingCapture()
|
||||
return
|
||||
end
|
||||
|
||||
local old1, old2 = GetBindingKey("FEEDOMATIC")
|
||||
if old1 then SetBinding(old1) end
|
||||
if old2 then SetBinding(old2) end
|
||||
|
||||
if key and key ~= "" and key ~= "ESCAPE" then
|
||||
SetBinding(key, "FEEDOMATIC")
|
||||
end
|
||||
|
||||
local bindingSet = 1
|
||||
if GetCurrentBindingSet then
|
||||
bindingSet = GetCurrentBindingSet() or 1
|
||||
end
|
||||
SaveBindings(bindingSet)
|
||||
MTH_FOM_StopBindingCapture()
|
||||
end
|
||||
|
||||
function MTH_RefreshFeedOMaticOptions()
|
||||
if not MTH_FOM_READY then return end
|
||||
MTH_EnsureFOMConfig()
|
||||
|
||||
if MTH_FOM_STATE.enableCheck then
|
||||
local module = MTH and MTH:GetModule("feedomatic")
|
||||
MTH_FOM_STATE.enableCheck:SetChecked(module and module.enabled and true or false)
|
||||
end
|
||||
|
||||
if MTH_FOM_STATE.keepOpenEdit then
|
||||
MTH_FOM_STATE.keepOpenEdit:SetText(tostring(FOM_Config.KeepOpenSlots or 0))
|
||||
end
|
||||
|
||||
MTH_FOM_UpdateBindingStatus()
|
||||
|
||||
if MTH_FOM_STATE.ctrl.AvoidQuestFood then MTH_FOM_STATE.ctrl.AvoidQuestFood:SetChecked(FOM_Config.AvoidQuestFood and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.AvoidBonusFood then MTH_FOM_STATE.ctrl.AvoidBonusFood:SetChecked(FOM_Config.AvoidBonusFood and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.PreferHigherQuality then MTH_FOM_STATE.ctrl.PreferHigherQuality:SetChecked(FOM_Config.PreferHigherQuality and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.Fallback then MTH_FOM_STATE.ctrl.Fallback:SetChecked(FOM_Config.Fallback and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.AudioWarning then MTH_FOM_STATE.ctrl.AudioWarning:SetChecked(FOM_Config.AudioWarning and true or false) end
|
||||
if MTH_FOM_STATE.ctrl.AudioWarningBell then MTH_FOM_STATE.ctrl.AudioWarningBell:SetChecked(FOM_Config.AudioWarning == "bell" and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.TextWarning then MTH_FOM_STATE.ctrl.TextWarning:SetChecked(FOM_Config.TextWarning and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.IconWarning then MTH_FOM_STATE.ctrl.IconWarning:SetChecked(FOM_Config.IconWarning and 1 or nil) end
|
||||
|
||||
if MTH_FOM_STATE.ctrl.AlertEmote then MTH_FOM_STATE.ctrl.AlertEmote:SetChecked(FOM_Config.Alert == "emote" and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.AlertChat then MTH_FOM_STATE.ctrl.AlertChat:SetChecked(FOM_Config.Alert == "chat" and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.AlertNone then MTH_FOM_STATE.ctrl.AlertNone:SetChecked((not FOM_Config.Alert) and 1 or nil) end
|
||||
|
||||
if MTH_FOM_STATE.ctrl.LevelContent then MTH_FOM_STATE.ctrl.LevelContent:SetChecked(FOM_Config.Level == "content" and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.LevelUnhappy then MTH_FOM_STATE.ctrl.LevelUnhappy:SetChecked(FOM_Config.Level == "unhappy" and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.LevelOff then MTH_FOM_STATE.ctrl.LevelOff:SetChecked((not FOM_Config.Level) and 1 or nil) end
|
||||
|
||||
if MTH_FOM_STATE.ctrl.SaveCookOrange then MTH_FOM_STATE.ctrl.SaveCookOrange:SetChecked(FOM_Config.SaveForCookingLevel == 3 and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.SaveCookYellow then MTH_FOM_STATE.ctrl.SaveCookYellow:SetChecked(FOM_Config.SaveForCookingLevel == 2 and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.SaveCookGreen then MTH_FOM_STATE.ctrl.SaveCookGreen:SetChecked(FOM_Config.SaveForCookingLevel == 1 and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.SaveCookAll then MTH_FOM_STATE.ctrl.SaveCookAll:SetChecked(FOM_Config.SaveForCookingLevel == 0 and 1 or nil) end
|
||||
if MTH_FOM_STATE.ctrl.SaveCookNone then MTH_FOM_STATE.ctrl.SaveCookNone:SetChecked(FOM_Config.SaveForCookingLevel and FOM_Config.SaveForCookingLevel >= 4 and 1 or nil) end
|
||||
end
|
||||
|
||||
function MTH_SetupFeedOMaticOptions()
|
||||
if not MTH_FOM_READY then return end
|
||||
local container = MTH_GetFrame("MetaHuntOptionsFeedOMatic")
|
||||
if not container then return end
|
||||
local rightColumnX = 300
|
||||
local yAdjust = -64
|
||||
|
||||
MTH_ClearContainer(container)
|
||||
MTH_FOM_STATE.ctrl = {}
|
||||
|
||||
local title = container:CreateFontString("MetaHuntOptionsFeedOMaticTitle", "ARTWORK", "GameFontHighlight")
|
||||
title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -10)
|
||||
title:SetText("Feed-O-Matic")
|
||||
|
||||
local statusNotice = container:CreateFontString("MetaHuntOptionsFeedOMaticStatusNotice", "ARTWORK", "GameFontNormalSmall")
|
||||
statusNotice:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -34)
|
||||
statusNotice:SetWidth(600)
|
||||
statusNotice:SetJustifyH("LEFT")
|
||||
statusNotice:SetJustifyV("TOP")
|
||||
statusNotice:SetTextColor(0.35, 0.65, 1)
|
||||
statusNotice:SetText("Feed-O-Matic, created by the great Fizzwidget, is not yet entirely ready for TurtleWoW because I am missing \na reliable list of all foods, mostly impossible to fetch from the DB.\n It still works much better in this version, but all stuff related to Food buff and Cooking isnt fully functional.")
|
||||
|
||||
local moduleCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsFeedOMaticEnabled", "Enable FeedOMatic module", -32 + yAdjust)
|
||||
if moduleCheck then
|
||||
moduleCheck:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
local enabled = self:GetChecked() == 1
|
||||
if MTH and MTH.SetModuleEnabled then
|
||||
local ok, err = MTH:SetModuleEnabled("feedomatic", enabled)
|
||||
if not ok and MTH and MTH.Print then
|
||||
MTH:Print("Failed to change FeedOMatic state: " .. tostring(err))
|
||||
end
|
||||
end
|
||||
MTH_RefreshFeedOMaticOptions()
|
||||
end)
|
||||
MTH_FOM_STATE.enableCheck = moduleCheck
|
||||
end
|
||||
|
||||
local leftHeader = container:CreateFontString("MetaHuntOptionsFeedOMaticGeneralHeader", "ARTWORK", "GameFontHighlight")
|
||||
leftHeader:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -62 + yAdjust)
|
||||
leftHeader:SetText("General")
|
||||
|
||||
local bindStatus = container:CreateFontString("MetaHuntOptionsFOMBindStatus", "ARTWORK", "GameFontNormalSmall")
|
||||
bindStatus:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -80 + yAdjust)
|
||||
bindStatus:SetText("Feed key: " .. MTH_FOM_GetBindingDisplayText())
|
||||
MTH_FOM_STATE.bindStatus = bindStatus
|
||||
|
||||
local bindButton = CreateFrame("Button", "MetaHuntOptionsFOMBindButton", container, "UIPanelButtonTemplate")
|
||||
bindButton:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -100 + yAdjust)
|
||||
bindButton:SetWidth(90)
|
||||
bindButton:SetHeight(22)
|
||||
bindButton:SetText("Set Key")
|
||||
bindButton:EnableKeyboard(false)
|
||||
bindButton:SetScript("OnClick", function()
|
||||
if MTH_FOM_STATE.bindingCapture then
|
||||
MTH_FOM_StopBindingCapture()
|
||||
else
|
||||
MTH_FOM_StartBindingCapture()
|
||||
end
|
||||
end)
|
||||
bindButton:SetScript("OnKeyDown", function(_, key)
|
||||
if not MTH_FOM_STATE.bindingCapture then
|
||||
return
|
||||
end
|
||||
key = key or arg1
|
||||
if not key or key == "" then
|
||||
return
|
||||
end
|
||||
if key == "ESCAPE" then
|
||||
MTH_FOM_SaveBinding(nil)
|
||||
return
|
||||
end
|
||||
if key == "UNKNOWN" or key == "PRINTSCREEN" then
|
||||
return
|
||||
end
|
||||
MTH_FOM_SaveBinding(MTH_FOM_BuildBindingChord(key))
|
||||
end)
|
||||
MTH_FOM_STATE.bindButton = bindButton
|
||||
|
||||
local bindClearButton = CreateFrame("Button", "MetaHuntOptionsFOMBindClearButton", container, "UIPanelButtonTemplate")
|
||||
bindClearButton:SetPoint("LEFT", bindButton, "RIGHT", 8, 0)
|
||||
bindClearButton:SetWidth(70)
|
||||
bindClearButton:SetHeight(22)
|
||||
bindClearButton:SetText("Clear")
|
||||
bindClearButton:SetScript("OnClick", function()
|
||||
MTH_FOM_SaveBinding(nil)
|
||||
end)
|
||||
MTH_FOM_STATE.bindClearButton = bindClearButton
|
||||
|
||||
local bindHint = container:CreateFontString("MetaHuntOptionsFOMBindHint", "ARTWORK", "GameFontNormalSmall")
|
||||
bindHint:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -128 + yAdjust)
|
||||
bindHint:SetWidth(360)
|
||||
bindHint:SetJustifyH("LEFT")
|
||||
bindHint:SetJustifyV("TOP")
|
||||
bindHint:SetTextColor(0.35, 0.65, 1)
|
||||
bindHint:SetText("You should assign a key bind for feed-o-matic,\nin order to feed your pet automatically.\nThe key \"P\" is an excellent candidate !")
|
||||
|
||||
MTH_FOM_STATE.ctrl.AvoidQuestFood = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAvoidQuest", FOM_OptionsButtonText and FOM_OptionsButtonText["AvoidQuestFood"] or "Avoid quest foods", -170 + yAdjust)
|
||||
MTH_FOM_STATE.ctrl.AvoidBonusFood = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAvoidBonus", FOM_OptionsButtonText and FOM_OptionsButtonText["AvoidBonusFood"] or "Avoid bonus foods", -195 + yAdjust)
|
||||
MTH_FOM_STATE.ctrl.PreferHigherQuality = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMPreferQuality", FOM_OptionsButtonText and FOM_OptionsButtonText["PreferHigherQuality"] or "Prefer higher quality food", -220 + yAdjust)
|
||||
MTH_FOM_STATE.ctrl.Fallback = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMFallback", FOM_OptionsButtonText and FOM_OptionsButtonText["Fallback"] or "Fallback to avoided foods", -245 + yAdjust)
|
||||
|
||||
if MTH_FOM_STATE.ctrl.AvoidQuestFood then MTH_FOM_STATE.ctrl.AvoidQuestFood:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.AvoidQuestFood = self:GetChecked() == 1 end) end
|
||||
if MTH_FOM_STATE.ctrl.AvoidBonusFood then MTH_FOM_STATE.ctrl.AvoidBonusFood:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.AvoidBonusFood = self:GetChecked() == 1 end) end
|
||||
if MTH_FOM_STATE.ctrl.PreferHigherQuality then MTH_FOM_STATE.ctrl.PreferHigherQuality:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.PreferHigherQuality = self:GetChecked() == 1 end) end
|
||||
if MTH_FOM_STATE.ctrl.Fallback then MTH_FOM_STATE.ctrl.Fallback:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.Fallback = self:GetChecked() == 1 end) end
|
||||
|
||||
local keepOpenLabel = container:CreateFontString("MetaHuntOptionsFOMKeepOpenLabel", "ARTWORK", "GameFontNormalSmall")
|
||||
keepOpenLabel:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -302 + yAdjust)
|
||||
keepOpenLabel:SetText("Keep open bag slots:")
|
||||
|
||||
local keepOpen = CreateFrame("EditBox", "MetaHuntOptionsFOMKeepOpen", container, "InputBoxTemplate")
|
||||
keepOpen:SetPoint("LEFT", keepOpenLabel, "RIGHT", 8, 0)
|
||||
keepOpen:SetWidth(40)
|
||||
keepOpen:SetHeight(20)
|
||||
keepOpen:SetNumeric(true)
|
||||
keepOpen:SetAutoFocus(false)
|
||||
keepOpen:SetScript("OnTextChanged", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
local value = tonumber(self:GetText()) or 0
|
||||
FOM_Config.KeepOpenSlots = value
|
||||
end)
|
||||
keepOpen:SetScript("OnEnterPressed", function(self) self = self or this if self then self:ClearFocus() end end)
|
||||
keepOpen:SetScript("OnEscapePressed", function(self) self = self or this if self then self:ClearFocus() end end)
|
||||
MTH_FOM_STATE.keepOpenEdit = keepOpen
|
||||
|
||||
local notifyHeader = container:CreateFontString("MetaHuntOptionsFOMNotifyHeader", "ARTWORK", "GameFontHighlight")
|
||||
notifyHeader:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -336 + yAdjust)
|
||||
notifyHeader:SetText("Notify when feeding")
|
||||
|
||||
MTH_FOM_STATE.ctrl.AlertEmote = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAlertEmote", FOM_OptionsButtonText and FOM_OptionsButtonText["AlertEmote"] or "Via emote", -360 + yAdjust)
|
||||
MTH_FOM_STATE.ctrl.AlertChat = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAlertChat", FOM_OptionsButtonText and FOM_OptionsButtonText["AlertChat"] or "In chat", -385 + yAdjust)
|
||||
MTH_FOM_STATE.ctrl.AlertNone = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAlertNone", FOM_OptionsButtonText and FOM_OptionsButtonText["AlertNone"] or "Don't notify", -410 + yAdjust)
|
||||
if MTH_FOM_STATE.ctrl.AlertEmote then MTH_FOM_STATE.ctrl.AlertEmote:SetScript("OnClick", function() MTH_FOM_SetAlert("emote"); MTH_RefreshFeedOMaticOptions() end) end
|
||||
if MTH_FOM_STATE.ctrl.AlertChat then MTH_FOM_STATE.ctrl.AlertChat:SetScript("OnClick", function() MTH_FOM_SetAlert("chat"); MTH_RefreshFeedOMaticOptions() end) end
|
||||
if MTH_FOM_STATE.ctrl.AlertNone then MTH_FOM_STATE.ctrl.AlertNone:SetScript("OnClick", function() MTH_FOM_SetAlert("none"); MTH_RefreshFeedOMaticOptions() end) end
|
||||
|
||||
local warnHeader = container:CreateFontString("MetaHuntOptionsFOMWarnHeader", "ARTWORK", "GameFontHighlight")
|
||||
warnHeader:SetPoint("TOPLEFT", container, "TOPLEFT", rightColumnX, -276 + yAdjust)
|
||||
warnHeader:SetText("Warn when pet needs feeding")
|
||||
|
||||
MTH_FOM_STATE.ctrl.LevelContent = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMLevelContent", FOM_OptionsButtonText and FOM_OptionsButtonText["LevelContent"] or "When content", -300 + yAdjust, rightColumnX)
|
||||
MTH_FOM_STATE.ctrl.LevelUnhappy = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMLevelUnhappy", FOM_OptionsButtonText and FOM_OptionsButtonText["LevelUnhappy"] or "When unhappy", -325 + yAdjust, rightColumnX)
|
||||
MTH_FOM_STATE.ctrl.LevelOff = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMLevelOff", FOM_OptionsButtonText and FOM_OptionsButtonText["LevelOff"] or "Don't warn", -350 + yAdjust, rightColumnX)
|
||||
if MTH_FOM_STATE.ctrl.LevelContent then MTH_FOM_STATE.ctrl.LevelContent:SetScript("OnClick", function() MTH_FOM_SetLevel("content"); MTH_RefreshFeedOMaticOptions() end) end
|
||||
if MTH_FOM_STATE.ctrl.LevelUnhappy then MTH_FOM_STATE.ctrl.LevelUnhappy:SetScript("OnClick", function() MTH_FOM_SetLevel("unhappy"); MTH_RefreshFeedOMaticOptions() end) end
|
||||
if MTH_FOM_STATE.ctrl.LevelOff then MTH_FOM_STATE.ctrl.LevelOff:SetScript("OnClick", function() MTH_FOM_SetLevel("off"); MTH_RefreshFeedOMaticOptions() end) end
|
||||
|
||||
MTH_FOM_STATE.ctrl.AudioWarning = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAudioWarning", FOM_OptionsButtonText and FOM_OptionsButtonText["AudioWarning"] or "Play sound", -378 + yAdjust, rightColumnX)
|
||||
MTH_FOM_STATE.ctrl.AudioWarningBell = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAudioWarningBell", FOM_OptionsButtonText and FOM_OptionsButtonText["AudioWarningBell"] or "Use bell sound", -403 + yAdjust, rightColumnX)
|
||||
MTH_FOM_STATE.ctrl.TextWarning = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMTextWarning", FOM_OptionsButtonText and FOM_OptionsButtonText["TextWarning"] or "Show text", -428 + yAdjust, rightColumnX)
|
||||
MTH_FOM_STATE.ctrl.IconWarning = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMIconWarning", FOM_OptionsButtonText and FOM_OptionsButtonText["IconWarning"] or "Flash icon", -453 + yAdjust, rightColumnX)
|
||||
if MTH_FOM_STATE.ctrl.AudioWarning then MTH_FOM_STATE.ctrl.AudioWarning:SetScript("OnClick", function(self) self = self or this if not self then return end if self:GetChecked() == 1 then if FOM_Config.AudioWarning ~= "bell" then FOM_Config.AudioWarning = true end else FOM_Config.AudioWarning = nil end MTH_RefreshFeedOMaticOptions() end) end
|
||||
if MTH_FOM_STATE.ctrl.AudioWarningBell then MTH_FOM_STATE.ctrl.AudioWarningBell:SetScript("OnClick", function(self) self = self or this if not self then return end if self:GetChecked() == 1 then FOM_Config.AudioWarning = "bell" else FOM_Config.AudioWarning = true end MTH_RefreshFeedOMaticOptions() end) end
|
||||
if MTH_FOM_STATE.ctrl.TextWarning then MTH_FOM_STATE.ctrl.TextWarning:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.TextWarning = self:GetChecked() == 1 end) end
|
||||
if MTH_FOM_STATE.ctrl.IconWarning then MTH_FOM_STATE.ctrl.IconWarning:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.IconWarning = self:GetChecked() == 1 end) end
|
||||
|
||||
local cookHeader = container:CreateFontString("MetaHuntOptionsFOMCookingHeader", "ARTWORK", "GameFontHighlight")
|
||||
cookHeader:SetPoint("TOPLEFT", container, "TOPLEFT", rightColumnX, -62 + yAdjust)
|
||||
cookHeader:SetText("Avoid foods used in cooking")
|
||||
|
||||
MTH_FOM_STATE.ctrl.SaveCookOrange = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookOrange", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_Orange"] or "Only difficult recipes", -86 + yAdjust, rightColumnX)
|
||||
MTH_FOM_STATE.ctrl.SaveCookYellow = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookYellow", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_Yellow"] or "Medium or better", -111 + yAdjust, rightColumnX)
|
||||
MTH_FOM_STATE.ctrl.SaveCookGreen = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookGreen", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_Green"] or "Easy or better", -136 + yAdjust, rightColumnX)
|
||||
MTH_FOM_STATE.ctrl.SaveCookAll = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookAll", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_All"] or "All foods", -161 + yAdjust, rightColumnX)
|
||||
MTH_FOM_STATE.ctrl.SaveCookNone = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookNone", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_None"] or "Do not save cooking foods", -186 + yAdjust, rightColumnX)
|
||||
if MTH_FOM_STATE.ctrl.SaveCookOrange then MTH_FOM_STATE.ctrl.SaveCookOrange:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(3); MTH_RefreshFeedOMaticOptions() end) end
|
||||
if MTH_FOM_STATE.ctrl.SaveCookYellow then MTH_FOM_STATE.ctrl.SaveCookYellow:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(2); MTH_RefreshFeedOMaticOptions() end) end
|
||||
if MTH_FOM_STATE.ctrl.SaveCookGreen then MTH_FOM_STATE.ctrl.SaveCookGreen:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(1); MTH_RefreshFeedOMaticOptions() end) end
|
||||
if MTH_FOM_STATE.ctrl.SaveCookAll then MTH_FOM_STATE.ctrl.SaveCookAll:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(0); MTH_RefreshFeedOMaticOptions() end) end
|
||||
if MTH_FOM_STATE.ctrl.SaveCookNone then MTH_FOM_STATE.ctrl.SaveCookNone:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(4); MTH_RefreshFeedOMaticOptions() end) end
|
||||
|
||||
MTH_RefreshFeedOMaticOptions()
|
||||
end
|
||||
@@ -0,0 +1,220 @@
|
||||
MetaHuntOptions = nil
|
||||
local MTH_OPTIONS_SETUP = {} -- Track which frames have been set up
|
||||
MTH_OPTIONS_CONST = MTH_OPTIONS_CONST or {}
|
||||
MTH_OPTIONS_CONST.NAV_DEFAULT_WIDTH = MTH_OPTIONS_CONST.NAV_DEFAULT_WIDTH or 120
|
||||
MTH_OPTIONS_CONST.WINDOW_PADDING = MTH_OPTIONS_CONST.WINDOW_PADDING or 12
|
||||
MTH_OPTIONS_CONST.CONTENT_TOP_OFFSET = MTH_OPTIONS_CONST.CONTENT_TOP_OFFSET or -72
|
||||
MTH_OPTIONS_CONST.CONTENT_HEIGHT = MTH_OPTIONS_CONST.CONTENT_HEIGHT or 540
|
||||
MTH_OPTIONS_CONST.CLOSE_BTN_SIZE = MTH_OPTIONS_CONST.CLOSE_BTN_SIZE or 22
|
||||
MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_X = MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_X or -8
|
||||
MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_Y = MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_Y or -8
|
||||
local MTH_OPTIONS_SHELL_READY = MTH_OptionsRequire and MTH_OptionsRequire("options-shell", {
|
||||
"MTH_GetFrame",
|
||||
"MTH_BuildOptionsTree",
|
||||
})
|
||||
|
||||
local function MTH_RegisterEscClose(frameName)
|
||||
if not (frameName and UISpecialFrames) then
|
||||
return
|
||||
end
|
||||
for i = 1, table.getn(UISpecialFrames) do
|
||||
if UISpecialFrames[i] == frameName then
|
||||
return
|
||||
end
|
||||
end
|
||||
table.insert(UISpecialFrames, frameName)
|
||||
end
|
||||
|
||||
local function MTH_EnsureOptionsFrame()
|
||||
if MetaHuntOptions then
|
||||
return MetaHuntOptions
|
||||
end
|
||||
|
||||
MetaHuntOptions = CreateFrame("Frame", "MetaHuntOptions", UIParent, "MetaHuntOptionsTemplate")
|
||||
if not MetaHuntOptions then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("[MTH OPTIONS] Failed to create MetaHuntOptions", "error")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
MTH_RegisterEscClose("MetaHuntOptions")
|
||||
|
||||
local nav = MTH_GetFrame("MetaHuntOptionsNav")
|
||||
if nav and nav.SetBackdropColor then
|
||||
nav:SetBackdropColor(0.08, 0.08, 0.08, 0.95)
|
||||
nav:SetBackdropBorderColor(0.25, 0.25, 0.25, 1)
|
||||
end
|
||||
|
||||
local nav = MTH_GetFrame("MetaHuntOptionsNav")
|
||||
local navWidth = nav and nav:GetWidth() or MTH_OPTIONS_CONST.NAV_DEFAULT_WIDTH
|
||||
local contentLeft = MTH_OPTIONS_CONST.WINDOW_PADDING + navWidth + MTH_OPTIONS_CONST.WINDOW_PADDING
|
||||
local contentWidth = (MetaHuntOptions:GetWidth() or 720) - contentLeft - MTH_OPTIONS_CONST.WINDOW_PADDING
|
||||
for i = 1, table.getn(MTH_OPTIONS_TABS) do
|
||||
local frame = MTH_GetFrame(MTH_OPTIONS_TABS[i].frame)
|
||||
if frame then
|
||||
frame:ClearAllPoints()
|
||||
frame:SetPoint("TOPLEFT", MetaHuntOptions, "TOPLEFT", contentLeft, MTH_OPTIONS_CONST.CONTENT_TOP_OFFSET)
|
||||
frame:SetWidth(contentWidth)
|
||||
frame:SetHeight(MTH_OPTIONS_CONST.CONTENT_HEIGHT)
|
||||
frame:SetFrameLevel(MetaHuntOptions:GetFrameLevel() + 1)
|
||||
frame:EnableMouse(true)
|
||||
frame:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local closeBtn = MTH_GetFrame("MetaHuntOptionsCloseButton")
|
||||
if closeBtn then
|
||||
closeBtn:ClearAllPoints()
|
||||
closeBtn:SetPoint("TOPRIGHT", MetaHuntOptions, "TOPRIGHT", MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_X, MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_Y)
|
||||
closeBtn:SetWidth(MTH_OPTIONS_CONST.CLOSE_BTN_SIZE)
|
||||
closeBtn:SetHeight(MTH_OPTIONS_CONST.CLOSE_BTN_SIZE)
|
||||
closeBtn:SetText("X")
|
||||
closeBtn:SetScript("OnClick", function() MetaHuntOptions:Hide() end)
|
||||
end
|
||||
|
||||
MTH_BuildOptionsTree()
|
||||
MTH_SelectOptionsTab("General")
|
||||
|
||||
return MetaHuntOptions
|
||||
end
|
||||
|
||||
function MTH_SelectOptionsTab(tabKey)
|
||||
if not MTH_OPTIONS_SHELL_READY then return end
|
||||
local optionsFrame = MTH_EnsureOptionsFrame()
|
||||
if not optionsFrame then
|
||||
return
|
||||
end
|
||||
MTH_OPTIONS_STATE = MTH_OPTIONS_STATE or {}
|
||||
MTH_OPTIONS_STATE.activeTab = tabKey
|
||||
MTH_OPTIONS_ACTIVE_TAB = tabKey
|
||||
|
||||
for i = 1, table.getn(MTH_OPTIONS_TABS) do
|
||||
local frame = MTH_GetFrame(MTH_OPTIONS_TABS[i].frame)
|
||||
if frame then
|
||||
frame:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, table.getn(MTH_OPTIONS_TABS) do
|
||||
if MTH_OPTIONS_TABS[i].key == tabKey then
|
||||
local frame = MTH_GetFrame(MTH_OPTIONS_TABS[i].frame)
|
||||
if frame then
|
||||
frame:Show()
|
||||
end
|
||||
end
|
||||
end
|
||||
MTH_BuildOptionsTree()
|
||||
|
||||
if tabKey == "General" then
|
||||
if MTH_SetupGeneralOptions then
|
||||
MTH_SetupGeneralOptions()
|
||||
MTH_OPTIONS_SETUP["General"] = true
|
||||
end
|
||||
elseif tabKey == "Pet" then
|
||||
if not MTH_OPTIONS_SETUP["Pet"] then
|
||||
MTH_SetupPetOptions()
|
||||
MTH_OPTIONS_SETUP["Pet"] = true
|
||||
end
|
||||
elseif tabKey == "Track" then
|
||||
if not MTH_OPTIONS_SETUP["Track"] then
|
||||
MTH_SetupTrackOptions()
|
||||
MTH_OPTIONS_SETUP["Track"] = true
|
||||
end
|
||||
elseif tabKey == "Aspect" then
|
||||
if not MTH_OPTIONS_SETUP["Aspect"] then
|
||||
MTH_SetupAspectOptions()
|
||||
MTH_OPTIONS_SETUP["Aspect"] = true
|
||||
end
|
||||
elseif tabKey == "Trap" then
|
||||
if not MTH_OPTIONS_SETUP["Trap"] then
|
||||
MTH_SetupTrapOptions()
|
||||
MTH_OPTIONS_SETUP["Trap"] = true
|
||||
end
|
||||
elseif tabKey == "Ammo" then
|
||||
if not MTH_OPTIONS_SETUP["Ammo"] then
|
||||
MTH_SetupAmmoOptions()
|
||||
MTH_OPTIONS_SETUP["Ammo"] = true
|
||||
else
|
||||
MTH_SetupAmmoOptions()
|
||||
end
|
||||
elseif tabKey == "Mounts" then
|
||||
if not MTH_OPTIONS_SETUP["Mounts"] then
|
||||
MTH_SetupMountsOptions()
|
||||
MTH_OPTIONS_SETUP["Mounts"] = true
|
||||
end
|
||||
elseif tabKey == "Companions" then
|
||||
if not MTH_OPTIONS_SETUP["Companions"] then
|
||||
MTH_SetupCompanionsOptions()
|
||||
MTH_OPTIONS_SETUP["Companions"] = true
|
||||
end
|
||||
elseif tabKey == "Toys" then
|
||||
if not MTH_OPTIONS_SETUP["Toys"] then
|
||||
MTH_SetupToysOptions()
|
||||
MTH_OPTIONS_SETUP["Toys"] = true
|
||||
end
|
||||
elseif tabKey == "SmartAmmo" then
|
||||
if not MTH_OPTIONS_SETUP["SmartAmmo"] then
|
||||
MTH_SetupSmartAmmoOptions()
|
||||
MTH_OPTIONS_SETUP["SmartAmmo"] = true
|
||||
end
|
||||
elseif tabKey == "FeedOMatic" then
|
||||
if not MTH_OPTIONS_SETUP["FeedOMatic"] then
|
||||
MTH_SetupFeedOMaticOptions()
|
||||
MTH_OPTIONS_SETUP["FeedOMatic"] = true
|
||||
end
|
||||
MTH_RefreshFeedOMaticOptions()
|
||||
elseif tabKey == "AutoBuy" then
|
||||
if not MTH_OPTIONS_SETUP["AutoBuy"] then
|
||||
MTH_SetupAutoBuyOptions()
|
||||
MTH_OPTIONS_SETUP["AutoBuy"] = true
|
||||
else
|
||||
MTH_SetupAutoBuyOptions()
|
||||
end
|
||||
elseif tabKey == "Credits" then
|
||||
local setupCredits = _G and _G["MTH_SetupCreditsOptions"]
|
||||
if type(setupCredits) == "function" then
|
||||
setupCredits()
|
||||
end
|
||||
MTH_OPTIONS_SETUP["Credits"] = true
|
||||
elseif string.sub(tabKey or "", 1, 11) == "Chronometer" then
|
||||
if not MTH_OPTIONS_SETUP[tabKey] then
|
||||
if _G then
|
||||
_G.MTH_CHRON_OPTIONS_SECTION = tabKey
|
||||
end
|
||||
if type(MTH_SetupChronometerOptions) == "function" then
|
||||
MTH_SetupChronometerOptions()
|
||||
MTH_OPTIONS_SETUP[tabKey] = true
|
||||
elseif MTH and MTH.Print then
|
||||
MTH:Print("Chronometer options module not loaded", "error")
|
||||
end
|
||||
else
|
||||
if _G then
|
||||
_G.MTH_CHRON_OPTIONS_SECTION = tabKey
|
||||
end
|
||||
if type(MTH_RefreshChronometerOptions) == "function" then
|
||||
MTH_RefreshChronometerOptions()
|
||||
elseif type(MTH_SetupChronometerOptions) == "function" then
|
||||
MTH_SetupChronometerOptions()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_ResetAndSelectOptionsTab(tabKey)
|
||||
if not MTH_OPTIONS_SHELL_READY then return end
|
||||
if not tabKey then
|
||||
return
|
||||
end
|
||||
MTH_OPTIONS_SETUP[tabKey] = nil
|
||||
MTH_SelectOptionsTab(tabKey)
|
||||
end
|
||||
|
||||
function MTH_OpenOptions(tabKey)
|
||||
if not MTH_OPTIONS_SHELL_READY then return end
|
||||
local optionsFrame = MTH_EnsureOptionsFrame()
|
||||
if not optionsFrame then
|
||||
return
|
||||
end
|
||||
optionsFrame:Show()
|
||||
MTH_SelectOptionsTab(tabKey or "General")
|
||||
end
|
||||
@@ -0,0 +1,184 @@
|
||||
MTH_OPTIONS_TREE_STATE = MTH_OPTIONS_TREE_STATE or { zButtons = true, chronometer = true }
|
||||
MTH_OPTIONS_TREE_BUTTONS = MTH_OPTIONS_TREE_BUTTONS or {}
|
||||
MTH_OPTIONS_STATE = MTH_OPTIONS_STATE or {}
|
||||
if MTH_OPTIONS_STATE.activeTab == nil and MTH_OPTIONS_ACTIVE_TAB ~= nil then
|
||||
MTH_OPTIONS_STATE.activeTab = MTH_OPTIONS_ACTIVE_TAB
|
||||
end
|
||||
MTH_OPTIONS_ACTIVE_TAB = MTH_OPTIONS_STATE.activeTab
|
||||
MTH_OPTIONS_CONST = MTH_OPTIONS_CONST or {
|
||||
NAV_DEFAULT_WIDTH = 120,
|
||||
NAV_INNER_PADDING = 16,
|
||||
TREE_START_Y = -10,
|
||||
TREE_BUTTON_HEIGHT = 20,
|
||||
TREE_ROW_STEP = 22,
|
||||
TREE_INDENT_BASE = 8,
|
||||
TREE_CHILD_INDENT = 14,
|
||||
HIGHLIGHT_ALPHA = 0.25,
|
||||
SELECTED_BG_R = 0.35,
|
||||
SELECTED_BG_G = 0.25,
|
||||
SELECTED_BG_B = 0.10,
|
||||
SELECTED_BG_A = 0.55,
|
||||
SELECTED_TEXT_R = 1.00,
|
||||
SELECTED_TEXT_G = 0.82,
|
||||
SELECTED_TEXT_B = 0.00,
|
||||
GROUP_TEXT_R = 1.00,
|
||||
GROUP_TEXT_G = 0.82,
|
||||
GROUP_TEXT_B = 0.00,
|
||||
}
|
||||
|
||||
MTH_OPTIONS_TABS = MTH_OPTIONS_TABS or {
|
||||
{ key = "General", label = "General", frame = "MetaHuntOptionsGeneral" },
|
||||
{ key = "Pet", label = "ZPet", frame = "MetaHuntOptionsPet" },
|
||||
{ key = "Track", label = "ZTrack", frame = "MetaHuntOptionsTrack" },
|
||||
{ key = "Aspect", label = "ZAspect", frame = "MetaHuntOptionsAspect" },
|
||||
{ key = "Trap", label = "ZTrap", frame = "MetaHuntOptionsTrap" },
|
||||
{ key = "Ammo", label = "ZAmmo", frame = "MetaHuntOptionsAmmo" },
|
||||
{ key = "Mounts", label = "ZMounts", frame = "MetaHuntOptionsMounts" },
|
||||
{ key = "Companions", label = "ZCompanions", frame = "MetaHuntOptionsCompanions" },
|
||||
{ key = "Toys", label = "ZToys", frame = "MetaHuntOptionsToys" },
|
||||
{ key = "SmartAmmo", label = "Smart Ammo", frame = "MetaHuntOptionsSmartAmmo" },
|
||||
{ key = "FeedOMatic", label = "FeedOMatic", frame = "MetaHuntOptionsFeedOMatic" },
|
||||
{ key = "AutoBuy", label = "Auto Buy", frame = "MetaHuntOptionsAutoBuy" },
|
||||
{ key = "ChronometerGeneral", label = "General", frame = "MetaHuntOptionsChronometer" },
|
||||
{ key = "ChronometerBar", label = "Bar", frame = "MetaHuntOptionsChronometer" },
|
||||
{ key = "ChronometerClassSpells", label = "Hunter Spells", frame = "MetaHuntOptionsChronometer" },
|
||||
{ key = "ChronometerClassEvents", label = "Hunter Events", frame = "MetaHuntOptionsChronometer" },
|
||||
{ key = "ChronometerRacial", label = "Racial", frame = "MetaHuntOptionsChronometer" },
|
||||
{ key = "Credits", label = "Credits", frame = "MetaHuntOptionsCredits" },
|
||||
}
|
||||
|
||||
MTH_OPTIONS_TREE = MTH_OPTIONS_TREE or {
|
||||
{ label = "General", key = "General" },
|
||||
{ label = "Auto Buy", key = "AutoBuy" },
|
||||
{ label = "Feed-O-Matic", key = "FeedOMatic" },
|
||||
{
|
||||
label = "zButtons",
|
||||
node = "zButtons",
|
||||
children = {
|
||||
{ label = "zAmmo", key = "Ammo" },
|
||||
{ label = "zPet", key = "Pet" },
|
||||
{ label = "zAspect", key = "Aspect" },
|
||||
{ label = "zTrack", key = "Track" },
|
||||
{ label = "zTrap", key = "Trap" },
|
||||
{ label = "zMounts", key = "Mounts" },
|
||||
{ label = "zCompanions", key = "Companions" },
|
||||
{ label = "zToys", key = "Toys" },
|
||||
},
|
||||
},
|
||||
{
|
||||
label = "Chronometer",
|
||||
node = "chronometer",
|
||||
children = {
|
||||
{ label = "General", key = "ChronometerGeneral" },
|
||||
{ label = "Bar", key = "ChronometerBar" },
|
||||
{ label = "Hunter Spells", key = "ChronometerClassSpells" },
|
||||
{ label = "Hunter Events", key = "ChronometerClassEvents" },
|
||||
{ label = "Racial", key = "ChronometerRacial" },
|
||||
},
|
||||
},
|
||||
{ label = "Credits", key = "Credits" },
|
||||
}
|
||||
|
||||
function MTH_BuildOptionsTree()
|
||||
local nav = MTH_GetFrame and MTH_GetFrame("MetaHuntOptionsNav") or nil
|
||||
if not nav then
|
||||
return
|
||||
end
|
||||
local navInnerWidth = (nav:GetWidth() or MTH_OPTIONS_CONST.NAV_DEFAULT_WIDTH) - MTH_OPTIONS_CONST.NAV_INNER_PADDING
|
||||
|
||||
local yOffset = MTH_OPTIONS_CONST.TREE_START_Y
|
||||
local buttonIndex = 1
|
||||
|
||||
local function acquireButton()
|
||||
local button = MTH_OPTIONS_TREE_BUTTONS[buttonIndex]
|
||||
if not button then
|
||||
button = CreateFrame("Button", "MetaHuntOptionsNavItem"..buttonIndex, nav)
|
||||
button:SetHeight(MTH_OPTIONS_CONST.TREE_BUTTON_HEIGHT)
|
||||
button:SetNormalTexture("")
|
||||
button:SetPushedTexture("")
|
||||
button:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
||||
button.highlight = button:GetHighlightTexture()
|
||||
if button.highlight then
|
||||
button.highlight:SetBlendMode("ADD")
|
||||
button.highlight:SetVertexColor(1, 1, 1, MTH_OPTIONS_CONST.HIGHLIGHT_ALPHA)
|
||||
end
|
||||
|
||||
button.selectedTexture = button:CreateTexture(nil, "BACKGROUND")
|
||||
button.selectedTexture:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
button.selectedTexture:SetVertexColor(MTH_OPTIONS_CONST.SELECTED_BG_R, MTH_OPTIONS_CONST.SELECTED_BG_G, MTH_OPTIONS_CONST.SELECTED_BG_B, MTH_OPTIONS_CONST.SELECTED_BG_A)
|
||||
button.selectedTexture:SetPoint("TOPLEFT", button, "TOPLEFT", 0, -1)
|
||||
button.selectedTexture:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 0, 1)
|
||||
button.selectedTexture:Hide()
|
||||
|
||||
button.expander = button:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
button.expander:SetPoint("LEFT", button, "LEFT", 4, 0)
|
||||
|
||||
button.label = button:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
button.label:SetJustifyH("LEFT")
|
||||
button.label:SetPoint("LEFT", button, "LEFT", 18, 0)
|
||||
button.label:SetPoint("RIGHT", button, "RIGHT", -4, 0)
|
||||
|
||||
MTH_OPTIONS_TREE_BUTTONS[buttonIndex] = button
|
||||
end
|
||||
button:Show()
|
||||
buttonIndex = buttonIndex + 1
|
||||
return button
|
||||
end
|
||||
|
||||
local function placeLeaf(node, indent)
|
||||
local button = acquireButton()
|
||||
local xOffset = MTH_OPTIONS_CONST.TREE_INDENT_BASE + (indent or 0)
|
||||
local width = navInnerWidth - (indent or 0)
|
||||
button:ClearAllPoints()
|
||||
button:SetPoint("TOPLEFT", nav, "TOPLEFT", xOffset, yOffset)
|
||||
button:SetWidth(width)
|
||||
button.expander:SetText("")
|
||||
button.label:SetPoint("LEFT", button, "LEFT", 4, 0)
|
||||
button.label:SetText(node.label)
|
||||
button:SetScript("OnClick", function() MTH_SelectOptionsTab(node.key) end)
|
||||
if MTH_OPTIONS_STATE.activeTab == node.key then
|
||||
button.selectedTexture:Show()
|
||||
button.label:SetTextColor(MTH_OPTIONS_CONST.SELECTED_TEXT_R, MTH_OPTIONS_CONST.SELECTED_TEXT_G, MTH_OPTIONS_CONST.SELECTED_TEXT_B)
|
||||
else
|
||||
button.selectedTexture:Hide()
|
||||
button.label:SetTextColor(1, 1, 1)
|
||||
end
|
||||
button.expander:SetTextColor(1, 1, 1)
|
||||
yOffset = yOffset - MTH_OPTIONS_CONST.TREE_ROW_STEP
|
||||
end
|
||||
|
||||
for i = 1, table.getn(MTH_OPTIONS_TREE) do
|
||||
local node = MTH_OPTIONS_TREE[i]
|
||||
if node.children then
|
||||
local expanded = MTH_OPTIONS_TREE_STATE[node.node] and true or false
|
||||
local button = acquireButton()
|
||||
button:ClearAllPoints()
|
||||
button:SetPoint("TOPLEFT", nav, "TOPLEFT", MTH_OPTIONS_CONST.TREE_INDENT_BASE, yOffset)
|
||||
button:SetWidth(navInnerWidth)
|
||||
button.expander:SetText(expanded and "-" or "+")
|
||||
button.expander:SetTextColor(MTH_OPTIONS_CONST.GROUP_TEXT_R, MTH_OPTIONS_CONST.GROUP_TEXT_G, MTH_OPTIONS_CONST.GROUP_TEXT_B)
|
||||
button.label:SetPoint("LEFT", button, "LEFT", 18, 0)
|
||||
button.label:SetText(node.label)
|
||||
button.label:SetTextColor(MTH_OPTIONS_CONST.GROUP_TEXT_R, MTH_OPTIONS_CONST.GROUP_TEXT_G, MTH_OPTIONS_CONST.GROUP_TEXT_B)
|
||||
button.selectedTexture:Hide()
|
||||
button:SetScript("OnClick", function()
|
||||
MTH_OPTIONS_TREE_STATE[node.node] = not MTH_OPTIONS_TREE_STATE[node.node]
|
||||
MTH_BuildOptionsTree()
|
||||
end)
|
||||
yOffset = yOffset - MTH_OPTIONS_CONST.TREE_ROW_STEP
|
||||
if expanded then
|
||||
for j = 1, table.getn(node.children) do
|
||||
placeLeaf(node.children[j], MTH_OPTIONS_CONST.TREE_CHILD_INDENT)
|
||||
end
|
||||
end
|
||||
else
|
||||
placeLeaf(node, 0)
|
||||
end
|
||||
end
|
||||
|
||||
for i = buttonIndex, table.getn(MTH_OPTIONS_TREE_BUTTONS) do
|
||||
if MTH_OPTIONS_TREE_BUTTONS[i] then
|
||||
MTH_OPTIONS_TREE_BUTTONS[i]:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
+125
@@ -0,0 +1,125 @@
|
||||
-- MetaHunt unified options window
|
||||
|
||||
function MTH_GetFrame(name)
|
||||
return getglobal(name)
|
||||
end
|
||||
|
||||
function MTH_OptionsRequire(moduleName, requiredGlobals)
|
||||
local missing = {}
|
||||
for i = 1, table.getn(requiredGlobals) do
|
||||
local key = requiredGlobals[i]
|
||||
if _G[key] == nil then
|
||||
table.insert(missing, key)
|
||||
end
|
||||
end
|
||||
|
||||
if table.getn(missing) == 0 then
|
||||
return true
|
||||
end
|
||||
|
||||
local message = "[MTH OPTIONS] " .. tostring(moduleName) .. " missing dependencies: " .. table.concat(missing, ", ")
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print(message, "error")
|
||||
elseif type(MTH_Log) == "function" then
|
||||
MTH_Log(message, "error")
|
||||
elseif DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(message)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_CreateActionButton(parent, name, text, x, y, width, height, onClick)
|
||||
local button = CreateFrame("Button", name, parent, "UIPanelButtonTemplate")
|
||||
if not button then return nil end
|
||||
button:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y)
|
||||
button:SetWidth(width or 120)
|
||||
button:SetHeight(height or 24)
|
||||
button:SetText(text)
|
||||
if onClick then
|
||||
button:SetScript("OnClick", onClick)
|
||||
end
|
||||
return button
|
||||
end
|
||||
|
||||
-- Helper: Create a slider control
|
||||
function MTH_CreateSlider(parent, name, label, minVal, maxVal, step, yOffset)
|
||||
local slider = CreateFrame("Slider", name, parent, "OptionsSliderTemplate")
|
||||
if not slider then return nil end
|
||||
|
||||
slider:SetPoint("TOPLEFT", parent, "TOPLEFT", 15, yOffset)
|
||||
slider:SetWidth(280)
|
||||
slider:SetMinMaxValues(minVal, maxVal)
|
||||
slider:SetValueStep(step)
|
||||
slider:SetValue(minVal)
|
||||
|
||||
-- Labels
|
||||
local text = slider:CreateFontString(name.."Text", "ARTWORK", "GameFontNormalSmall")
|
||||
text:SetPoint("BOTTOM", slider, "TOP", 0, 2)
|
||||
text:SetText(label)
|
||||
|
||||
local value = slider:CreateFontString(name.."Value", "ARTWORK", "GameFontHighlight")
|
||||
value:SetPoint("TOP", slider, "BOTTOM", 0, 2)
|
||||
value:SetText(tostring(minVal))
|
||||
|
||||
-- Hide default low/high labels from OptionsSliderTemplate
|
||||
local lowLabel = getglobal(name.."Low")
|
||||
local highLabel = getglobal(name.."High")
|
||||
if lowLabel then lowLabel:Hide() end
|
||||
if highLabel then highLabel:Hide() end
|
||||
|
||||
slider:SetScript("OnValueChanged", function(self, newValue)
|
||||
local activeSlider = self or this or slider
|
||||
local current = tonumber(newValue)
|
||||
if not current and activeSlider and activeSlider.GetValue then
|
||||
current = tonumber(activeSlider:GetValue())
|
||||
end
|
||||
if not current then
|
||||
current = minVal
|
||||
end
|
||||
value:SetText(tostring(math.floor(current + 0.5)))
|
||||
if slider.onChange then slider.onChange(current, activeSlider) end
|
||||
end)
|
||||
|
||||
return slider
|
||||
end
|
||||
|
||||
-- Helper: Create a checkbox control
|
||||
MTH_CreateCheckbox = function(parent, name, label, yOffset, xOffset)
|
||||
local check = CreateFrame("CheckButton", name, parent, "UICheckButtonTemplate")
|
||||
if not check then return nil end
|
||||
|
||||
check:SetPoint("TOPLEFT", parent, "TOPLEFT", xOffset or 15, yOffset)
|
||||
|
||||
local text = check:CreateFontString(name.."Text", "ARTWORK", "GameFontNormalSmall")
|
||||
text:SetPoint("LEFT", check, "RIGHT", 5, 0)
|
||||
text:SetText(label)
|
||||
|
||||
return check
|
||||
end
|
||||
|
||||
-- Helper: Clear all children from a container
|
||||
MTH_ClearContainer = function(container)
|
||||
if not container then return end
|
||||
-- Hide all child frames
|
||||
local children = { container:GetChildren() }
|
||||
for i = 1, table.getn(children) do
|
||||
local child = children[i]
|
||||
if child then
|
||||
child:Hide()
|
||||
child:ClearAllPoints()
|
||||
end
|
||||
end
|
||||
-- Hide all font strings and textures
|
||||
local regions = { container:GetRegions() }
|
||||
for i = 1, table.getn(regions) do
|
||||
local region = regions[i]
|
||||
if region and region ~= container then
|
||||
region:Hide()
|
||||
if region.ClearAllPoints then
|
||||
region:ClearAllPoints()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Frame name="MetaHuntOptionsTemplate" toplevel="true" frameStrata="DIALOG" movable="true" enableMouse="true" hidden="true" parent="UIParent" virtual="true">
|
||||
<Size>
|
||||
<AbsDimension x="720" y="650"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
</Anchors>
|
||||
<Backdrop bgFile="Interface\DialogFrame\UI-DialogBox-Background" edgeFile="Interface\DialogFrame\UI-DialogBox-Border" tile="true">
|
||||
<BackgroundInsets>
|
||||
<AbsInset left="11" right="12" top="12" bottom="11"/>
|
||||
</BackgroundInsets>
|
||||
<TileSize>
|
||||
<AbsValue val="32"/>
|
||||
</TileSize>
|
||||
<EdgeSize>
|
||||
<AbsValue val="32"/>
|
||||
</EdgeSize>
|
||||
</Backdrop>
|
||||
<Layers>
|
||||
<Layer level="ARTWORK">
|
||||
<Texture name="$parentHeader" file="Interface\DialogFrame\UI-DialogBox-Header">
|
||||
<Size>
|
||||
<AbsDimension x="256" y="64"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOP">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="12"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
<FontString name="$parentHeaderText" inherits="GameFontNormal" text="MetaHunt Options">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="$parentHeader">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="-14"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Frame name="$parentNav" enableMouse="true">
|
||||
<Size>
|
||||
<AbsDimension x="120" y="540"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="12" y="-72"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Backdrop bgFile="Interface\Buttons\WHITE8X8" edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
|
||||
<BackgroundInsets>
|
||||
<AbsInset left="3" right="3" top="3" bottom="3"/>
|
||||
</BackgroundInsets>
|
||||
<TileSize><AbsValue val="16"/></TileSize>
|
||||
<EdgeSize><AbsValue val="16"/></EdgeSize>
|
||||
</Backdrop>
|
||||
</Frame>
|
||||
|
||||
<!-- Content Areas -->
|
||||
<Frame name="$parentGeneral" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentPet" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentTrack" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentAspect" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentTrap" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentAmmo" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentMounts" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentCompanions" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentToys" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentSmartAmmo" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentFeedOMatic" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentAutoBuy" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentChronometer" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentCredits" hidden="true" enableMouse="true"/>
|
||||
|
||||
<!-- Top-right close button -->
|
||||
<Button name="$parentCloseButton" inherits="UIPanelButtonTemplate" text="X">
|
||||
<Size>
|
||||
<AbsDimension x="22" y="22"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPRIGHT">
|
||||
<Offset>
|
||||
<AbsDimension x="-8" y="-8"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
this:RegisterForDrag("LeftButton")
|
||||
</OnLoad>
|
||||
<OnMouseUp>
|
||||
if ( this.isMoving ) then
|
||||
this:StopMovingOrSizing();
|
||||
this.isMoving = false;
|
||||
end
|
||||
</OnMouseUp>
|
||||
<OnDragStart>
|
||||
this:StartMoving();
|
||||
this.isMoving = true;
|
||||
</OnDragStart>
|
||||
<OnDragStop>
|
||||
this:StopMovingOrSizing();
|
||||
this.isMoving = false;
|
||||
</OnDragStop>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
</Ui>
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
MTH_UI = MTH_UI or {}
|
||||
|
||||
MTH_UI.version = "0.1.0"
|
||||
MTH_UI.pages = MTH_UI.pages or {}
|
||||
MTH_UI.pageOrder = MTH_UI.pageOrder or {}
|
||||
MTH_UI.callbacks = MTH_UI.callbacks or {}
|
||||
|
||||
local function MTH_UI_SafeCall(handler, a1, a2, a3)
|
||||
if type(handler) ~= "function" then
|
||||
return false, "handler is not a function"
|
||||
end
|
||||
local ok, resultOrError = pcall(handler, a1, a2, a3)
|
||||
if not ok then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("[MTH_UI] " .. tostring(resultOrError), "error")
|
||||
end
|
||||
return false, resultOrError
|
||||
end
|
||||
return true, resultOrError
|
||||
end
|
||||
|
||||
local function MTH_UI_CopyTable(value)
|
||||
if type(value) ~= "table" then return value end
|
||||
local copy = {}
|
||||
for key, nestedValue in pairs(value) do
|
||||
copy[key] = MTH_UI_CopyTable(nestedValue)
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
local function MTH_UI_ApplyDefaults(target, defaults)
|
||||
if type(target) ~= "table" or type(defaults) ~= "table" then return target end
|
||||
for key, defaultValue in pairs(defaults) do
|
||||
if target[key] == nil then
|
||||
target[key] = MTH_UI_CopyTable(defaultValue)
|
||||
elseif type(target[key]) == "table" and type(defaultValue) == "table" then
|
||||
MTH_UI_ApplyDefaults(target[key], defaultValue)
|
||||
end
|
||||
end
|
||||
return target
|
||||
end
|
||||
|
||||
function MTH_UI.CloneTable(value)
|
||||
return MTH_UI_CopyTable(value)
|
||||
end
|
||||
|
||||
function MTH_UI.MergeDefaults(target, defaults)
|
||||
return MTH_UI_ApplyDefaults(target, defaults)
|
||||
end
|
||||
|
||||
function MTH_UI.ResolveValue(value, context)
|
||||
if type(value) == "function" then
|
||||
local ok, resolved = MTH_UI_SafeCall(value, context)
|
||||
if ok then return resolved end
|
||||
return nil
|
||||
end
|
||||
return value
|
||||
end
|
||||
|
||||
function MTH_UI.RegisterCallback(eventName, callbackKey, handler)
|
||||
if type(eventName) ~= "string" or eventName == "" then return false end
|
||||
if type(callbackKey) ~= "string" or callbackKey == "" then return false end
|
||||
if type(handler) ~= "function" then return false end
|
||||
|
||||
if not MTH_UI.callbacks[eventName] then
|
||||
MTH_UI.callbacks[eventName] = {}
|
||||
end
|
||||
MTH_UI.callbacks[eventName][callbackKey] = handler
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_UI.UnregisterCallback(eventName, callbackKey)
|
||||
if not MTH_UI.callbacks[eventName] then return false end
|
||||
if not MTH_UI.callbacks[eventName][callbackKey] then return false end
|
||||
MTH_UI.callbacks[eventName][callbackKey] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_UI.FireCallback(eventName, payload)
|
||||
local listeners = MTH_UI.callbacks[eventName]
|
||||
if not listeners then return end
|
||||
for _, handler in pairs(listeners) do
|
||||
MTH_UI_SafeCall(handler, payload)
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_UI.RegisterPage(pageId, pageDefinition)
|
||||
if type(pageId) ~= "string" or pageId == "" then return false, "invalid page id" end
|
||||
if type(pageDefinition) ~= "table" then return false, "page definition must be a table" end
|
||||
|
||||
local rootType = pageDefinition.type or "group"
|
||||
if rootType ~= "group" then
|
||||
return false, "root page type must be group"
|
||||
end
|
||||
|
||||
local existing = MTH_UI.pages[pageId]
|
||||
MTH_UI.pages[pageId] = MTH_UI_CopyTable(pageDefinition)
|
||||
MTH_UI.pages[pageId].id = pageId
|
||||
|
||||
if not existing then
|
||||
table.insert(MTH_UI.pageOrder, pageId)
|
||||
end
|
||||
|
||||
MTH_UI.FireCallback("PageRegistered", pageId)
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_UI.GetPage(pageId)
|
||||
return MTH_UI.pages[pageId]
|
||||
end
|
||||
|
||||
function MTH_UI.ListPages()
|
||||
local pageList = {}
|
||||
for _, pageId in ipairs(MTH_UI.pageOrder) do
|
||||
if MTH_UI.pages[pageId] then
|
||||
table.insert(pageList, pageId)
|
||||
end
|
||||
end
|
||||
return pageList
|
||||
end
|
||||
|
||||
function MTH_UI.NotifyPageChanged(pageId)
|
||||
MTH_UI.FireCallback("PageChanged", pageId)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
MTH_UIRenderer = MTH_UIRenderer or {}
|
||||
|
||||
MTH_UIRenderer.activeWidgets = MTH_UIRenderer.activeWidgets or {}
|
||||
|
||||
local function MTH_UIR_Clear(frame)
|
||||
if not frame then return end
|
||||
local widgets = frame._mthWidgets
|
||||
if not widgets then return end
|
||||
for _, widget in ipairs(widgets) do
|
||||
if widget and widget.Hide then
|
||||
widget:Hide()
|
||||
end
|
||||
end
|
||||
frame._mthWidgets = {}
|
||||
end
|
||||
|
||||
local function MTH_UIR_AddWidget(frame, widget)
|
||||
if not frame._mthWidgets then
|
||||
frame._mthWidgets = {}
|
||||
end
|
||||
table.insert(frame._mthWidgets, widget)
|
||||
end
|
||||
|
||||
local function MTH_UIR_MakeLabel(frame, text, x, y, width, template)
|
||||
local label = frame:CreateFontString(nil, "ARTWORK", template or "GameFontNormalSmall")
|
||||
label:SetPoint("TOPLEFT", frame, "TOPLEFT", x, y)
|
||||
label:SetWidth(width)
|
||||
label:SetJustifyH("LEFT")
|
||||
label:SetText(text or "")
|
||||
MTH_UIR_AddWidget(frame, label)
|
||||
return label
|
||||
end
|
||||
|
||||
local function MTH_UIR_SetCheckboxLabel(checkButton, text)
|
||||
local textRegion = nil
|
||||
local checkName = checkButton and checkButton.GetName and checkButton:GetName()
|
||||
if type(checkName) == "string" and checkName ~= "" then
|
||||
textRegion = getglobal(checkName .. "Text")
|
||||
end
|
||||
if not textRegion and checkButton and checkButton.Text then
|
||||
textRegion = checkButton.Text
|
||||
end
|
||||
if textRegion then
|
||||
textRegion:SetText(text or "")
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_UIR_CreateControl(frame, key, option, context, x, y, width)
|
||||
local optionType = option.type or "description"
|
||||
local optionName = MTH_UI.ResolveValue(option.name, context)
|
||||
if optionType == "description" then
|
||||
local label = MTH_UIR_MakeLabel(frame, optionName or "", x, y, width, "GameFontHighlightSmall")
|
||||
if label then label:SetTextColor(0.90, 0.90, 0.90) end
|
||||
return 18
|
||||
end
|
||||
|
||||
if optionType == "header" then
|
||||
local label = MTH_UIR_MakeLabel(frame, optionName or "", x, y, width, "GameFontNormal")
|
||||
if label then label:SetTextColor(1.00, 0.82, 0.00) end
|
||||
return 24
|
||||
end
|
||||
|
||||
if optionType == "toggle" then
|
||||
local checkButton = CreateFrame("CheckButton", nil, frame, "UICheckButtonTemplate")
|
||||
checkButton:SetPoint("TOPLEFT", frame, "TOPLEFT", x, y + 2)
|
||||
checkButton:SetChecked(MTH_UI.ResolveValue(option.get, context) and 1 or nil)
|
||||
MTH_UIR_SetCheckboxLabel(checkButton, optionName or "")
|
||||
checkButton:SetScript("OnClick", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
if option.set then
|
||||
option.set(context, self:GetChecked() == 1)
|
||||
end
|
||||
end)
|
||||
if MTH_UI.ResolveValue(option.disabled, context) then
|
||||
checkButton:Disable()
|
||||
end
|
||||
MTH_UIR_AddWidget(frame, checkButton)
|
||||
return 24
|
||||
end
|
||||
|
||||
if optionType == "execute" then
|
||||
local button = CreateFrame("Button", nil, frame)
|
||||
button:SetPoint("TOPLEFT", frame, "TOPLEFT", x, y)
|
||||
button:SetHeight(20)
|
||||
button:SetWidth(math.min(180, width))
|
||||
button:SetBackdrop({
|
||||
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true,
|
||||
tileSize = 16,
|
||||
edgeSize = 16,
|
||||
insets = { left = 3, right = 3, top = 3, bottom = 3 },
|
||||
})
|
||||
button:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
|
||||
button:SetBackdropBorderColor(0.4, 0.4, 0.4)
|
||||
|
||||
local text = button:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
text:SetPoint("CENTER", button, "CENTER", 0, 0)
|
||||
text:SetText(optionName or "Run")
|
||||
text:SetTextColor(1.00, 0.82, 0.00)
|
||||
|
||||
button:SetScript("OnEnter", function(self) self = self or this if self then self:SetBackdropBorderColor(0.6, 0.6, 0.6) end end)
|
||||
button:SetScript("OnLeave", function(self) self = self or this if self then self:SetBackdropBorderColor(0.4, 0.4, 0.4) end end)
|
||||
button:SetScript("OnClick", function()
|
||||
if option.func then
|
||||
option.func(context)
|
||||
end
|
||||
end)
|
||||
if MTH_UI.ResolveValue(option.disabled, context) then
|
||||
button:Disable()
|
||||
end
|
||||
MTH_UIR_AddWidget(frame, button)
|
||||
return 24
|
||||
end
|
||||
|
||||
if optionType == "select" then
|
||||
local label = MTH_UIR_MakeLabel(frame, optionName or "Select", x, y, width, "GameFontNormalSmall")
|
||||
if label then label:SetTextColor(1.00, 0.82, 0.00) end
|
||||
|
||||
local button = CreateFrame("Button", nil, frame)
|
||||
button:SetPoint("TOPLEFT", frame, "TOPLEFT", x, y - 16)
|
||||
button:SetHeight(20)
|
||||
button:SetWidth(math.min(220, width))
|
||||
button:SetBackdrop({
|
||||
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true,
|
||||
tileSize = 16,
|
||||
edgeSize = 16,
|
||||
insets = { left = 3, right = 3, top = 3, bottom = 3 },
|
||||
})
|
||||
button:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
|
||||
button:SetBackdropBorderColor(0.4, 0.4, 0.4)
|
||||
|
||||
local currentValue = MTH_UI.ResolveValue(option.get, context)
|
||||
local text = button:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
text:SetPoint("CENTER", button, "CENTER", 0, 0)
|
||||
text:SetText(tostring(currentValue or "Choose"))
|
||||
text:SetTextColor(0.90, 0.90, 0.90)
|
||||
|
||||
button:SetScript("OnEnter", function(self) self = self or this if self then self:SetBackdropBorderColor(0.6, 0.6, 0.6) end end)
|
||||
button:SetScript("OnLeave", function(self) self = self or this if self then self:SetBackdropBorderColor(0.4, 0.4, 0.4) end end)
|
||||
button:SetScript("OnClick", function()
|
||||
if option.onClick then
|
||||
option.onClick(context, option)
|
||||
end
|
||||
end)
|
||||
if MTH_UI.ResolveValue(option.disabled, context) then
|
||||
button:Disable()
|
||||
end
|
||||
MTH_UIR_AddWidget(frame, button)
|
||||
return 42
|
||||
end
|
||||
|
||||
MTH_UIR_MakeLabel(frame, optionName or ("Unsupported type: " .. tostring(optionType)), x, y, width, "GameFontDisableSmall")
|
||||
return 18
|
||||
end
|
||||
|
||||
function MTH_UIRenderer.RenderPage(frame, pageDefinition, context)
|
||||
if not frame or not pageDefinition then return false, "invalid render target" end
|
||||
|
||||
MTH_UIR_Clear(frame)
|
||||
|
||||
local args = pageDefinition.args or {}
|
||||
local orderedKeys = {}
|
||||
for key in pairs(args) do
|
||||
table.insert(orderedKeys, key)
|
||||
end
|
||||
table.sort(orderedKeys, function(leftKey, rightKey)
|
||||
local leftOrder = (args[leftKey] and args[leftKey].order) or 9999
|
||||
local rightOrder = (args[rightKey] and args[rightKey].order) or 9999
|
||||
if leftOrder ~= rightOrder then return leftOrder < rightOrder end
|
||||
return tostring(leftKey) < tostring(rightKey)
|
||||
end)
|
||||
|
||||
local cursorY = -8
|
||||
for _, key in ipairs(orderedKeys) do
|
||||
local option = args[key]
|
||||
if option then
|
||||
local hidden = MTH_UI.ResolveValue(option.hidden, context)
|
||||
if not hidden then
|
||||
local consumedHeight = MTH_UIR_CreateControl(frame, key, option, context, 8, cursorY, frame:GetWidth() - 16)
|
||||
cursorY = cursorY - consumedHeight
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
Reference in New Issue
Block a user