diff --git a/api/beastlore-scan.lua b/api/beastlore-scan.lua
deleted file mode 100644
index 13e2240..0000000
--- a/api/beastlore-scan.lua
+++ /dev/null
@@ -1,8 +0,0 @@
-------------------------------------------------------
--- MetaHunt: Beast Lore Scanner (removed)
-------------------------------------------------------
--- This file previously contained a feature that recorded
--- unknown beasts to SavedVariables when scanned with
--- Beast Lore. The feature has been removed; the static
--- beast database (ds-beasts) is now the sole data source.
-------------------------------------------------------
diff --git a/api/options-smartpet.lua b/api/options-smartpet.lua
deleted file mode 100644
index bfb9b51..0000000
--- a/api/options-smartpet.lua
+++ /dev/null
@@ -1,321 +0,0 @@
-------------------------------------------------------
--- MetaHunt: SmartPet Options Panel
--- TWoW 1.18.1
-------------------------------------------------------
-
-local function MTH_SP_OPT_Ready()
- return type(MTH_GetFrame) == "function"
- and type(MTH_CreateCheckbox) == "function"
- and type(MTH_CreateSlider) == "function"
-end
-
-local function MTH_SP_OPT_GetCfg()
- if not MTH_SmartPet or not MTH_SmartPet.GetConfig then return {} end
- return MTH_SmartPet.GetConfig()
-end
-
-local MTH_SP_OPT_built = false
-
--- Layout constants
-local OPT_L = 16 -- left column X
-local OPT_R = 290 -- right column X
-local OPT_CW = 250 -- column width (for tip text wrapping)
-
--- Spacing constants
-local GAP_SECTION = 34 -- between sections
-local GAP_CB = 24 -- checkbox height (icon + text)
-local GAP_TIP = 16 -- small tip text height
-local GAP_BTN = 28 -- button height + margin
-local GAP_SLIDER = 52 -- slider total height (label + track + value)
-local GAP_CB_SLIDER = 40 -- checkbox to slider (checkbox + slider label above track)
-
--- Helper: checkbox with click handler
-local function SP_CB(container, name, label, yOff, xOff, isChecked, onClick)
- local cb = MTH_CreateCheckbox(container, name, label, yOff, xOff or OPT_L)
- if cb then
- cb:SetChecked(isChecked and 1 or 0)
- cb:SetScript("OnClick", function()
- local v = this:GetChecked()
- onClick(v == 1 or v == true)
- end)
- end
- return cb
-end
-
--- Helper: section header label
-local function SP_Header(container, text, yOff, xOff)
- local fs = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
- fs:SetPoint("TOPLEFT", container, "TOPLEFT", xOff or OPT_L, yOff)
- fs:SetText("|cffff9900" .. text .. "|r")
- return fs
-end
-
--- Helper: small descriptive text
-local function SP_Tip(container, text, yOff, xOff)
- local fs = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
- fs:SetPoint("TOPLEFT", container, "TOPLEFT", xOff or OPT_L, yOff)
- fs:SetWidth(OPT_CW)
- fs:SetJustifyH("LEFT")
- fs:SetTextColor(0.4, 0.6, 1.0)
- fs:SetText(text)
- return fs
-end
-
--- Taunt Mode cycle
-local TAUNT_MODES = { "auto", "growl", "cower", "off" }
-local TAUNT_LABELS = {
- auto = "Auto (solo=Growl, tank=Cower)",
- growl = "Always Growl",
- cower = "Always Cower",
- off = "Off (manual)",
-}
-
-local function SP_NextTauntMode(current)
- for i = 1, 4 do
- if TAUNT_MODES[i] == current then
- return TAUNT_MODES[math.mod(i, 4) + 1]
- end
- end
- return "auto"
-end
-
--- Channel cycle
-local CHANNELS = { "SAY", "PARTY", "RAID", "GUILD" }
-
-local function SP_NextChannel(current)
- for i = 1, 4 do
- if CHANNELS[i] == current then
- return CHANNELS[math.mod(i, 4) + 1]
- end
- end
- return "SAY"
-end
-
--- Build the UI
-local function MTH_SP_OPT_BuildUI(container)
- if MTH_SP_OPT_built then return end
- MTH_SP_OPT_built = true
-
- local cfg = MTH_SP_OPT_GetCfg()
- local api = MTH_SmartPet or {}
-
- ---------------------------------------------------------------------------
- -- Title (large, like ExpAmmo)
- ---------------------------------------------------------------------------
- local title = container:CreateFontString(nil, "ARTWORK", "GameFontHighlightLarge")
- title:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L, -16)
- title:SetText("Smart Pet - Pet Combat Management")
-
- ---------------------------------------------------------------------------
- -- Module enable checkbox
- ---------------------------------------------------------------------------
- local moduleEnabled = true
- if MTH and MTH.IsModuleEnabled then
- moduleEnabled = MTH:IsModuleEnabled("smartpet", false) and true or false
- end
- SP_CB(container, "MTH_SP_OPT_ModuleEnable", "Enable Smart Pet module",
- -46, OPT_L, moduleEnabled, function(v)
- if MTH and MTH.SetModuleEnabled then MTH:SetModuleEnabled("smartpet", v) end
- end)
-
- -- Columns start below the module checkbox
- local COL_TOP = -80
-
- -- Vertical divider
- local divider = container:CreateTexture(nil, "BACKGROUND")
- divider:SetTexture(0.3, 0.3, 0.3, 0.6)
- divider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R - 14, COL_TOP)
- divider:SetPoint("BOTTOMLEFT", container, "TOPLEFT", OPT_R - 14, -530)
- divider:SetWidth(1)
-
- ---------------------------------------------------------------------------
- -- LEFT COLUMN — Focus Management
- ---------------------------------------------------------------------------
- local y = COL_TOP
-
- SP_Header(container, "Focus Management", y)
- y = y - 22
-
- -- Smart Focus
- SP_CB(container, "MTH_SP_OPT_SmartFocus", "Enable Smart Focus",
- y, OPT_L, cfg.smartFocus, function(v)
- if api.SetSmartFocus then api.SetSmartFocus(v) end
- end)
- y = y - GAP_CB
- SP_Tip(container, "Budget pet focus across DPS abilities.", y)
- y = y - GAP_TIP
- SP_Tip(container, "|cff80ff80High focus|r — all DPS abilities ON.", y)
- y = y - GAP_TIP
- SP_Tip(container, "|cffffcc00Mid focus|r — expensive ability first, others wait.", y)
- y = y - GAP_TIP
- SP_Tip(container, "|cffff8080Low focus|r — cheapest ability always ON (never idle).", y)
- y = y - GAP_TIP
- SP_Tip(container, "Taunt cost is always reserved.", y)
- y = y - GAP_SECTION
-
- ---------------------------------------------------------------------------
- -- LEFT COLUMN — Taunt Management
- ---------------------------------------------------------------------------
- SP_Header(container, "Taunt Management", y)
- y = y - GAP_TIP
- SP_Tip(container, "Controls Growl/Cower autocast based on party.", y)
- y = y - 22
-
- local tauntBtn = CreateFrame("Button", "MTH_SP_OPT_TauntBtn", container, "UIPanelButtonTemplate")
- tauntBtn:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L, y)
- tauntBtn:SetWidth(250)
- tauntBtn:SetHeight(22)
- tauntBtn:SetText("Mode: " .. (TAUNT_LABELS[cfg.tauntMode] or "Auto"))
- tauntBtn:SetScript("OnClick", function()
- local c = MTH_SP_OPT_GetCfg()
- local newMode = SP_NextTauntMode(c.tauntMode)
- if api.SetTauntMode then api.SetTauntMode(newMode) end
- this:SetText("Mode: " .. (TAUNT_LABELS[newMode] or newMode))
- end)
- y = y - GAP_BTN
-
- SP_CB(container, "MTH_SP_OPT_PvpDetaunt", "PVP Auto-Detaunt",
- y, OPT_L, cfg.pvpDetaunt, function(v)
- if api.SetPvpDetaunt then api.SetPvpDetaunt(v) end
- end)
- y = y - GAP_SECTION
-
- ---------------------------------------------------------------------------
- -- LEFT COLUMN — CC-Break Prevention
- ---------------------------------------------------------------------------
- SP_Header(container, "CC-Break Prevention", y)
- y = y - 22
-
- SP_CB(container, "MTH_SP_OPT_CcBreak", "Check for breakable CC",
- y, OPT_L, cfg.ccBreakCheck, function(v)
- if api.SetCcBreakCheck then api.SetCcBreakCheck(v) end
- end)
- y = y - GAP_CB
-
- local ccLabels = { block = "Block attack", warn = "Warn only" }
- local ccModeBtn = CreateFrame("Button", "MTH_SP_OPT_CcModeBtn", container, "UIPanelButtonTemplate")
- ccModeBtn:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L + 20, y)
- ccModeBtn:SetWidth(180)
- ccModeBtn:SetHeight(20)
- ccModeBtn:SetText("CC Mode: " .. (ccLabels[cfg.ccBreakMode] or "Block"))
- ccModeBtn:SetScript("OnClick", function()
- local c = MTH_SP_OPT_GetCfg()
- local newMode = (c.ccBreakMode == "block") and "warn" or "block"
- if api.SetCcBreakMode then api.SetCcBreakMode(newMode) end
- this:SetText("CC Mode: " .. (ccLabels[newMode] or newMode))
- end)
- y = y - GAP_SECTION
-
- ---------------------------------------------------------------------------
- -- RIGHT COLUMN — General
- ---------------------------------------------------------------------------
- local ry = COL_TOP
-
- -- The Button
- SP_Header(container, "The Button", ry, OPT_R)
- ry = ry - 22
-
- SP_CB(container, "MTH_SP_OPT_TheButton", "Enable The Button keybind",
- ry, OPT_R, cfg.theButton, function(v)
- if api.SetTheButton then api.SetTheButton(v) end
- end)
- ry = ry - GAP_CB
- SP_Tip(container, "Enemy=attack, friendly=assist, none=recall.", ry, OPT_R)
- ry = ry - GAP_SECTION
-
- -- Rush on Attack
- SP_CB(container, "MTH_SP_OPT_Rush", "Rush on initial attack",
- ry, OPT_R, cfg.rushOnAttack, function(v)
- if api.SetRushOnAttack then api.SetRushOnAttack(v) end
- end)
- ry = ry - GAP_CB
- SP_Tip(container, "Charge / Dash / Dive when pet is sent to attack.", ry, OPT_R)
- ry = ry - GAP_SECTION
-
- -- NoChase
- SP_CB(container, "MTH_SP_OPT_NoChase", "NoChase (recall on mob flee)",
- ry, OPT_R, cfg.noChase, function(v)
- if api.SetNoChase then api.SetNoChase(v) end
- end)
- ry = ry - GAP_CB
- SP_Tip(container, "Recall pet when target attempts to flee.", ry, OPT_R)
- ry = ry - GAP_SECTION
-
- -- AutoCower
- SP_Header(container, "AutoCower", ry, OPT_R)
- ry = ry - 22
-
- SP_CB(container, "MTH_SP_OPT_AutoCower", "Enable AutoCower on low HP",
- ry, OPT_R, cfg.autoCower, function(v)
- if api.SetAutoCower then api.SetAutoCower(v) end
- end)
- ry = ry - GAP_CB_SLIDER
-
- local acSlider = MTH_CreateSlider(container, "MTH_SP_OPT_AutoCowerSlider",
- "HP Threshold (%)", 5, 80, 5, ry)
- if acSlider then
- acSlider:ClearAllPoints()
- acSlider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, ry)
- acSlider:SetWidth(200)
- acSlider:SetValue(cfg.autoCowerPct or 30)
- local valText = getglobal("MTH_SP_OPT_AutoCowerSliderValue")
- if valText then valText:SetText(tostring(cfg.autoCowerPct or 30)) end
- acSlider.onChange = function(val)
- if api.SetAutoCowerPct then api.SetAutoCowerPct(val) end
- end
- end
- ry = ry - GAP_SLIDER
-
- -- AutoWarn
- SP_Header(container, "AutoWarn", ry, OPT_R)
- ry = ry - 22
-
- SP_CB(container, "MTH_SP_OPT_AutoWarn", "Warn when pet HP is low",
- ry, OPT_R, cfg.autoWarn, function(v)
- if api.SetAutoWarn then api.SetAutoWarn(v) end
- end)
- ry = ry - GAP_CB_SLIDER
-
- local awSlider = MTH_CreateSlider(container, "MTH_SP_OPT_AutoWarnSlider",
- "HP Threshold (%)", 5, 60, 5, ry)
- if awSlider then
- awSlider:ClearAllPoints()
- awSlider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, ry)
- awSlider:SetWidth(200)
- awSlider:SetValue(cfg.autoWarnPct or 20)
- local valText = getglobal("MTH_SP_OPT_AutoWarnSliderValue")
- if valText then valText:SetText(tostring(cfg.autoWarnPct or 20)) end
- awSlider.onChange = function(val)
- if api.SetAutoWarnPct then api.SetAutoWarnPct(val) end
- end
- end
- ry = ry - GAP_SLIDER
-
- local chanBtn = CreateFrame("Button", "MTH_SP_OPT_ChanBtn", container, "UIPanelButtonTemplate")
- chanBtn:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, ry)
- chanBtn:SetWidth(180)
- chanBtn:SetHeight(22)
- chanBtn:SetText("Channel: " .. (cfg.autoWarnChannel or "SAY"))
- chanBtn:SetScript("OnClick", function()
- local c = MTH_SP_OPT_GetCfg()
- local newChan = SP_NextChannel(c.autoWarnChannel or "SAY")
- if api.SetAutoWarnChannel then api.SetAutoWarnChannel(newChan) end
- this:SetText("Channel: " .. newChan)
- end)
- ry = ry - GAP_CB
- SP_Tip(container, "Chat channel for low-HP warnings.", ry, OPT_R)
-end
-
--- Public setup function (called by options-shell.lua)
-function MTH_SetupSmartPetOptions()
- if not MTH_SP_OPT_Ready() then return end
- local container = MTH_GetFrame("MetaHuntOptionsSmartPet")
- if not container then return end
- local ok, err = pcall(MTH_SP_OPT_BuildUI, container)
- if not ok then
- if MTH and MTH.Print then
- MTH:Print("[SmartPet Options] " .. tostring(err), "error")
- end
- MTH_SP_OPT_built = false
- end
-end
diff --git a/data/ds-pet-spells-acquire.lua b/data/ds-pet-spells-acquire.lua
deleted file mode 100644
index df0a7d1..0000000
--- a/data/ds-pet-spells-acquire.lua
+++ /dev/null
@@ -1,140 +0,0 @@
--- MetaHunt: Pet spell DBC acquire-method overlay (learnMethod, minRank, nextSpellId)
--- AUTO-GENERATED by .tools — do not edit by hand.
--- Source DBC(s): SkillLineAbility.dbc, SkillLine.dbc
--- Generated: 2026-03-20 13:57 UTC
-
--- This file patches MTH_DS_PetSpells.allSpells entries with DBC-authoritative
--- learnMethod, minRank, and nextSpellId values.
--- Load order: after ds-pet-spells.lua and ds-pet-spells-trainer.lua
-
-if not MTH_DS_PetSpells or not MTH_DS_PetSpells.allSpells then return end
-
-local _acquire = {
- [1747] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [1748] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [1749] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [1750] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [1751] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [1853] = { learnMethod="unknown(1)", minRank=1, nextSpellId=0, skillLineId=261 },
- [2975] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [2976] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [2977] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [2980] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [2981] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [2982] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [3666] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [3667] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [4195] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [4196] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [4197] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [4198] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [4199] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [4200] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [4201] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [4202] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [5048] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [5049] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [5149] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [7370] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [14922] = { learnMethod="unknown(1)", minRank=1, nextSpellId=0, skillLineId=261 },
- [14923] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [14924] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [14925] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [14926] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [14927] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [16698] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [17254] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [17262] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [17263] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [17264] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [17265] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [17266] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [17267] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [17268] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [23100] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [23111] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [23112] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [23146] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [23149] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [23150] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [23163] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [23166] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [23167] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24424] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24440] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24441] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24451] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24454] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24455] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24463] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24464] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24475] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24476] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24477] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24478] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24490] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24494] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24495] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24508] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24509] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24510] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24511] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24512] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24513] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24514] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24515] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24516] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24547] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24556] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24557] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24558] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24559] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24560] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24561] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24562] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24580] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24581] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24582] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24584] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24588] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24589] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24599] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24607] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24608] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24609] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24631] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24632] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24641] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [24845] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [25013] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [25014] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [25015] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [25016] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [25017] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [25077] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [26065] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [26094] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [26184] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [26185] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [26186] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [26189] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [26190] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [26202] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [28343] = { learnMethod="trainer", minRank=1, nextSpellId=0, skillLineId=261 },
- [46023] = { learnMethod="trainer", minRank=0, nextSpellId=0, skillLineId=261 },
- [46303] = { learnMethod="trainer", minRank=0, nextSpellId=0, skillLineId=261 },
- [46307] = { learnMethod="trainer", minRank=0, nextSpellId=0, skillLineId=261 },
- [51154] = { learnMethod="trainer", minRank=0, nextSpellId=0, skillLineId=261 },
- [51155] = { learnMethod="trainer", minRank=0, nextSpellId=0, skillLineId=261 },
- [51157] = { learnMethod="trainer", minRank=0, nextSpellId=0, skillLineId=261 },
-}
-
-for _, spell in ipairs(MTH_DS_PetSpells.allSpells) do
- local meta = _acquire[spell.id]
- if meta then
- spell.learnMethod = meta.learnMethod
- spell.minRank = meta.minRank
- spell.nextSpellId = meta.nextSpellId
- spell.skillLineId = meta.skillLineId
- end
-end
diff --git a/data/ds-stable-prices.lua b/data/ds-stable-prices.lua
deleted file mode 100644
index 9bced7e..0000000
--- a/data/ds-stable-prices.lua
+++ /dev/null
@@ -1,41 +0,0 @@
--- MetaHunt: Stable slot prices
--- AUTO-GENERATED by .tools — do not edit by hand.
--- Source DBC(s): StableSlotPrices.dbc
--- Generated: 2026-03-20 13:57 UTC
-
-if not MTH_DS then MTH_DS = {} end
-
-MTH_DS_StableSlotPrices = {
- [1] = {
- ["cost_copper"] = 500,
- ["cost_gold"] = 0,
- ["cost_silver"] = 5,
- ["cost_copper_remainder"] = 0,
- ["display"] = "0g 5s 0c",
- ["acquireMethod"] = "gold",
- },
- [2] = {
- ["cost_copper"] = 50000,
- ["cost_gold"] = 5,
- ["cost_silver"] = 0,
- ["cost_copper_remainder"] = 0,
- ["display"] = "5g 0s 0c",
- ["acquireMethod"] = "gold",
- },
- [3] = {
- ["cost_copper"] = 2500000,
- ["cost_gold"] = 250,
- ["cost_silver"] = 0,
- ["cost_copper_remainder"] = 0,
- ["display"] = "250g 0s 0c",
- ["acquireMethod"] = "gold",
- },
- [4] = {
- ["cost_copper"] = 0,
- ["cost_gold"] = 0,
- ["cost_silver"] = 0,
- ["cost_copper_remainder"] = 0,
- ["display"] = "Shop",
- ["acquireMethod"] = "shop",
- },
-}
diff --git a/data/ds-zones-dbc.lua b/data/ds-zones-dbc.lua
deleted file mode 100644
index d784623..0000000
--- a/data/ds-zones-dbc.lua
+++ /dev/null
@@ -1,97 +0,0 @@
--- MetaHunt: Zone DBC enrichment (worldBounds, dbcAreaId, dbcMapId, dbcName)
--- AUTO-GENERATED by .tools — do not edit by hand.
--- Source DBC(s): WorldMapArea.dbc, AreaTable.dbc
--- Generated: 2026-03-20 13:57 UTC
-
-if not MTH_DS_Zones then return end
-
--- WMA ID=301 map=0 'Stormwind'
-if MTH_DS_Zones[1519] then
- MTH_DS_Zones[1519]["dbcAreaId"] = 1519
- MTH_DS_Zones[1519]["dbcMapId"] = 0
- MTH_DS_Zones[1519]["dbcName"] = "Stormwind"
- MTH_DS_Zones[1519]["worldBounds"] = {
- ["left"] = 1722.92,
- ["right"] = -14.58,
- ["top"] = -7995.8301,
- ["bottom"] = -9154.1699,
-}
-end
-
--- WMA ID=321 map=1 'Ogrimmar'
-if MTH_DS_Zones[1637] then
- MTH_DS_Zones[1637]["dbcAreaId"] = 1637
- MTH_DS_Zones[1637]["dbcMapId"] = 1
- MTH_DS_Zones[1637]["dbcName"] = "Ogrimmar"
- MTH_DS_Zones[1637]["worldBounds"] = {
- ["left"] = -3680.6001,
- ["right"] = -5083.21,
- ["top"] = 2273.8799,
- ["bottom"] = 1338.46,
-}
-end
-
--- WMA ID=362 map=1 'ThunderBluff'
-if MTH_DS_Zones[1638] then
- MTH_DS_Zones[1638]["dbcAreaId"] = 1638
- MTH_DS_Zones[1638]["dbcMapId"] = 1
- MTH_DS_Zones[1638]["dbcName"] = "ThunderBluff"
- MTH_DS_Zones[1638]["worldBounds"] = {
- ["left"] = 516.67,
- ["right"] = -527.08,
- ["top"] = -850.0,
- ["bottom"] = -1545.83,
-}
-end
-
--- WMA ID=381 map=1 'Darnassis'
-if MTH_DS_Zones[1657] then
- MTH_DS_Zones[1657]["dbcAreaId"] = 1657
- MTH_DS_Zones[1657]["dbcMapId"] = 1
- MTH_DS_Zones[1657]["dbcName"] = "Darnassis"
- MTH_DS_Zones[1657]["worldBounds"] = {
- ["left"] = 2938.3601,
- ["right"] = 1880.03,
- ["top"] = 10238.3203,
- ["bottom"] = 9532.5898,
-}
-end
-
--- WMA ID=382 map=0 'Undercity'
-if MTH_DS_Zones[1497] then
- MTH_DS_Zones[1497]["dbcAreaId"] = 1497
- MTH_DS_Zones[1497]["dbcMapId"] = 0
- MTH_DS_Zones[1497]["dbcName"] = "Undercity"
- MTH_DS_Zones[1497]["worldBounds"] = {
- ["left"] = 873.19,
- ["right"] = -86.18,
- ["top"] = 1877.95,
- ["bottom"] = 1237.84,
-}
-end
-
--- WMA ID=508 map=1 'AmaniAlor'
-if MTH_DS_Zones[2041] then
- MTH_DS_Zones[2041]["dbcAreaId"] = 2041
- MTH_DS_Zones[2041]["dbcMapId"] = 1
- MTH_DS_Zones[2041]["dbcName"] = "AmaniAlor"
- MTH_DS_Zones[2041]["worldBounds"] = {
- ["left"] = 3112.0,
- ["right"] = 1599.0,
- ["top"] = 3437.0,
- ["bottom"] = 2434.0,
-}
-end
-
--- WMA ID=509 map=0 'AlahThalas'
-if MTH_DS_Zones[2040] then
- MTH_DS_Zones[2040]["dbcAreaId"] = 2040
- MTH_DS_Zones[2040]["dbcMapId"] = 0
- MTH_DS_Zones[2040]["dbcName"] = "AlahThalas"
- MTH_DS_Zones[2040]["worldBounds"] = {
- ["left"] = -2169.0,
- ["right"] = -3637.0,
- ["top"] = 4907.0,
- ["bottom"] = 3931.0,
-}
-end
diff --git a/data/ds-zones-new-dbc.lua b/data/ds-zones-new-dbc.lua
deleted file mode 100644
index 12985b4..0000000
--- a/data/ds-zones-new-dbc.lua
+++ /dev/null
@@ -1,1475 +0,0 @@
--- MetaHunt: Zones present in DBC but not in MTH_DS_Zones (TWoW custom zones)
--- AUTO-GENERATED by .tools — do not edit by hand.
--- Source DBC(s): WorldMapArea.dbc, AreaTable.dbc
--- Generated: 2026-03-20 13:57 UTC
-
-if not MTH_DS_Zones then MTH_DS_Zones = {} end
-
--- DBC WMA ID=4 MapID=1 'Durotar'
-if not MTH_DS_Zones[14] then
- MTH_DS_Zones[14] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 14,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Durotar",
- ["worldBounds"] = { left=-1962.50, right=-7250.00, top=1808.33, bottom=-1716.67 },
- ["names"] = { ["enUS"] = "Durotar", ["esES"] = "Durotar", ["ptBR"] = "Durotar", ["zhCN"] = "杜隆塔尔" },
- }
-end
-
--- DBC WMA ID=9 MapID=1 'Mulgore'
-if not MTH_DS_Zones[215] then
- MTH_DS_Zones[215] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 215,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Mulgore",
- ["worldBounds"] = { left=2047.92, right=-3089.58, top=-272.92, bottom=-3697.92 },
- ["names"] = { ["enUS"] = "Mulgore", ["esES"] = "Mulgore", ["ptBR"] = "Mulgore", ["zhCN"] = "莫高雷" },
- }
-end
-
--- DBC WMA ID=11 MapID=1 'Barrens'
-if not MTH_DS_Zones[17] then
- MTH_DS_Zones[17] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 17,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Barrens",
- ["worldBounds"] = { left=2622.92, right=-7510.42, top=1612.50, bottom=-5143.75 },
- ["names"] = { ["enUS"] = "The Barrens", ["esES"] = "Los Baldíos", ["ptBR"] = "Os Barrens", ["zhCN"] = "贫瘠之地" },
- }
-end
-
--- DBC WMA ID=13 MapID=1 'Kalimdor'
-if not MTH_DS_Zones[0] then
- MTH_DS_Zones[0] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 0,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Kalimdor",
- ["worldBounds"] = { left=17066.60, right=-19733.21, top=12799.90, bottom=-11733.30 },
- ["names"] = { ["enUS"] = "Kalimdor" },
- }
-end
-
--- DBC WMA ID=14 MapID=0 'Azeroth'
-if not MTH_DS_Zones[0] then
- MTH_DS_Zones[0] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 0,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Azeroth",
- ["worldBounds"] = { left=16000.00, right=-19199.90, top=7466.60, bottom=-16000.00 },
- ["names"] = { ["enUS"] = "Azeroth" },
- }
-end
-
--- DBC WMA ID=15 MapID=0 'Alterac'
-if not MTH_DS_Zones[36] then
- MTH_DS_Zones[36] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 36,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Alterac",
- ["worldBounds"] = { left=783.33, right=-2016.67, top=1500.00, bottom=-366.67 },
- ["names"] = { ["enUS"] = "Alterac Mountains", ["esES"] = "Montañas de Alterac", ["ptBR"] = "Montanhas de Alterac", ["zhCN"] = "奥特兰克山脉" },
- }
-end
-
--- DBC WMA ID=16 MapID=0 'Arathi'
-if not MTH_DS_Zones[45] then
- MTH_DS_Zones[45] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 45,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Arathi",
- ["worldBounds"] = { left=-866.67, right=-4466.67, top=-133.33, bottom=-2533.33 },
- ["names"] = { ["enUS"] = "Arathi Highlands", ["esES"] = "Tierras Altas de Arathi", ["ptBR"] = "Planalto Arathi", ["zhCN"] = "阿拉希高地" },
- }
-end
-
--- DBC WMA ID=17 MapID=0 'Badlands'
-if not MTH_DS_Zones[3] then
- MTH_DS_Zones[3] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 3,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Badlands",
- ["worldBounds"] = { left=-2079.17, right=-4566.67, top=-5889.58, bottom=-7547.92 },
- ["names"] = { ["enUS"] = "Badlands", ["esES"] = "Tierras Inhóspitas", ["ptBR"] = "Ermos", ["zhCN"] = "荒芜之地" },
- }
-end
-
--- DBC WMA ID=19 MapID=0 'BlastedLands'
-if not MTH_DS_Zones[4] then
- MTH_DS_Zones[4] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 4,
- ["dbcMapId"] = 0,
- ["dbcName"] = "BlastedLands",
- ["worldBounds"] = { left=-1241.67, right=-4591.67, top=-10566.67, bottom=-12800.00 },
- ["names"] = { ["enUS"] = "Blasted Lands", ["esES"] = "Tierras Devastadas", ["ptBR"] = "Terras Devastadas", ["zhCN"] = "诅咒之地" },
- }
-end
-
--- DBC WMA ID=20 MapID=0 'Tirisfal'
-if not MTH_DS_Zones[85] then
- MTH_DS_Zones[85] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 85,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Tirisfal",
- ["worldBounds"] = { left=3033.33, right=-1485.42, top=3837.50, bottom=825.00 },
- ["names"] = { ["enUS"] = "Tirisfal Glades", ["esES"] = "Claros de Tirisfal", ["ptBR"] = "Os Bosques de Tirisfal", ["zhCN"] = "提瑞斯法林地" },
- }
-end
-
--- DBC WMA ID=21 MapID=0 'Silverpine'
-if not MTH_DS_Zones[130] then
- MTH_DS_Zones[130] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 130,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Silverpine",
- ["worldBounds"] = { left=3450.00, right=-750.00, top=1666.67, bottom=-1133.33 },
- ["names"] = { ["enUS"] = "Silverpine Forest", ["esES"] = "Bosque de Argénteos", ["ptBR"] = "Floresta Silverpine", ["zhCN"] = "银松森林" },
- }
-end
-
--- DBC WMA ID=22 MapID=0 'WesternPlaguelands'
-if not MTH_DS_Zones[28] then
- MTH_DS_Zones[28] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 28,
- ["dbcMapId"] = 0,
- ["dbcName"] = "WesternPlaguelands",
- ["worldBounds"] = { left=416.67, right=-3883.33, top=3366.67, bottom=500.00 },
- ["names"] = { ["enUS"] = "Western Plaguelands", ["esES"] = "Tierras de la Peste del Oeste", ["ptBR"] = "Plaguelands Ocidentais", ["zhCN"] = "西瘟疫之地" },
- }
-end
-
--- DBC WMA ID=23 MapID=0 'EasternPlaguelands'
-if not MTH_DS_Zones[139] then
- MTH_DS_Zones[139] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 139,
- ["dbcMapId"] = 0,
- ["dbcName"] = "EasternPlaguelands",
- ["worldBounds"] = { left=-2185.42, right=-6056.25, top=3800.00, bottom=1218.75 },
- ["names"] = { ["enUS"] = "Eastern Plaguelands", ["esES"] = "Tierras de la Peste del Este", ["ptBR"] = "Terras Pestilentas Orientais", ["zhCN"] = "东瘟疫之地" },
- }
-end
-
--- DBC WMA ID=24 MapID=0 'Hilsbrad'
-if not MTH_DS_Zones[267] then
- MTH_DS_Zones[267] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 267,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Hilsbrad",
- ["worldBounds"] = { left=1066.67, right=-2133.33, top=400.00, bottom=-1733.33 },
- ["names"] = { ["enUS"] = "Hillsbrad Foothills", ["esES"] = "Laderas de Trabalomas", ["ptBR"] = "Contrafortes de Eira dos Montes", ["zhCN"] = "希尔斯布莱德丘陵" },
- }
-end
-
--- DBC WMA ID=26 MapID=0 'Hinterlands'
-if not MTH_DS_Zones[47] then
- MTH_DS_Zones[47] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 47,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Hinterlands",
- ["worldBounds"] = { left=-1575.00, right=-5425.00, top=1466.67, bottom=-1100.00 },
- ["names"] = { ["enUS"] = "The Hinterlands", ["esES"] = "Tierras del Interior", ["ptBR"] = "As Terras Interiores", ["zhCN"] = "辛特兰" },
- }
-end
-
--- DBC WMA ID=27 MapID=0 'DunMorogh'
-if not MTH_DS_Zones[1] then
- MTH_DS_Zones[1] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 1,
- ["dbcMapId"] = 0,
- ["dbcName"] = "DunMorogh",
- ["worldBounds"] = { left=1802.08, right=-3122.92, top=-3877.08, bottom=-7160.42 },
- ["names"] = { ["enUS"] = "Dun Morogh", ["esES"] = "Dun Morogh", ["ptBR"] = "Dun Morogh", ["zhCN"] = "丹莫罗" },
- }
-end
-
--- DBC WMA ID=28 MapID=0 'SearingGorge'
-if not MTH_DS_Zones[51] then
- MTH_DS_Zones[51] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 51,
- ["dbcMapId"] = 0,
- ["dbcName"] = "SearingGorge",
- ["worldBounds"] = { left=-322.92, right=-2554.17, top=-6100.00, bottom=-7587.50 },
- ["names"] = { ["enUS"] = "Searing Gorge", ["esES"] = "La Garganta de Fuego", ["ptBR"] = "Desfiladeiro Searing", ["zhCN"] = "灼热峡谷" },
- }
-end
-
--- DBC WMA ID=29 MapID=0 'BurningSteppes'
-if not MTH_DS_Zones[46] then
- MTH_DS_Zones[46] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 46,
- ["dbcMapId"] = 0,
- ["dbcName"] = "BurningSteppes",
- ["worldBounds"] = { left=-266.67, right=-3195.83, top=-7031.25, bottom=-8983.33 },
- ["names"] = { ["enUS"] = "Burning Steppes", ["esES"] = "Las Estepas Ardientes", ["ptBR"] = "Estepes Ardentes", ["zhCN"] = "燃烧平原" },
- }
-end
-
--- DBC WMA ID=30 MapID=0 'Elwynn'
-if not MTH_DS_Zones[12] then
- MTH_DS_Zones[12] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 12,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Elwynn",
- ["worldBounds"] = { left=1535.42, right=-1935.42, top=-7939.58, bottom=-10254.17 },
- ["names"] = { ["enUS"] = "Elwynn Forest", ["esES"] = "Bosque de Elwynn", ["ptBR"] = "Floresta de Elwynn", ["zhCN"] = "艾尔文森林" },
- }
-end
-
--- DBC WMA ID=32 MapID=0 'DeadwindPass'
-if not MTH_DS_Zones[41] then
- MTH_DS_Zones[41] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 41,
- ["dbcMapId"] = 0,
- ["dbcName"] = "DeadwindPass",
- ["worldBounds"] = { left=-833.33, right=-3333.33, top=-9866.67, bottom=-11533.33 },
- ["names"] = { ["enUS"] = "Deadwind Pass", ["esES"] = "Paso de la Muerte", ["ptBR"] = "Desfiladeiro da Morte", ["zhCN"] = "逆风小径" },
- }
-end
-
--- DBC WMA ID=34 MapID=0 'Duskwood'
-if not MTH_DS_Zones[10] then
- MTH_DS_Zones[10] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 10,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Duskwood",
- ["worldBounds"] = { left=833.33, right=-1866.67, top=-9716.67, bottom=-11516.67 },
- ["names"] = { ["enUS"] = "Duskwood", ["esES"] = "Bosque del Ocaso", ["ptBR"] = "Floresta do Crepúsculo", ["zhCN"] = "暮色森林" },
- }
-end
-
--- DBC WMA ID=35 MapID=0 'LochModan'
-if not MTH_DS_Zones[38] then
- MTH_DS_Zones[38] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 38,
- ["dbcMapId"] = 0,
- ["dbcName"] = "LochModan",
- ["worldBounds"] = { left=-1993.75, right=-4752.08, top=-4487.50, bottom=-6327.08 },
- ["names"] = { ["enUS"] = "Loch Modan", ["esES"] = "Loch Modan", ["ptBR"] = "Loch Modan", ["zhCN"] = "洛克莫丹" },
- }
-end
-
--- DBC WMA ID=36 MapID=0 'Redridge'
-if not MTH_DS_Zones[44] then
- MTH_DS_Zones[44] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 44,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Redridge",
- ["worldBounds"] = { left=-1570.83, right=-3741.67, top=-8575.00, bottom=-10022.92 },
- ["names"] = { ["enUS"] = "Redridge Mountains", ["esES"] = "Montañas Crestagrana", ["ptBR"] = "Montanhas Redridge", ["zhCN"] = "赤脊山" },
- }
-end
-
--- DBC WMA ID=37 MapID=0 'Stranglethorn'
-if not MTH_DS_Zones[33] then
- MTH_DS_Zones[33] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 33,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Stranglethorn",
- ["worldBounds"] = { left=2220.83, right=-4160.42, top=-11168.75, bottom=-15422.92 },
- ["names"] = { ["enUS"] = "Stranglethorn Vale", ["esES"] = "Vega de Tuercespina", ["ptBR"] = "Vale Stranglethorn", ["zhCN"] = "荆棘谷" },
- }
-end
-
--- DBC WMA ID=38 MapID=0 'SwampOfSorrows'
-if not MTH_DS_Zones[8] then
- MTH_DS_Zones[8] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 8,
- ["dbcMapId"] = 0,
- ["dbcName"] = "SwampOfSorrows",
- ["worldBounds"] = { left=-2222.92, right=-4516.67, top=-9620.83, bottom=-11150.00 },
- ["names"] = { ["enUS"] = "Swamp of Sorrows", ["esES"] = "Pantano de las Penas", ["ptBR"] = "Pantano das Tristezas", ["zhCN"] = "悲伤沼泽" },
- }
-end
-
--- DBC WMA ID=39 MapID=0 'Westfall'
-if not MTH_DS_Zones[40] then
- MTH_DS_Zones[40] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 40,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Westfall",
- ["worldBounds"] = { left=3016.67, right=-483.33, top=-9400.00, bottom=-11733.33 },
- ["names"] = { ["enUS"] = "Westfall", ["esES"] = "Páramos del Poniente", ["ptBR"] = "Westfall", ["zhCN"] = "西部荒野" },
- }
-end
-
--- DBC WMA ID=40 MapID=0 'Wetlands'
-if not MTH_DS_Zones[11] then
- MTH_DS_Zones[11] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 11,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Wetlands",
- ["worldBounds"] = { left=-389.58, right=-4525.00, top=-2147.92, bottom=-4904.17 },
- ["names"] = { ["enUS"] = "Wetlands", ["esES"] = "Los Humedales", ["ptBR"] = "Pântanos", ["zhCN"] = "湿地" },
- }
-end
-
--- DBC WMA ID=41 MapID=1 'Teldrassil'
-if not MTH_DS_Zones[141] then
- MTH_DS_Zones[141] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 141,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Teldrassil",
- ["worldBounds"] = { left=3814.58, right=-1277.08, top=11831.25, bottom=8437.50 },
- ["names"] = { ["enUS"] = "Teldrassil", ["esES"] = "Teldrassil", ["ptBR"] = "Teldrassil", ["zhCN"] = "泰达希尔" },
- }
-end
-
--- DBC WMA ID=42 MapID=1 'Darkshore'
-if not MTH_DS_Zones[148] then
- MTH_DS_Zones[148] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 148,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Darkshore",
- ["worldBounds"] = { left=2941.67, right=-3608.33, top=8333.33, bottom=3966.67 },
- ["names"] = { ["enUS"] = "Darkshore", ["esES"] = "Costa Oscura", ["ptBR"] = "Costa Negra", ["zhCN"] = "黑海岸" },
- }
-end
-
--- DBC WMA ID=43 MapID=1 'Ashenvale'
-if not MTH_DS_Zones[331] then
- MTH_DS_Zones[331] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 331,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Ashenvale",
- ["worldBounds"] = { left=1700.00, right=-4066.67, top=4672.92, bottom=829.17 },
- ["names"] = { ["enUS"] = "Ashenvale", ["esES"] = "Vallefresno", ["ptBR"] = "Vale das Cinzas", ["zhCN"] = "灰谷" },
- }
-end
-
--- DBC WMA ID=61 MapID=1 'ThousandNeedles'
-if not MTH_DS_Zones[400] then
- MTH_DS_Zones[400] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 400,
- ["dbcMapId"] = 1,
- ["dbcName"] = "ThousandNeedles",
- ["worldBounds"] = { left=-433.33, right=-4833.33, top=-3966.67, bottom=-6900.00 },
- ["names"] = { ["enUS"] = "Thousand Needles", ["esES"] = "Las Mil Agujas", ["ptBR"] = "As Mil Agulhas", ["zhCN"] = "千针石林" },
- }
-end
-
--- DBC WMA ID=81 MapID=1 'StonetalonMountains'
-if not MTH_DS_Zones[406] then
- MTH_DS_Zones[406] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 406,
- ["dbcMapId"] = 1,
- ["dbcName"] = "StonetalonMountains",
- ["worldBounds"] = { left=3245.83, right=-1637.50, top=2916.67, bottom=-339.58 },
- ["names"] = { ["enUS"] = "Stonetalon Mountains", ["esES"] = "Montañas de Colina Roca", ["ptBR"] = "Montanhas Stonetalon", ["zhCN"] = "石爪山脉" },
- }
-end
-
--- DBC WMA ID=101 MapID=1 'Desolace'
-if not MTH_DS_Zones[405] then
- MTH_DS_Zones[405] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 405,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Desolace",
- ["worldBounds"] = { left=4233.33, right=-262.50, top=452.08, bottom=-2545.83 },
- ["names"] = { ["enUS"] = "Desolace", ["esES"] = "Desolace", ["ptBR"] = "Desolação", ["zhCN"] = "凄凉之地" },
- }
-end
-
--- DBC WMA ID=121 MapID=1 'Feralas'
-if not MTH_DS_Zones[357] then
- MTH_DS_Zones[357] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 357,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Feralas",
- ["worldBounds"] = { left=5441.67, right=-1508.33, top=-2366.67, bottom=-7000.00 },
- ["names"] = { ["enUS"] = "Feralas", ["esES"] = "Feralas", ["ptBR"] = "Feralas", ["zhCN"] = "菲拉斯" },
- }
-end
-
--- DBC WMA ID=141 MapID=1 'Dustwallow'
-if not MTH_DS_Zones[15] then
- MTH_DS_Zones[15] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 15,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Dustwallow",
- ["worldBounds"] = { left=-975.00, right=-6225.00, top=-2033.33, bottom=-5533.33 },
- ["names"] = { ["enUS"] = "Dustwallow Marsh", ["esES"] = "Marjal Revolcafango", ["ptBR"] = "Pântano Vadeante", ["zhCN"] = "尘泥沼泽" },
- }
-end
-
--- DBC WMA ID=161 MapID=1 'Tanaris'
-if not MTH_DS_Zones[440] then
- MTH_DS_Zones[440] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 440,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Tanaris",
- ["worldBounds"] = { left=-218.75, right=-7118.75, top=-5875.00, bottom=-10475.00 },
- ["names"] = { ["enUS"] = "Tanaris", ["esES"] = "Tanaris", ["ptBR"] = "Tanaris", ["zhCN"] = "塔纳利斯" },
- }
-end
-
--- DBC WMA ID=181 MapID=1 'Aszhara'
-if not MTH_DS_Zones[16] then
- MTH_DS_Zones[16] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 16,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Aszhara",
- ["worldBounds"] = { left=-3277.08, right=-8347.92, top=5341.67, bottom=1960.42 },
- ["names"] = { ["enUS"] = "Azshara", ["esES"] = "Azshara", ["ptBR"] = "Azshara", ["zhCN"] = "艾萨拉" },
- }
-end
-
--- DBC WMA ID=182 MapID=1 'Felwood'
-if not MTH_DS_Zones[361] then
- MTH_DS_Zones[361] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 361,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Felwood",
- ["worldBounds"] = { left=1641.67, right=-4108.33, top=7133.33, bottom=3300.00 },
- ["names"] = { ["enUS"] = "Felwood", ["esES"] = "Frondavil", ["ptBR"] = "Selva Maleva", ["zhCN"] = "费伍德森林" },
- }
-end
-
--- DBC WMA ID=201 MapID=1 'UngoroCrater'
-if not MTH_DS_Zones[490] then
- MTH_DS_Zones[490] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 490,
- ["dbcMapId"] = 1,
- ["dbcName"] = "UngoroCrater",
- ["worldBounds"] = { left=533.33, right=-3166.67, top=-5966.67, bottom=-8433.33 },
- ["names"] = { ["enUS"] = "Un'Goro Crater", ["esES"] = "Crater Un'Goro", ["ptBR"] = "Cratera Un'Goro", ["zhCN"] = "安戈洛环形山" },
- }
-end
-
--- DBC WMA ID=241 MapID=1 'Moonglade'
-if not MTH_DS_Zones[493] then
- MTH_DS_Zones[493] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 493,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Moonglade",
- ["worldBounds"] = { left=-1381.25, right=-3689.58, top=8491.67, bottom=6952.08 },
- ["names"] = { ["enUS"] = "Moonglade", ["esES"] = "Claro de la Luna", ["ptBR"] = "Moonglade", ["zhCN"] = "月光林地" },
- }
-end
-
--- DBC WMA ID=261 MapID=1 'Silithus'
-if not MTH_DS_Zones[1377] then
- MTH_DS_Zones[1377] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 1377,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Silithus",
- ["worldBounds"] = { left=2537.50, right=-945.83, top=-5958.33, bottom=-8281.25 },
- ["names"] = { ["enUS"] = "Silithus", ["esES"] = "Silithus", ["ptBR"] = "Silithus", ["zhCN"] = "希利苏斯" },
- }
-end
-
--- DBC WMA ID=281 MapID=1 'Winterspring'
-if not MTH_DS_Zones[618] then
- MTH_DS_Zones[618] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 618,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Winterspring",
- ["worldBounds"] = { left=-316.67, right=-7416.67, top=8533.33, bottom=3800.00 },
- ["names"] = { ["enUS"] = "Winterspring", ["esES"] = "Winterspring", ["ptBR"] = "Inverno", ["zhCN"] = "冬泉谷" },
- }
-end
-
--- DBC WMA ID=341 MapID=0 'Ironforge'
-if not MTH_DS_Zones[1537] then
- MTH_DS_Zones[1537] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 1537,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Ironforge",
- ["worldBounds"] = { left=-713.59, right=-1504.22, top=-4569.24, bottom=-5096.85 },
- ["names"] = { ["enUS"] = "Ironforge", ["esES"] = "Forjaz", ["ptBR"] = "Altaforja", ["zhCN"] = "铁炉堡" },
- }
-end
-
--- DBC WMA ID=401 MapID=30 'AlteracValley'
-if not MTH_DS_Zones[2597] then
- MTH_DS_Zones[2597] = {
- ["continent"] = 30,
- ["parent"] = 30,
- ["dbcAreaId"] = 2597,
- ["dbcMapId"] = 30,
- ["dbcName"] = "AlteracValley",
- ["worldBounds"] = { left=1781.25, right=-2456.25, top=1085.42, bottom=-1739.58 },
- ["names"] = { ["enUS"] = "Alterac Valley", ["esES"] = "Valle de Alterac", ["ptBR"] = "Vale Alterac", ["zhCN"] = "奥特兰克山谷" },
- }
-end
-
--- DBC WMA ID=443 MapID=489 'WarsongGulch'
-if not MTH_DS_Zones[3277] then
- MTH_DS_Zones[3277] = {
- ["continent"] = 489,
- ["parent"] = 489,
- ["dbcAreaId"] = 3277,
- ["dbcMapId"] = 489,
- ["dbcName"] = "WarsongGulch",
- ["worldBounds"] = { left=2041.67, right=895.83, top=1627.08, bottom=862.50 },
- ["names"] = { ["enUS"] = "Warsong Gulch", ["esES"] = "Garganta de Warsong", ["ptBR"] = "Garganta Grito de Guerra", ["zhCN"] = "战歌峡谷" },
- }
-end
-
--- DBC WMA ID=461 MapID=529 'ArathiBasin'
-if not MTH_DS_Zones[3358] then
- MTH_DS_Zones[3358] = {
- ["continent"] = 529,
- ["parent"] = 529,
- ["dbcAreaId"] = 3358,
- ["dbcMapId"] = 529,
- ["dbcName"] = "ArathiBasin",
- ["worldBounds"] = { left=1858.33, right=102.08, top=1508.33, bottom=337.50 },
- ["names"] = { ["enUS"] = "Arathi Basin", ["esES"] = "Cuenca de Arathi", ["ptBR"] = "Bacia Arathi", ["zhCN"] = "阿拉希盆地" },
- }
-end
-
--- DBC WMA ID=500 MapID=1 'GMIsland'
-if not MTH_DS_Zones[876] then
- MTH_DS_Zones[876] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 876,
- ["dbcMapId"] = 1,
- ["dbcName"] = "GMIsland",
- ["worldBounds"] = { left=16748.00, right=15920.00, top=16525.00, bottom=15984.00 },
- ["names"] = { ["enUS"] = "GM Island", ["esES"] = "Isla GM", ["ptBR"] = "Ilha GM", ["zhCN"] = "GM岛" },
- }
-end
-
--- DBC WMA ID=501 MapID=1 'Hyjal'
-if not MTH_DS_Zones[616] then
- MTH_DS_Zones[616] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 616,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Hyjal",
- ["worldBounds"] = { left=-1080.00, right=-4286.00, top=6125.00, bottom=3983.00 },
- ["names"] = { ["enUS"] = "Hyjal", ["esES"] = "Hyjal", ["ptBR"] = "Hyjal", ["zhCN"] = "海加尔山" },
- }
-end
-
--- DBC WMA ID=502 MapID=0 'ScarletEnclave'
-if not MTH_DS_Zones[4012] then
- MTH_DS_Zones[4012] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 4012,
- ["dbcMapId"] = 0,
- ["dbcName"] = "ScarletEnclave",
- ["worldBounds"] = { left=-4050.00, right=-7209.00, top=3087.00, bottom=979.00 },
- ["names"] = { ["enUS"] = "Scarlet Enclave", ["esES"] = "Enclave Escarlata", ["ptBR"] = "Enclave Scarlet", ["zhCN"] = "东瘟疫之地:血色领地" },
- }
-end
-
--- DBC WMA ID=503 MapID=27 'Sunnyglade'
-if not MTH_DS_Zones[5023] then
- MTH_DS_Zones[5023] = {
- ["continent"] = 27,
- ["parent"] = 27,
- ["dbcAreaId"] = 5023,
- ["dbcMapId"] = 27,
- ["dbcName"] = "Sunnyglade",
- ["worldBounds"] = { left=719.47, right=-269.16, top=2000.77, bottom=607.60 },
- ["names"] = { ["enUS"] = "Sunnyglade Valley", ["esES"] = "Valle Claro Sol", ["ptBR"] = "Vale Sunnyglade", ["zhCN"] = "阳光林地山谷" },
- }
-end
-
--- DBC WMA ID=504 MapID=0 'Lapidis'
-if not MTH_DS_Zones[409] then
- MTH_DS_Zones[409] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 409,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Lapidis",
- ["worldBounds"] = { left=4933.33, right=2031.88, top=-11043.48, bottom=-12959.42 },
- ["names"] = { ["enUS"] = "Lapidis Isle", ["esES"] = "Isla Lapidis", ["ptBR"] = "Isla Lapidis", ["zhCN"] = "拉匹迪斯之岛" },
- }
-end
-
--- DBC WMA ID=505 MapID=0 'Gillijim'
-if not MTH_DS_Zones[408] then
- MTH_DS_Zones[408] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 408,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Gillijim",
- ["worldBounds"] = { left=4436.22, right=1344.12, top=-12528.71, bottom=-14575.72 },
- ["names"] = { ["enUS"] = "Gillijim's Isle", ["esES"] = "Isla de Gillijim", ["ptBR"] = "Ilha de Gillijim", ["zhCN"] = "吉利吉姆之岛" },
- }
-end
-
--- DBC WMA ID=506 MapID=31 'AlteracValleyClassic'
-if not MTH_DS_Zones[2597] then
- MTH_DS_Zones[2597] = {
- ["continent"] = 31,
- ["parent"] = 31,
- ["dbcAreaId"] = 2597,
- ["dbcMapId"] = 31,
- ["dbcName"] = "AlteracValleyClassic",
- ["worldBounds"] = { left=1781.25, right=-2456.25, top=1085.42, bottom=-1739.58 },
- ["names"] = { ["enUS"] = "Alterac Valley", ["esES"] = "Valle de Alterac", ["ptBR"] = "Vale Alterac", ["zhCN"] = "奥特兰克山谷" },
- }
-end
-
--- DBC WMA ID=507 MapID=1 'TelAbim'
-if not MTH_DS_Zones[5121] then
- MTH_DS_Zones[5121] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 5121,
- ["dbcMapId"] = 1,
- ["dbcName"] = "TelAbim",
- ["worldBounds"] = { left=-5179.00, right=-8406.00, top=-7184.00, bottom=-9371.00 },
- ["names"] = { ["enUS"] = "Tel'Abim", ["esES"] = "Tel'Abim", ["ptBR"] = "Tel'Abim", ["zhCN"] = "泰拉比姆" },
- }
-end
-
--- DBC WMA ID=510 MapID=0 'Gilneas'
-if not MTH_DS_Zones[5179] then
- MTH_DS_Zones[5179] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 5179,
- ["dbcMapId"] = 0,
- ["dbcName"] = "Gilneas",
- ["worldBounds"] = { left=3253.00, right=-413.00, top=-359.00, bottom=-2801.00 },
- ["names"] = { ["enUS"] = "Gilneas", ["esES"] = "Gilneas", ["ptBR"] = "Gilneas", ["zhCN"] = "吉尔尼斯" },
- }
-end
-
--- DBC WMA ID=511 MapID=1 'Icepoint'
-if not MTH_DS_Zones[5024] then
- MTH_DS_Zones[5024] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 5024,
- ["dbcMapId"] = 1,
- ["dbcName"] = "Icepoint",
- ["worldBounds"] = { left=-5595.00, right=-7203.00, top=14405.00, bottom=13330.00 },
- ["names"] = { ["enUS"] = "Icepoint Rock", ["esES"] = "Roca Punto de Hielo", ["ptBR"] = "Roca Punto de Hielo", ["zhCN"] = "冰点岩" },
- }
-end
-
--- DBC WMA ID=512 MapID=1 'BlackstoneIsland'
-if not MTH_DS_Zones[5536] then
- MTH_DS_Zones[5536] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 5536,
- ["dbcMapId"] = 1,
- ["dbcName"] = "BlackstoneIsland",
- ["worldBounds"] = { left=-6274.00, right=-8746.00, top=799.00, bottom=-866.00 },
- ["names"] = { ["enUS"] = "Blackstone Island", ["esES"] = "Isla Piedra Negra", ["ptBR"] = "Ilha Negrito", ["zhCN"] = "黑石岛" },
- }
-end
-
--- DBC WMA ID=513 MapID=0 'ThalassianHighlands'
-if not MTH_DS_Zones[5225] then
- MTH_DS_Zones[5225] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 5225,
- ["dbcMapId"] = 0,
- ["dbcName"] = "ThalassianHighlands",
- ["worldBounds"] = { left=-1005.00, right=-4087.00, top=4952.00, bottom=2891.00 },
- ["names"] = { ["enUS"] = "Thalassian Highlands", ["esES"] = "Tierras Altas Thalassianas", ["ptBR"] = "Terras Altas Thalassianas", ["zhCN"] = "萨拉斯高地" },
- }
-end
-
--- DBC WMA ID=514 MapID=0 'DeadminesEntrance'
-if not MTH_DS_Zones[1581] then
- MTH_DS_Zones[1581] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 1581,
- ["dbcMapId"] = 0,
- ["dbcName"] = "DeadminesEntrance",
- ["worldBounds"] = { left=1793.18, right=1343.29, top=-11054.94, bottom=-11354.86 },
- ["names"] = { ["enUS"] = "The Deadmines", ["esES"] = "Las Minas de la Muerte", ["ptBR"] = "As Minas Mortas", ["zhCN"] = "死亡矿井" },
- }
-end
-
--- DBC WMA ID=516 MapID=1 'WailingCavernsEntrance'
-if not MTH_DS_Zones[718] then
- MTH_DS_Zones[718] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 718,
- ["dbcMapId"] = 1,
- ["dbcName"] = "WailingCavernsEntrance",
- ["worldBounds"] = { left=-1900.87, right=-2473.65, top=-488.64, bottom=-870.49 },
- ["names"] = { ["enUS"] = "Wailing Caverns", ["esES"] = "Cuevas de los Lamentos", ["ptBR"] = "Cavernas Uivantes", ["zhCN"] = "哀嚎洞穴" },
- }
-end
-
--- DBC WMA ID=517 MapID=1 'MaraudonEntrance'
-if not MTH_DS_Zones[2100] then
- MTH_DS_Zones[2100] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 2100,
- ["dbcMapId"] = 1,
- ["dbcName"] = "MaraudonEntrance",
- ["worldBounds"] = { left=3208.00, right=2384.00, top=-1096.00, bottom=-1646.00 },
- ["names"] = { ["enUS"] = "Maraudon", ["esES"] = "Maraudon", ["ptBR"] = "Maraudon", ["zhCN"] = "玛拉顿" },
- }
-end
-
--- DBC WMA ID=518 MapID=0 'GnomereganEntrance'
-if not MTH_DS_Zones[721] then
- MTH_DS_Zones[721] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 721,
- ["dbcMapId"] = 0,
- ["dbcName"] = "GnomereganEntrance",
- ["worldBounds"] = { left=1027.64, right=456.45, top=-4806.32, bottom=-5185.46 },
- ["names"] = { ["enUS"] = "Gnomeregan", ["esES"] = "Gnomeregan", ["ptBR"] = "Gnomeregan", ["zhCN"] = "诺莫瑞根" },
- }
-end
-
--- DBC WMA ID=519 MapID=0 'BlackrockMountain'
-if not MTH_DS_Zones[25] then
- MTH_DS_Zones[25] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 25,
- ["dbcMapId"] = 0,
- ["dbcName"] = "BlackrockMountain",
- ["worldBounds"] = { left=-760.94, right=-1472.50, top=-7327.82, bottom=-7796.50 },
- ["names"] = { ["enUS"] = "Blackrock Mountain", ["esES"] = "Montaña Roca Negra", ["ptBR"] = "Montanha Rocha Negra", ["zhCN"] = "黑石山" },
- }
-end
-
--- DBC WMA ID=520 MapID=0 'ScarletMonasteryEntrance'
-if not MTH_DS_Zones[796] then
- MTH_DS_Zones[796] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 796,
- ["dbcMapId"] = 0,
- ["dbcName"] = "ScarletMonasteryEntrance",
- ["worldBounds"] = { left=-659.96, right=-863.62, top=2947.37, bottom=2812.33 },
- ["names"] = { ["enUS"] = "Scarlet Monastery", ["esES"] = "Monasterio Escarlata", ["ptBR"] = "Monastério Scarlet", ["zhCN"] = "血色修道院" },
- }
-end
-
--- DBC WMA ID=521 MapID=0 'UldamanEntrance'
-if not MTH_DS_Zones[1337] then
- MTH_DS_Zones[1337] = {
- ["continent"] = 0,
- ["parent"] = 0,
- ["dbcAreaId"] = 1337,
- ["dbcMapId"] = 0,
- ["dbcName"] = "UldamanEntrance",
- ["worldBounds"] = { left=-2747.15, right=-3310.46, top=-5953.41, bottom=-6329.51 },
- ["names"] = { ["enUS"] = "Uldaman", ["esES"] = "Uldaman", ["ptBR"] = "Uldaman", ["zhCN"] = "奥达曼" },
- }
-end
-
--- DBC WMA ID=522 MapID=1 'AhnQirajEntrance'
-if not MTH_DS_Zones[3478] then
- MTH_DS_Zones[3478] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 3478,
- ["dbcMapId"] = 1,
- ["dbcName"] = "AhnQirajEntrance",
- ["worldBounds"] = { left=3932.67, right=-206.35, top=-8005.20, bottom=-10951.25 },
- ["names"] = { ["enUS"] = "Gates of Ahn'Qiraj", ["esES"] = "Puertas de Ahn'Qiraj", ["ptBR"] = "Portões de Ahn'Qiraj", ["zhCN"] = "安其拉之门" },
- }
-end
-
--- DBC WMA ID=523 MapID=1 'CavernsOfTime'
-if not MTH_DS_Zones[1941] then
- MTH_DS_Zones[1941] = {
- ["continent"] = 1,
- ["parent"] = 1,
- ["dbcAreaId"] = 1941,
- ["dbcMapId"] = 1,
- ["dbcName"] = "CavernsOfTime",
- ["worldBounds"] = { left=-3695.85, right=-5044.09, top=-8023.09, bottom=-8911.18 },
- ["names"] = { ["enUS"] = "Caverns of Time", ["esES"] = "Cavernas del Tiempo", ["ptBR"] = "Cavernas do Tempo", ["zhCN"] = "时光之穴" },
- }
-end
-
--- DBC WMA ID=601 MapID=813 'WinterVeilVale'
-if not MTH_DS_Zones[5130] then
- MTH_DS_Zones[5130] = {
- ["continent"] = 813,
- ["parent"] = 813,
- ["dbcAreaId"] = 5130,
- ["dbcMapId"] = 813,
- ["dbcName"] = "WinterVeilVale",
- ["worldBounds"] = { left=1916.00, right=484.00, top=-2332.00, bottom=-3309.00 },
- ["names"] = { ["enUS"] = "Winter Veil Vale", ["esES"] = "Valle del Velo Invernal", ["ptBR"] = "Vale do Véu de Inverno", ["zhCN"] = "冬幕谷" },
- }
-end
-
--- DBC WMA ID=603 MapID=389 'Ragefire'
-if not MTH_DS_Zones[2437] then
- MTH_DS_Zones[2437] = {
- ["continent"] = 389,
- ["parent"] = 389,
- ["dbcAreaId"] = 2437,
- ["dbcMapId"] = 389,
- ["dbcName"] = "Ragefire",
- ["worldBounds"] = { left=452.87, right=-285.99, top=39.62, bottom=-452.95 },
- ["names"] = { ["enUS"] = "Ragefire Chasm", ["esES"] = "Sima Ígnea", ["ptBR"] = "Cavernas Ragefire", ["zhCN"] = "怒焰裂谷" },
- }
-end
-
--- DBC WMA ID=605 MapID=209 'ZulFarrak'
-if not MTH_DS_Zones[1176] then
- MTH_DS_Zones[1176] = {
- ["continent"] = 209,
- ["parent"] = 209,
- ["dbcAreaId"] = 1176,
- ["dbcMapId"] = 209,
- ["dbcName"] = "ZulFarrak",
- ["worldBounds"] = { left=1625.00, right=241.67, top=2052.08, bottom=1129.17 },
- ["names"] = { ["enUS"] = "Zul'Farrak", ["esES"] = "Zul'Farrak", ["ptBR"] = "Zul'Farrak", ["zhCN"] = "祖尔法拉克" },
- }
-end
-
--- DBC WMA ID=607 MapID=109 'TheTempleOfAtalHakkar'
-if not MTH_DS_Zones[1477] then
- MTH_DS_Zones[1477] = {
- ["continent"] = 109,
- ["parent"] = 109,
- ["dbcAreaId"] = 1477,
- ["dbcMapId"] = 109,
- ["dbcName"] = "TheTempleOfAtalHakkar",
- ["worldBounds"] = { left=442.76, right=-252.27, top=-255.17, bottom=-718.52 },
- ["names"] = { ["enUS"] = "The Temple of Atal'Hakkar", ["esES"] = "Templo de Atal'Hakkar", ["ptBR"] = "O Templo de Atal'Hakkar", ["zhCN"] = "阿塔哈卡神庙" },
- }
-end
-
--- DBC WMA ID=609 MapID=48 'BlackFathomDeeps'
-if not MTH_DS_Zones[719] then
- MTH_DS_Zones[719] = {
- ["continent"] = 48,
- ["parent"] = 48,
- ["dbcAreaId"] = 719,
- ["dbcMapId"] = 48,
- ["dbcName"] = "BlackFathomDeeps",
- ["worldBounds"] = { left=485.41, right=-736.46, top=-107.91, bottom=-914.33 },
- ["names"] = { ["enUS"] = "Blackfathom Deeps", ["esES"] = "Cavernas de Brazanegra", ["ptBR"] = "Profundezas Negras", ["zhCN"] = "黑暗深渊" },
- }
-end
-
--- DBC WMA ID=611 MapID=34 'TheStockade'
-if not MTH_DS_Zones[717] then
- MTH_DS_Zones[717] = {
- ["continent"] = 34,
- ["parent"] = 34,
- ["dbcAreaId"] = 717,
- ["dbcMapId"] = 34,
- ["dbcName"] = "TheStockade",
- ["worldBounds"] = { left=189.82, right=-188.33, top=220.63, bottom=-31.47 },
- ["names"] = { ["enUS"] = "The Stockade", ["esES"] = "La Mazmorras", ["ptBR"] = "A Prisão", ["zhCN"] = "监狱" },
- }
-end
-
--- DBC WMA ID=613 MapID=90 'Gnomeregan'
-if not MTH_DS_Zones[721] then
- MTH_DS_Zones[721] = {
- ["continent"] = 90,
- ["parent"] = 90,
- ["dbcAreaId"] = 721,
- ["dbcMapId"] = 90,
- ["dbcName"] = "Gnomeregan",
- ["worldBounds"] = { left=491.90, right=-277.77, top=-180.89, bottom=-694.00 },
- ["names"] = { ["enUS"] = "Gnomeregan", ["esES"] = "Gnomeregan", ["ptBR"] = "Gnomeregan", ["zhCN"] = "诺莫瑞根" },
- }
-end
-
--- DBC WMA ID=615 MapID=70 'Uldaman'
-if not MTH_DS_Zones[1337] then
- MTH_DS_Zones[1337] = {
- ["continent"] = 70,
- ["parent"] = 70,
- ["dbcAreaId"] = 1337,
- ["dbcMapId"] = 70,
- ["dbcName"] = "Uldaman",
- ["worldBounds"] = { left=645.12, right=-248.55, top=204.31, bottom=-391.47 },
- ["names"] = { ["enUS"] = "Uldaman", ["esES"] = "Uldaman", ["ptBR"] = "Uldaman", ["zhCN"] = "奥达曼" },
- }
-end
-
--- DBC WMA ID=617 MapID=409 'MoltenCore'
-if not MTH_DS_Zones[2717] then
- MTH_DS_Zones[2717] = {
- ["continent"] = 409,
- ["parent"] = 409,
- ["dbcAreaId"] = 2717,
- ["dbcMapId"] = 409,
- ["dbcName"] = "MoltenCore",
- ["worldBounds"] = { left=-130.76, right=-1395.56, top=1303.06, bottom=459.86 },
- ["names"] = { ["enUS"] = "Molten Core", ["esES"] = "Núcleo de Magma", ["ptBR"] = "Núcleo Derretido", ["zhCN"] = "熔火之心" },
- }
-end
-
--- DBC WMA ID=619 MapID=309 'ZulGurub'
-if not MTH_DS_Zones[1977] then
- MTH_DS_Zones[1977] = {
- ["continent"] = 309,
- ["parent"] = 309,
- ["dbcAreaId"] = 1977,
- ["dbcMapId"] = 309,
- ["dbcName"] = "ZulGurub",
- ["worldBounds"] = { left=-612.50, right=-2733.33, top=-11225.00, bottom=-12639.58 },
- ["names"] = { ["enUS"] = "Zul'Gurub", ["esES"] = "Zul'Gurub", ["ptBR"] = "Zul'Gurub", ["zhCN"] = "祖尔格拉布" },
- }
-end
-
--- DBC WMA ID=621 MapID=429 'DireMaul'
-if not MTH_DS_Zones[2557] then
- MTH_DS_Zones[2557] = {
- ["continent"] = 429,
- ["parent"] = 429,
- ["dbcAreaId"] = 2557,
- ["dbcMapId"] = 429,
- ["dbcName"] = "DireMaul",
- ["worldBounds"] = { left=887.50, right=-387.50, top=1050.00, bottom=200.00 },
- ["names"] = { ["enUS"] = "Dire Maul", ["esES"] = "La Masacre", ["ptBR"] = "Gládio Cruel", ["zhCN"] = "厄运之槌" },
- }
-end
-
--- DBC WMA ID=623 MapID=230 'BlackrockDepths'
-if not MTH_DS_Zones[1584] then
- MTH_DS_Zones[1584] = {
- ["continent"] = 230,
- ["parent"] = 230,
- ["dbcAreaId"] = 1584,
- ["dbcMapId"] = 230,
- ["dbcName"] = "BlackrockDepths",
- ["worldBounds"] = { left=522.34, right=-884.72, top=1186.68, bottom=248.64 },
- ["names"] = { ["enUS"] = "Blackrock Depths", ["esES"] = "Profundidades de Roca Negra", ["ptBR"] = "Abismo Rocha Negra", ["zhCN"] = "黑石深渊" },
- }
-end
-
--- DBC WMA ID=625 MapID=509 'RuinsofAhnQiraj'
-if not MTH_DS_Zones[3429] then
- MTH_DS_Zones[3429] = {
- ["continent"] = 509,
- ["parent"] = 509,
- ["dbcAreaId"] = 3429,
- ["dbcMapId"] = 509,
- ["dbcName"] = "RuinsofAhnQiraj",
- ["worldBounds"] = { left=3035.42, right=522.92, top=-8233.33, bottom=-9908.33 },
- ["names"] = { ["enUS"] = "Ruins of Ahn'Qiraj", ["esES"] = "Ruinas de Ahn'Qiraj", ["ptBR"] = "Ruínas de Ahn'Qiraj", ["zhCN"] = "安其拉废墟" },
- }
-end
-
--- DBC WMA ID=627 MapID=249 'OnyxiasLair'
-if not MTH_DS_Zones[2159] then
- MTH_DS_Zones[2159] = {
- ["continent"] = 249,
- ["parent"] = 249,
- ["dbcAreaId"] = 2159,
- ["dbcMapId"] = 249,
- ["dbcName"] = "OnyxiasLair",
- ["worldBounds"] = { left=111.38, right=-371.73, top=98.25, bottom=-223.83 },
- ["names"] = { ["enUS"] = "Onyxia's Lair", ["esES"] = "Guarida de Onyxia", ["ptBR"] = "Caverna de Onyxia", ["zhCN"] = "奥妮克希亚的巢穴" },
- }
-end
-
--- DBC WMA ID=629 MapID=229 'BlackrockSpire'
-if not MTH_DS_Zones[1583] then
- MTH_DS_Zones[1583] = {
- ["continent"] = 229,
- ["parent"] = 229,
- ["dbcAreaId"] = 1583,
- ["dbcMapId"] = 229,
- ["dbcName"] = "BlackrockSpire",
- ["worldBounds"] = { left=10.59, right=-876.25, top=304.40, bottom=-286.83 },
- ["names"] = { ["enUS"] = "Blackrock Spire", ["esES"] = "Cumbre de Roca Negra", ["ptBR"] = "Pico Rocha Negra", ["zhCN"] = "黑石塔" },
- }
-end
-
--- DBC WMA ID=631 MapID=43 'WailingCaverns'
-if not MTH_DS_Zones[718] then
- MTH_DS_Zones[718] = {
- ["continent"] = 43,
- ["parent"] = 43,
- ["dbcAreaId"] = 718,
- ["dbcMapId"] = 43,
- ["dbcName"] = "WailingCaverns",
- ["worldBounds"] = { left=790.00, right=-380.00, top=215.00, bottom=-570.00 },
- ["names"] = { ["enUS"] = "Wailing Caverns", ["esES"] = "Cuevas de los Lamentos", ["ptBR"] = "Cavernas Uivantes", ["zhCN"] = "哀嚎洞穴" },
- }
-end
-
--- DBC WMA ID=633 MapID=349 'Maraudon'
-if not MTH_DS_Zones[2100] then
- MTH_DS_Zones[2100] = {
- ["continent"] = 349,
- ["parent"] = 349,
- ["dbcAreaId"] = 2100,
- ["dbcMapId"] = 349,
- ["dbcName"] = "Maraudon",
- ["worldBounds"] = { left=485.41, right=-1626.68, top=1199.76, bottom=-211.13 },
- ["names"] = { ["enUS"] = "Maraudon", ["esES"] = "Maraudon", ["ptBR"] = "Maraudon", ["zhCN"] = "玛拉顿" },
- }
-end
-
--- DBC WMA ID=635 MapID=469 'BlackwingLair'
-if not MTH_DS_Zones[2677] then
- MTH_DS_Zones[2677] = {
- ["continent"] = 469,
- ["parent"] = 469,
- ["dbcAreaId"] = 2677,
- ["dbcMapId"] = 469,
- ["dbcName"] = "BlackwingLair",
- ["worldBounds"] = { left=-844.62, right=-1344.05, top=-7394.12, bottom=-7727.07 },
- ["names"] = { ["enUS"] = "Blackwing Lair", ["esES"] = "Guarida Alanegra", ["ptBR"] = "Covil Asa Negra", ["zhCN"] = "黑翼之巢" },
- }
-end
-
--- DBC WMA ID=637 MapID=36 'TheDeadmines'
-if not MTH_DS_Zones[5138] then
- MTH_DS_Zones[5138] = {
- ["continent"] = 36,
- ["parent"] = 36,
- ["dbcAreaId"] = 5138,
- ["dbcMapId"] = 36,
- ["dbcName"] = "TheDeadmines",
- ["worldBounds"] = { left=-322.94, right=-979.53, top=36.93, bottom=-398.04 },
- ["names"] = { ["enUS"] = "The Deadmines", ["esES"] = "Las Minas de la Muerte", ["ptBR"] = "As Minas Mortas", ["zhCN"] = "死亡矿井" },
- }
-end
-
--- DBC WMA ID=639 MapID=129 'RazorfenDowns'
-if not MTH_DS_Zones[722] then
- MTH_DS_Zones[722] = {
- ["continent"] = 129,
- ["parent"] = 129,
- ["dbcAreaId"] = 722,
- ["dbcMapId"] = 129,
- ["dbcName"] = "RazorfenDowns",
- ["worldBounds"] = { left=1279.94, right=570.89, top=2682.55, bottom=2209.85 },
- ["names"] = { ["enUS"] = "Razorfen Downs", ["esES"] = "Tierras Bajas de Rajacieno", ["ptBR"] = "Razorfen Downs", ["zhCN"] = "剃刀高地" },
- }
-end
-
--- DBC WMA ID=641 MapID=47 'RazorfenKraul'
-if not MTH_DS_Zones[491] then
- MTH_DS_Zones[491] = {
- ["continent"] = 47,
- ["parent"] = 47,
- ["dbcAreaId"] = 491,
- ["dbcMapId"] = 47,
- ["dbcName"] = "RazorfenKraul",
- ["worldBounds"] = { left=2058.92, right=1322.47, top=2349.64, bottom=1858.68 },
- ["names"] = { ["enUS"] = "Razorfen Kraul", ["esES"] = "Madriguera de Rajacieno", ["ptBR"] = "Razorfen Kraul", ["zhCN"] = "剃刀沼泽" },
- }
-end
-
--- DBC WMA ID=643 MapID=189 'ScarletMonastery'
-if not MTH_DS_Zones[5136] then
- MTH_DS_Zones[5136] = {
- ["continent"] = 189,
- ["parent"] = 189,
- ["dbcAreaId"] = 5136,
- ["dbcMapId"] = 189,
- ["dbcName"] = "ScarletMonastery",
- ["worldBounds"] = { left=1567.97, right=947.99, top=2030.18, bottom=1616.86 },
- ["names"] = { ["enUS"] = "Scarlet Monastery Graveyard", ["esES"] = "Cementerio Monasterio Escarlata", ["ptBR"] = "Cemitério do Monastério Escarlate", ["zhCN"] = "血色修道院-墓地" },
- }
-end
-
--- DBC WMA ID=644 MapID=189 'ScarletMonastery2f'
-if not MTH_DS_Zones[5135] then
- MTH_DS_Zones[5135] = {
- ["continent"] = 189,
- ["parent"] = 189,
- ["dbcAreaId"] = 5135,
- ["dbcMapId"] = 189,
- ["dbcName"] = "ScarletMonastery2f",
- ["worldBounds"] = { left=-162.27, right=-482.46, top=307.37, bottom=93.91 },
- ["names"] = { ["enUS"] = "Scarlet Monastery Library", ["esES"] = "Biblioteca Monasterio Escarlata", ["ptBR"] = "Biblioteca do Monastério Escarlate", ["zhCN"] = "血色修道院-图书馆" },
- }
-end
-
--- DBC WMA ID=645 MapID=189 'ScarletMonastery3f'
-if not MTH_DS_Zones[5153] then
- MTH_DS_Zones[5153] = {
- ["continent"] = 189,
- ["parent"] = 189,
- ["dbcAreaId"] = 5153,
- ["dbcMapId"] = 189,
- ["dbcName"] = "ScarletMonastery3f",
- ["worldBounds"] = { left=50.27, right=-562.42, top=2009.10, bottom=1600.64 },
- ["names"] = { ["enUS"] = "Scarlet Monastery Armory", ["esES"] = "Armería Monasterio Escarlata", ["ptBR"] = "Arsenal do Monastério Escarlate", ["zhCN"] = "血色修道院-军械库" },
- }
-end
-
--- DBC WMA ID=646 MapID=189 'ScarletMonastery4f'
-if not MTH_DS_Zones[5163] then
- MTH_DS_Zones[5163] = {
- ["continent"] = 189,
- ["parent"] = 189,
- ["dbcAreaId"] = 5163,
- ["dbcMapId"] = 189,
- ["dbcName"] = "ScarletMonastery4f",
- ["worldBounds"] = { left=1743.99, right=1040.69, top=1281.29, bottom=812.42 },
- ["names"] = { ["enUS"] = "Scarlet Monastery Cathedral", ["esES"] = "Catedral Monasterio Escarlata", ["ptBR"] = "Catedral do Monastério Escarlate", ["zhCN"] = "血色修道院-大教堂" },
- }
-end
-
--- DBC WMA ID=648 MapID=289 'Scholomance'
-if not MTH_DS_Zones[2057] then
- MTH_DS_Zones[2057] = {
- ["continent"] = 289,
- ["parent"] = 289,
- ["dbcAreaId"] = 2057,
- ["dbcMapId"] = 289,
- ["dbcName"] = "Scholomance",
- ["worldBounds"] = { left=251.41, right=-68.64, top=317.70, bottom=104.33 },
- ["names"] = { ["enUS"] = "Scholomance", ["esES"] = "Scholomance", ["ptBR"] = "Scholomance", ["zhCN"] = "通灵学院" },
- }
-end
-
--- DBC WMA ID=650 MapID=33 'ShadowfangKeep'
-if not MTH_DS_Zones[209] then
- MTH_DS_Zones[209] = {
- ["continent"] = 33,
- ["parent"] = 33,
- ["dbcAreaId"] = 209,
- ["dbcMapId"] = 33,
- ["dbcName"] = "ShadowfangKeep",
- ["worldBounds"] = { left=2436.00, right=2055.00, top=-61.00, bottom=-315.00 },
- ["names"] = { ["enUS"] = "Shadowfang Keep", ["esES"] = "Fortaleza Colmillo Oscuro", ["ptBR"] = "Fortaleza Shadowfang", ["zhCN"] = "影牙城堡" },
- }
-end
-
--- DBC WMA ID=652 MapID=329 'Stratholme'
-if not MTH_DS_Zones[2017] then
- MTH_DS_Zones[2017] = {
- ["continent"] = 329,
- ["parent"] = 329,
- ["dbcAreaId"] = 2017,
- ["dbcMapId"] = 329,
- ["dbcName"] = "Stratholme",
- ["worldBounds"] = { left=-2639.83, right=-3825.18, top=4163.90, bottom=3374.04 },
- ["names"] = { ["enUS"] = "Stratholme", ["esES"] = "Stratholme", ["ptBR"] = "Stratholme", ["zhCN"] = "斯坦索姆" },
- }
-end
-
--- DBC WMA ID=654 MapID=531 'AhnQiraj'
-if not MTH_DS_Zones[3428] then
- MTH_DS_Zones[3428] = {
- ["continent"] = 531,
- ["parent"] = 531,
- ["dbcAreaId"] = 3428,
- ["dbcMapId"] = 531,
- ["dbcName"] = "AhnQiraj",
- ["worldBounds"] = { left=2515.63, right=1538.07, top=-8051.80, bottom=-8703.50 },
- ["names"] = { ["enUS"] = "Ahn'Qiraj", ["esES"] = "Ahn'Qiraj", ["ptBR"] = "Ahn'Qiraj", ["zhCN"] = "安其拉" },
- }
-end
-
--- DBC WMA ID=655 MapID=531 'AhnQiraj2f'
-if not MTH_DS_Zones[5147] then
- MTH_DS_Zones[5147] = {
- ["continent"] = 531,
- ["parent"] = 531,
- ["dbcAreaId"] = 5147,
- ["dbcMapId"] = 531,
- ["dbcName"] = "AhnQiraj2f",
- ["worldBounds"] = { left=2915.62, right=138.08, top=-7659.29, bottom=-9510.98 },
- ["names"] = { ["enUS"] = "Ahn'Qiraj", ["esES"] = "Ahn'Qiraj", ["ptBR"] = "Ahn'Qiraj", ["zhCN"] = "安其拉" },
- }
-end
-
--- DBC WMA ID=657 MapID=532 'Karazhan'
-if not MTH_DS_Zones[3457] then
- MTH_DS_Zones[3457] = {
- ["continent"] = 532,
- ["parent"] = 532,
- ["dbcAreaId"] = 3457,
- ["dbcMapId"] = 532,
- ["dbcName"] = "Karazhan",
- ["worldBounds"] = { left=-1614.00, right=-2212.00, top=-10780.00, bottom=-11179.00 },
- ["names"] = { ["enUS"] = "Tower of Karazhan", ["esES"] = "Torre de Karazhan", ["ptBR"] = "Karazhan", ["zhCN"] = "卡拉赞" },
- }
-end
-
--- DBC WMA ID=659 MapID=369 'DeeprunTram'
-if not MTH_DS_Zones[2257] then
- MTH_DS_Zones[2257] = {
- ["continent"] = 369,
- ["parent"] = 369,
- ["dbcAreaId"] = 2257,
- ["dbcMapId"] = 369,
- ["dbcName"] = "DeeprunTram",
- ["worldBounds"] = { left=2623.50, right=2311.50, top=95.50, bottom=-112.50 },
- ["names"] = { ["enUS"] = "Deeprun Tram", ["esES"] = "Tranvía Subterráneo", ["ptBR"] = "Metrô Correfundo", ["zhCN"] = "矿道地铁" },
- }
-end
-
--- DBC WMA ID=660 MapID=369 'DeeprunTram2f'
-if not MTH_DS_Zones[5144] then
- MTH_DS_Zones[5144] = {
- ["continent"] = 369,
- ["parent"] = 369,
- ["dbcAreaId"] = 5144,
- ["dbcMapId"] = 369,
- ["dbcName"] = "DeeprunTram2f",
- ["worldBounds"] = { left=187.50, right=-121.50, top=95.50, bottom=-112.50 },
- ["names"] = { ["enUS"] = "Deeprun Tram", ["esES"] = "Tranvía Profundocircuito", ["ptBR"] = "Metrô Correfundo", ["zhCN"] = "矿道地铁" },
- }
-end
-
--- DBC WMA ID=662 MapID=269 'BlackMorass'
-if not MTH_DS_Zones[5204] then
- MTH_DS_Zones[5204] = {
- ["continent"] = 269,
- ["parent"] = 269,
- ["dbcAreaId"] = 5204,
- ["dbcMapId"] = 269,
- ["dbcName"] = "BlackMorass",
- ["worldBounds"] = { left=7338.47, right=6066.48, top=-1247.55, bottom=-2093.03 },
- ["names"] = { ["enUS"] = "The Black Morass", ["esES"] = "El Morass Negro", ["ptBR"] = "Lamaçal Negro", ["zhCN"] = "黑色沼泽" },
- }
-end
-
--- DBC WMA ID=663 MapID=269 'BlackMorass2f'
-if not MTH_DS_Zones[2366] then
- MTH_DS_Zones[2366] = {
- ["continent"] = 269,
- ["parent"] = 269,
- ["dbcAreaId"] = 2366,
- ["dbcMapId"] = 269,
- ["dbcName"] = "BlackMorass2f",
- ["worldBounds"] = { left=7651.85, right=6565.99, top=-1500.47, bottom=-2227.08 },
- ["names"] = { ["enUS"] = "The Black Morass", ["esES"] = "La Ciénaga Negra", ["ptBR"] = "O Morass Negro", ["zhCN"] = "黑色沼泽" },
- }
-end
-
--- DBC WMA ID=665 MapID=815 'GilneasCity'
-if not MTH_DS_Zones[5208] then
- MTH_DS_Zones[5208] = {
- ["continent"] = 815,
- ["parent"] = 815,
- ["dbcAreaId"] = 5208,
- ["dbcMapId"] = 815,
- ["dbcName"] = "GilneasCity",
- ["worldBounds"] = { left=3290.32, right=2040.14, top=-760.72, bottom=-1598.16 },
- ["names"] = { ["enUS"] = "Gilneas City", ["esES"] = "Ciudad Gilneas", ["ptBR"] = "Cidade de Gilneas", ["zhCN"] = "吉尔尼斯城" },
- }
-end
-
--- DBC WMA ID=667 MapID=533 'Naxxramas'
-if not MTH_DS_Zones[3456] then
- MTH_DS_Zones[3456] = {
- ["continent"] = 533,
- ["parent"] = 533,
- ["dbcAreaId"] = 3456,
- ["dbcMapId"] = 533,
- ["dbcName"] = "Naxxramas",
- ["worldBounds"] = { left=-2375.27, right=-4366.96, top=3656.70, bottom=2338.28 },
- ["names"] = { ["enUS"] = "Naxxramas", ["esES"] = "Naxxramas", ["ptBR"] = "Naxxramas", ["zhCN"] = "纳克萨玛斯" },
- }
-end
-
--- DBC WMA ID=668 MapID=533 'Naxxramas2f'
-if not MTH_DS_Zones[5148] then
- MTH_DS_Zones[5148] = {
- ["continent"] = 533,
- ["parent"] = 533,
- ["dbcAreaId"] = 5148,
- ["dbcMapId"] = 533,
- ["dbcName"] = "Naxxramas2f",
- ["worldBounds"] = { left=-4869.36, right=-5521.46, top=3816.56, bottom=3376.89 },
- ["names"] = { ["enUS"] = "The Upper Necropolis", ["esES"] = "La Necropolis Superior", ["ptBR"] = "Necrópole Superior", ["zhCN"] = "上层大墓地" },
- }
-end
-
--- DBC WMA ID=670 MapID=802 'CrescentGrove'
-if not MTH_DS_Zones[5077] then
- MTH_DS_Zones[5077] = {
- ["continent"] = 802,
- ["parent"] = 802,
- ["dbcAreaId"] = 5077,
- ["dbcMapId"] = 802,
- ["dbcName"] = "CrescentGrove",
- ["worldBounds"] = { left=1207.72, right=-1435.49, top=882.98, bottom=-868.18 },
- ["names"] = { ["enUS"] = "Crescent Grove", ["esES"] = "Claro de la Media Luna", ["ptBR"] = "Bosque Crescente", ["zhCN"] = "新月林地" },
- }
-end
-
--- DBC WMA ID=672 MapID=808 'HateforgeQuarry'
-if not MTH_DS_Zones[5103] then
- MTH_DS_Zones[5103] = {
- ["continent"] = 808,
- ["parent"] = 808,
- ["dbcAreaId"] = 5103,
- ["dbcMapId"] = 808,
- ["dbcName"] = "HateforgeQuarry",
- ["worldBounds"] = { left=-3024.33, right=-3776.45, top=-7888.28, bottom=-8398.61 },
- ["names"] = { ["enUS"] = "Hateforge Quarry", ["esES"] = "Cantera Hateforge", ["ptBR"] = "Pedreira Forja do Ódio", ["zhCN"] = "仇恨熔炉采石场" },
- }
-end
-
--- DBC WMA ID=674 MapID=800 'KarazhanCrypt'
-if not MTH_DS_Zones[5086] then
- MTH_DS_Zones[5086] = {
- ["continent"] = 800,
- ["parent"] = 800,
- ["dbcAreaId"] = 5086,
- ["dbcMapId"] = 800,
- ["dbcName"] = "KarazhanCrypt",
- ["worldBounds"] = { left=-1302.51, right=-1849.26, top=-10916.17, bottom=-11308.14 },
- ["names"] = { ["enUS"] = "Karazhan Crypt", ["esES"] = "Cripta de Karazhan", ["ptBR"] = "Cripta Karazhan", ["zhCN"] = "卡拉赞墓穴" },
- }
-end
-
--- DBC WMA ID=676 MapID=35 'StormwindVault'
-if not MTH_DS_Zones[5087] then
- MTH_DS_Zones[5087] = {
- ["continent"] = 35,
- ["parent"] = 35,
- ["dbcAreaId"] = 5087,
- ["dbcMapId"] = 35,
- ["dbcName"] = "StormwindVault",
- ["worldBounds"] = { left=314.34, right=-40.16, top=89.30, bottom=-145.44 },
- ["names"] = { ["enUS"] = "Stormwind Vault", ["esES"] = "Bóveda de Ventormenta", ["ptBR"] = "Cofre Stormwind", ["zhCN"] = "暴风城地牢" },
- }
-end
-
--- DBC WMA ID=678 MapID=807 'EmeraldSanctum'
-if not MTH_DS_Zones[5097] then
- MTH_DS_Zones[5097] = {
- ["continent"] = 807,
- ["parent"] = 807,
- ["dbcAreaId"] = 5097,
- ["dbcMapId"] = 807,
- ["dbcName"] = "EmeraldSanctum",
- ["worldBounds"] = { left=3939.10, right=2666.00, top=3519.72, bottom=2666.00 },
- ["names"] = { ["enUS"] = "Emerald Sanctum", ["esES"] = "Sanctum Esmeralda", ["ptBR"] = "Santuário Esmeralda", ["zhCN"] = "翡翠圣殿" },
- }
-end
-
--- DBC WMA ID=680 MapID=801 'Moomoo'
-if not MTH_DS_Zones[5053] then
- MTH_DS_Zones[5053] = {
- ["continent"] = 801,
- ["parent"] = 801,
- ["dbcAreaId"] = 5053,
- ["dbcMapId"] = 801,
- ["dbcName"] = "Moomoo",
- ["worldBounds"] = { left=17296.30, right=16288.62, top=17158.52, bottom=16486.73 },
- ["names"] = { ["enUS"] = "Moomoo Grove", ["esES"] = "Claro Moomoo", ["ptBR"] = "Bosque Moomoo", ["zhCN"] = "奶牛树林" },
- }
-end
-
--- DBC WMA ID=682 MapID=814 'UpperKarazhan'
-if not MTH_DS_Zones[3457] then
- MTH_DS_Zones[3457] = {
- ["continent"] = 814,
- ["parent"] = 814,
- ["dbcAreaId"] = 3457,
- ["dbcMapId"] = 814,
- ["dbcName"] = "UpperKarazhan",
- ["worldBounds"] = { left=-1378.00, right=-2201.00, top=-10887.00, bottom=-11436.00 },
- ["names"] = { ["enUS"] = "Tower of Karazhan", ["esES"] = "Torre de Karazhan", ["ptBR"] = "Karazhan", ["zhCN"] = "卡拉赞" },
- }
-end
-
--- DBC WMA ID=683 MapID=814 'UpperKarazhan2f'
-if not MTH_DS_Zones[5557] then
- MTH_DS_Zones[5557] = {
- ["continent"] = 814,
- ["parent"] = 814,
- ["dbcAreaId"] = 5557,
- ["dbcMapId"] = 814,
- ["dbcName"] = "UpperKarazhan2f",
- ["worldBounds"] = { left=-1483.00, right=-4283.00, top=-5800.00, bottom=-7668.00 },
- ["names"] = { ["enUS"] = "???", ["esES"] = "???", ["ptBR"] = "???", ["zhCN"] = "外域" },
- }
-end
diff --git a/init/api.xml b/init/api.xml
index 9bc6d81..a73951e 100644
--- a/init/api.xml
+++ b/init/api.xml
@@ -43,7 +43,6 @@
-
diff --git a/modules/smartpet/engine.lua b/modules/smartpet/engine.lua
deleted file mode 100644
index 5517f6d..0000000
--- a/modules/smartpet/engine.lua
+++ /dev/null
@@ -1,1293 +0,0 @@
-------------------------------------------------------
--- MetaHunt: SmartPet — Pet Combat Management
--- TWoW 1.18.1
-------------------------------------------------------
---
--- Integrated pet combat manager inspired by SmartPet v2.5.1.
--- Handles taunt management, focus budgeting, CC-break
--- prevention, PVP auto-detaunt, The Button keybind,
--- NoChase, Dash/Dive on attack, auto-cower, and
--- low-health chat warnings.
-------------------------------------------------------
-
--- ── Localised ability names ──────────────────────────────────
--- Resolved from MTH_LocaleData.spells via MTH:LocalizeSpell().
--- PET_ACTION_FOLLOW / PET_ACTION_ATTACK are built-in WoW tokens,
--- not localised spell names.
-local L_GROWL, L_COWER, L_CLAW, L_BITE, L_DASH, L_DIVE
-local L_LIGHTNING_BREATH, L_SCREECH, L_SCORPID_POISON
-local L_CHARGE, L_DEATH_ROLL, L_FURIOUS_HOWL, L_SAVAGE_REND
-local L_THUNDERSTOMP, L_POISON_SPIT, L_POLLEN_BURST
-local L_PROWL, L_SHELL_SHIELD, L_GRACE, L_BUBBLE_BARRIER
-local L_WEB, L_ROAR_OF_FORTITUDE, L_PACKLEADER, L_STRIDER_PRESENCE
-local L_FOLLOW = "PET_ACTION_FOLLOW"
-local L_ATTACK = "PET_ACTION_ATTACK"
-local L_FLEE = "attempts to run away in fear"
-
--- ── CC debuff token list (English keys into spell locale) ────
-local CC_DEBUFF_TOKENS = {
- "Gouge", "Sap", "Charm", "Seduction", "Sheep", "Polymorph",
- "Tame Beast", "Scare Beast", "Sleep", "Hibernate", "Fear",
- "Mind Control", "Blind", "Scatter Shot", "Enslave Demon",
- "Shackle Undead", "Reckless Charge", "Freezing Trap Effect",
- "Intimidating Shout", "Repentance", "Wyvern Sting",
-}
-
--- Populated once MTH:LocalizeSpell is available (VARIABLES_LOADED)
-local CC_DEBUFF_SET = {}
-
--- Maps localised ability names to rt.slot keys.
-local ABILITY_MAP = {}
-local function BuildAbilityMap()
- ABILITY_MAP = {
- [L_GROWL] = "growl",
- [L_COWER] = "cower",
- [L_CLAW] = "claw",
- [L_BITE] = "bite",
- [L_FOLLOW] = "follow",
- [L_ATTACK] = "attack",
- [L_DASH] = "dash",
- [L_DIVE] = "dive",
- }
- -- Conditionally add family-specific abilities (nil-safe: L_ vars may be nil before locale resolution)
- if L_LIGHTNING_BREATH then ABILITY_MAP[L_LIGHTNING_BREATH] = "lightningBreath" end
- if L_SCREECH then ABILITY_MAP[L_SCREECH] = "screech" end
- if L_SCORPID_POISON then ABILITY_MAP[L_SCORPID_POISON] = "scorpidPoison" end
- if L_CHARGE then ABILITY_MAP[L_CHARGE] = "charge" end
- if L_DEATH_ROLL then ABILITY_MAP[L_DEATH_ROLL] = "deathRoll" end
- if L_FURIOUS_HOWL then ABILITY_MAP[L_FURIOUS_HOWL] = "furiousHowl" end
- if L_SAVAGE_REND then ABILITY_MAP[L_SAVAGE_REND] = "savageRend" end
- if L_THUNDERSTOMP then ABILITY_MAP[L_THUNDERSTOMP] = "thunderstomp" end
- if L_POISON_SPIT then ABILITY_MAP[L_POISON_SPIT] = "poisonSpit" end
- if L_POLLEN_BURST then ABILITY_MAP[L_POLLEN_BURST] = "pollenBurst" end
- if L_PROWL then ABILITY_MAP[L_PROWL] = "prowl" end
- if L_SHELL_SHIELD then ABILITY_MAP[L_SHELL_SHIELD] = "shellShield" end
- if L_GRACE then ABILITY_MAP[L_GRACE] = "grace" end
- if L_BUBBLE_BARRIER then ABILITY_MAP[L_BUBBLE_BARRIER] = "bubbleBarrier" end
- if L_WEB then ABILITY_MAP[L_WEB] = "web" end
- if L_ROAR_OF_FORTITUDE then ABILITY_MAP[L_ROAR_OF_FORTITUDE] = "roarOfFortitude" end
- if L_PACKLEADER then ABILITY_MAP[L_PACKLEADER] = "packleader" end
- if L_STRIDER_PRESENCE then ABILITY_MAP[L_STRIDER_PRESENCE] = "striderPresence" end
-end
-
--- Called at VARIABLES_LOADED when the locale system is ready
-local function ResolveLocaleStrings()
- if not (MTH and MTH.LocalizeSpell) then return end
-
- L_GROWL = MTH:LocalizeSpell("Growl")
- L_COWER = MTH:LocalizeSpell("Cower")
- L_CLAW = MTH:LocalizeSpell("Claw")
- L_BITE = MTH:LocalizeSpell("Bite")
- L_DASH = MTH:LocalizeSpell("Dash")
- L_DIVE = MTH:LocalizeSpell("Dive")
-
- -- Family-specific abilities
- L_LIGHTNING_BREATH = MTH:LocalizeSpell("Lightning Breath")
- L_SCREECH = MTH:LocalizeSpell("Screech")
- L_SCORPID_POISON = MTH:LocalizeSpell("Scorpid Poison")
- L_CHARGE = MTH:LocalizeSpell("Charge")
- L_DEATH_ROLL = MTH:LocalizeSpell("Death Roll")
- L_FURIOUS_HOWL = MTH:LocalizeSpell("Furious Howl")
- L_SAVAGE_REND = MTH:LocalizeSpell("Savage Rend")
- L_THUNDERSTOMP = MTH:LocalizeSpell("Thunderstomp")
- L_POISON_SPIT = MTH:LocalizeSpell("Poison Spit")
- L_POLLEN_BURST = MTH:LocalizeSpell("Pollen Burst")
- L_PROWL = MTH:LocalizeSpell("Prowl")
- L_SHELL_SHIELD = MTH:LocalizeSpell("Shell Shield")
- L_GRACE = MTH:LocalizeSpell("Grace")
- L_BUBBLE_BARRIER = MTH:LocalizeSpell("Bubble Barrier")
- L_WEB = MTH:LocalizeSpell("Web")
- L_ROAR_OF_FORTITUDE = MTH:LocalizeSpell("Roar of Fortitude")
- L_PACKLEADER = MTH:LocalizeSpell("Packleader")
- L_STRIDER_PRESENCE = MTH:LocalizeSpell("Strider Presence")
-
- -- Flee pattern from spell locale (special key)
- local spellLocales = MTH_LocaleData and MTH_LocaleData.spells
- if spellLocales then
- local locale = MTH.currentLocale or "enUS"
- local aliases = { enGB = "enUS", esMX = "esES", zhTW = "zhCN" }
- if aliases[locale] then locale = aliases[locale] end
- local map = spellLocales[locale] or spellLocales.enUS
- if map and map["SP_FLEE_PATTERN"] then
- L_FLEE = map["SP_FLEE_PATTERN"]
- end
- end
-
- -- Rebuild CC debuff set from localised names
- CC_DEBUFF_SET = {}
- for _, token in ipairs(CC_DEBUFF_TOKENS) do
- local localized = MTH:LocalizeSpell(token)
- CC_DEBUFF_SET[localized] = true
- end
-
- -- Rebuild ability map with resolved names
- BuildAbilityMap()
-end
-
--- ── Constants ────────────────────────────────────────────────
-local FOCUS_REGEN_INTERVAL = 5 -- pet focus ticks roughly every 5s
-
--- Pet action bar slot count (WoW 1.12)
-local NUM_PET_SLOTS = NUM_PET_ACTION_SLOTS or 10
-
--- Ability metadata: focus cost and behaviour type.
--- type: "taunt", "autocastDPS" (spammable, no meaningful CD),
--- "semiAutocast" (short CD, still autocasts frequently),
--- "cdDPS" (significant cooldown), "mobility", "defensive",
--- "utility", "partyBuff"
-local ABILITY_INFO = {
- growl = { cost = 15, type = "taunt" },
- cower = { cost = 15, type = "taunt" },
- bite = { cost = 35, type = "autocastDPS" },
- claw = { cost = 25, type = "autocastDPS" },
- lightningBreath = { cost = 50, type = "autocastDPS" },
- scorpidPoison = { cost = 25, type = "semiAutocast" },
- screech = { cost = 20, type = "semiAutocast" },
- charge = { cost = 35, type = "cdDPS" },
- deathRoll = { cost = 50, type = "cdDPS" },
- furiousHowl = { cost = 50, type = "cdDPS" },
- savageRend = { cost = 25, type = "cdDPS" },
- thunderstomp = { cost = 60, type = "cdDPS" },
- poisonSpit = { cost = 35, type = "cdDPS" },
- pollenBurst = { cost = 40, type = "cdDPS" },
- dash = { cost = 20, type = "mobility" },
- dive = { cost = 20, type = "mobility" },
- prowl = { cost = 40, type = "utility" },
- shellShield = { cost = 10, type = "defensive" },
- grace = { cost = 10, type = "defensive" },
- bubbleBarrier = { cost = 20, type = "defensive" },
- web = { cost = 0, type = "utility" },
- roarOfFortitude = { cost = 50, type = "partyBuff" },
- packleader = { cost = 50, type = "partyBuff" },
- striderPresence = { cost = 50, type = "partyBuff" },
-}
-
--- Ability keys whose autocast is managed by the focus budget system.
-local MANAGED_DPS_TYPES = { autocastDPS = true, semiAutocast = true, cdDPS = true }
-
--- ── Config defaults (persisted per-character) ────────────────
-local CONFIG_DEFAULTS = {
- tauntMode = "auto", -- "auto" | "growl" | "cower" | "off"
- pvpDetaunt = true,
- ccBreakCheck = true,
- ccBreakMode = "block", -- "block" | "warn"
- theButton = true,
- smartFocus = false,
- autoCower = false,
- autoCowerPct = 30,
- noChase = false,
- rushOnAttack = true,
- autoWarn = false,
- autoWarnPct = 20,
- autoWarnChannel = "SAY",
-}
-
--- ── Runtime state (not persisted) ────────────────────────────
-local rt = {
- inCombat = false,
- inPVP = false,
- -- Backed-up pre-PVP autocast states
- pvpBackup = nil, -- { growl=bool, cower=bool, autoCower=bool }
- -- Pet action bar slot indices (-1 = not found)
- slot = {
- attack = -1,
- follow = -1,
- growl = -1,
- cower = -1,
- bite = -1,
- claw = -1,
- dash = -1,
- dive = -1,
- lightningBreath = -1,
- screech = -1,
- scorpidPoison = -1,
- charge = -1,
- deathRoll = -1,
- furiousHowl = -1,
- savageRend = -1,
- thunderstomp = -1,
- poisonSpit = -1,
- pollenBurst = -1,
- prowl = -1,
- shellShield = -1,
- grace = -1,
- bubbleBarrier = -1,
- web = -1,
- roarOfFortitude = -1,
- packleader = -1,
- striderPresence = -1,
- },
- -- Pet spellbook indices for rush abilities (used for CastSpell)
- spellbook = {
- dash = -1,
- dive = -1,
- charge = -1,
- },
- -- Focus tracking
- focus1 = 100,
- focus2 = 100,
- lastFocusTick = 0,
- nextFocusTick = 0,
- -- Smart Focus: remember which DPS autocasts were ON at combat start
- preCombatAutocast = {}, -- { [slotKey] = bool }
- -- AutoCower: track if we swapped to cower
- autoCowered = false,
- -- AutoWarn throttle
- lastWarnTime = 0,
- lastWarnPct = 0,
- -- NoChase recall flag
- recallPending = false,
- -- TheButton context
- theButtonRecall = false,
- -- Party has a tank (cached)
- partyHasTank = false,
-}
-
--- ── Hidden tooltip for debuff scanning (legacy path) ─────────
-local debuffTip = nil
-local function EnsureDebuffTip()
- if debuffTip then return debuffTip end
- debuffTip = CreateFrame("GameTooltip", "MTH_SP_DebuffTip", UIParent, "GameTooltipTemplate")
- debuffTip:SetOwner(UIParent, "ANCHOR_NONE")
- return debuffTip
-end
-
--- ── Config helpers ───────────────────────────────────────────
-local function GetCfg()
- if type(MTH_SavedVariables) ~= "table" then return CONFIG_DEFAULTS end
- if type(MTH_SavedVariables.modules) ~= "table" then
- MTH_SavedVariables.modules = {}
- end
- local cfg = MTH_SavedVariables.modules["smartpet"]
- if type(cfg) ~= "table" then
- cfg = {}
- MTH_SavedVariables.modules["smartpet"] = cfg
- end
- -- Fill defaults for missing keys
- for k, v in pairs(CONFIG_DEFAULTS) do
- if cfg[k] == nil then
- cfg[k] = v
- end
- end
- return cfg
-end
-
-local function IsEnabled()
- if not (MTH and MTH.IsModuleEnabled) then return false end
- return MTH:IsModuleEnabled("smartpet", false)
-end
-
--- ── Pet action bar helpers ───────────────────────────────────
-local function GetAutocast(slotIndex)
- if slotIndex < 1 then return false end
- local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(slotIndex)
- return autoCastEnabled and true or false
-end
-
-local function SetAutocast(slotIndex, enabled)
- if slotIndex < 1 then return end
- local current = GetAutocast(slotIndex)
- if (current and not enabled) or (not current and enabled) then
- TogglePetAutocast(slotIndex)
- end
-end
-
--- ── Pet action bar scanning ──────────────────────────────────
-
-local function ScanPetActionBar()
- for key in pairs(rt.slot) do
- rt.slot[key] = -1
- end
- for i = 1, NUM_PET_SLOTS do
- local name = GetPetActionInfo(i)
- if name then
- local key = ABILITY_MAP[name]
- if key then
- rt.slot[key] = i
- end
- end
- end
-end
-
--- Scan pet spellbook for rush ability indices (for CastSpell)
-local function ScanPetSpellbook()
- rt.spellbook.dash = -1
- rt.spellbook.dive = -1
- rt.spellbook.charge = -1
- for i = 1, 20 do
- local spellName = GetSpellName(i, BOOKTYPE_PET)
- if not spellName then break end
- if spellName == L_DASH then
- rt.spellbook.dash = i
- elseif spellName == L_DIVE then
- rt.spellbook.dive = i
- elseif spellName == L_CHARGE then
- rt.spellbook.charge = i
- end
- end
-end
-
--- ── Party/tank detection ─────────────────────────────────────
-local TANK_CLASSES = { WARRIOR = true, PALADIN = true }
--- Druid tanks detected by class + bear form buff (approximate)
-
-local function ScanPartyForTank()
- local members = GetNumPartyMembers and GetNumPartyMembers() or 0
- local raid = GetNumRaidMembers and GetNumRaidMembers() or 0
-
- if members == 0 and raid == 0 then
- rt.partyHasTank = false
- return
- end
-
- -- Raid scan
- if raid > 0 then
- for i = 1, raid do
- local unit = "raid" .. i
- if UnitExists(unit) and not UnitIsUnit(unit, "player") then
- local _, cls = UnitClass(unit)
- if cls and TANK_CLASSES[cls] then
- rt.partyHasTank = true
- return
- end
- if cls == "DRUID" then
- -- Check for bear form via power type (rage = 1)
- if UnitPowerType and UnitPowerType(unit) == 1 then
- rt.partyHasTank = true
- return
- end
- end
- end
- end
- rt.partyHasTank = false
- return
- end
-
- -- Party scan
- for i = 1, members do
- local unit = "party" .. i
- if UnitExists(unit) then
- local _, cls = UnitClass(unit)
- if cls and TANK_CLASSES[cls] then
- rt.partyHasTank = true
- return
- end
- if cls == "DRUID" then
- if UnitPowerType and UnitPowerType(unit) == 1 then
- rt.partyHasTank = true
- return
- end
- end
- end
- end
- rt.partyHasTank = false
-end
-
--- ── Taunt Management ─────────────────────────────────────────
-local function ApplyTauntMode()
- if not IsEnabled() then return end
- local cfg = GetCfg()
- local mode = cfg.tauntMode
-
- if mode == "off" then return end
- if mode == "growl" then
- SetAutocast(rt.slot.growl, true)
- SetAutocast(rt.slot.cower, false)
- return
- end
- if mode == "cower" then
- SetAutocast(rt.slot.growl, false)
- SetAutocast(rt.slot.cower, true)
- return
- end
- -- auto: solo → growl, party with tank → cower
- if rt.partyHasTank then
- SetAutocast(rt.slot.growl, false)
- if rt.slot.cower > 0 then
- SetAutocast(rt.slot.cower, true)
- end
- else
- SetAutocast(rt.slot.growl, true)
- SetAutocast(rt.slot.cower, false)
- end
-end
-
--- ── PVP Auto-Detaunt ─────────────────────────────────────────
-local function StartPVP()
- if rt.inPVP then return end
- local cfg = GetCfg()
- if not cfg.pvpDetaunt then return end
-
- rt.inPVP = true
- rt.pvpBackup = {
- growl = GetAutocast(rt.slot.growl),
- cower = GetAutocast(rt.slot.cower),
- autoCower = cfg.autoCower,
- }
- SetAutocast(rt.slot.growl, false)
- SetAutocast(rt.slot.cower, false)
-end
-
-local function EndPVP()
- if not rt.inPVP then return end
- rt.inPVP = false
- if rt.pvpBackup then
- SetAutocast(rt.slot.growl, rt.pvpBackup.growl)
- SetAutocast(rt.slot.cower, rt.pvpBackup.cower)
- -- Restore autoCower setting — it doesn't get toggled on the bar, just the config flag
- rt.pvpBackup = nil
- end
-end
-
--- ── CC-Break Prevention ──────────────────────────────────────
--- Returns true if the target has a breakable CC debuff.
-local function TargetHasBreakableCC()
- local cfg = GetCfg()
- if not cfg.ccBreakCheck then return false end
-
- -- NamPower path: GetUnitField aura array
- if type(GetUnitField) == "function" then
- local auras = GetUnitField("target", "aura")
- if type(auras) == "table" then
- for _, aura in ipairs(auras) do
- if type(aura) == "table" then
- local auraName = aura.name or (aura.spellId and GetSpellRecField and GetSpellRecField(aura.spellId, "name"))
- if auraName and CC_DEBUFF_SET[auraName] then
- return true
- end
- end
- end
- return false
- end
- end
-
- -- Legacy path: hidden tooltip scan
- local tip = EnsureDebuffTip()
- tip:ClearLines()
- for j = 1, 16 do
- if not UnitDebuff("target", j) then break end
- tip:SetUnitDebuff("target", j)
- local nameRegion = getglobal("MTH_SP_DebuffTipTextLeft1")
- if nameRegion then
- local debuffName = nameRegion:GetText()
- if debuffName and CC_DEBUFF_SET[debuffName] then
- return true
- end
- end
- end
- return false
-end
-
--- ── Rush on attack (Dash / Dive / Charge) ────────────────────
-local function CastChargeAbility()
- local cfg = GetCfg()
- if not cfg.rushOnAttack then return end
- if rt.inCombat then return end -- only on initial engage
-
- -- Try Charge first (Boar — rush + damage), then Dash, then Dive
- local idx = -1
- if rt.spellbook.charge > 0 then
- idx = rt.spellbook.charge
- elseif rt.spellbook.dash > 0 then
- idx = rt.spellbook.dash
- elseif rt.spellbook.dive > 0 then
- idx = rt.spellbook.dive
- end
- if idx < 1 then return end
-
- local start, duration = GetSpellCooldown(idx, BOOKTYPE_PET)
- if start == 0 and duration == 0 then
- CastSpell(idx, BOOKTYPE_PET)
- end
-end
-
--- ── Smart Focus ──────────────────────────────────────────────
--- Estimate how many focus-regen ticks will occur during an ability's cooldown.
-local function EstimatedFocusRegen(slotIndex)
- if slotIndex < 1 then return 0 end
- local start, duration = GetPetActionCooldown(slotIndex)
- if not start or start == 0 then return 0 end
- local remaining = (start + duration) - GetTime()
- if remaining <= 0 then return 0 end
- -- Each tick gives ~FOCUS_REGEN_INTERVAL worth of focus ticks at ~24 focus per 5s cycle
- -- SmartPet used FocusManager * 4.8; we use a simpler remaining/5 * 24 estimate
- local ticks = remaining / FOCUS_REGEN_INTERVAL
- return ticks * 24
-end
-
-local function HandleFocusEvent()
- if not IsEnabled() then return end
- local cfg = GetCfg()
- if not cfg.smartFocus and cfg.tauntMode == "off" then return end
- if not rt.inCombat then return end
- if rt.inPVP then return end
-
- -- Track focus regen tick timing
- rt.focus2 = rt.focus1
- rt.focus1 = UnitMana("pet") or 0
- if rt.focus1 > rt.focus2 then
- rt.lastFocusTick = GetTime()
- rt.nextFocusTick = rt.lastFocusTick + 4.5
- end
-
- local currentFocus = rt.focus1
-
- -- Determine taunt cost (must always be reservable)
- local tauntCost = 0
- if cfg.tauntMode ~= "off" then
- if GetAutocast(rt.slot.cower) then
- tauntCost = ABILITY_INFO.cower.cost
- elseif rt.slot.growl > 0 then
- tauntCost = ABILITY_INFO.growl.cost
- end
- end
-
- -- Collect managed DPS abilities (on bar AND pre-combat autocast was ON)
- local managed = {} -- { {key, cost, slot}, ... }
- local managedN = 0
- for key, wasOn in pairs(rt.preCombatAutocast) do
- if wasOn and rt.slot[key] > 0 and ABILITY_INFO[key] then
- managedN = managedN + 1
- managed[managedN] = { key = key, cost = ABILITY_INFO[key].cost, slot = rt.slot[key] }
- end
- end
-
- if managedN == 0 then return end
-
- -- Sort by cost descending (most expensive = highest priority to keep active)
- table.sort(managed, function(a, b) return a.cost > b.cost end)
-
- if cfg.smartFocus and managedN >= 2 then
- -- Smart Focus: reserve taunt cost once, then budget the rest across DPS.
- -- Always keep the cheapest ability ON so the pet never sits idle;
- -- the worst case is growl is delayed by one regen tick (~5s).
- local budget = currentFocus - tauntCost
- if budget < 0 then budget = 0 end
- if MTH and MTH.Print then
- MTH:Print("[SP] focus=" .. currentFocus .. " reserve=" .. tauntCost .. " budget=" .. budget .. " managed=" .. managedN, "debug")
- end
- local anyEnabled = false
- for i = 1, managedN do
- local m = managed[i]
- if budget >= m.cost then
- SetAutocast(m.slot, true)
- anyEnabled = true
- if MTH and MTH.Print then
- MTH:Print("[SP] " .. m.key .. " ON (cost=" .. m.cost .. " left=" .. (budget - m.cost) .. ")", "debug")
- end
- budget = budget - m.cost
- elseif i == managedN and not anyEnabled then
- -- Cheapest ability: keep ON so pet isn't idle
- SetAutocast(m.slot, true)
- if MTH and MTH.Print then
- MTH:Print("[SP] " .. m.key .. " ON* (cheapest, waiting for regen)", "debug")
- end
- else
- SetAutocast(m.slot, false)
- if MTH and MTH.Print then
- MTH:Print("[SP] " .. m.key .. " OFF (cost=" .. m.cost .. " budget=" .. budget .. ")", "debug")
- end
- end
- end
- return
- end
-
- -- Basic taunt-priority: disable any DPS ability that would starve the taunt
- if cfg.tauntMode ~= "off" and tauntCost > 0 then
- for i = 1, managedN do
- local m = managed[i]
- local regen = EstimatedFocusRegen(m.slot)
- if (currentFocus + regen) - m.cost < tauntCost then
- SetAutocast(m.slot, false)
- if MTH and MTH.Print then
- MTH:Print("[SP] tauntGuard " .. m.key .. " OFF (focus=" .. currentFocus .. " regen=" .. math.floor(regen) .. ")", "debug")
- end
- else
- SetAutocast(m.slot, true)
- end
- end
- end
-end
-
--- ── AutoCower on low HP ──────────────────────────────────────
-local function HandlePetHealth()
- if not IsEnabled() then return end
- if not rt.inCombat then return end
- local cfg = GetCfg()
-
- local hp = UnitHealth("pet") or 0
- local hpMax = UnitHealthMax("pet") or 1
- if hpMax == 0 then hpMax = 1 end
- local pct = (100 * hp) / hpMax
-
- -- AutoCower
- if cfg.autoCower and rt.slot.cower > 0 then
- if pct < cfg.autoCowerPct then
- if not rt.autoCowered then
- rt.autoCowered = true
- SetAutocast(rt.slot.growl, false)
- SetAutocast(rt.slot.cower, true)
- end
- else
- if rt.autoCowered then
- rt.autoCowered = false
- ApplyTauntMode()
- end
- end
- end
-
- -- AutoWarn
- if cfg.autoWarn then
- local warnInterval
- if pct >= 60 then
- warnInterval = 10
- elseif pct >= 50 then
- warnInterval = 8
- elseif pct >= 30 then
- warnInterval = 6
- else
- warnInterval = 4
- end
- if pct < cfg.autoWarnPct
- and (GetTime() - rt.lastWarnTime) > warnInterval
- and rt.lastWarnPct > pct then
- local petName = UnitName("pet") or "Pet"
- local msg = petName .. " needs healing! (" .. string.format("%d", pct) .. "% Health)"
- local chan = string.upper(cfg.autoWarnChannel or "SAY")
- -- Validate channel
- if chan == "PARTY" then
- if not (UnitInParty and UnitInParty("player")) then chan = "SAY" end
- elseif chan == "RAID" then
- if not (GetNumRaidMembers and GetNumRaidMembers() > 0) then chan = "SAY" end
- elseif chan == "GUILD" then
- if not (IsInGuild and IsInGuild()) then chan = "SAY" end
- elseif chan ~= "SAY" then
- chan = "SAY"
- end
- SendChatMessage(msg, chan)
- rt.lastWarnTime = GetTime()
- end
- rt.lastWarnPct = pct
- end
-end
-
--- ── NoChase ──────────────────────────────────────────────────
-local function HandleMonsterEmote(emoteText, sourceName)
- if not IsEnabled() then return end
- local cfg = GetCfg()
- if not cfg.noChase then return end
-
- local petTarget = UnitName("pettarget")
- if sourceName and petTarget and sourceName == petTarget
- and strfind(emoteText or "", L_FLEE) then
- PetFollow()
- rt.theButtonRecall = true
- if UIErrorsFrame then
- UIErrorsFrame:AddMessage("Recall Pet", 0.0, 0.0, 1.0, 1.0, UIERRORS_HOLD_TIME)
- end
- end
-end
-
--- ── Pet Damage Meter ─────────────────────────────────────────
-local meter = {
- active = false,
- recording = false, -- true while actively collecting data
- startTime = 0,
- duration = 0, -- 0 = unlimited, >0 = auto-stop after N seconds
- totalDmg = 0,
- totalHits = 0,
- totalCrits = 0,
- totalMisses = 0,
- abilities = {}, -- [name] = { dmg=N, hits=N, crits=N, misses=N }
- petName = "",
- autocastSnapshot = "", -- "Bite=ON Claw=ON Growl=OFF ..."
-}
-
-local MeterPrintSummary -- forward declaration
-local MeterStop -- forward declaration
-
-local function MeterReset()
- meter.startTime = GetTime()
- meter.recording = true
- meter.totalDmg = 0
- meter.totalHits = 0
- meter.totalCrits = 0
- meter.totalMisses = 0
- meter.abilities = {}
- -- Snapshot pet name
- meter.petName = UnitName("pet") or "Unknown"
- -- Ensure slot map is current before reading autocast states
- ScanPetActionBar()
- -- Snapshot all known ability autocast states
- local parts = {}
- local partN = 0
- for key, info in pairs(ABILITY_INFO) do
- if rt.slot[key] and rt.slot[key] > 0 then
- local on = GetAutocast(rt.slot[key])
- partN = partN + 1
- parts[partN] = key .. "=" .. (on and "ON" or "OFF")
- end
- end
- table.sort(parts)
- meter.autocastSnapshot = table.concat(parts, " ")
-end
-
-MeterStop = function()
- meter.recording = false
- meter.active = false
- if meter.totalDmg > 0 then MeterPrintSummary() end
- MTH:Print("|cffff9900[SP Meter]|r Stopped.")
-end
-
-local function MeterGetAbility(name)
- if not meter.abilities[name] then
- meter.abilities[name] = { dmg = 0, hits = 0, crits = 0, misses = 0 }
- end
- return meter.abilities[name]
-end
-
-local function MeterParseCombatMsg(msg)
- if not meter.recording or not msg then return end
-
- local ability, dmg = nil, nil
-
- -- hit: "'s hits for ."
- _, _, ability, dmg = strfind(msg, "'s ([%a ]-) hits .+ for (%d+)")
- if ability and dmg then
- dmg = tonumber(dmg) or 0
- local a = MeterGetAbility(ability)
- a.hits = a.hits + 1
- a.dmg = a.dmg + dmg
- meter.totalHits = meter.totalHits + 1
- meter.totalDmg = meter.totalDmg + dmg
- return
- end
-
- -- crit: "'s crits for ."
- _, _, ability, dmg = strfind(msg, "'s ([%a ]-) crits .+ for (%d+)")
- if ability and dmg then
- dmg = tonumber(dmg) or 0
- local a = MeterGetAbility(ability)
- a.crits = a.crits + 1
- a.dmg = a.dmg + dmg
- meter.totalCrits = meter.totalCrits + 1
- meter.totalDmg = meter.totalDmg + dmg
- return
- end
-
- -- miss/resist: "'s misses" / "'s was resisted"
- _, _, ability = strfind(msg, "'s ([%a ]-) misses")
- if not ability then
- _, _, ability = strfind(msg, "'s ([%a ]-) was ")
- end
- if ability then
- local a = MeterGetAbility(ability)
- a.misses = a.misses + 1
- meter.totalMisses = meter.totalMisses + 1
- return
- end
-end
-
-local function MeterParsePetMelee(msg)
- if not meter.recording or not msg then return end
-
- local dmg = nil
-
- -- hit: " hits for ."
- _, _, dmg = strfind(msg, " hits .+ for (%d+)")
- if dmg then
- dmg = tonumber(dmg) or 0
- local a = MeterGetAbility("Melee")
- a.hits = a.hits + 1
- a.dmg = a.dmg + dmg
- meter.totalHits = meter.totalHits + 1
- meter.totalDmg = meter.totalDmg + dmg
- return
- end
-
- -- crit: " crits for ."
- _, _, dmg = strfind(msg, " crits .+ for (%d+)")
- if dmg then
- dmg = tonumber(dmg) or 0
- local a = MeterGetAbility("Melee")
- a.crits = a.crits + 1
- a.dmg = a.dmg + dmg
- meter.totalCrits = meter.totalCrits + 1
- meter.totalDmg = meter.totalDmg + dmg
- return
- end
-
- -- miss: " misses ."
- if strfind(msg, " misses ") then
- local a = MeterGetAbility("Melee")
- a.misses = a.misses + 1
- meter.totalMisses = meter.totalMisses + 1
- return
- end
-end
-
-MeterPrintSummary = function()
- if not MTH or not MTH.Print then return end
- local elapsed = GetTime() - meter.startTime
- if elapsed < 1 then elapsed = 1 end
- local dps = meter.totalDmg / elapsed
- local smartLabel = GetCfg().smartFocus and "ON" or "OFF"
- local totalSwings = meter.totalHits + meter.totalCrits + meter.totalMisses
-
- MTH:Print(string.format(
- "[SP Meter] Pet=%s SmartFocus=%s Duration=%.1fs TotalDmg=%d DPS=%.1f Swings=%d (H:%d C:%d M:%d)",
- meter.petName, smartLabel, elapsed, meter.totalDmg, dps, totalSwings,
- meter.totalHits, meter.totalCrits, meter.totalMisses), "debug")
-
- MTH:Print("[SP Meter] Autocasts: " .. meter.autocastSnapshot, "debug")
-
- -- Per-ability breakdown, sorted by damage
- local sorted = {}
- local sortN = 0
- for name, data in pairs(meter.abilities) do
- sortN = sortN + 1
- sorted[sortN] = { name = name, dmg = data.dmg, hits = data.hits, crits = data.crits, misses = data.misses }
- end
- table.sort(sorted, function(a, b) return a.dmg > b.dmg end)
- for i = 1, sortN do
- local e = sorted[i]
- local uses = e.hits + e.crits + e.misses
- local pct = (meter.totalDmg > 0) and (100 * e.dmg / meter.totalDmg) or 0
- MTH:Print(string.format(
- " %s: %d dmg (%.0f%%) %d uses (H:%d C:%d M:%d)",
- e.name, e.dmg, pct, uses, e.hits, e.crits, e.misses), "debug")
- end
-end
-
--- ── Combat lifecycle ─────────────────────────────────────────
-local function OnCombatStart()
- if not IsEnabled() then return end
- ScanPetActionBar()
- rt.inCombat = true
- rt.theButtonRecall = false
- rt.autoCowered = false
- rt.lastWarnTime = 0
- rt.lastWarnPct = (100 * (UnitHealth("pet") or 0)) / math.max(UnitHealthMax("pet") or 1, 1)
-
- -- Reset meter on new fight (only for untimed meters; timed meters keep recording across fights)
- if meter.active and meter.duration == 0 then
- MeterReset()
- end
-
- -- Record pre-combat autocast states for all managed DPS abilities
- rt.preCombatAutocast = {}
- for key, info in pairs(ABILITY_INFO) do
- if MANAGED_DPS_TYPES[info.type] and rt.slot[key] and rt.slot[key] > 0 then
- rt.preCombatAutocast[key] = GetAutocast(rt.slot[key])
- if MTH and MTH.Print then
- MTH:Print("[SP] snapshot " .. key .. "=" .. (GetAutocast(rt.slot[key]) and "ON" or "OFF") .. " slot=" .. rt.slot[key] .. " cost=" .. ABILITY_INFO[key].cost, "debug")
- end
- end
- end
-
- -- Smart Focus initial state: keep only the most expensive ability enabled
- local cfg = GetCfg()
- if cfg.smartFocus then
- local managed = {}
- local managedN = 0
- for key, wasOn in pairs(rt.preCombatAutocast) do
- if wasOn and ABILITY_INFO[key] then
- managedN = managedN + 1
- managed[managedN] = { key = key, cost = ABILITY_INFO[key].cost, slot = rt.slot[key] }
- end
- end
- if MTH and MTH.Print then
- MTH:Print("[SP] combatStart smartFocus=ON managedON=" .. managedN, "debug")
- end
- if managedN >= 2 then
- table.sort(managed, function(a, b) return a.cost > b.cost end)
- for i = 1, managedN do
- local state = (i == 1) and "ON" or "OFF"
- if MTH and MTH.Print then
- MTH:Print("[SP] init " .. managed[i].key .. "=" .. state .. " (cost=" .. managed[i].cost .. ")", "debug")
- end
- SetAutocast(managed[i].slot, i == 1)
- end
- end
- else
- if MTH and MTH.Print then
- MTH:Print("[SP] combatStart smartFocus=OFF", "debug")
- end
- end
-end
-
-local function OnCombatEnd()
- -- Print meter summary on combat end (only for untimed meters)
- if meter.active and meter.duration == 0 and meter.totalDmg > 0 then
- MeterPrintSummary()
- end
-
- -- Restore all managed DPS autocasts to pre-combat state
- if rt.preCombatAutocast then
- for key, wasOn in pairs(rt.preCombatAutocast) do
- if rt.slot[key] and rt.slot[key] > 0 then
- SetAutocast(rt.slot[key], wasOn)
- if MTH and MTH.Print then
- MTH:Print("[SP] restore " .. key .. "=" .. (wasOn and "ON" or "OFF"), "debug")
- end
- end
- end
- end
-
- if rt.inPVP then EndPVP() end
-
- rt.inCombat = false
- rt.autoCowered = false
- rt.theButtonRecall = false
-
- -- Re-apply taunt mode (restores growl/cower to config state)
- ApplyTauntMode()
-end
-
--- ── PetAttack wrapper (used by The Button and hook) ──────────
-local MTH_SP_OriginalPetAttack = nil
-
-local function SmartPetAttack()
- if not IsEnabled() then
- if MTH_SP_OriginalPetAttack then MTH_SP_OriginalPetAttack() end
- return
- end
-
- local cfg = GetCfg()
-
- -- CC-Break check
- if cfg.ccBreakCheck and UnitExists("target") then
- if TargetHasBreakableCC() then
- if cfg.ccBreakMode == "block" then
- if UIErrorsFrame then
- UIErrorsFrame:AddMessage("Breakable debuff found — pet attack blocked", 1.0, 0.3, 0.0, 1.0, UIERRORS_HOLD_TIME)
- end
- return
- else
- if UIErrorsFrame then
- UIErrorsFrame:AddMessage("Warning: target has breakable CC", 1.0, 1.0, 0.0, 1.0, UIERRORS_HOLD_TIME)
- end
- end
- end
- end
-
- -- PVP check
- if UnitExists("target") and UnitIsPlayer("target") and UnitCanAttack("player", "target") then
- StartPVP()
- end
-
- -- Dash/Dive on attack
- CastChargeAbility()
-
- -- Issue the actual PetAttack
- if MTH_SP_OriginalPetAttack then MTH_SP_OriginalPetAttack() end
-end
-
--- ── The Button ───────────────────────────────────────────────
--- Global function called from Bindings.xml
-function MTH_SmartPet_TheButton()
- if not IsEnabled() then return end
- local cfg = GetCfg()
- if not cfg.theButton then return end
-
- -- NoChase recall pending
- if rt.theButtonRecall then
- PetFollow()
- rt.theButtonRecall = false
- return
- end
-
- -- No pet alive
- if not UnitExists("pet") or UnitIsDead("pet") then return end
-
- -- No target or dead target → recall
- if not UnitExists("target") or UnitIsDead("target") then
- PetFollow()
- return
- end
-
- -- Friendly target → assist (attack their target)
- if UnitIsPlayer("target") and UnitCanCooperate("player", "target") then
- if UnitExists("targettarget") and not UnitIsDead("targettarget")
- and UnitCanAttack("player", "targettarget") then
- AssistUnit("target")
- SmartPetAttack()
- end
- return
- end
-
- -- Enemy target → attack
- if UnitCanAttack("player", "target") and not UnitIsDead("target") then
- SmartPetAttack()
- return
- end
-
- -- Fallback: recall
- PetFollow()
-end
-
--- ── Taunt toggle (keybind) ───────────────────────────────────
-function MTH_SmartPet_TauntToggle()
- if not IsEnabled() then return end
- local growlOn = GetAutocast(rt.slot.growl)
- local cowerOn = GetAutocast(rt.slot.cower)
-
- if growlOn then
- -- Switch to cower
- SetAutocast(rt.slot.growl, false)
- if rt.slot.cower > 0 then SetAutocast(rt.slot.cower, true) end
- elseif cowerOn then
- -- Switch to growl
- SetAutocast(rt.slot.cower, false)
- if rt.slot.growl > 0 then SetAutocast(rt.slot.growl, true) end
- else
- -- Neither on: enable growl
- if rt.slot.growl > 0 then SetAutocast(rt.slot.growl, true) end
- end
-end
-
--- ── Event frame ──────────────────────────────────────────────
-local frame = CreateFrame("Frame", "MTH_SmartPetFrame")
-frame:Hide()
-
--- Meter auto-stop ticker (runs only while meter is active with a duration)
-local meterTickFrame = CreateFrame("Frame")
-meterTickFrame:Hide()
-meterTickFrame:SetScript("OnUpdate", function()
- if not meter.active or not meter.recording then
- meterTickFrame:Hide()
- return
- end
- if meter.duration > 0 and (GetTime() - meter.startTime) >= meter.duration then
- MeterStop()
- meterTickFrame:Hide()
- end
-end)
-
-frame:RegisterEvent("VARIABLES_LOADED")
-frame:RegisterEvent("PET_BAR_UPDATE")
-frame:RegisterEvent("PET_UI_UPDATE")
-frame:RegisterEvent("PET_ATTACK_START")
-frame:RegisterEvent("PET_ATTACK_STOP")
-frame:RegisterEvent("PLAYER_DEAD")
-frame:RegisterEvent("UNIT_HEALTH")
-frame:RegisterEvent("UNIT_FOCUS")
-frame:RegisterEvent("PARTY_MEMBERS_CHANGED")
-frame:RegisterEvent("RAID_ROSTER_UPDATE")
-frame:RegisterEvent("CHAT_MSG_MONSTER_EMOTE")
-frame:RegisterEvent("PLAYER_ENTERING_WORLD")
-frame:RegisterEvent("CHAT_MSG_SPELL_PET_DAMAGE")
-frame:RegisterEvent("CHAT_MSG_COMBAT_PET_HITS")
-frame:RegisterEvent("CHAT_MSG_COMBAT_PET_MISSES")
-
-frame:SetScript("OnEvent", function()
- -- Meter events bypass the module-enabled check
- if event == "CHAT_MSG_SPELL_PET_DAMAGE" then
- if meter.recording then
- MTH:Print("[SP Meter] " .. event .. ": " .. (arg1 or "nil"), "debug")
- MeterParseCombatMsg(arg1)
- end
- return
- end
- if event == "CHAT_MSG_COMBAT_PET_HITS" or event == "CHAT_MSG_COMBAT_PET_MISSES" then
- if meter.recording then
- MTH:Print("[SP Meter] " .. event .. ": " .. (arg1 or "nil"), "debug")
- MeterParsePetMelee(arg1)
- end
- return
- end
-
- if not IsEnabled() then
- -- Even with module off, handle meter start/stop (untimed only)
- if meter.active and meter.duration == 0 then
- if event == "PET_ATTACK_START" then
- MeterReset()
- elseif event == "PET_ATTACK_STOP" or event == "PLAYER_DEAD" then
- if meter.totalDmg > 0 then MeterPrintSummary() end
- end
- end
- return
- end
-
- if event == "VARIABLES_LOADED" or event == "PLAYER_ENTERING_WORLD" then
- ResolveLocaleStrings()
- ScanPetActionBar()
- ScanPetSpellbook()
- ScanPartyForTank()
- -- Hook PetAttack once
- if not MTH_SP_OriginalPetAttack and type(PetAttack) == "function" then
- MTH_SP_OriginalPetAttack = PetAttack
- PetAttack = SmartPetAttack
- end
- ApplyTauntMode()
- return
- end
-
- if event == "PET_BAR_UPDATE" or event == "PET_UI_UPDATE" then
- ScanPetActionBar()
- ScanPetSpellbook()
- return
- end
-
- if event == "PET_ATTACK_START" then
- OnCombatStart()
- return
- end
-
- if event == "PET_ATTACK_STOP" or event == "PLAYER_DEAD" then
- OnCombatEnd()
- return
- end
-
- if event == "UNIT_FOCUS" and arg1 == "pet" then
- HandleFocusEvent()
- return
- end
-
- if event == "UNIT_HEALTH" and arg1 == "pet" then
- HandlePetHealth()
- return
- end
-
- if event == "PARTY_MEMBERS_CHANGED" or event == "RAID_ROSTER_UPDATE" then
- ScanPartyForTank()
- if not rt.inCombat then
- ApplyTauntMode()
- end
- return
- end
-
- if event == "CHAT_MSG_MONSTER_EMOTE" then
- HandleMonsterEmote(arg1, arg2)
- return
- end
-end)
-
--- ── Public API (for options panel and external access) ────────
-MTH_SmartPet = {
- GetConfig = GetCfg,
-
- SetTauntMode = function(mode)
- local cfg = GetCfg()
- if mode == "auto" or mode == "growl" or mode == "cower" or mode == "off" then
- cfg.tauntMode = mode
- if not rt.inCombat then ApplyTauntMode() end
- end
- end,
-
- SetPvpDetaunt = function(v) GetCfg().pvpDetaunt = v and true or false end,
-
- SetCcBreakCheck = function(v) GetCfg().ccBreakCheck = v and true or false end,
- SetCcBreakMode = function(mode)
- if mode == "block" or mode == "warn" then
- GetCfg().ccBreakMode = mode
- end
- end,
-
- SetTheButton = function(v) GetCfg().theButton = v and true or false end,
- SetSmartFocus = function(v) GetCfg().smartFocus = v and true or false end,
-
- SetAutoCower = function(v) GetCfg().autoCower = v and true or false end,
- SetAutoCowerPct = function(v)
- v = tonumber(v)
- if v and v >= 1 and v <= 99 then GetCfg().autoCowerPct = v end
- end,
-
- SetNoChase = function(v) GetCfg().noChase = v and true or false end,
- SetRushOnAttack = function(v) GetCfg().rushOnAttack = v and true or false end,
-
- SetAutoWarn = function(v) GetCfg().autoWarn = v and true or false end,
- SetAutoWarnPct = function(v)
- v = tonumber(v)
- if v and v >= 1 and v <= 99 then GetCfg().autoWarnPct = v end
- end,
- SetAutoWarnChannel = function(chan)
- chan = string.upper(tostring(chan or "SAY"))
- if chan == "SAY" or chan == "PARTY" or chan == "RAID" or chan == "GUILD" then
- GetCfg().autoWarnChannel = chan
- end
- end,
-
- -- Force a rescan (useful after pet swap)
- Rescan = function()
- ScanPetActionBar()
- ScanPetSpellbook()
- ScanPartyForTank()
- if not rt.inCombat then ApplyTauntMode() end
- end,
-
- -- Damage meter (duration=0 for unlimited, >0 for timed auto-stop)
- StartMeter = function(duration)
- meter.active = true
- meter.duration = duration or 0
- MeterReset()
- if duration and duration > 0 then
- meterTickFrame:Show()
- MTH:Print(string.format("|cffff9900[SP Meter]|r ON — recording for %ds.", duration))
- else
- MTH:Print("|cffff9900[SP Meter]|r ON — /sp meter to stop.")
- end
- end,
-
- StopMeter = function()
- if meter.active then
- MeterStop()
- meterTickFrame:Hide()
- else
- MTH:Print("|cffff9900[SP Meter]|r Not running.")
- end
- end,
-
- -- Reprint last summary
- PrintMeter = function()
- if meter.totalDmg > 0 then
- MeterPrintSummary()
- else
- MTH:Print("|cffff9900[SP Meter]|r No data recorded.")
- end
- end,
-}
-
--- ── Register with MTH framework so IsModuleEnabled works ─────
-local MTH_SmartPetModule = {
- name = "smartpet",
- enabled = false,
- events = {},
-}
-
-function MTH_SmartPetModule:init()
- self.initialized = true
-end
-
-function MTH_SmartPetModule:setEnabled(enabled)
- -- No-op: SmartPet checks IsEnabled() on every event
-end
-
-if MTH and MTH.RegisterModule then
- MTH:RegisterModule("smartpet", MTH_SmartPetModule)
-end
-
--- ── Slash command ────────────────────────────────────────────
-SLASH_MTHSmartPet1 = "/sp"
-SlashCmdList["MTHSmartPet"] = function(msg)
- msg = string.lower(msg or "")
- if msg == "meter" then
- if meter.active then
- MTH_SmartPet.StopMeter()
- else
- MTH_SmartPet.StartMeter(0)
- end
- elseif msg == "meter print" then
- MTH_SmartPet.PrintMeter()
- else
- -- "/sp meter 60" — timed start
- local _, _, secs = strfind(msg, "^meter (%d+)$")
- if secs then
- MTH_SmartPet.StartMeter(tonumber(secs))
- else
- MTH:Print("|cffff9900[SmartPet]|r /sp meter — toggle damage meter")
- MTH:Print("|cffff9900[SmartPet]|r /sp meter 60 — record for 60s then stop")
- MTH:Print("|cffff9900[SmartPet]|r /sp meter print — reprint last fight")
- end
- end
-end
diff --git a/modules/smartpet/loader.xml b/modules/smartpet/loader.xml
deleted file mode 100644
index 6d68df7..0000000
--- a/modules/smartpet/loader.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-