From ee8328e9037a114111cf8841851efafc568b34ed Mon Sep 17 00:00:00 2001 From: DuvelCorp Date: Tue, 24 Feb 2026 23:00:59 +0100 Subject: [PATCH] First version --- .gitignore | 6 + Bindings.xml | 121 + MetaHunt.lua | 10 + MetaHunt.toc | 45 + README.MD | 52 + api/AntiDaze.lua | 147 + api/AutoStrip.lua | 189 + api/AutoStrip.xml | 97 + api/config.lua | 58 + api/constants.lua | 152 + api/core-feed-tracking.lua | 1547 + api/core-framework.lua | 1509 + api/core-scan-beasttraining.lua | 912 + api/core-scan-spellbookpet.lua | 295 + api/core-scan-stablemaster.lua | 2934 ++ api/core.lua | 264 + api/debug-frame.lua | 374 + api/hunterbook-tab-beastlore.lua | 390 + api/hunterbook-tab-families.lua | 398 + api/hunterbook-tab-npcfinder.lua | 189 + api/hunterbook-tab-petabilities.lua | 903 + api/hunterbook-tab-pethistory.lua | 29 + api/hunterbook-tab-stable.lua | 1801 ++ api/hunterbook-tab-weapons.lua | 73 + api/hunterbook.lua | 4946 +++ api/hunterbook.xml | 333 + api/localization.lua | 180 + api/map-markers.lua | 1262 + api/minimap-button.lua | 171 + api/options-autobuy.lua | 845 + api/options-chronometer.lua | 597 + api/options-credits.lua | 193 + api/options-feedomatic.lua | 373 + api/options-shell.lua | 220 + api/options-tree.lua | 184 + api/options-zbuttons.lua | 1008 + api/options.lua | 125 + api/options.xml | 117 + api/uilib.lua | 125 + api/uirenderer.lua | 190 + data/ds-beasts-extra.lua | 2101 ++ data/ds-beasts-families.lua | 250 + data/ds-beasts-html-enrichment.lua | 14520 +++++++++ data/ds-beasts.lua | 8840 ++++++ data/ds-creature-coords.lua | 13044 ++++++++ data/ds-items-atlas-sources.lua | 150 + data/ds-items-extra.lua | 7100 +++++ data/ds-items.lua | 1133 + data/ds-minimap-sizes.lua | 121 + data/ds-pet-spells-trainer.lua | 236 + data/ds-pet-spells.lua | 4352 +++ data/ds-racial.lua | 70 + data/ds-unit-coords.lua | 25145 ++++++++++++++++ data/ds-vendors-extra.lua | 1930 ++ data/ds-vendors.lua | 2229 ++ data/ds-zones-fallback.lua | 75 + data/ds-zones.lua | 12054 ++++++++ data/init.lua | 598 + img/node.tga | Bin 0 -> 4140 bytes init/api.xml | 37 + init/compat.xml | 4 + init/env.xml | 4 + init/libs.xml | 5 + init/modules.xml | 23 + init/skins.xml | 4 + modules/autobuy/engine.lua | 1259 + modules/autobuy/loader.xml | 8 + modules/autobuy/module.lua | 161 + modules/chronometer/data-hunter.lua | 78 + modules/chronometer/engine.lua | 1379 + .../libs/AceAddon-2.0/AceAddon-2.0.lua | 1078 + .../libs/AceEvent-2.0/AceEvent-2.0.lua | 973 + .../libs/AceHook-2.1/AceHook-2.1.lua | 569 + .../libs/AceLibrary/AceLibrary.lua | 757 + .../chronometer/libs/AceOO-2.0/AceOO-2.0.lua | 1004 + .../libs/CandyBar-2.0/CandyBar-2.0.lua | 1912 ++ .../libs/ParserLib/FixGlobalString.lua | 32 + .../chronometer/libs/ParserLib/ParserLib.lua | 2577 ++ .../libs/ParserLib/optimizer-enUS.lua | 215 + modules/chronometer/libs/ParserLib/readme.txt | 1 + modules/chronometer/loader.xml | 17 + modules/chronometer/module.lua | 45 + modules/feedomatic/Bindings.xml | 5 + modules/feedomatic/FeedOMatic.lua | 2221 ++ modules/feedomatic/FeedOMatic_Emotes.lua | 86 + modules/feedomatic/FeedOMatic_Foods.lua | 238 + modules/feedomatic/GFWTable.lua | 174 + modules/feedomatic/GFWUtils.lua | 202 + modules/feedomatic/GFW_FeedOMatic.toc | 12 + modules/feedomatic/UpdateUI.xml | 5 + modules/feedomatic/loader.xml | 18 + modules/feedomatic/localization.lua | 304 + modules/feedomatic/module.lua | 340 + modules/feedomatic/readme.txt | 87 + modules/feedomatic/runtime.xml | 25 + modules/smartammo/engine.lua | 672 + modules/smartammo/loader.xml | 8 + modules/smartammo/module.lua | 171 + modules/tooltips/loader.xml | 7 + modules/tooltips/module.lua | 1366 + modules/zhunter/!---VERSION---!.txt | 67 + modules/zhunter/Bindings.xml | 98 + modules/zhunter/DeferredInit.lua | 237 + modules/zhunter/Localization.lua | 325 + modules/zhunter/ZHunterMod.lua | 149 + modules/zhunter/ZSpellButtonTemplate.lua | 508 + modules/zhunter/ZSpellButtonTemplate.xml | 124 + modules/zhunter/loader.xml | 50 + modules/zhunter/module.lua | 326 + modules/zhunter/zButtonAmmo.lua | 1037 + modules/zhunter/zButtonAmmo.xml | 14 + modules/zhunter/zButtonAspect.lua | 281 + modules/zhunter/zButtonAspect.xml | 14 + modules/zhunter/zButtonCompanions.lua | 323 + modules/zhunter/zButtonCompanions.xml | 14 + modules/zhunter/zButtonMounts.lua | 322 + modules/zhunter/zButtonMounts.xml | 14 + modules/zhunter/zButtonPet.lua | 454 + modules/zhunter/zButtonPet.xml | 14 + modules/zhunter/zButtonToys.lua | 322 + modules/zhunter/zButtonToys.xml | 14 + modules/zhunter/zButtonTrack.lua | 260 + modules/zhunter/zButtonTrack.xml | 14 + modules/zhunter/zButtonTrap.lua | 262 + modules/zhunter/zButtonTrap.xml | 14 + 125 files changed, 140652 insertions(+) create mode 100644 .gitignore create mode 100644 Bindings.xml create mode 100644 MetaHunt.lua create mode 100644 MetaHunt.toc create mode 100644 README.MD create mode 100644 api/AntiDaze.lua create mode 100644 api/AutoStrip.lua create mode 100644 api/AutoStrip.xml create mode 100644 api/config.lua create mode 100644 api/constants.lua create mode 100644 api/core-feed-tracking.lua create mode 100644 api/core-framework.lua create mode 100644 api/core-scan-beasttraining.lua create mode 100644 api/core-scan-spellbookpet.lua create mode 100644 api/core-scan-stablemaster.lua create mode 100644 api/core.lua create mode 100644 api/debug-frame.lua create mode 100644 api/hunterbook-tab-beastlore.lua create mode 100644 api/hunterbook-tab-families.lua create mode 100644 api/hunterbook-tab-npcfinder.lua create mode 100644 api/hunterbook-tab-petabilities.lua create mode 100644 api/hunterbook-tab-pethistory.lua create mode 100644 api/hunterbook-tab-stable.lua create mode 100644 api/hunterbook-tab-weapons.lua create mode 100644 api/hunterbook.lua create mode 100644 api/hunterbook.xml create mode 100644 api/localization.lua create mode 100644 api/map-markers.lua create mode 100644 api/minimap-button.lua create mode 100644 api/options-autobuy.lua create mode 100644 api/options-chronometer.lua create mode 100644 api/options-credits.lua create mode 100644 api/options-feedomatic.lua create mode 100644 api/options-shell.lua create mode 100644 api/options-tree.lua create mode 100644 api/options-zbuttons.lua create mode 100644 api/options.lua create mode 100644 api/options.xml create mode 100644 api/uilib.lua create mode 100644 api/uirenderer.lua create mode 100644 data/ds-beasts-extra.lua create mode 100644 data/ds-beasts-families.lua create mode 100644 data/ds-beasts-html-enrichment.lua create mode 100644 data/ds-beasts.lua create mode 100644 data/ds-creature-coords.lua create mode 100644 data/ds-items-atlas-sources.lua create mode 100644 data/ds-items-extra.lua create mode 100644 data/ds-items.lua create mode 100644 data/ds-minimap-sizes.lua create mode 100644 data/ds-pet-spells-trainer.lua create mode 100644 data/ds-pet-spells.lua create mode 100644 data/ds-racial.lua create mode 100644 data/ds-unit-coords.lua create mode 100644 data/ds-vendors-extra.lua create mode 100644 data/ds-vendors.lua create mode 100644 data/ds-zones-fallback.lua create mode 100644 data/ds-zones.lua create mode 100644 data/init.lua create mode 100644 img/node.tga create mode 100644 init/api.xml create mode 100644 init/compat.xml create mode 100644 init/env.xml create mode 100644 init/libs.xml create mode 100644 init/modules.xml create mode 100644 init/skins.xml create mode 100644 modules/autobuy/engine.lua create mode 100644 modules/autobuy/loader.xml create mode 100644 modules/autobuy/module.lua create mode 100644 modules/chronometer/data-hunter.lua create mode 100644 modules/chronometer/engine.lua create mode 100644 modules/chronometer/libs/AceAddon-2.0/AceAddon-2.0.lua create mode 100644 modules/chronometer/libs/AceEvent-2.0/AceEvent-2.0.lua create mode 100644 modules/chronometer/libs/AceHook-2.1/AceHook-2.1.lua create mode 100644 modules/chronometer/libs/AceLibrary/AceLibrary.lua create mode 100644 modules/chronometer/libs/AceOO-2.0/AceOO-2.0.lua create mode 100644 modules/chronometer/libs/CandyBar-2.0/CandyBar-2.0.lua create mode 100644 modules/chronometer/libs/ParserLib/FixGlobalString.lua create mode 100644 modules/chronometer/libs/ParserLib/ParserLib.lua create mode 100644 modules/chronometer/libs/ParserLib/optimizer-enUS.lua create mode 100644 modules/chronometer/libs/ParserLib/readme.txt create mode 100644 modules/chronometer/loader.xml create mode 100644 modules/chronometer/module.lua create mode 100644 modules/feedomatic/Bindings.xml create mode 100644 modules/feedomatic/FeedOMatic.lua create mode 100644 modules/feedomatic/FeedOMatic_Emotes.lua create mode 100644 modules/feedomatic/FeedOMatic_Foods.lua create mode 100644 modules/feedomatic/GFWTable.lua create mode 100644 modules/feedomatic/GFWUtils.lua create mode 100644 modules/feedomatic/GFW_FeedOMatic.toc create mode 100644 modules/feedomatic/UpdateUI.xml create mode 100644 modules/feedomatic/loader.xml create mode 100644 modules/feedomatic/localization.lua create mode 100644 modules/feedomatic/module.lua create mode 100644 modules/feedomatic/readme.txt create mode 100644 modules/feedomatic/runtime.xml create mode 100644 modules/smartammo/engine.lua create mode 100644 modules/smartammo/loader.xml create mode 100644 modules/smartammo/module.lua create mode 100644 modules/tooltips/loader.xml create mode 100644 modules/tooltips/module.lua create mode 100644 modules/zhunter/!---VERSION---!.txt create mode 100644 modules/zhunter/Bindings.xml create mode 100644 modules/zhunter/DeferredInit.lua create mode 100644 modules/zhunter/Localization.lua create mode 100644 modules/zhunter/ZHunterMod.lua create mode 100644 modules/zhunter/ZSpellButtonTemplate.lua create mode 100644 modules/zhunter/ZSpellButtonTemplate.xml create mode 100644 modules/zhunter/loader.xml create mode 100644 modules/zhunter/module.lua create mode 100644 modules/zhunter/zButtonAmmo.lua create mode 100644 modules/zhunter/zButtonAmmo.xml create mode 100644 modules/zhunter/zButtonAspect.lua create mode 100644 modules/zhunter/zButtonAspect.xml create mode 100644 modules/zhunter/zButtonCompanions.lua create mode 100644 modules/zhunter/zButtonCompanions.xml create mode 100644 modules/zhunter/zButtonMounts.lua create mode 100644 modules/zhunter/zButtonMounts.xml create mode 100644 modules/zhunter/zButtonPet.lua create mode 100644 modules/zhunter/zButtonPet.xml create mode 100644 modules/zhunter/zButtonToys.lua create mode 100644 modules/zhunter/zButtonToys.xml create mode 100644 modules/zhunter/zButtonTrack.lua create mode 100644 modules/zhunter/zButtonTrack.xml create mode 100644 modules/zhunter/zButtonTrap.lua create mode 100644 modules/zhunter/zButtonTrap.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..87b4ca5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Ignore all hidden files and hidden directories +.* +**/.* + +# Keep this ignore file tracked +!.gitignore diff --git a/Bindings.xml b/Bindings.xml new file mode 100644 index 0000000..a2caab5 --- /dev/null +++ b/Bindings.xml @@ -0,0 +1,121 @@ + + + + FOM_Feed(); + + + + + zButtonAspect_KeyBinding() + + + zButtonAspect_KeyBinding(1) + + + zButtonAspect_KeyBinding(2) + + + zButtonAspect_KeyBinding(3) + + + zButtonAspect_KeyBinding(4) + + + zButtonAspect_KeyBinding(5) + + + zButtonAspect_KeyBinding(6) + + + + + zButtonTrack_KeyBinding() + + + zButtonTrack_KeyBinding(1) + + + zButtonTrack_KeyBinding(2) + + + zButtonTrack_KeyBinding(3) + + + zButtonTrack_KeyBinding(4) + + + zButtonTrack_KeyBinding(5) + + + zButtonTrack_KeyBinding(6) + + + + + zButtonTrap_KeyBinding() + + + zButtonTrap_KeyBinding(1) + + + zButtonTrap_KeyBinding(2) + + + zButtonTrap_KeyBinding(3) + + + zButtonTrap_KeyBinding(4) + + + zButtonTrap_KeyBinding(5) + + + zButtonTrap_KeyBinding(6) + + + + + zButtonAmmo_KeyBinding() + + + zButtonAmmo_KeyBinding(1) + + + zButtonAmmo_KeyBinding(2) + + + zButtonAmmo_KeyBinding(3) + + + zButtonAmmo_KeyBinding(4) + + + zButtonAmmo_KeyBinding(5) + + + zButtonAmmo_KeyBinding(6) + + + + + zButtonPet_KeyBinding() + + + zButtonPet_KeyBinding(1) + + + zButtonPet_KeyBinding(2) + + + zButtonPet_KeyBinding(3) + + + zButtonPet_KeyBinding(4) + + + zButtonPet_KeyBinding(5) + + + zButtonPet_KeyBinding(6) + + \ No newline at end of file diff --git a/MetaHunt.lua b/MetaHunt.lua new file mode 100644 index 0000000..2dbc0c1 --- /dev/null +++ b/MetaHunt.lua @@ -0,0 +1,10 @@ +------------------------------------------------------ +-- MetaHunt: Hunter's Toolkit Addon +-- A unified addon merging FeedOMatic, Tooltips, and zhunter +-- Modern modular architecture with legacy addon support +------------------------------------------------------ + +-- Initialize beast database after all files loaded +if MTH_DS_OnLoad then + MTH_DS_OnLoad() +end diff --git a/MetaHunt.toc b/MetaHunt.toc new file mode 100644 index 0000000..b8e261d --- /dev/null +++ b/MetaHunt.toc @@ -0,0 +1,45 @@ +## Interface: 11200 +## Version: 1.0.3 +## Title: MetaHunt - |cff00ff00Hunter +## Author: Metasploit +## Notes: Unified addon suite for huntards making old addons compatible with TurtleWoW, and adding an arsenal of never seen Hunter's utilities. +## SavedVariables: MTH_SavedVariables, FOM_Config, FOM_FoodQuality, FOM_AddedFoods, FOM_RemovedFoods, FOM_Cooking, FOM_QuestFood, FOM_LocaleInfo +## SavedVariablesPerCharacter: ZHunterMod_Saved, MTH_CharSavedVariables + + +data\init.lua +Bindings.xml + +## Load initialization and core framework +init\env.xml +init\compat.xml +init\libs.xml +init\api.xml +api\core-feed-tracking.lua +init\skins.xml + +## Load database files (DB files must come before data\init.lua) +data\ds-beasts.lua +data\ds-beasts-extra.lua +data\ds-beasts-html-enrichment.lua +data\ds-vendors.lua +data\ds-vendors-extra.lua +data\ds-items.lua +data\ds-items-extra.lua +data\ds-items-atlas-sources.lua +data\ds-zones.lua +data\ds-zones-fallback.lua +data\ds-minimap-sizes.lua +data\ds-unit-coords.lua +data\ds-creature-coords.lua +data\ds-beasts-families.lua +data\ds-pet-spells.lua +data\ds-pet-spells-trainer.lua +data\ds-racial.lua +data\init.lua + +## Load main addon entry point +MetaHunt.lua + +## Load modules (each addon is now a self-contained module) +init\modules.xml diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..ec51590 --- /dev/null +++ b/README.MD @@ -0,0 +1,52 @@ +# MetaHunt + +MetaHunt is a Turtle WoW Hunter toolkit. +It combines never seen pet-centric features and revives old broken hunter add-ons. + + ## Twow Data + +Metahunt ships with larges up-to-date datastores for all data a hunter needs : + +- All beasts, their location, and the abilities they can learn you +- All Pet families, their abilities and their diet +- All pet abilities ranks +- All Ammo (Arrows and Bullets) +- All Stable, Hunter and Pet Masters +- All Ammo vendors + + + ## Smart Ammo + +Auto swap your main ammo to junk ammo for shots that doesn't scale on damage, and re-equip you main ammo right away. + +# Tooltips + + Get enhanced tooltips on beasts, ammo vendors, and your pet. + +# zBouttons + +Old zHunterMod addon on steroids. Fully compatible with Twow, and enhanced with new functionalities + +- zAmmo : Track all your ammo Live and swap them in a click or bind +- zPet : Intelligent collapsible pet Bar regrouping all pet spells and providing an intelligent button that always show you the right spell to use depending on the situation +- zTrack: All your trackings in one collapsible bar +- zAspect: All your trackings in one collapsible bar +- But also zMounts, zToys and zCompanions. + +# Feed-O-Matic +Now compatible with Turtle, feed your pet with one key bind, it will always pickup the best food for your pet. + +# Chronometer + +The good old Chronometer addon, purged of non-necessary things and reconfigured for a pure hunter use. + +# Autobuy + +Define your own simple rules for auto buy ammo and pet food. + +# The book of Hunters +- Browse the Metahunt datastores of NPCs and open the map to locate them like in pfQuest +- Track all info about the pet abilities that you know and don't know yet +- Track all info about your stabled pets +- Keeep an history of all your pets after you abbandon them + diff --git a/api/AntiDaze.lua b/api/AntiDaze.lua new file mode 100644 index 0000000..a8cd08a --- /dev/null +++ b/api/AntiDaze.lua @@ -0,0 +1,147 @@ +local function AntiDaze_Print(msg) + if MTH and MTH.Print then + MTH:Print(msg) + elseif DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + DEFAULT_CHAT_FRAME:AddMessage(tostring(msg)) + end +end + +local function AntiDaze_GetSaved() + if type(MTH_CharSavedVariables) ~= "table" then + MTH_CharSavedVariables = {} + end + if type(MTH_CharSavedVariables.antiDaze) ~= "table" then + MTH_CharSavedVariables.antiDaze = {} + end + return MTH_CharSavedVariables.antiDaze +end + +local function AntiDaze_IsEnabled() + local saved = AntiDaze_GetSaved() + return saved["enabled"] and true or false +end + +local function AntiDaze_RegisterCombatEvents() + if not AntiDazeFrame then + return + end + AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE") + AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE") + AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE") + AntiDazeFrame:RegisterEvent("CHAT_MSG_SPELL_PARTY_DAMAGE") +end + +local function AntiDaze_UnregisterCombatEvents() + if not AntiDazeFrame then + return + end + AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE") + AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE") + AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE") + AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_PARTY_DAMAGE") +end + +function AntiDaze_SetEnabled(enabled, silent) + local saved = AntiDaze_GetSaved() + if enabled then + saved["enabled"] = 1 + AntiDaze_RegisterCombatEvents() + if not silent then + AntiDaze_Print("AntiDaze enabled.") + end + else + saved["enabled"] = nil + AntiDaze_UnregisterCombatEvents() + if not silent then + AntiDaze_Print("AntiDaze disabled.") + end + end +end + +function AntiDaze_GetEnabled() + return AntiDaze_IsEnabled() +end + +local function AntiDaze_ResolvePlayerFromMessage(msg) + local afflictedPattern = _G["ZHUNTER_AFFLICTED_DAZED"] or "(.+) (.+) afflicted by Dazed" + local _, _, player = string.find(msg or "", afflictedPattern) + if player then + return player + end + + local lowerMsg = string.lower(msg or "") + if string.find(lowerMsg, "dazed", 1, true) or string.find(lowerMsg, "daze", 1, true) then + local playerName = UnitName("player") + if string.find(msg or "", "You", 1, true) or (playerName and string.find(msg or "", playerName, 1, true)) then + return _G["ZHUNTER_YOU"] or "You" + end + + for i = 1, GetNumPartyMembers() do + local partyName = UnitName("party" .. i) + if partyName and string.find(msg or "", partyName, 1, true) then + return partyName + end + end + end + + return nil +end + +local function AntiDaze_CancelRelevantAspect(isSelfDazed) + local packName = _G["ZHUNTER_ASPECT_PACK"] or "Aspect of the Pack" + local cheetahName = _G["ZHUNTER_ASPECT_CHEETAH"] or "Aspect of the Cheetah" + + for i = 0, 32 do + if GetPlayerBuff(i, "HELPFUL") < 0 then + break + end + + AntiDazeTooltip:SetOwner(UIParent, "ANCHOR_NONE") + AntiDazeTooltip:SetPlayerBuff(i, "HELPFUL") + local text = AntiDazeTooltipTextLeft1 and AntiDazeTooltipTextLeft1:GetText() + if text == packName or (isSelfDazed and text == cheetahName) then + CancelPlayerBuff(i) + return 1 + end + end + + return nil +end + +local function AntiDaze_HandleSlash() + AntiDaze_SetEnabled(not AntiDaze_IsEnabled()) +end + +AntiDazeTooltip = CreateFrame("GameTooltip", "AntiDazeTooltip", nil, "GameTooltipTemplate") +AntiDazeFrame = CreateFrame("Frame", "AntiDazeFrame") +if AntiDazeFrame then + AntiDazeFrame:RegisterEvent("VARIABLES_LOADED") + AntiDazeFrame:SetScript("OnEvent", function() + if event == "VARIABLES_LOADED" then + if AntiDaze_IsEnabled() then + AntiDaze_RegisterCombatEvents() + else + AntiDaze_UnregisterCombatEvents() + end + return + end + + local player = AntiDaze_ResolvePlayerFromMessage(arg1) + if player then + local you = _G["ZHUNTER_YOU"] or "You" + local group = {} + group[you] = 1 + for i = 1, GetNumPartyMembers() do + group[UnitName("party" .. i)] = 1 + end + if group[player] then + AntiDaze_CancelRelevantAspect(player == you) + end + end + end) +end + +SLASH_ANTIDAZE1 = "/antidaze" +SlashCmdList["ANTIDAZE"] = AntiDaze_HandleSlash +SLASH_ZANTIDAZE1 = "/zantidaze" +SlashCmdList["ZANTIDAZE"] = AntiDaze_HandleSlash diff --git a/api/AutoStrip.lua b/api/AutoStrip.lua new file mode 100644 index 0000000..ce17ff3 --- /dev/null +++ b/api/AutoStrip.lua @@ -0,0 +1,189 @@ +local function AutoStrip_Print(msg) + if MTH and MTH.Print then + MTH:Print(msg) + elseif DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + DEFAULT_CHAT_FRAME:AddMessage(tostring(msg)) + end +end + +function AutoStrip_GetSaved() + if type(MTH_CharSavedVariables) ~= "table" then + MTH_CharSavedVariables = {} + end + if type(MTH_CharSavedVariables.autoStrip) ~= "table" then + MTH_CharSavedVariables.autoStrip = {} + end + return MTH_CharSavedVariables.autoStrip +end + +local AUTO_STRIP_ORDER = {16, 17, 18, 5, 7, 1, 3, 10, 8, 6, 9} +AutoStrip_On = nil +AutoStrip_WasInCombat = nil +AutoStrip_Frame = nil + +function AutoStrip_SetDisplayIcon() + local icon = getglobal("AutoStripDisplayIcon") or getglobal("AutoStripDisplayIconTexture") + if icon then + icon:SetTexture("Interface\\Icons\\Ability_Creature_Cursed_02") + icon:Show() + end +end + +function AutoStrip_GetEnabled() + return AutoStrip_On and true or false +end + +function AutoStrip_SetAutoStripToggle(enabled, silent) + local saved = AutoStrip_GetSaved() + if enabled then + saved["autostrip"] = 1 + AutoStrip_On = 1 + if AutoStripDisplay and AutoStripDisplay:IsVisible() and AutoStripDisplayAutoCast then + AutoStripDisplayAutoCast:Show() + end + if not silent then + AutoStrip_Print("Auto-Strip enabled.") + end + else + saved["autostrip"] = nil + AutoStrip_On = nil + if AutoStripDisplayAutoCast then + AutoStripDisplayAutoCast:Hide() + end + if not silent then + AutoStrip_Print("Auto-Strip disabled.") + end + end +end + +function AutoStrip_SetDisplayToggle(enabled, silent) + local saved = AutoStrip_GetSaved() + if enabled then + saved["display"] = 1 + if AutoStripDisplay then + AutoStripDisplay:Show() + AutoStrip_SetDisplayIcon() + if AutoStrip_On and AutoStripDisplayAutoCast then + AutoStripDisplayAutoCast:Show() + end + end + if not silent then + AutoStrip_Print("AutoStrip button is now visible.") + end + else + saved["display"] = nil + if AutoStripDisplay then + AutoStripDisplay:Hide() + end + if not silent then + AutoStrip_Print("AutoStrip button is now hidden.") + end + end +end + +function AutoStrip_UnequipAll(weaponsOnly) + AutoStrip_On = nil + if AutoStripDisplayAutoCast then + AutoStripDisplayAutoCast:Hide() + end + + local hasEmptySlot = nil + for bag = 0, 4 do + for slot = 1, GetContainerNumSlots(bag) do + if not GetContainerItemLink(bag, slot) then + hasEmptySlot = 1 + break + end + end + if hasEmptySlot then + break + end + end + + if not hasEmptySlot then + AutoStrip_Print("You need at least one empty bag slot to auto-strip.") + return + end + + local startIndex = 1 + local finishIndex = weaponsOnly and 3 or table.getn(AUTO_STRIP_ORDER) + for bag = 0, 4 do + for slot = 1, GetContainerNumSlots(bag) do + if not GetContainerItemLink(bag, slot) then + for index = startIndex, finishIndex do + if GetInventoryItemLink("player", AUTO_STRIP_ORDER[index]) then + PickupInventoryItem(AUTO_STRIP_ORDER[index]) + PickupContainerItem(bag, slot) + startIndex = index + 1 + break + end + end + end + end + end +end + +local function AutoStrip_OnEvent() + if event == "VARIABLES_LOADED" then + local saved = AutoStrip_GetSaved() + AutoStrip_WasInCombat = UnitAffectingCombat("player") and 1 or nil + AutoStrip_SetDisplayToggle(saved["display"] and true or false, 1) + AutoStrip_SetAutoStripToggle(saved["autostrip"] and true or false, 1) + return + end + + if event == "PLAYER_ENTERING_WORLD" then + AutoStrip_WasInCombat = UnitAffectingCombat("player") and 1 or nil + return + end + + if event == "PLAYER_REGEN_DISABLED" then + AutoStrip_WasInCombat = 1 + return + end + + if event == "PLAYER_REGEN_ENABLED" then + if AutoStrip_On and AutoStrip_WasInCombat then + AutoStrip_UnequipAll() + end + AutoStrip_WasInCombat = nil + end +end + +local function AutoStrip_HandleSlash(msg) + local saved = AutoStrip_GetSaved() + if msg == "display" then + AutoStrip_SetDisplayToggle(not (saved["display"] and true or false)) + elseif msg == "toggle" then + local enableAutoStrip = not AutoStrip_GetEnabled() + if enableAutoStrip and not saved["display"] then + AutoStrip_SetDisplayToggle(true, 1) + end + AutoStrip_SetAutoStripToggle(enableAutoStrip, 1) + if enableAutoStrip then + AutoStrip_Print("You will strip when you leave combat.") + else + AutoStrip_Print("You will no longer strip when you leave combat.") + end + else + AutoStrip_Print("Possible Commands: \"display\", \"toggle\"") + end +end + +AutoStrip_Frame = CreateFrame("Frame", "AutoStripFrame") +if AutoStrip_Frame then + AutoStrip_Frame:RegisterEvent("VARIABLES_LOADED") + AutoStrip_Frame:RegisterEvent("PLAYER_REGEN_DISABLED") + AutoStrip_Frame:RegisterEvent("PLAYER_REGEN_ENABLED") + AutoStrip_Frame:RegisterEvent("PLAYER_ENTERING_WORLD") + AutoStrip_Frame:SetScript("OnEvent", AutoStrip_OnEvent) +end + +if AutoStripDisplay then + AutoStrip_SetDisplayIcon() +end + +SLASH_AUTOSTRIP1 = "/autostrip" +SlashCmdList["AUTOSTRIP"] = AutoStrip_HandleSlash +SLASH_ZSTRIP1 = "/zstrip" +SlashCmdList["ZSTRIP"] = AutoStrip_HandleSlash diff --git a/api/AutoStrip.xml b/api/AutoStrip.xml new file mode 100644 index 0000000..b7646ff --- /dev/null +++ b/api/AutoStrip.xml @@ -0,0 +1,97 @@ + + + diff --git a/api/config.lua b/api/config.lua new file mode 100644 index 0000000..ecdb00b --- /dev/null +++ b/api/config.lua @@ -0,0 +1,58 @@ +------------------------------------------------------ +-- MetaHunt Configuration System +------------------------------------------------------ + +-- Global config management for modules +MTH.config = {} + +local function MTH_ConfigEnsureStore(module_name) + if type(module_name) ~= "string" or module_name == "" then + module_name = "core" + end + + local root = _G and _G.MTH_SavedVariables or MTH_SavedVariables + if type(root) ~= "table" then + if MTH and MTH.InitSavedVariables then + MTH:InitSavedVariables() + end + root = _G and _G.MTH_SavedVariables or MTH_SavedVariables + end + + if type(root) ~= "table" then + root = { modules = {} } + if _G then + _G.MTH_SavedVariables = root + end + MTH_SavedVariables = root + end + + if type(root.modules) ~= "table" then + root.modules = {} + end + + if type(root.modules[module_name]) ~= "table" then + local legacy = root[module_name] + if type(legacy) ~= "table" then + legacy = {} + end + root.modules[module_name] = legacy + end + + root[module_name] = root.modules[module_name] + MTH_SavedVariables = root + return root.modules[module_name] +end + +function MTH:GetConfig(module_name, key, default) + local store = MTH_ConfigEnsureStore(module_name) + local value = store[key] + if value == nil then + return default + end + return value +end + +function MTH:SetConfig(module_name, key, value) + local store = MTH_ConfigEnsureStore(module_name) + store[key] = value +end diff --git a/api/constants.lua b/api/constants.lua new file mode 100644 index 0000000..f428a6a --- /dev/null +++ b/api/constants.lua @@ -0,0 +1,152 @@ +MTH_CONST = MTH_CONST or {} +MTH_CONST.version = MTH_CONST.version or "1.0.3" + +MTH_CONST.WEAPON_TYPES = { + BOWS = "Bows", + CROSSBOWS = "Crossbows", + GUNS = "Guns", +} + +MTH_CONST.AMMO = { + BULLETS = { + LIGHT = "Light Shot", + CRAFTLIGHT = "Crafted Light Shot", + FLASH = "Flash Pellet", + HEAVY = "Heavy Shot", + PEBBLE = "Smooth Pebble", + CRAFTHEAVY = "Crafted Heavy Shot", + SOLID = "Solid Shot", + CRAFTSOLID = "Crafted Solid Shot", + EXPLODING = "Exploding Shot", + MITHRILSLUG = "Hi-impact Mithril Slugs", + ACCURATE = "Accurate Slugs", + GYROSHOT = "Mithril Gyro-Shot", + ICETHREADED = "Ice Threaded Bullet", + THORIUM = "Thorium Shells", + ROCKSHARD = "Rockshard Pellets", + CANNONBALL = "Miniature Cannon Balls", + }, + ARROWS = { + ROUGH = "Rough Arrow", + SHARP = "Sharp Arrow", + RAZOR = "Razor Arrow", + FEATHER = "Feathered Arrow", + PRECISION = "Precision Arrow", + JAGGED = "Jagged Arrow", + ICETHREADED = "Ice Threaded Arrow", + THORIUM = "Thorium Headed Arrow", + DOOMSHOT = "Doomshot", + }, + JUNKSHOTS = { + SERPENT = "Serpent Sting", + SCORPID = "Scorpid Sting", + VIPER = "Viper Sting", + ARCANE = "Arcane Shot", + CONCUSSIVE = "Concussive Shot", + DISTRACTING = "Distracting Shot", + BAITED = "Baited Shot", + TRANQ = "Tranquilizing Shot", + }, +} + +MTH_WEAPON_BOWS = MTH_CONST.WEAPON_TYPES.BOWS +MTH_WEAPON_CROSSBOWS = MTH_CONST.WEAPON_TYPES.CROSSBOWS +MTH_WEAPON_GUNS = MTH_CONST.WEAPON_TYPES.GUNS + +BULLETS_LIGHT = MTH_CONST.AMMO.BULLETS.LIGHT +BULLETS_CRAFTLIGHT = MTH_CONST.AMMO.BULLETS.CRAFTLIGHT +BULLETS_FLASH = MTH_CONST.AMMO.BULLETS.FLASH +BULLETS_HEAVY = MTH_CONST.AMMO.BULLETS.HEAVY +BULLETS_PEBBLE = MTH_CONST.AMMO.BULLETS.PEBBLE +BULLETS_CRAFTHEAVY = MTH_CONST.AMMO.BULLETS.CRAFTHEAVY +BULLETS_SOLID = MTH_CONST.AMMO.BULLETS.SOLID +BULLETS_CRAFTSOLID = MTH_CONST.AMMO.BULLETS.CRAFTSOLID +BULLETS_EXPLODING = MTH_CONST.AMMO.BULLETS.EXPLODING +BULLETS_MITHRILSLUG = MTH_CONST.AMMO.BULLETS.MITHRILSLUG +BULLETS_ACCURATE = MTH_CONST.AMMO.BULLETS.ACCURATE +BULLETS_GYROSHOT = MTH_CONST.AMMO.BULLETS.GYROSHOT +BULLETS_ICETHREADED = MTH_CONST.AMMO.BULLETS.ICETHREADED +BULLETS_THORIUM = MTH_CONST.AMMO.BULLETS.THORIUM +BULLETS_ROCKSHARD = MTH_CONST.AMMO.BULLETS.ROCKSHARD +BULLETS_CANNONBALL = MTH_CONST.AMMO.BULLETS.CANNONBALL + +ARROWS_ROUGH = MTH_CONST.AMMO.ARROWS.ROUGH +ARROWS_SHARP = MTH_CONST.AMMO.ARROWS.SHARP +ARROWS_RAZOR = MTH_CONST.AMMO.ARROWS.RAZOR +ARROWS_FEATHER = MTH_CONST.AMMO.ARROWS.FEATHER +ARROWS_PRECISION = MTH_CONST.AMMO.ARROWS.PRECISION +ARROWS_JAGGED = MTH_CONST.AMMO.ARROWS.JAGGED +ARROWS_ICETHREADED = MTH_CONST.AMMO.ARROWS.ICETHREADED +ARROWS_THORIUM = MTH_CONST.AMMO.ARROWS.THORIUM +ARROWS_DOOMSHOT = MTH_CONST.AMMO.ARROWS.DOOMSHOT + +JUNKSHOT_SERPENT = MTH_CONST.AMMO.JUNKSHOTS.SERPENT +JUNKSHOT_SCORPID = MTH_CONST.AMMO.JUNKSHOTS.SCORPID +JUNKSHOT_VIPER = MTH_CONST.AMMO.JUNKSHOTS.VIPER +JUNKSHOT_ARCANE = MTH_CONST.AMMO.JUNKSHOTS.ARCANE +JUNKSHOT_CONCUSSIVE = MTH_CONST.AMMO.JUNKSHOTS.CONCUSSIVE +JUNKSHOT_DISTRACTING = MTH_CONST.AMMO.JUNKSHOTS.DISTRACTING + +MTH_AMMO_BULLETS = { + BULLETS_LIGHT, + BULLETS_CRAFTLIGHT, + BULLETS_FLASH, + BULLETS_HEAVY, + BULLETS_PEBBLE, + BULLETS_CRAFTHEAVY, + BULLETS_SOLID, + BULLETS_CRAFTSOLID, + BULLETS_EXPLODING, + BULLETS_MITHRILSLUG, + BULLETS_ACCURATE, + BULLETS_GYROSHOT, + BULLETS_ICETHREADED, + BULLETS_THORIUM, + BULLETS_ROCKSHARD, + BULLETS_CANNONBALL, +} + +MTH_AMMO_ARROWS = { + ARROWS_ROUGH, + ARROWS_SHARP, + ARROWS_RAZOR, + ARROWS_FEATHER, + ARROWS_PRECISION, + ARROWS_JAGGED, + ARROWS_ICETHREADED, + ARROWS_THORIUM, + ARROWS_DOOMSHOT, +} + +MTH_AMMO_JUNKSHOTS = { + JUNKSHOT_SERPENT, + JUNKSHOT_SCORPID, + JUNKSHOT_VIPER, + JUNKSHOT_ARCANE, + JUNKSHOT_CONCUSSIVE, + JUNKSHOT_DISTRACTING, +} + +MTH_AMMO_BULLET_RANK = {} +for i = 1, table.getn(MTH_AMMO_BULLETS) do + local ammoName = MTH_AMMO_BULLETS[i] + if ammoName then + MTH_AMMO_BULLET_RANK[ammoName] = i + end +end + +MTH_AMMO_ARROW_RANK = {} +for i = 1, table.getn(MTH_AMMO_ARROWS) do + local ammoName = MTH_AMMO_ARROWS[i] + if ammoName then + MTH_AMMO_ARROW_RANK[ammoName] = i + end +end + +MTH_AMMO_JUNKSHOT_SET = {} +for i = 1, table.getn(MTH_AMMO_JUNKSHOTS) do + local spellName = MTH_AMMO_JUNKSHOTS[i] + if spellName then + MTH_AMMO_JUNKSHOT_SET[spellName] = 1 + end +end diff --git a/api/core-feed-tracking.lua b/api/core-feed-tracking.lua new file mode 100644 index 0000000..c19923a --- /dev/null +++ b/api/core-feed-tracking.lua @@ -0,0 +1,1547 @@ +local MTH_FEED_SCHEMA_VERSION = 3 +local MTH_FEED_RECENT_LIMIT = 40 + +MTH_FEED_API_VERSION = 1 + +local MTH_FEED_Runtime = { + nextAttemptId = 1, + attempts = {}, + sessionFeedsByPetId = {}, +} + +local MTH_FEED_CORE_HOOK_BOUNDARY_KEY = "core-feed-tracking-hooks" +MTH_FEED_Original_DropItemOnUnit = MTH_FEED_Original_DropItemOnUnit or nil +local MTH_FEED_TrackerFrame = nil +local MTH_FEED_ActiveCoreAttemptId = nil +local MTH_FEED_BootstrapFrame = nil +local MTH_FEED_NameToItemCache = {} + +local MTH_FEED_HARDCODED_LEVEL_RULES = { + { minPetLevel = 1, minFoodLevel = 1 }, + { minPetLevel = 20, minFoodLevel = 5 }, + { minPetLevel = 31, minFoodLevel = 15 }, + { minPetLevel = 40, minFoodLevel = 25 }, + { minPetLevel = 50, minFoodLevel = 35 }, + { minPetLevel = 60, minFoodLevel = 45 }, +} + +MTH_FEED_TRACE = false + +local function MTH_FEED_Trace(message) + return +end + +local function MTH_FEED_Now() + return time() +end + +local function MTH_FEED_Trim(text) + local value = tostring(text or "") + value = string.gsub(value, "^%s+", "") + value = string.gsub(value, "%s+$", "") + return value +end + +local function MTH_FEED_SafeLower(text) + return string.lower(MTH_FEED_Trim(text)) +end + +local function MTH_FEED_NormalizeItemName(text) + local value = MTH_FEED_SafeLower(text) + value = string.gsub(value, "[%[%]]", "") + value = string.gsub(value, "^%s+", "") + value = string.gsub(value, "%s+$", "") + value = string.gsub(value, "%s+", " ") + return value +end + +local function MTH_FEED_IsHyperlinkText(linkText) + if type(linkText) ~= "string" then + return false + end + if string.find(linkText, "|Hitem:", 1, true) then + return true + end + if string.find(linkText, "^item:%d+") then + return true + end + return false +end + +local function MTH_FEED_NormalizeItemLink(itemId, itemName, itemLink) + local raw = MTH_FEED_Trim(itemLink) + if raw ~= "" and MTH_FEED_IsHyperlinkText(raw) then + return raw + end + + local numericId = tonumber(itemId) + if numericId then + if type(MTH_GetClickableItemLink) == "function" then + local clickable = MTH_GetClickableItemLink(numericId, itemName, false) + if clickable and clickable ~= "" and MTH_FEED_IsHyperlinkText(tostring(clickable)) then + return tostring(clickable) + end + end + return "item:" .. tostring(numericId) .. ":0:0:0" + end + + return nil +end + +local function MTH_FEED_MigrateItemLinkShapes(store) + if type(store) ~= "table" then + return + end + + if type(store.byPetId) == "table" then + for _, petRow in pairs(store.byPetId) do + if type(petRow) == "table" and type(petRow.byItemId) == "table" then + for itemKey, itemRow in pairs(petRow.byItemId) do + if type(itemRow) == "table" then + local normalized = MTH_FEED_NormalizeItemLink(itemKey, itemRow.itemName, itemRow.itemLink) + itemRow.itemLink = normalized + end + end + end + end + end + + if type(store.unknownFoods) == "table" and type(store.unknownFoods.byItemId) == "table" then + for itemKey, unknownRow in pairs(store.unknownFoods.byItemId) do + if type(unknownRow) == "table" then + local normalized = MTH_FEED_NormalizeItemLink(itemKey, unknownRow.itemName, unknownRow.itemLink) + unknownRow.itemLink = normalized + end + end + end +end + +local function MTH_FEED_IndexBagFoods() + if type(GetContainerNumSlots) ~= "function" or type(GetContainerItemLink) ~= "function" then + return false + end + + local cache = {} + for bag = 0, 4 do + local slots = tonumber(GetContainerNumSlots(bag)) or 0 + for slot = 1, slots do + local link = GetContainerItemLink(bag, slot) + if link then + local itemId = nil + local _, _, idText = string.find(tostring(link), "item:(%d+)") + if idText then + itemId = tonumber(idText) + end + + local linkName = nil + local _, _, parsedName = string.find(tostring(link), "|h%[([^%]]+)%]|h") + if parsedName and parsedName ~= "" then + linkName = parsedName + end + + local infoName = nil + if itemId and type(GetItemInfo) == "function" then + infoName = GetItemInfo(itemId) + end + + local resolvedName = infoName or linkName + local key = MTH_FEED_NormalizeItemName(resolvedName) + if key ~= "" then + link = MTH_FEED_NormalizeItemLink(itemId, resolvedName, link) + cache[key] = { + itemId = itemId, + itemLink = link, + itemName = resolvedName, + seenAt = MTH_FEED_Now(), + } + end + end + end + end + + MTH_FEED_NameToItemCache = cache + return true +end + +local function MTH_FEED_HasPetFeedBuff() + if type(UnitBuff) ~= "function" then + return false + end + local index = 1 + local buff = UnitBuff("pet", index) + while buff do + if string.find(tostring(buff), "Ability_Hunter_BeastTraining", 1, true) then + return true + end + index = index + 1 + buff = UnitBuff("pet", index) + end + return false +end + +local function MTH_FEED_GetCursorItemSnapshot() + if type(GetCursorInfo) ~= "function" then + return nil, nil, nil, nil + end + local infoType, idOrLink, maybeLink = GetCursorInfo() + if tostring(infoType or "") ~= "item" then + return nil, nil, nil, nil + end + + local itemId = tonumber(idOrLink) + local itemLink = nil + if type(maybeLink) == "string" and maybeLink ~= "" then + itemLink = maybeLink + elseif type(idOrLink) == "string" and string.find(idOrLink, "|Hitem:", 1, true) then + itemLink = idOrLink + end + + local itemName = nil + local itemIcon = nil + if itemId and type(GetItemInfo) == "function" then + itemName, _, _, _, _, _, _, _, _, itemIcon = GetItemInfo(itemId) + end + if (not itemIcon or itemIcon == "") and itemId and type(GetItemIcon) == "function" then + itemIcon = GetItemIcon(itemId) + end + itemLink = MTH_FEED_NormalizeItemLink(itemId, itemName, itemLink) + + return itemId, itemName, itemLink, itemIcon +end + +local function MTH_FEED_FindItemByNameInBags(foodName) + local target = MTH_FEED_NormalizeItemName(foodName) + if target == "" then + return nil, nil + end + if type(GetContainerNumSlots) ~= "function" or type(GetContainerItemLink) ~= "function" then + return nil, nil + end + + for bag = 0, 4 do + local slots = tonumber(GetContainerNumSlots(bag)) or 0 + for slot = 1, slots do + local link = GetContainerItemLink(bag, slot) + if link then + local linkName = nil + local _, _, parsedName = string.find(tostring(link), "|h%[([^%]]+)%]|h") + if parsedName and parsedName ~= "" then + linkName = parsedName + end + + local itemId = nil + local _, _, idText = string.find(tostring(link), "item:(%d+)") + if idText then + itemId = tonumber(idText) + end + + local itemName = nil + if itemId and type(GetItemInfo) == "function" then + itemName = GetItemInfo(itemId) + if itemName and itemName ~= "" then + itemName = tostring(itemName) + end + end + + local resolvedName = itemName or linkName + if resolvedName and MTH_FEED_NormalizeItemName(resolvedName) == target then + return itemId, link + end + end + end + end + + local cached = MTH_FEED_NameToItemCache and MTH_FEED_NameToItemCache[target] or nil + if type(cached) == "table" then + return cached.itemId, cached.itemLink + end + + return nil, nil +end + +local function MTH_FEED_FindItemIconInBags(itemId, itemName) + if type(GetContainerNumSlots) ~= "function" or type(GetContainerItemLink) ~= "function" or type(GetContainerItemInfo) ~= "function" then + return nil + end + + local targetId = tonumber(itemId) + local targetName = MTH_FEED_NormalizeItemName(itemName) + for bag = 0, 4 do + local slots = tonumber(GetContainerNumSlots(bag)) or 0 + for slot = 1, slots do + local link = GetContainerItemLink(bag, slot) + if link then + local matched = false + if targetId then + local _, _, idText = string.find(tostring(link), "item:(%d+)") + if tonumber(idText) == targetId then + matched = true + end + end + if not matched and targetName ~= "" then + local _, _, parsedName = string.find(tostring(link), "|h%[([^%]]+)%]|h") + if parsedName and MTH_FEED_NormalizeItemName(parsedName) == targetName then + matched = true + end + end + if matched then + local texture = GetContainerItemInfo(bag, slot) + if texture and texture ~= "" then + return texture + end + end + end + end + end + + return nil +end + +local function MTH_FEED_GetAttemptElapsed(attemptId) + local row = MTH_FEED_Runtime.attempts[attemptId] + if type(row) ~= "table" then + return nil + end + local now = (type(GetTime) == "function") and (GetTime() or 0) or 0 + local startedAt = tonumber(row.startedAt) or 0 + return now - startedAt +end + +local function MTH_FEED_ClearActiveCoreAttempt(attemptId) + if tostring(MTH_FEED_ActiveCoreAttemptId or "") == tostring(attemptId or "") then + MTH_FEED_ActiveCoreAttemptId = nil + end +end + +local function MTH_FEED_HandleCoreReject(reason, rawMessage) + local attemptId = MTH_FEED_ActiveCoreAttemptId + if attemptId == nil then + return + end + MTH_FEED_RecordRejectEvent(attemptId, { + reason = reason, + rawMessage = rawMessage, + }) + MTH_FEED_FinalizeAttempt(attemptId, { + outcome = "rejected", + reason = reason, + }) + MTH_FEED_ClearActiveCoreAttempt(attemptId) +end + +local function MTH_FEED_HandleCoreAccept() + local attemptId = MTH_FEED_ActiveCoreAttemptId + if attemptId == nil then + return + end + local elapsed = MTH_FEED_GetAttemptElapsed(attemptId) + MTH_FEED_RecordBuffOutcome(attemptId, { + hasFeedBuff = true, + elapsed = elapsed, + }) + MTH_FEED_FinalizeAttempt(attemptId, { + outcome = "accepted", + reason = "accepted", + }) + MTH_FEED_ClearActiveCoreAttempt(attemptId) +end + +local function MTH_FEED_EnsureActiveAttemptFromCursor(sourceTag) + if MTH_FEED_ActiveCoreAttemptId then + return MTH_FEED_ActiveCoreAttemptId + end + if not (CursorHasItem and CursorHasItem()) then + return nil + end + + local itemId, itemName, itemLink, itemIcon = MTH_FEED_GetCursorItemSnapshot() + local pet = MTH_FEED_GetCurrentPetContext() + if type(pet) ~= "table" then + return nil + end + + local attemptId = MTH_FEED_BeginAttempt({ + itemId = itemId, + itemName = itemName, + itemLink = itemLink, + itemIcon = itemIcon, + source = sourceTag or "core:ui-error", + petId = pet.petId, + petName = pet.petName, + family = pet.family, + petLevel = pet.level, + foodLevel = itemId and MTH_FEED_GetFoodLevel(itemId) or nil, + }) + if attemptId then + MTH_FEED_ActiveCoreAttemptId = attemptId + MTH_FEED_RecordClientDropResult(attemptId, { consumedFromCursor = false }) + MTH_FEED_Trace("Core tracker bootstrapped attempt from cursor id=" .. tostring(attemptId) + .. " itemId=" .. tostring(itemId) + .. " source=" .. tostring(sourceTag or "core:ui-error")) + end + return attemptId +end + +local function MTH_FEED_CoreTrackerOnEvent() + local evt = event + if evt == "UI_ERROR_MESSAGE" then + local msg = tostring(arg1 or "") + local lower = MTH_FEED_SafeLower(msg) + local isLowLevel = (SPELL_FAILED_FOOD_LOWLEVEL and string.find(msg, SPELL_FAILED_FOOD_LOWLEVEL)) + or string.find(lower, "low level", 1, true) + or string.find(lower, "too low level", 1, true) + or string.find(lower, "level too low", 1, true) + local isWrongFood = (SPELL_FAILED_WRONG_PET_FOOD and string.find(msg, SPELL_FAILED_WRONG_PET_FOOD)) + or string.find(lower, "wrong pet food", 1, true) + or string.find(lower, "doesn't like", 1, true) + or string.find(lower, "does not like", 1, true) + + if isLowLevel then + MTH_FEED_EnsureActiveAttemptFromCursor("core:ui-error-low-level") + MTH_FEED_Trace("Core tracker UI_ERROR_MESSAGE => low-level") + MTH_FEED_HandleCoreReject("low-level", msg) + elseif isWrongFood then + MTH_FEED_EnsureActiveAttemptFromCursor("core:ui-error-wrong-food") + MTH_FEED_Trace("Core tracker UI_ERROR_MESSAGE => wrong-food") + MTH_FEED_HandleCoreReject("wrong-food", msg) + end + elseif evt == "CHAT_MSG_SPELL_TRADESKILLS" then + local line = tostring(arg1 or "") + local feedPattern = nil + if FEEDPET_LOG_FIRSTPERSON then + feedPattern = GFWUtils and GFWUtils.FormatToPattern and GFWUtils.FormatToPattern(FEEDPET_LOG_FIRSTPERSON) or nil + end + if feedPattern then + local _, _, foodName = string.find(line, feedPattern) + if foodName and foodName ~= "" then + local nowTs = (type(GetTime) == "function") and (GetTime() or 0) or 0 + local suppressUntil = tonumber((_G and _G["MTH_FEED_SuppressChatUntil"]) or 0) or 0 + local suppressFood = tostring((_G and _G["MTH_FEED_SuppressChatFoodName"]) or "") + if suppressUntil > 0 and nowTs > suppressUntil then + if _G then + _G["MTH_FEED_SuppressChatUntil"] = nil + _G["MTH_FEED_SuppressChatFoodName"] = nil + end + suppressUntil = 0 + suppressFood = "" + end + if suppressUntil > 0 and nowTs <= suppressUntil then + local normalizedSuppress = MTH_FEED_NormalizeItemName(suppressFood) + local normalizedFood = MTH_FEED_NormalizeItemName(foodName) + if normalizedSuppress == "" or normalizedSuppress == normalizedFood then + MTH_FEED_Trace("Core tracker chat suppressed for food='" .. tostring(foodName) .. "' (owned by FeedOMatic)") + return + end + end + + local itemId, itemLink = MTH_FEED_FindItemByNameInBags(foodName) + local pet = MTH_FEED_GetCurrentPetContext() + if pet and (itemId or foodName) then + local attemptId = MTH_FEED_BeginAttempt({ + itemId = itemId, + itemName = foodName, + itemLink = itemLink, + source = "core:manual-chat", + petId = pet.petId, + petName = pet.petName, + family = pet.family, + petLevel = pet.level, + foodLevel = MTH_FEED_GetFoodLevel(itemId), + }) + if attemptId then + MTH_FEED_ActiveCoreAttemptId = attemptId + MTH_FEED_Trace("Core tracker chat started attempt id=" .. tostring(attemptId) + .. " food='" .. tostring(foodName) .. "' itemId=" .. tostring(itemId)) + end + else + MTH_FEED_Trace("Core tracker chat feed detected but item lookup failed for '" .. tostring(foodName) .. "'") + end + end + end + elseif evt == "BAG_UPDATE" then + MTH_FEED_IndexBagFoods() + elseif evt == "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS" then + if MTH_FEED_ActiveCoreAttemptId and MTH_FEED_HasPetFeedBuff() then + MTH_FEED_Trace("Core tracker buff event confirms acceptance") + MTH_FEED_HandleCoreAccept() + end + end +end + +local function MTH_FEED_CoreTrackerOnUpdate() + local attemptId = MTH_FEED_ActiveCoreAttemptId + if attemptId == nil then + return + end + local elapsed = MTH_FEED_GetAttemptElapsed(attemptId) + if elapsed == nil then + MTH_FEED_ClearActiveCoreAttempt(attemptId) + return + end + + if MTH_FEED_HasPetFeedBuff() then + MTH_FEED_Trace("Core tracker OnUpdate detects feed buff") + MTH_FEED_HandleCoreAccept() + return + end + + if elapsed >= 2.50 then + MTH_FEED_Trace("Core tracker timeout => no-buff reject elapsed=" .. tostring(elapsed)) + MTH_FEED_RecordBuffOutcome(attemptId, { + hasFeedBuff = false, + elapsed = elapsed, + }) + MTH_FEED_HandleCoreReject("no-buff", nil) + end +end + +local function MTH_FEED_EnsureTrackerFrame() + if MTH_FEED_TrackerFrame then + return MTH_FEED_TrackerFrame + end + local frame = CreateFrame("Frame", "MTH_FeedTrackingFrame") + if not frame then + return nil + end + frame:RegisterEvent("UI_ERROR_MESSAGE") + frame:RegisterEvent("BAG_UPDATE") + frame:RegisterEvent("CHAT_MSG_SPELL_TRADESKILLS") + frame:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS") + frame:SetScript("OnEvent", MTH_FEED_CoreTrackerOnEvent) + frame:SetScript("OnUpdate", MTH_FEED_CoreTrackerOnUpdate) + MTH_FEED_TrackerFrame = frame + MTH_FEED_IndexBagFoods() + return frame +end + +local function MTH_FEED_CoreDropItemOnUnitHook(unit) + local target = tostring(unit or "") + local skip = (_G and _G["MTH_FEED_SuppressCoreDropHook"]) and true or false + if skip then + if type(MTH_FEED_Original_DropItemOnUnit) == "function" then + return MTH_FEED_Original_DropItemOnUnit(unit) + end + MTH_FEED_Trace("Core hook bypass failed: original DropItemOnUnit missing") + return + end + + if target == "pet" and CursorHasItem and CursorHasItem() then + local itemId, itemName, itemLink, itemIcon = MTH_FEED_GetCursorItemSnapshot() + local pet = MTH_FEED_GetCurrentPetContext() + if not itemId then + MTH_FEED_Trace("Core hook pet drop detected but cursor item could not be resolved") + end + if pet and itemId then + local attemptId = MTH_FEED_BeginAttempt({ + itemId = itemId, + itemName = itemName, + itemLink = itemLink, + itemIcon = itemIcon, + source = "core:manual-drop", + petId = pet.petId, + petName = pet.petName, + family = pet.family, + petLevel = pet.level, + foodLevel = MTH_FEED_GetFoodLevel(itemId), + }) + MTH_FEED_ActiveCoreAttemptId = attemptId + MTH_FEED_Trace("Core hook started manual attempt id=" .. tostring(attemptId) + .. " itemId=" .. tostring(itemId) + .. " petId=" .. tostring(pet.petId)) + end + end + + if type(MTH_FEED_Original_DropItemOnUnit) == "function" then + MTH_FEED_Original_DropItemOnUnit(unit) + else + MTH_FEED_Trace("Core hook failed: original DropItemOnUnit missing") + return + end + + local activeId = MTH_FEED_ActiveCoreAttemptId + if activeId then + local consumed = (CursorHasItem and CursorHasItem()) and false or true + MTH_FEED_RecordClientDropResult(activeId, { consumedFromCursor = consumed }) + if not consumed then + MTH_FEED_Trace("Core hook immediate reject: cursor still has item") + MTH_FEED_HandleCoreReject("client-drop-fail", nil) + end + end +end + +local function MTH_FEED_InstallCoreHooks() + if type(DropItemOnUnit) ~= "function" then + MTH_FEED_Trace("InstallCoreHooks skipped: DropItemOnUnit unavailable") + return false + end + + if MTH_FEED_Original_DropItemOnUnit == nil or MTH_FEED_Original_DropItemOnUnit == MTH_FEED_CoreDropItemOnUnitHook then + MTH_FEED_Original_DropItemOnUnit = DropItemOnUnit + end + if DropItemOnUnit ~= MTH_FEED_CoreDropItemOnUnitHook then + DropItemOnUnit = MTH_FEED_CoreDropItemOnUnitHook + end + + if MTH and MTH.CaptureHookBoundary then + MTH:CaptureHookBoundary(MTH_FEED_CORE_HOOK_BOUNDARY_KEY, { + { globalName = "DropItemOnUnit", originalName = "MTH_FEED_Original_DropItemOnUnit" }, + }) + end + + return true +end + +local function MTH_FEED_IsCoreHookInstalled() + return type(DropItemOnUnit) == "function" and DropItemOnUnit == MTH_FEED_CoreDropItemOnUnitHook +end + +function MTH_FEED_DebugStatus() + local owner = nil + if MTH and MTH.GetGlobalHookOwner then + owner = MTH:GetGlobalHookOwner("DropItemOnUnit") + end + MTH_FEED_Trace("Status hookInstalled=" .. tostring(MTH_FEED_IsCoreHookInstalled()) + .. " owner=" .. tostring(owner) + .. " trackerFrame=" .. tostring(MTH_FEED_TrackerFrame ~= nil) + .. " activeAttemptId=" .. tostring(MTH_FEED_ActiveCoreAttemptId)) + return true +end + +function MTH_FEED_ReinstallCoreTracking() + MTH_FEED_EnsureTrackerFrame() + local ok = MTH_FEED_InstallCoreHooks() + return ok and true or false +end + +local function MTH_FEED_EnsureBootstrapFrame() + if MTH_FEED_BootstrapFrame ~= nil then + return MTH_FEED_BootstrapFrame + end + local frame = CreateFrame("Frame", "MTH_FeedTrackingBootstrapFrame") + if not frame then + return nil + end + frame:RegisterEvent("VARIABLES_LOADED") + frame:RegisterEvent("PLAYER_LOGIN") + frame:RegisterEvent("PLAYER_ENTERING_WORLD") + frame:SetScript("OnEvent", function() + MTH_FEED_ReinstallCoreTracking() + end) + MTH_FEED_BootstrapFrame = frame + return frame +end + +local function MTH_FEED_EnsureStore() + if type(MTH_CharSavedVariables) ~= "table" then + MTH_CharSavedVariables = {} + end + if type(MTH_CharSavedVariables.feedTracking) ~= "table" then + MTH_CharSavedVariables.feedTracking = {} + end + + local store = MTH_CharSavedVariables.feedTracking + if store.schemaVersion ~= MTH_FEED_SCHEMA_VERSION then + store.schemaVersion = MTH_FEED_SCHEMA_VERSION + end + if store.updatedAt == nil then + store.updatedAt = 0 + end + + if type(store.byPetId) ~= "table" then + store.byPetId = {} + end + if type(store.levelRule) ~= "table" then + store.levelRule = {} + end + if type(store.levelRule.observations) ~= "table" then + store.levelRule.observations = {} + end + if type(store.levelRule.derived) ~= "table" then + store.levelRule.derived = {} + end + if type(store.levelRule.derived.minAcceptedFoodLevelByPetLevel) ~= "table" then + store.levelRule.derived.minAcceptedFoodLevelByPetLevel = {} + end + if type(store.levelRule.derived.confidenceByPetLevel) ~= "table" then + store.levelRule.derived.confidenceByPetLevel = {} + end + if store.levelRule.derived.lastRecomputeAt == nil then + store.levelRule.derived.lastRecomputeAt = 0 + end + + if type(store.exceptions) ~= "table" then + store.exceptions = {} + end + if type(store.exceptions.byItemId) ~= "table" then + store.exceptions.byItemId = {} + end + + if type(store.unknownFoods) ~= "table" then + store.unknownFoods = {} + end + if type(store.unknownFoods.byItemId) ~= "table" then + store.unknownFoods.byItemId = {} + end + + if type(store.runtime) ~= "table" then + store.runtime = {} + end + if store.runtime.nextAttemptId == nil then + store.runtime.nextAttemptId = 1 + end + + MTH_FEED_MigrateItemLinkShapes(store) + + return store +end + +local function MTH_FEED_TouchStore(store) + store.updatedAt = MTH_FEED_Now() +end + +local function MTH_FEED_GetRuntimeAttemptId(store) + local nextId = tonumber(store.runtime.nextAttemptId) or 1 + store.runtime.nextAttemptId = nextId + 1 + return "feed-attempt-" .. tostring(nextId) +end + +local function MTH_FEED_EnsurePetRow(store, petContext) + local petId = tostring((petContext and petContext.petId) or "") + if petId == "" then + return nil + end + + local row = store.byPetId[petId] + if type(row) ~= "table" then + row = { + petId = petId, + petName = petContext and petContext.petName or nil, + family = petContext and petContext.family or nil, + totals = { + attempts = 0, + accepted = 0, + rejected = 0, + rejectedNoBuff = 0, + rejectedLowLevel = 0, + rejectedWrongFood = 0, + }, + byItemId = {}, + recent = {}, + } + store.byPetId[petId] = row + end + + if type(row.totals) ~= "table" then + row.totals = {} + end + row.totals.attempts = tonumber(row.totals.attempts) or 0 + row.totals.accepted = tonumber(row.totals.accepted) or 0 + row.totals.rejected = tonumber(row.totals.rejected) or 0 + row.totals.rejectedNoBuff = tonumber(row.totals.rejectedNoBuff) or 0 + row.totals.rejectedLowLevel = tonumber(row.totals.rejectedLowLevel) or 0 + row.totals.rejectedWrongFood = tonumber(row.totals.rejectedWrongFood) or 0 + + if type(row.byItemId) ~= "table" then + row.byItemId = {} + end + if type(row.recent) ~= "table" then + row.recent = {} + end + + if petContext then + if petContext.petName and petContext.petName ~= "" then + row.petName = petContext.petName + end + if petContext.family and petContext.family ~= "" then + row.family = petContext.family + end + end + + return row +end + +local function MTH_FEED_EnsurePetItemRow(petRow, itemId) + local key = tonumber(itemId) + if key == nil then + key = itemId + end + if key == nil then + return nil + end + if type(petRow.byItemId[key]) ~= "table" then + petRow.byItemId[key] = { + attempts = 0, + accepted = 0, + rejected = 0, + lastOutcome = nil, + lastReason = nil, + itemName = nil, + itemLink = nil, + itemIcon = nil, + } + end + local row = petRow.byItemId[key] + row.attempts = tonumber(row.attempts) or 0 + row.accepted = tonumber(row.accepted) or 0 + row.rejected = tonumber(row.rejected) or 0 + return row +end + +local function MTH_FEED_RecordPetRecent(petRow, entry) + table.insert(petRow.recent, 1, entry) + while table.getn(petRow.recent) > MTH_FEED_RECENT_LIMIT do + table.remove(petRow.recent) + end +end + +local function MTH_FEED_NormalizeReason(reason, rawMessage) + local normalized = MTH_FEED_SafeLower(reason) + if normalized == "accepted" then return "accepted" end + if normalized == "low-level" then return "low-level" end + if normalized == "wrong-food" then return "wrong-food" end + if normalized == "no-buff" then return "no-buff" end + if normalized == "client-drop-fail" then return "client-drop-fail" end + if normalized == "unknown" then return "unknown" end + + local msg = MTH_FEED_SafeLower(rawMessage) + if msg ~= "" then + if (SPELL_FAILED_FOOD_LOWLEVEL and string.find(msg, MTH_FEED_SafeLower(SPELL_FAILED_FOOD_LOWLEVEL), 1, true)) + or string.find(msg, "low level", 1, true) + or string.find(msg, "too low level", 1, true) + or string.find(msg, "level too low", 1, true) then + return "low-level" + end + if (SPELL_FAILED_WRONG_PET_FOOD and string.find(msg, MTH_FEED_SafeLower(SPELL_FAILED_WRONG_PET_FOOD), 1, true)) + or string.find(msg, "wrong pet food", 1, true) + or string.find(msg, "doesn't like", 1, true) + or string.find(msg, "does not like", 1, true) then + return "wrong-food" + end + end + + return "unknown" +end + +local function MTH_FEED_GetDietByItemIdMap() + local map = {} + if type(FOM_Foods) ~= "table" then + return map + end + for dietName, ids in pairs(FOM_Foods) do + if type(ids) == "table" then + for _, itemId in ids do + local numeric = tonumber(itemId) + if numeric then + map[numeric] = dietName + end + end + end + end + return map +end + +local function MTH_FEED_ResolveFoodLevel(itemId) + local numeric = tonumber(itemId) + if not numeric then + return nil + end + + local store = MTH_FEED_EnsureStore() + local unknown = store.unknownFoods and store.unknownFoods.byItemId and store.unknownFoods.byItemId[numeric] or nil + if type(unknown) == "table" and type(unknown.resolved) == "table" and tonumber(unknown.resolved.foodLevel) then + return tonumber(unknown.resolved.foodLevel) + end + + if type(GetItemInfo) == "function" then + local _, _, _, itemLevel, minLevel = GetItemInfo(numeric) + local resolved = tonumber(minLevel) or tonumber(itemLevel) + if resolved and resolved > 0 then + return resolved + end + end + + return nil +end + +function MTH_FEED_GetStore() + return MTH_FEED_EnsureStore() +end + +function MTH_FEED_GetCurrentPetContext() + local info = (type(MTH_GetCurrentPetInfo) == "function") and MTH_GetCurrentPetInfo() or nil + if type(info) ~= "table" then + return nil + end + local petId = info.id + if petId == nil or tostring(petId) == "" then + return nil + end + return { + petId = tostring(petId), + petName = info.name, + family = info.family, + level = tonumber(info.level), + happiness = tonumber(info.happiness), + } +end + +function MTH_FEED_GetFoodLevel(itemId) + return MTH_FEED_ResolveFoodLevel(itemId) +end + +function MTH_FEED_GetFoodMeta(itemId) + local numeric = tonumber(itemId) + if not numeric then + return nil + end + local dietMap = MTH_FEED_GetDietByItemIdMap() + local known = dietMap[numeric] ~= nil + return { + itemId = numeric, + foodLevel = MTH_FEED_ResolveFoodLevel(numeric), + diet = dietMap[numeric], + known = known, + } +end + +function MTH_FEED_IsKnownFoodItem(itemId) + local numeric = tonumber(itemId) + if not numeric then + return false + end + if type(FOM_IsKnownFood) == "function" then + local ok, value = pcall(FOM_IsKnownFood, numeric) + if ok and value then + return true + end + end + local dietMap = MTH_FEED_GetDietByItemIdMap() + return dietMap[numeric] ~= nil +end + +function MTH_FEED_BeginAttempt(payload) + local store = MTH_FEED_EnsureStore() + local currentPet = MTH_FEED_GetCurrentPetContext() + local payloadPetId = payload and payload.petId or nil + if payloadPetId ~= nil and tostring(payloadPetId) == "" then + payloadPetId = nil + end + local petId = payloadPetId or (currentPet and currentPet.petId) + if petId == nil or tostring(petId) == "" then + return nil + end + local attemptId = MTH_FEED_GetRuntimeAttemptId(store) + local itemId = payload and tonumber(payload.itemId) or nil + local meta = itemId and MTH_FEED_GetFoodMeta(itemId) or nil + local itemIcon = payload and payload.itemIcon or nil + if (not itemIcon or itemIcon == "") and itemId and type(GetItemInfo) == "function" then + local _, _, _, _, _, _, _, _, _, infoTexture = GetItemInfo(itemId) + if infoTexture and infoTexture ~= "" then + itemIcon = infoTexture + end + end + if (not itemIcon or itemIcon == "") then + itemIcon = MTH_FEED_FindItemIconInBags(itemId, payload and payload.itemName) + end + + MTH_FEED_Runtime.attempts[attemptId] = { + id = attemptId, + startedAt = GetTime and (GetTime() or 0) or 0, + startedAtTs = MTH_FEED_Now(), + petId = tostring(petId), + petName = payload and payload.petName or (currentPet and currentPet.petName) or nil, + family = payload and payload.family or (currentPet and currentPet.family) or nil, + petLevel = payload and tonumber(payload.petLevel) or (currentPet and tonumber(currentPet.level) or nil), + itemId = itemId, + itemName = payload and payload.itemName or nil, + itemLink = MTH_FEED_NormalizeItemLink(itemId, payload and payload.itemName or nil, payload and payload.itemLink or nil), + itemIcon = itemIcon, + foodLevel = (payload and tonumber(payload.foodLevel)) or (meta and meta.foodLevel) or nil, + source = payload and payload.source or "unknown", + consumedFromCursor = nil, + rejectReason = nil, + rejectRawMessage = nil, + hasFeedBuff = nil, + happinessTick = nil, + elapsed = nil, + finalized = false, + } + + if itemId and not (meta and meta.known) then + MTH_FEED_RecordUnknownFoodCandidate({ + itemId = itemId, + itemName = payload and payload.itemName or nil, + itemLink = MTH_FEED_NormalizeItemLink(itemId, payload and payload.itemName or nil, payload and payload.itemLink or nil), + source = "feed-attempt", + observedFoodLevel = meta and meta.foodLevel or nil, + observedDietHint = nil, + evidenceType = "feed-attempt", + }) + end + + MTH_FEED_Trace("Begin attempt id=" .. tostring(attemptId) + .. " petId=" .. tostring(petId) + .. " itemId=" .. tostring(itemId) + .. " foodLevel=" .. tostring((payload and tonumber(payload.foodLevel)) or (meta and meta.foodLevel) or nil) + .. " source=" .. tostring(payload and payload.source or "unknown")) + + MTH_FEED_TouchStore(store) + return attemptId +end + +function MTH_FEED_RecordClientDropResult(attemptId, payload) + local row = MTH_FEED_Runtime.attempts[attemptId] + if type(row) ~= "table" then + MTH_FEED_Trace("RecordClientDropResult missing attempt id=" .. tostring(attemptId)) + return false + end + row.consumedFromCursor = (payload and payload.consumedFromCursor) and true or false + MTH_FEED_Trace("RecordClientDropResult id=" .. tostring(attemptId) + .. " consumedFromCursor=" .. tostring(row.consumedFromCursor and true or false)) + return true +end + +function MTH_FEED_RecordRejectEvent(attemptId, payload) + local row = MTH_FEED_Runtime.attempts[attemptId] + if type(row) ~= "table" then + MTH_FEED_Trace("RecordRejectEvent missing attempt id=" .. tostring(attemptId)) + return false + end + local rawMessage = payload and payload.rawMessage or nil + row.rejectReason = MTH_FEED_NormalizeReason(payload and payload.reason or nil, rawMessage) + row.rejectRawMessage = rawMessage + MTH_FEED_Trace("RecordRejectEvent id=" .. tostring(attemptId) + .. " reason=" .. tostring(row.rejectReason) + .. " raw='" .. tostring(rawMessage or "") .. "'") + return true +end + +function MTH_FEED_RecordBuffOutcome(attemptId, payload) + local row = MTH_FEED_Runtime.attempts[attemptId] + if type(row) ~= "table" then + MTH_FEED_Trace("RecordBuffOutcome missing attempt id=" .. tostring(attemptId)) + return false + end + row.hasFeedBuff = (payload and payload.hasFeedBuff) and true or false + row.elapsed = payload and tonumber(payload.elapsed) or row.elapsed + MTH_FEED_Trace("RecordBuffOutcome id=" .. tostring(attemptId) + .. " hasFeedBuff=" .. tostring(row.hasFeedBuff and true or false) + .. " elapsed=" .. tostring(row.elapsed)) + return true +end + +function MTH_FEED_RecordHappinessTick(attemptId, payload) + local row = MTH_FEED_Runtime.attempts[attemptId] + if type(row) ~= "table" then + MTH_FEED_Trace("RecordHappinessTick missing attempt id=" .. tostring(attemptId)) + return false + end + row.happinessTick = payload and tonumber(payload.happinessTick) or row.happinessTick + if payload and tonumber(payload.petLevel) then + row.petLevel = tonumber(payload.petLevel) + end + MTH_FEED_Trace("RecordHappinessTick id=" .. tostring(attemptId) + .. " happinessTick=" .. tostring(row.happinessTick) + .. " petLevel=" .. tostring(row.petLevel)) + return true +end + +function MTH_FEED_RecordLevelObservation(petLevel, foodLevel, outcome) + return true +end + +function MTH_FEED_RecomputeDerivedLevelRule() + local store = MTH_FEED_EnsureStore() + store.levelRule.derived.lastRecomputeAt = MTH_FEED_Now() + MTH_FEED_TouchStore(store) + return true +end + +local function MTH_FEED_GetHardcodedMinFoodLevel(petLevel) + local level = tonumber(petLevel) + if not level then + return nil + end + local minFood = nil + for i = 1, table.getn(MTH_FEED_HARDCODED_LEVEL_RULES) do + local row = MTH_FEED_HARDCODED_LEVEL_RULES[i] + if level >= (tonumber(row.minPetLevel) or 0) then + minFood = tonumber(row.minFoodLevel) + else + break + end + end + return minFood +end + +function MTH_FEED_GetDerivedMinFoodLevel(petLevel) + return MTH_FEED_GetHardcodedMinFoodLevel(petLevel) +end + +function MTH_FEED_GetFoodCompatibilityWindow(petLevel) + local numericPet = tonumber(petLevel) + if not numericPet then + return nil, nil + end + local minAccepted = MTH_FEED_GetHardcodedMinFoodLevel(numericPet) + if minAccepted == nil then + minAccepted = numericPet - 15 + if minAccepted < 1 then + minAccepted = 1 + end + end + local maxAccepted = numericPet + if maxAccepted < 1 then + maxAccepted = 1 + end + return tonumber(minAccepted), tonumber(maxAccepted) +end + +function MTH_FEED_IsFoodCompatible(petLevel, foodLevel) + local numericPet = tonumber(petLevel) + local numericFood = tonumber(foodLevel) + if not numericPet or not numericFood then + return false + end + local minAccepted, maxAccepted = MTH_FEED_GetFoodCompatibilityWindow(numericPet) + if minAccepted and numericFood < minAccepted then + return false + end + if maxAccepted and numericFood > maxAccepted then + return false + end + return true +end + +function MTH_FEED_IsExceptionReject(itemId) + local key = tonumber(itemId) + if not key then + return false + end + local store = MTH_FEED_EnsureStore() + local row = store.exceptions.byItemId[key] + if type(row) ~= "table" then + return false + end + if row.anomalyReject ~= true then + return false + end + local lastObservedAt = tonumber(row.lastObservedAt) or 0 + if lastObservedAt > 0 then + local age = MTH_FEED_Now() - lastObservedAt + if age > 900 then + return false + end + end + local reasons = type(row.reasons) == "table" and row.reasons or nil + if not reasons then + return false + end + local wrongFoodCount = tonumber(reasons.wrongFood) or 0 + if wrongFoodCount < 2 then + return false + end + return true +end + +function MTH_FEED_GetPetFeedStats(petId) + local key = tostring(petId or "") + if key == "" then + return nil + end + local store = MTH_FEED_EnsureStore() + return store.byPetId[key] +end + +local function MTH_FEED_UpsertException(store, itemId, reason) + if not itemId then + return + end + local key = tonumber(itemId) or itemId + if type(store.exceptions.byItemId[key]) ~= "table" then + store.exceptions.byItemId[key] = { + anomalyReject = true, + reasons = { noBuff = 0, wrongFood = 0, lowLevel = 0, unknown = 0 }, + lastObservedAt = 0, + } + end + local row = store.exceptions.byItemId[key] + if type(row.reasons) ~= "table" then + row.reasons = { noBuff = 0, wrongFood = 0, lowLevel = 0, unknown = 0 } + end + if reason == "no-buff" then + row.reasons.noBuff = (tonumber(row.reasons.noBuff) or 0) + 1 + elseif reason == "wrong-food" then + row.reasons.wrongFood = (tonumber(row.reasons.wrongFood) or 0) + 1 + elseif reason == "low-level" then + row.reasons.lowLevel = (tonumber(row.reasons.lowLevel) or 0) + 1 + else + row.reasons.unknown = (tonumber(row.reasons.unknown) or 0) + 1 + end + row.anomalyReject = true + row.lastObservedAt = MTH_FEED_Now() +end + +function MTH_FEED_FinalizeAttempt(attemptId, payload) + local row = MTH_FEED_Runtime.attempts[attemptId] + if type(row) ~= "table" then + MTH_FEED_Trace("FinalizeAttempt missing attempt id=" .. tostring(attemptId)) + return false + end + if row.finalized then + MTH_FEED_Trace("FinalizeAttempt already finalized id=" .. tostring(attemptId)) + return true + end + + local store = MTH_FEED_EnsureStore() + local outcome = MTH_FEED_SafeLower(payload and payload.outcome or "") + local reason = MTH_FEED_NormalizeReason(payload and payload.reason or row.rejectReason, row.rejectRawMessage) + if outcome ~= "accepted" and outcome ~= "rejected" then + if row.hasFeedBuff == true then + outcome = "accepted" + else + outcome = "rejected" + end + end + + if outcome == "accepted" then + reason = "accepted" + elseif reason == "accepted" then + reason = "unknown" + end + + local petContext = { + petId = row.petId, + petName = row.petName, + family = row.family, + } + if petContext.petId == nil or tostring(petContext.petId) == "" then + local currentPet = MTH_FEED_GetCurrentPetContext() + if type(currentPet) == "table" and currentPet.petId and tostring(currentPet.petId) ~= "" then + petContext.petId = tostring(currentPet.petId) + if row.petId == nil or tostring(row.petId) == "" then + row.petId = petContext.petId + end + end + end + local petRow = MTH_FEED_EnsurePetRow(store, petContext) + if type(petRow) ~= "table" then + MTH_FEED_Trace("FinalizeAttempt failed pet row id=" .. tostring(attemptId) .. " petId=" .. tostring(row.petId)) + return false + end + local itemRow = MTH_FEED_EnsurePetItemRow(petRow, row.itemId) + + petRow.totals.attempts = petRow.totals.attempts + 1 + if itemRow then + itemRow.attempts = itemRow.attempts + 1 + end + + if outcome == "accepted" then + petRow.totals.accepted = petRow.totals.accepted + 1 + if itemRow then + itemRow.accepted = itemRow.accepted + 1 + end + local petKey = tostring((petRow and petRow.petId) or row.petId or "") + if petKey ~= "" then + MTH_FEED_Runtime.sessionFeedsByPetId[petKey] = (tonumber(MTH_FEED_Runtime.sessionFeedsByPetId[petKey]) or 0) + 1 + end + else + petRow.totals.rejected = petRow.totals.rejected + 1 + if reason == "no-buff" then + petRow.totals.rejectedNoBuff = petRow.totals.rejectedNoBuff + 1 + elseif reason == "low-level" then + petRow.totals.rejectedLowLevel = petRow.totals.rejectedLowLevel + 1 + elseif reason == "wrong-food" then + petRow.totals.rejectedWrongFood = petRow.totals.rejectedWrongFood + 1 + end + if itemRow then + itemRow.rejected = itemRow.rejected + 1 + end + MTH_FEED_UpsertException(store, row.itemId, reason) + end + + if itemRow then + itemRow.lastOutcome = outcome + itemRow.lastReason = reason + if row.itemName and row.itemName ~= "" then + itemRow.itemName = row.itemName + end + itemRow.itemLink = MTH_FEED_NormalizeItemLink(row.itemId, row.itemName, row.itemLink) + local persistedIcon = row.itemIcon + if (not persistedIcon or persistedIcon == "") and row.itemId and type(GetItemInfo) == "function" then + local _, _, _, _, _, _, _, _, _, infoTexture = GetItemInfo(row.itemId) + if infoTexture and infoTexture ~= "" then + persistedIcon = infoTexture + end + end + if (not persistedIcon or persistedIcon == "") and row.itemId and type(GetItemIcon) == "function" then + local directTexture = GetItemIcon(row.itemId) + if directTexture and directTexture ~= "" then + persistedIcon = directTexture + end + end + if (not persistedIcon or persistedIcon == "") then + persistedIcon = MTH_FEED_FindItemIconInBags(row.itemId, row.itemName) + end + if persistedIcon and persistedIcon ~= "" then + itemRow.itemIcon = persistedIcon + end + end + + MTH_FEED_RecordPetRecent(petRow, { + ts = MTH_FEED_Now(), + itemId = row.itemId, + itemName = row.itemName, + petLevel = row.petLevel, + foodLevel = row.foodLevel, + outcome = outcome, + reason = reason, + source = row.source, + }) + + row.finalized = true + MTH_FEED_Trace("FinalizeAttempt id=" .. tostring(attemptId) + .. " outcome=" .. tostring(outcome) + .. " reason=" .. tostring(reason) + .. " petId=" .. tostring(row.petId) + .. " itemId=" .. tostring(row.itemId) + .. " petAttempts=" .. tostring(petRow.totals.attempts) + .. " petAccepted=" .. tostring(petRow.totals.accepted) + .. " petRejected=" .. tostring(petRow.totals.rejected)) + MTH_FEED_Runtime.attempts[attemptId] = nil + MTH_FEED_TouchStore(store) + return true +end + +function MTH_FEED_GetSessionFeedCount(petId) + local petKey = tostring(petId or "") + if petKey == "" then + local pet = MTH_FEED_GetCurrentPetContext() + petKey = tostring((pet and pet.petId) or "") + end + if petKey == "" then + return 0 + end + return tonumber(MTH_FEED_Runtime.sessionFeedsByPetId[petKey]) or 0 +end + +function MTH_FEED_RecordUnknownFoodCandidate(payload) + local store = MTH_FEED_EnsureStore() + local itemId = payload and tonumber(payload.itemId) or nil + if not itemId then + MTH_FEED_Trace("RecordUnknownFoodCandidate ignored: missing itemId") + return false + end + + local queue = store.unknownFoods.byItemId + if type(queue[itemId]) ~= "table" then + queue[itemId] = { + itemId = itemId, + itemName = payload and payload.itemName or nil, + itemLink = payload and payload.itemLink or nil, + firstSeenAt = MTH_FEED_Now(), + lastSeenAt = MTH_FEED_Now(), + seenCount = 0, + sources = {}, + observedFoodLevel = nil, + observedDietHints = {}, + state = "unknown", + confidence = 0, + resolved = { + foodLevel = nil, + familyMask = nil, + resolvedFrom = nil, + resolvedAt = nil, + }, + } + end + + local row = queue[itemId] + row.lastSeenAt = MTH_FEED_Now() + row.seenCount = (tonumber(row.seenCount) or 0) + 1 + if payload and payload.itemName and payload.itemName ~= "" then + row.itemName = payload.itemName + end + if payload and payload.itemLink and payload.itemLink ~= "" then + row.itemLink = MTH_FEED_NormalizeItemLink(itemId, row.itemName, payload.itemLink) + end + + local source = payload and tostring(payload.source or "") or "" + if source ~= "" then + local seen = false + for i = 1, table.getn(row.sources) do + if row.sources[i] == source then + seen = true + break + end + end + if not seen then + table.insert(row.sources, source) + end + end + + if payload and payload.observedFoodLevel ~= nil then + row.observedFoodLevel = tonumber(payload.observedFoodLevel) + end + local dietHint = payload and payload.observedDietHint or nil + if dietHint ~= nil and tostring(dietHint) ~= "" then + local hint = tostring(dietHint) + local seen = false + for i = 1, table.getn(row.observedDietHints) do + if row.observedDietHints[i] == hint then + seen = true + break + end + end + if not seen then + table.insert(row.observedDietHints, hint) + end + end + + if row.state == "unknown" then + row.state = "candidate-food" + end + if payload and payload.evidenceType == "runtime-outcome" and row.state == "candidate-food" then + row.confidence = math.min(1, (tonumber(row.confidence) or 0) + 0.2) + else + row.confidence = math.min(1, (tonumber(row.confidence) or 0) + 0.1) + end + + MTH_FEED_Trace("RecordUnknownFoodCandidate itemId=" .. tostring(itemId) + .. " state=" .. tostring(row.state) + .. " confidence=" .. tostring(row.confidence) + .. " source=" .. tostring(payload and payload.source or "")) + + MTH_FEED_TouchStore(store) + return true +end + +function MTH_FEED_TryClassifyUnknownFood(itemId, evidence) + local store = MTH_FEED_EnsureStore() + local key = tonumber(itemId) + if not key then + return false + end + local row = store.unknownFoods.byItemId[key] + if type(row) ~= "table" then + return false + end + + local verdict = evidence and MTH_FEED_SafeLower(evidence.verdict or "") or "" + if verdict == "confirmed-food" then + row.state = "confirmed-food" + row.confidence = 1 + if evidence.foodLevel ~= nil then + row.resolved.foodLevel = tonumber(evidence.foodLevel) + end + if evidence.familyMask ~= nil then + row.resolved.familyMask = evidence.familyMask + end + row.resolved.resolvedFrom = evidence.resolvedFrom or "runtime-observation" + row.resolved.resolvedAt = MTH_FEED_Now() + elseif verdict == "confirmed-not-food" then + row.state = "confirmed-not-food" + row.confidence = 1 + elseif verdict == "candidate-food" then + row.state = "candidate-food" + row.confidence = math.max(tonumber(row.confidence) or 0, 0.5) + end + + MTH_FEED_TouchStore(store) + return true +end + +function MTH_FEED_GetUnknownFoodQueue() + local store = MTH_FEED_EnsureStore() + local rows = {} + for _, row in pairs(store.unknownFoods.byItemId) do + if type(row) == "table" and row.state ~= "confirmed-food" and row.state ~= "confirmed-not-food" then + table.insert(rows, row) + end + end + table.sort(rows, function(a, b) + return (tonumber(a.lastSeenAt) or 0) > (tonumber(b.lastSeenAt) or 0) + end) + return rows +end + +function MTH_FEED_ResolveFoodMeta(itemId, payload) + local key = tonumber(itemId) + if not key then + MTH_FEED_Trace("ResolveFoodMeta ignored: invalid itemId") + return false + end + local store = MTH_FEED_EnsureStore() + if type(store.unknownFoods.byItemId[key]) ~= "table" then + MTH_FEED_RecordUnknownFoodCandidate({ + itemId = key, + itemName = payload and payload.itemName or nil, + itemLink = MTH_FEED_NormalizeItemLink(key, payload and payload.itemName or nil, payload and payload.itemLink or nil), + source = "manual-resolve", + observedFoodLevel = payload and payload.foodLevel or nil, + observedDietHint = nil, + evidenceType = "manual", + }) + end + local row = store.unknownFoods.byItemId[key] + if type(row.resolved) ~= "table" then + row.resolved = {} + end + row.state = "confirmed-food" + row.confidence = 1 + row.resolved.foodLevel = payload and tonumber(payload.foodLevel) or nil + row.resolved.familyMask = payload and payload.familyMask or nil + row.resolved.resolvedFrom = payload and payload.resolvedFrom or "manual" + row.resolved.resolvedAt = MTH_FEED_Now() + MTH_FEED_Trace("ResolveFoodMeta itemId=" .. tostring(key) + .. " foodLevel=" .. tostring(row.resolved.foodLevel) + .. " resolvedFrom=" .. tostring(row.resolved.resolvedFrom)) + MTH_FEED_TouchStore(store) + return true +end + +function MTH_FEED_DebugDumpCurrentPet(limit) + local ctx = MTH_FEED_GetCurrentPetContext() + if type(ctx) ~= "table" then + MTH_FEED_Trace("DebugDumpCurrentPet: no active pet context") + return false + end + local stats = MTH_FEED_GetPetFeedStats(ctx.petId) + if type(stats) ~= "table" then + MTH_FEED_Trace("DebugDumpCurrentPet: no stats for petId=" .. tostring(ctx.petId)) + return false + end + + local totals = stats.totals or {} + MTH_FEED_Trace("DebugDumpCurrentPet petId=" .. tostring(ctx.petId) + .. " name='" .. tostring(stats.petName or "") .. "'" + .. " attempts=" .. tostring(tonumber(totals.attempts) or 0) + .. " accepted=" .. tostring(tonumber(totals.accepted) or 0) + .. " rejected=" .. tostring(tonumber(totals.rejected) or 0) + .. " noBuff=" .. tostring(tonumber(totals.rejectedNoBuff) or 0) + .. " lowLevel=" .. tostring(tonumber(totals.rejectedLowLevel) or 0) + .. " wrongFood=" .. tostring(tonumber(totals.rejectedWrongFood) or 0)) + + local maxRows = tonumber(limit) or 5 + if maxRows < 1 then maxRows = 1 end + if maxRows > 20 then maxRows = 20 end + local recent = stats.recent or {} + for i = 1, maxRows do + local row = recent[i] + if type(row) ~= "table" then + break + end + MTH_FEED_Trace("Recent#" .. tostring(i) + .. " itemId=" .. tostring(row.itemId) + .. " foodLevel=" .. tostring(row.foodLevel) + .. " petLevel=" .. tostring(row.petLevel) + .. " outcome=" .. tostring(row.outcome) + .. " reason=" .. tostring(row.reason) + .. " source=" .. tostring(row.source)) + end + return true +end + +MTH_FEED_EnsureTrackerFrame() +MTH_FEED_InstallCoreHooks() +MTH_FEED_EnsureBootstrapFrame() diff --git a/api/core-framework.lua b/api/core-framework.lua new file mode 100644 index 0000000..818d7dd --- /dev/null +++ b/api/core-framework.lua @@ -0,0 +1,1509 @@ +MTH = MTH or { + version = "1.0.3", + name = "MetaHunt", + modules = {}, + config = {}, + debug = false, +} + +local MTH_CHAT_PREFIX = "|cFFFFFFFF[|r|cFFD14A4AMeta|r|cFFABD473Hunt|r|cFFFFFFFF]|r " +local MTH_NON_HUNTER_BLOCK_MESSAGE = "You are not a hunter and you are not allowed here. Disable the add-on for this character." + +local function MTH_ClassGateTrace(_step, _detail) + return +end + +local function MTH_ClassGateClearAnnounceFrame(self) + if not self or not self._classGateAnnounceFrame then + return + end + self._classGateAnnounceFrame:UnregisterAllEvents() + self._classGateAnnounceFrame:SetScript("OnEvent", nil) + self._classGateAnnounceFrame:SetScript("OnUpdate", nil) + self._classGateAnnounceFrame = nil +end + +local function MTH_ClassGateEnsureAnnounceFrame(self) + if not self or self._classGateAnnounceFrame then + return + end + local frame = CreateFrame("Frame", "MTH_ClassGateAnnounceFrame") + if not frame then + return + end + frame:RegisterEvent("PLAYER_ENTERING_WORLD") + frame:SetScript("OnEvent", function() + if MTH and MTH.AnnounceClassGateBlocked then + MTH:AnnounceClassGateBlocked() + end + end) + frame._mthElapsed = 0 + frame:SetScript("OnUpdate", function(self, elapsed) + self._mthElapsed = (self._mthElapsed or 0) + (elapsed or arg1 or 0) + if self._mthElapsed < 0.5 then + return + end + self._mthElapsed = 0 + if MTH and MTH._classGateAnnouncedChat then + MTH_ClassGateClearAnnounceFrame(MTH) + return + end + if MTH and MTH._classGateAnnouncePending and MTH.AnnounceClassGateBlocked then + MTH:AnnounceClassGateBlocked() + end + end) + self._classGateAnnounceFrame = frame +end + +local function MTH_TryOutputClassGateMessage(text) + local msg = tostring(text or "") + if msg == "" then + return nil + end + + local formattedMsg = MTH_CHAT_PREFIX .. msg + if MTH and MTH.Log and DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + MTH:Log(msg) + return "chat" + end + + local shown = false + local sink = nil + local function tryFrame(frame) + if frame and frame.AddMessage then + frame:AddMessage(formattedMsg, 1, 0.25, 0.25) + shown = true + sink = "chat" + end + end + + tryFrame(DEFAULT_CHAT_FRAME) + if not shown and type(getglobal) == "function" then + tryFrame(getglobal("ChatFrame1")) + if not shown then + for i = 2, 7 do + tryFrame(getglobal("ChatFrame" .. tostring(i))) + if shown then break end + end + end + end + + if not shown and _G then + tryFrame(_G["ChatFrame1"]) + if not shown then + for i = 2, 7 do + tryFrame(_G["ChatFrame" .. tostring(i)]) + if shown then break end + end + end + end + + if shown then + return sink + end + + if UIErrorsFrame and UIErrorsFrame.AddMessage then + UIErrorsFrame:AddMessage(formattedMsg, 1, 0.25, 0.25, 1.0) + return "uierrors" + end + + if type(print) == "function" then + print(formattedMsg) + return "print" + end + + return nil +end + +function MTH:IsClassGateBlocked() + return self and self._classGateBlocked and true or false +end + +function MTH:IsHardBlocked() + return self and self._hardBlocked and true or false +end + +function MTH:CheckClassGate() + if type(UnitClass) ~= "function" then + return self:IsClassGateBlocked() + end + + local className, classToken = UnitClass("player") + if type(classToken) == "string" and classToken ~= "" then + self._playerClassToken = classToken + self._classGateBlocked = classToken ~= "HUNTER" + end + + return self:IsClassGateBlocked() +end + +function MTH:AnnounceClassGateBlocked() + if self._classGateAnnouncedChat then + return + end + + if type(IsLoggedIn) == "function" and not IsLoggedIn() then + self._classGateAnnouncePending = true + MTH_ClassGateEnsureAnnounceFrame(self) + return + end + + local sink = MTH_TryOutputClassGateMessage(MTH_NON_HUNTER_BLOCK_MESSAGE) + if sink == "chat" or sink == "uierrors" then + self._classGateAnnouncedChat = true + self._classGateAnnounced = true + self._classGateAnnouncePending = nil + MTH_ClassGateClearAnnounceFrame(self) + return + end + + self._classGateAnnouncePending = true + MTH_ClassGateEnsureAnnounceFrame(self) +end + +function MTH:ApplyClassGate(_source) + local blocked = self:CheckClassGate() + if blocked then + self:AnnounceClassGateBlocked() + if self.ShutdownForNonHunter then + self:ShutdownForNonHunter(_source or "class-gate") + end + end + return blocked +end + +MTH:ApplyClassGate("startup") + +if not MTH._classGateLifecycleFrame then + local classGateFrame = CreateFrame("Frame", "MTH_ClassGateLifecycleFrame") + if classGateFrame then + classGateFrame:RegisterEvent("PLAYER_LOGIN") + classGateFrame:RegisterEvent("PLAYER_ENTERING_WORLD") + classGateFrame:SetScript("OnEvent", function(self, eventName) + self = self or this + eventName = eventName or event + if MTH and eventName == "PLAYER_LOGIN" then + MTH._classGateAnnouncedChat = nil + MTH._classGateAnnounced = nil + MTH._classGateAnnouncePending = nil + end + if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate(eventName) and MTH._classGateAnnouncedChat then + self:UnregisterAllEvents() + self:SetScript("OnEvent", nil) + end + end) + MTH._classGateLifecycleFrame = classGateFrame + end +end + +MTH.Const = MTH.Const or {} +if type(MTH.Const.SlashAliases) ~= "table" then + MTH.Const.SlashAliases = { "/mth", "/metahunt" } +end + +if not ITEM_QUALITY_COLORS then + ITEM_QUALITY_COLORS = { + [0] = { r = 0.62, g = 0.62, b = 0.62 }, + [1] = { r = 1.00, g = 1.00, b = 1.00 }, + [2] = { r = 0.12, g = 1.00, b = 0.00 }, + [3] = { r = 0.00, g = 0.44, b = 0.87 }, + [4] = { r = 0.64, g = 0.21, b = 0.93 }, + [5] = { r = 1.00, g = 0.50, b = 0.00 }, + } +end + +MTH_ITEM_QUALITY_COLORS = ITEM_QUALITY_COLORS + +function MTH:RegisterSlashAliases(key, aliases) + if type(key) ~= "string" or key == "" then + return + end + + if type(aliases) ~= "table" then + aliases = self.Const and self.Const.SlashAliases or nil + end + if type(aliases) ~= "table" then + return + end + + for i = 1, table.getn(aliases) do + local alias = aliases[i] + if type(alias) == "string" and alias ~= "" then + _G["SLASH_" .. key .. i] = alias + end + end +end + +if type(SlashCmdList) ~= "table" then + SlashCmdList = {} +end + +if type(MTH_SavedVariables) ~= "table" then + MTH_SavedVariables = {} +end +if type(MTH_SavedVariables.modules) ~= "table" then + MTH_SavedVariables.modules = {} +end +if type(MTH_CharSavedVariables) ~= "table" then + MTH_CharSavedVariables = {} +end +if type(MTH_CharSavedVariables.modules) ~= "table" then + MTH_CharSavedVariables.modules = {} +end + +local function MTH_ModuleStateDebug(message) + local text = "[MODULE STATE] " .. tostring(message or "") + if MTH and MTH.debug and MTH.Log then + MTH:Log(text, "debug") + elseif MTH and MTH.debug and MTH.Print then + MTH:Print(text, "debug") + end +end + +local function MTH_IsPetUiFrameName(frameName) + local name = tostring(frameName or "") + if name == "" then + return false + end + return ( + string.find(name, "PetFrame", 1, true) + or string.find(name, "PetName", 1, true) + or string.find(name, "PetActionButton", 1, true) + or string.find(name, "PetActionBar", 1, true) + or string.find(name, "PetBar", 1, true) + or string.find(name, "PetButton", 1, true) + or string.find(name, "ShaguPet", 1, true) + or string.find(name, "pfPet", 1, true) + or string.find(name, "DominosActionButton", 1, true) + or string.find(name, "BT4PetButton", 1, true) + or string.find(name, "DominosPet", 1, true) + ) and true or false +end + +local function MTH_DetectPetUiFromFrameChain(frame) + local state = { + matched = false, + matchedFrameName = nil, + } + local current = frame + local depth = 0 + while current and depth < 6 do + local frameName = (type(current.GetName) == "function") and current:GetName() or "" + if MTH_IsPetUiFrameName(frameName) then + state.matched = true + state.matchedFrameName = tostring(frameName or "") + return state + end + if type(current.GetParent) == "function" then + current = current:GetParent() + else + current = nil + end + depth = depth + 1 + end + return state +end + +function MTH_IsMouseoverOwnPet() + if type(UnitClass) == "function" then + local _, classToken = UnitClass("player") + if tostring(classToken or "") ~= "HUNTER" then + return false + end + end + + if type(UnitExists) == "function" and type(UnitIsUnit) == "function" then + if UnitExists("pet") and UnitExists("mouseover") then + local ok, sameUnit = pcall(UnitIsUnit, "mouseover", "pet") + if ok and sameUnit then + return true + end + end + end + + if GameTooltip and type(GameTooltip.GetOwner) == "function" then + local owner = GameTooltip:GetOwner() + local tooltipCheck = MTH_DetectPetUiFromFrameChain(owner) + if tooltipCheck and tooltipCheck.matched then + return true + end + end + + local getMouseFocus = (_G and _G["GetMouseFocus"]) or nil + if type(getMouseFocus) ~= "function" then + return false + end + + local focus = getMouseFocus() + local focusCheck = MTH_DetectPetUiFromFrameChain(focus) + return focusCheck and focusCheck.matched and true or false +end + +function MTH_GetMouseoverOwnPetState() + local state = { + isHunter = false, + hasPet = false, + mouseoverIsPetUnit = false, + mouseoverIsPetFrame = false, + matchedFrameName = nil, + detected = false, + } + + if type(UnitClass) == "function" then + local _, classToken = UnitClass("player") + state.isHunter = tostring(classToken or "") == "HUNTER" + else + state.isHunter = true + end + + if type(UnitExists) == "function" then + state.hasPet = UnitExists("pet") and true or false + end + + if type(UnitExists) == "function" and type(UnitIsUnit) == "function" then + if UnitExists("pet") and UnitExists("mouseover") then + local ok, sameUnit = pcall(UnitIsUnit, "mouseover", "pet") + state.mouseoverIsPetUnit = ok and sameUnit and true or false + end + end + + local getMouseFocus = (_G and _G["GetMouseFocus"]) or nil + if type(getMouseFocus) == "function" then + local focus = getMouseFocus() + local focusCheck = MTH_DetectPetUiFromFrameChain(focus) + if focusCheck and focusCheck.matched then + state.mouseoverIsPetFrame = true + state.matchedFrameName = focusCheck.matchedFrameName + end + end + + if not state.mouseoverIsPetFrame and GameTooltip and type(GameTooltip.GetOwner) == "function" then + local owner = GameTooltip:GetOwner() + local tooltipCheck = MTH_DetectPetUiFromFrameChain(owner) + if tooltipCheck and tooltipCheck.matched then + state.mouseoverIsPetFrame = true + state.matchedFrameName = tooltipCheck.matchedFrameName + end + end + + state.detected = state.isHunter and (state.mouseoverIsPetUnit or state.mouseoverIsPetFrame) and true or false + return state +end + +local function MTH_NormalizeModuleName(name) + if type(name) ~= "string" then + return name + end + local lowered = string.lower(name) + if lowered == "zhunter" then + return "zhunter" + end + return name +end + +local function MTH_ModuleNameCandidates(name) + local normalized = MTH_NormalizeModuleName(name) + return { normalized } +end + +local function MTH_GetPersistedModuleEnabled(name, defaultEnabled) + if type(name) ~= "string" or name == "" then + MTH_ModuleStateDebug("read fallback-default=" .. tostring(defaultEnabled and true or false)) + return defaultEnabled and true or false + end + + local resolvedName = MTH_NormalizeModuleName(name) + local candidates = MTH_ModuleNameCandidates(name) + + if type(MTH_SavedVariables) ~= "table" then + MTH_ModuleStateDebug("read " .. tostring(name) .. " no-savedvars fallback-default=" .. tostring(defaultEnabled and true or false)) + return defaultEnabled and true or false + end + + if type(MTH_SavedVariables.moduleStates) == "table" then + for i = 1, table.getn(candidates) do + local key = candidates[i] + if MTH_SavedVariables.moduleStates[key] ~= nil then + local resolved = MTH_SavedVariables.moduleStates[key] and true or false + MTH_ModuleStateDebug("read " .. tostring(name) .. " from moduleStates." .. tostring(key) .. "=" .. tostring(resolved)) + MTH_SavedVariables.moduleStates[resolvedName] = resolved + return resolved + end + end + end + + if type(MTH_CharSavedVariables) == "table" + and type(MTH_CharSavedVariables.moduleStates) == "table" + then + for i = 1, table.getn(candidates) do + local key = candidates[i] + if MTH_CharSavedVariables.moduleStates[key] ~= nil then + local resolved = MTH_CharSavedVariables.moduleStates[key] and true or false + MTH_ModuleStateDebug("read " .. tostring(name) .. " from char.moduleStates." .. tostring(key) .. "=" .. tostring(resolved)) + if type(MTH_SavedVariables.moduleStates) ~= "table" then + MTH_SavedVariables.moduleStates = {} + end + MTH_SavedVariables.moduleStates[resolvedName] = resolved + MTH_CharSavedVariables.moduleStates[resolvedName] = resolved + return resolved + end + end + end + + if type(MTH_SavedVariables.modules) == "table" then + for i = 1, table.getn(candidates) do + local key = candidates[i] + if type(MTH_SavedVariables.modules[key]) == "table" and MTH_SavedVariables.modules[key].enabled ~= nil then + local resolved = MTH_SavedVariables.modules[key].enabled and true or false + MTH_ModuleStateDebug("read " .. tostring(name) .. " from modules." .. tostring(key) .. ".enabled=" .. tostring(resolved)) + if type(MTH_SavedVariables.modules[resolvedName]) ~= "table" then + MTH_SavedVariables.modules[resolvedName] = MTH_SavedVariables.modules[key] + end + return resolved + end + end + end + + for i = 1, table.getn(candidates) do + local key = candidates[i] + if type(MTH_SavedVariables[key]) == "table" and MTH_SavedVariables[key].enabled ~= nil then + local resolved = MTH_SavedVariables[key].enabled and true or false + MTH_ModuleStateDebug("read " .. tostring(name) .. " from legacy-top-level." .. tostring(key) .. ".enabled=" .. tostring(resolved)) + if type(MTH_SavedVariables.modules) ~= "table" then + MTH_SavedVariables.modules = {} + end + if type(MTH_SavedVariables.modules[resolvedName]) ~= "table" then + MTH_SavedVariables.modules[resolvedName] = MTH_SavedVariables[key] + end + if type(MTH_SavedVariables.moduleStates) ~= "table" then + MTH_SavedVariables.moduleStates = {} + end + MTH_SavedVariables.moduleStates[resolvedName] = resolved + return resolved + end + end + + MTH_ModuleStateDebug("read " .. tostring(name) .. " no-persisted-value fallback-default=" .. tostring(defaultEnabled and true or false)) + return defaultEnabled and true or false +end + +local function MTH_SetPersistedModuleEnabled(name, enabled) + if type(name) ~= "string" or name == "" then + return + end + + name = MTH_NormalizeModuleName(name) + + if type(MTH_SavedVariables) ~= "table" then + MTH_SavedVariables = {} + end + if type(MTH_SavedVariables.moduleStates) ~= "table" then + MTH_SavedVariables.moduleStates = {} + end + if type(MTH_SavedVariables.modules) ~= "table" then + MTH_SavedVariables.modules = {} + end + if type(MTH_SavedVariables.modules[name]) ~= "table" then + MTH_SavedVariables.modules[name] = {} + end + if type(MTH_CharSavedVariables) ~= "table" then + MTH_CharSavedVariables = {} + end + if type(MTH_CharSavedVariables.moduleStates) ~= "table" then + MTH_CharSavedVariables.moduleStates = {} + end + + enabled = enabled and true or false + MTH_SavedVariables.moduleStates[name] = enabled + MTH_SavedVariables.modules[name].enabled = enabled + MTH_SavedVariables[name] = MTH_SavedVariables.modules[name] + MTH_CharSavedVariables.moduleStates[name] = enabled + MTH_ModuleStateDebug("write " .. tostring(name) .. " enabled=" .. tostring(enabled)) +end + +function MTH:InitEventRouter() + if self._eventRouter and self._eventRouter.frame then + return self._eventRouter.frame + end + + local frame = CreateFrame("Frame", "MTH_EventRouterFrame") + frame:SetScript("OnEvent", function(self, evt, a1, a2, a3, a4, a5, a6, a7, a8, a9) + if MTH and MTH.DispatchEvent then + MTH:DispatchEvent(evt, a1, a2, a3, a4, a5, a6, a7, a8, a9) + end + end) + + self._eventRouter = { + frame = frame, + eventRefs = {}, + } + + return frame +end + +function MTH:RegisterModuleEvents(name, events) + local module = self.modules[name] + if not module then + return false, "module not registered" + end + + local eventSet = {} + if type(events) == "table" then + for key, value in pairs(events) do + if type(key) == "number" and type(value) == "string" and value ~= "" then + eventSet[value] = true + elseif type(key) == "string" and value then + eventSet[key] = true + end + end + end + + module._mth_eventSet = eventSet + return true +end + +function MTH:_SetModuleEventSubscriptions(name, enabled) + local module = self.modules[name] + if not module or not module._mth_eventSet then + return + end + + local frame = self:InitEventRouter() + local refs = self._eventRouter.eventRefs + + for evt in pairs(module._mth_eventSet) do + if enabled then + refs[evt] = (refs[evt] or 0) + 1 + if refs[evt] == 1 then + frame:RegisterEvent(evt) + end + else + if refs[evt] and refs[evt] > 0 then + refs[evt] = refs[evt] - 1 + if refs[evt] <= 0 then + refs[evt] = nil + frame:UnregisterEvent(evt) + end + end + end + end +end + +function MTH:DispatchEvent(evt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + for name, module in pairs(self.modules) do + if module.enabled and module.onEvent then + local allow = true + if module._mth_eventSet then + allow = module._mth_eventSet[evt] and true or false + end + if allow then + local ok, err = pcall(function() + module:onEvent(evt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end) + if not ok then + self:Print("Module '" .. name .. "' event error on '" .. tostring(evt) .. "': " .. tostring(err)) + MTH:Print("[MTH] Module event error (" .. name .. ", " .. tostring(evt) .. "): " .. tostring(err), "error") + end + end + end + end +end + +function MTH:RegisterModule(name, module) + if self:IsHardBlocked() then + self:AnnounceClassGateBlocked() + return false + end + + MTH_ClassGateTrace("RegisterModule enter", "name=" .. tostring(name) .. " blocked=" .. tostring(self:IsClassGateBlocked())) + if self:IsClassGateBlocked() then + MTH_ClassGateTrace("RegisterModule blocked", "name=" .. tostring(name)) + self:AnnounceClassGateBlocked() + return false + end + + if type(name) ~= "string" or name == "" then + self:Print("Cannot register module: invalid module name") + return false + end + + if type(module) ~= "table" then + self:Print("Cannot register module '" .. name .. "': module must be a table") + return false + end + + if self.modules[name] then + if self.debug then print("|cFFFFAA00MetaHunt|r: Module '" .. name .. "' already registered") end + return false + end + + module.name = module.name or name + module._mth_initialized = false + + local defaultEnabled = true + if module.enabled ~= nil then + defaultEnabled = module.enabled and true or false + end + module._mth_defaultEnabled = defaultEnabled + + local initialEnabled = MTH_GetPersistedModuleEnabled(name, defaultEnabled) + module.enabled = initialEnabled and true or false + MTH_ModuleStateDebug("register " .. tostring(name) .. " default=" .. tostring(defaultEnabled) .. " initial=" .. tostring(module.enabled)) + + self.modules[name] = module + self:RegisterModuleEvents(name, module.events) + + if module.init and type(module.init) == "function" then + MTH_ClassGateTrace("RegisterModule init begin", "name=" .. tostring(name)) + local ok, err = pcall(function() + module:init() + end) + if not ok then + MTH_ClassGateTrace("RegisterModule init failed", "name=" .. tostring(name) .. " err=" .. tostring(err)) + module.enabled = false + self:Print("Module '" .. name .. "' init failed: " .. tostring(err)) + MTH:Print("[MTH] Module init failed (" .. name .. "): " .. tostring(err), "error") + return false + end + MTH_ClassGateTrace("RegisterModule init end", "name=" .. tostring(name)) + module._mth_initialized = true + end + + if module.enabled then + self:_SetModuleEventSubscriptions(name, true) + end + + if self.debug then print("|cFF00AA00MetaHunt|r: Module '" .. name .. "' registered") end + return true +end + +function MTH:GetModule(name) + name = MTH_NormalizeModuleName(name) + return self.modules[name] +end + +function MTH:IsModuleEnabled(name, defaultEnabled) + if self:IsHardBlocked() then + return false + end + + if self:IsClassGateBlocked() then + return false + end + + if defaultEnabled == nil then + defaultEnabled = true + end + + if type(name) ~= "string" or name == "" then + return defaultEnabled and true or false + end + + name = MTH_NormalizeModuleName(name) + + local module = self:GetModule(name) + if not module then + return defaultEnabled and true or false + end + + return module.enabled and true or false +end + +function MTH:SetModuleEnabled(name, enabled) + if self:IsHardBlocked() then + if enabled then + self:AnnounceClassGateBlocked() + return false, "Class gate blocked" + end + return true + end + + if self:IsClassGateBlocked() then + if enabled then + self:AnnounceClassGateBlocked() + return false, "Class gate blocked" + end + return true + end + + name = MTH_NormalizeModuleName(name) + local module = self:GetModule(name) + if not module then + return false, "Module '" .. tostring(name) .. "' not found" + end + + enabled = enabled and true or false + if module.enabled == enabled then + MTH_SetPersistedModuleEnabled(name, enabled) + if self.SetConfig then + self:SetConfig(name, "enabled", enabled) + end + MTH_ModuleStateDebug("request " .. tostring(name) .. " no-op runtime=" .. tostring(module.enabled) .. " persisted-synced") + return true + end + + if module.setEnabled and type(module.setEnabled) == "function" then + local ok, err = pcall(function() + module:setEnabled(enabled) + end) + if not ok then + self:Print("Module '" .. name .. "' state change failed: " .. tostring(err)) + MTH:Print("[MTH] Module state change failed (" .. name .. "): " .. tostring(err), "error") + return false, tostring(err) + end + end + + self:_SetModuleEventSubscriptions(name, enabled) + + module.enabled = enabled + MTH_SetPersistedModuleEnabled(name, enabled) + if self.SetConfig then + self:SetConfig(name, "enabled", enabled) + end + MTH_ModuleStateDebug("request " .. tostring(name) .. " applied runtime=" .. tostring(module.enabled)) + + return true +end + +function MTH:UnloadModule(name) + local module = self:GetModule(name) + if module then + if module.enabled then + self:_SetModuleEventSubscriptions(name, false) + end + if module.cleanup and type(module.cleanup) == "function" then + local ok, err = pcall(function() + module:cleanup() + end) + if not ok then + self:Print("Module '" .. name .. "' cleanup failed: " .. tostring(err)) + MTH:Print("[MTH] Module cleanup failed (" .. name .. "): " .. tostring(err), "error") + end + end + self.modules[name] = nil + end +end + +function MTH:FireEvent(event, arg1, arg2, arg3, arg4, arg5) + self:DispatchEvent(event, arg1, arg2, arg3, arg4, arg5) +end + +local function MTH_GetGlobalByName(name) + if type(name) ~= "string" or name == "" then + return nil + end + if type(getglobal) == "function" then + return getglobal(name) + end + if _G then + return _G[name] + end + return nil +end + +local function MTH_SetGlobalByName(name, value) + if type(name) ~= "string" or name == "" then + return + end + if type(setglobal) == "function" then + setglobal(name, value) + return + end + if _G then + _G[name] = value + end +end + +local function MTH_GetHookOwnerStore() + if not MTH._globalHookOwners then + MTH._globalHookOwners = {} + end + return MTH._globalHookOwners +end + +function MTH:GetGlobalHookOwner(globalName) + if type(globalName) ~= "string" or globalName == "" then + return nil + end + local owners = MTH_GetHookOwnerStore() + local owner = owners[globalName] + if type(owner) ~= "table" then + return nil + end + return owner.key +end + +function MTH:GetGlobalHookOwnersSnapshot() + local snapshot = {} + local owners = MTH_GetHookOwnerStore() + for globalName, owner in pairs(owners) do + if type(globalName) == "string" and type(owner) == "table" then + local active = false + if type(owner.hookFunc) == "function" then + active = MTH_GetGlobalByName(globalName) == owner.hookFunc + end + snapshot[globalName] = { + key = owner.key, + active = active, + } + end + end + return snapshot +end + +function MTH:PruneInactiveGlobalHookOwners() + local owners = MTH_GetHookOwnerStore() + local removed = 0 + + for globalName, owner in pairs(owners) do + local keep = false + if type(globalName) == "string" and type(owner) == "table" and type(owner.key) == "string" and owner.key ~= "" then + if type(owner.hookFunc) == "function" and MTH_GetGlobalByName(globalName) == owner.hookFunc then + keep = true + end + end + + if not keep then + owners[globalName] = nil + removed = removed + 1 + end + end + + return removed +end + +function MTH:CaptureHookBoundary(key, defs) + if type(key) ~= "string" or key == "" then + return nil, "invalid boundary key" + end + if type(defs) ~= "table" then + return nil, "invalid boundary definitions" + end + + self._hookBoundaries = self._hookBoundaries or {} + local boundary = self._hookBoundaries[key] + if boundary and boundary.captured then + return boundary + end + + boundary = { + captured = true, + entries = {}, + } + + local owners = MTH_GetHookOwnerStore() + + for _, def in ipairs(defs) do + if type(def) == "table" and type(def.globalName) == "string" and def.globalName ~= "" then + local currentGlobal = MTH_GetGlobalByName(def.globalName) + local entry = { + globalName = def.globalName, + originalName = def.originalName, + hookFunc = currentGlobal, + originalFunc = MTH_GetGlobalByName(def.originalName), + } + + local existingOwner = owners[def.globalName] + if type(existingOwner) == "table" + and existingOwner.key ~= key + and existingOwner.hookFunc + and currentGlobal == existingOwner.hookFunc + then + entry.ownerConflict = existingOwner.key + else + owners[def.globalName] = { + key = key, + hookFunc = currentGlobal, + } + end + + table.insert(boundary.entries, entry) + end + end + + self._hookBoundaries[key] = boundary + return boundary +end + +function MTH:RestoreHookBoundary(key) + if type(key) ~= "string" or key == "" then + return false, "invalid boundary key" + end + + local boundary = self._hookBoundaries and self._hookBoundaries[key] + if not boundary or not boundary.captured then + return false + end + + local owners = MTH_GetHookOwnerStore() + + for _, entry in ipairs(boundary.entries) do + if type(entry) == "table" and type(entry.globalName) == "string" then + local owner = owners[entry.globalName] + local ownerAllowsRestore = true + if type(owner) == "table" and owner.key and owner.key ~= key then + ownerAllowsRestore = false + end + + local current = MTH_GetGlobalByName(entry.globalName) + if ownerAllowsRestore and type(entry.originalFunc) == "function" and current == entry.hookFunc then + MTH_SetGlobalByName(entry.globalName, entry.originalFunc) + end + + if type(owner) == "table" and owner.key == key then + owners[entry.globalName] = nil + end + end + end + + boundary.captured = false + return true +end + +local function MTH_NormalizeSeverity(severity) + if severity == "debug" or severity == "error" then + return severity + end + return "" +end + +local function MTH_BuildChatPrefix(severity) + return "|cFFFFFFFF[|r|cFFD14A4AMeta|r|cFFABD473Hunt|r|cFFFFFFFF]|r " +end + +function MTH:Log(msg, severity) + local text = tostring(msg or "") + local level = MTH_NormalizeSeverity(severity) + local chatText = MTH_BuildChatPrefix(level) .. text + + if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + if level == "debug" then + DEFAULT_CHAT_FRAME:AddMessage(chatText, 0.4, 0.8, 1) + elseif level == "error" then + DEFAULT_CHAT_FRAME:AddMessage(chatText, 1, 0.2, 0.2) + else + DEFAULT_CHAT_FRAME:AddMessage(chatText, 1, 0.9, 0.3) + end + else + print(chatText) + end + + if MTH_DebugFrame then + if level == "debug" and MTH_DebugFrame.AddInfo then + MTH_DebugFrame:AddInfo(text) + elseif level == "error" and MTH_DebugFrame.AddError then + MTH_DebugFrame:AddError(text) + end + end + + return chatText +end + +function MTH:Print(msg, severity) + return self:Log(msg, severity) +end + +function MTH:DebugPrint(msg) + if self.debug then + self:Print("[DEBUG] " .. tostring(msg), "debug") + end +end + +function MTH_Log(message, severity) + if MTH and MTH.Log then + return MTH:Log(message, severity) + end + if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + DEFAULT_CHAT_FRAME:AddMessage("[MetaHunt] " .. tostring(message or ""), 1, 0.9, 0.3) + else + print("[MetaHunt] " .. tostring(message or "")) + end +end + +local function MTH_SplitMoney(money) + local value = math.floor(tonumber(money) or 0) + if value < 0 then value = 0 end + local gold = math.floor(value / 10000) + local silver = math.floor((value - (gold * 10000)) / 100) + local copper = value - (gold * 10000) - (silver * 100) + return gold, silver, copper +end + +function MTH_FormatMoney(money) + local value = math.floor(tonumber(money) or 0) + if value < 0 then value = 0 end + + local gold, silver, copper = MTH_SplitMoney(value) + local text = "" + if gold > 0 then + text = text .. "|cffffffff" .. tostring(gold) .. "|cffffd700g" + end + if silver > 0 or gold > 0 then + text = text .. "|cffffffff " .. tostring(silver) .. "|cffc7c7cfs" + end + text = text .. "|cffffffff " .. tostring(copper) .. "|cffeda55fc" + return text +end + +function MTH:FormatMoney(money) + return MTH_FormatMoney(money) +end + +function MTH_IsModuleEnabled(name, defaultEnabled) + if not (MTH and MTH.IsModuleEnabled) then + if defaultEnabled == nil then + return true + end + return defaultEnabled and true or false + end + return MTH:IsModuleEnabled(name, defaultEnabled) +end + +function MTH:InitSavedVariables() + if not MTH_SavedVariables then + MTH_SavedVariables = { + modules = {}, + feedomatic = {}, + zhunter = {}, + } + end + + if not MTH_SavedVariables.modules then + MTH_SavedVariables.modules = {} + end + if not MTH_SavedVariables.moduleStates then + MTH_SavedVariables.moduleStates = {} + end + if not MTH_SavedVariables.modules.feedomatic then + MTH_SavedVariables.modules.feedomatic = MTH_SavedVariables.feedomatic or {} + end + if not MTH_SavedVariables.modules.zhunter then + MTH_SavedVariables.modules.zhunter = MTH_SavedVariables.zhunter or {} + end + + MTH_SavedVariables.feedomatic = MTH_SavedVariables.modules.feedomatic + MTH_SavedVariables.zhunter = MTH_SavedVariables.modules.zhunter + + if not MTH_CharSavedVariables then + MTH_CharSavedVariables = {} + end + if not MTH_CharSavedVariables.modules then + MTH_CharSavedVariables.modules = {} + end + if not MTH_CharSavedVariables.moduleStates then + MTH_CharSavedVariables.moduleStates = {} + end + if not MTH_CharSavedVariables.petTraining then + MTH_CharSavedVariables.petTraining = { + spellMap = {}, + lastScan = 0, + lastPrompt = 0, + hasCompletedPetScan = nil, + } + end + if not MTH_CharSavedVariables.trainScan then + MTH_CharSavedVariables.trainScan = MTH_CharSavedVariables.petTraining + end + if MTH_CharSavedVariables.petTraining ~= MTH_CharSavedVariables.trainScan then + MTH_CharSavedVariables.petTraining = MTH_CharSavedVariables.trainScan + end + if MTH_CharSavedVariables.petTraining.hasCompletedPetScan == nil then + local pt = MTH_CharSavedVariables.petTraining + if tonumber(pt.lastScan) and tonumber(pt.lastScan) > 0 then + pt.hasCompletedPetScan = 1 + elseif pt.spellMap then + for _ in pairs(pt.spellMap) do + pt.hasCompletedPetScan = 1 + break + end + end + end + + if type(MTH_PETS_GetRootStore) == "function" then + local pets = MTH_PETS_GetRootStore() + if type(pets) == "table" and type(pets.trainScan or pets.petTraining) == "table" then + MTH_CharSavedVariables.trainScan = pets.trainScan or pets.petTraining + MTH_CharSavedVariables.petTraining = MTH_CharSavedVariables.trainScan + if MTH_CharSavedVariables.petTraining.hasCompletedPetScan == nil then + local pt = MTH_CharSavedVariables.petTraining + if (tonumber(pt.lastScan) or 0) > 0 then + pt.hasCompletedPetScan = 1 + elseif type(pt.spellMap) == "table" then + for _ in pairs(pt.spellMap) do + pt.hasCompletedPetScan = 1 + break + end + end + end + end + end +end + +function MTH:GetModuleSavedVariables(name) + name = MTH_NormalizeModuleName(name) + if not MTH_SavedVariables then + self:InitSavedVariables() + end + if not MTH_SavedVariables.modules then + MTH_SavedVariables.modules = {} + end + if not MTH_SavedVariables.modules[name] then + MTH_SavedVariables.modules[name] = {} + end + return MTH_SavedVariables.modules[name] +end + +function MTH:GetModuleCharSavedVariables(name) + name = MTH_NormalizeModuleName(name) + if not MTH_CharSavedVariables then + self:InitSavedVariables() + end + if not MTH_CharSavedVariables.modules then + MTH_CharSavedVariables.modules = {} + end + if not MTH_CharSavedVariables.modules[name] then + MTH_CharSavedVariables.modules[name] = {} + end + return MTH_CharSavedVariables.modules[name] +end + +function MTH:ApplyPersistedModuleStates(source) + if self:IsClassGateBlocked() then + return + end + + self:InitSavedVariables() + for name, module in pairs(self.modules) do + local defaultEnabled = module._mth_defaultEnabled + if defaultEnabled == nil then + defaultEnabled = module.enabled and true or false + end + + local persistedEnabled = MTH_GetPersistedModuleEnabled(name, defaultEnabled) + MTH_ModuleStateDebug("bootstrap " .. tostring(source or "") .. " " .. tostring(name) .. " persisted=" .. tostring(persistedEnabled) .. " runtime=" .. tostring(module.enabled and true or false)) + + if module.enabled ~= persistedEnabled then + local ok, err = self:SetModuleEnabled(name, persistedEnabled) + if not ok then + MTH_ModuleStateDebug("bootstrap apply failed " .. tostring(name) .. ": " .. tostring(err)) + end + else + MTH_SetPersistedModuleEnabled(name, persistedEnabled) + end + end +end + +function MTH:EnsureModuleStateBootstrap() + if self:IsClassGateBlocked() then + return nil + end + + if self._moduleStateBootstrapFrame then + return self._moduleStateBootstrapFrame + end + + local frame = CreateFrame("Frame", "MTH_ModuleStateBootstrapFrame") + if not frame then + return nil + end + + frame:RegisterEvent("VARIABLES_LOADED") + frame:SetScript("OnEvent", function(self, eventName) + self = self or this + eventName = eventName or event + if eventName ~= "VARIABLES_LOADED" then + return + end + if MTH and MTH.ApplyPersistedModuleStates then + MTH:ApplyPersistedModuleStates("VARIABLES_LOADED") + end + if not self then return end + self:UnregisterAllEvents() + self:SetScript("OnEvent", nil) + end) + + self._moduleStateBootstrapFrame = frame + return frame +end + +function MTH:AnnounceLoadComplete() + if self:IsClassGateBlocked() then + return + end + + if self._loadCompleteAnnounced then + return + end + + local enabled = {} + for name, module in pairs(self.modules or {}) do + if module and module.enabled then + table.insert(enabled, tostring(name)) + end + end + table.sort(enabled) + + local moduleList = "none" + if table.getn(enabled) > 0 then + moduleList = table.concat(enabled, ", ") + end + + self:Print("Check ignition: OK! Version " .. tostring(self.version or "unknown") .. " loaded.") + --self:Print("Modules enabled : " .. moduleList .. ".") + + local petTrainingStore = nil + if type(MTH_PETS_GetRootStore) == "function" then + local pets = MTH_PETS_GetRootStore() + if type(pets) == "table" and type(pets.trainScan or pets.petTraining) == "table" then + petTrainingStore = pets.trainScan or pets.petTraining + if MTH_CharSavedVariables then + MTH_CharSavedVariables.trainScan = petTrainingStore + MTH_CharSavedVariables.petTraining = petTrainingStore + end + end + end + if not petTrainingStore then + petTrainingStore = MTH_CharSavedVariables and (MTH_CharSavedVariables.trainScan or MTH_CharSavedVariables.petTraining) + end + local hasCompletedPetScan = false + if petTrainingStore then + if petTrainingStore.hasCompletedPetScan then + hasCompletedPetScan = true + elseif (tonumber(petTrainingStore.lastScan) or 0) > 0 then + hasCompletedPetScan = true + elseif type(petTrainingStore.spellMap) == "table" then + for _ in pairs(petTrainingStore.spellMap) do + hasCompletedPetScan = true + break + end + elseif type(petTrainingStore.hunterKnownMap) == "table" then + for _ in pairs(petTrainingStore.hunterKnownMap) do + hasCompletedPetScan = true + break + end + end + end + if not hasCompletedPetScan then + local playerLevel = (type(UnitLevel) == "function") and (tonumber(UnitLevel("player")) or 0) or 0 + if playerLevel >= 12 then + self:Print("You should open your Beast Training once to save your learned pet abilities.") + self._loadCompleteAnnounced = true + return + end + end + + local welcomeMessages = { + "You are now shipping the latest cutting edge technology.", + "SHELLS ARE FOR PUSSIES.", + "Sometimes, you just need a little less gun.", + "Repeat 5 times : \"Un chasseur sachant chasser sans son chien\".", + "Welcome back, hunter — your pet filed three HR complaints while you were offline.", + "Fresh build loaded: now with 12% more pew and 38% less panic.", + "If it moves, track it. If it doesn't move, it’s probably a trap you already dropped.", + "Your arrows are sorted, your bars are neat, and your excuses are currently on cooldown.", + "Remember: missing shots are just warning shots for nearby wildlife.", + "Pet status: loyal, hungry, and judging your last pull.", + "Consider joining if you like being called a Silly Goose.", + "Today’s strategy: confidence, chaos, and pretending it was all planned.", + "You don’t need luck — just line of sight and questionable self-control.", + "Chronometer is awake, traps are armed, and diplomacy has left the chat.", + "Go make bad decisions at range.", + "Hunter online. Morals offline. Ammo questionable.", + "If your pull feels safe, you probably pulled the wrong pack.", + "Pet AI says \"follow.\" Pet heart says \"chaos.\"", + "Today’s build is stable, unlike your threat meter.", + "Get ready for a bloody hunting session.", + "You miss 100% of the shots you don’t blame on lag.", + "Traps are temporary, embarrassment in party chat is forever.", + "Your quiver is full, your bags are not, your choices are yours.", + "Keep calm and feign absolutely everything.", + "If diplomacy fails, send the pet. If pet fails, send apology.", + "Welcome back. Try not to invent a new wipe mechanic.", + "Welcome back, hunter. Your pet says it carried the last dungeon and requests hazard pay.", + "MetaHunt loaded. Confidence initialized. Accuracy still loading...", + "Arrows: stocked. Traps: armed. Group chat patience: limited.", + "If the pull looks clean, you forgot at least one patrol.", + "Your pet is ready, your bags are not, and destiny is slightly overpulled.", + "Good news: your aim is back. Bad news: so is your aggro.", + "The cake is a lie.", + "Today’s forecast: scattered crits with a high chance of panic kiting.", + "Welcome back. Remember: Feign Death is not a lifestyle, it’s a tool.", + "Fresh session started. Please keep all limbs inside line of sight.", + "Your pet has accepted this mission. Your healer has not.", + "Calm mind, steady hand, absolutely chaotic pull order.", + "Reload complete. Morale high. Ammo probably in the wrong bag.", + "You bring the damage, the pet brings the drama, the party brings questions.", + "Traps placed with confidence and minimal understanding of consequences.", + "You are now entering a no-fault zone. Any wipes are purely atmospheric.", + "Welcome back, ranger. Please ping responsibly and overpull professionally.", + "Target acquired. Plan prepared. Plan ignored.", + "Pet mood: loyal. Pet hunger: eternal. Pet pathing: experimental.", + "Another day, another masterpiece in ranged chaos.", + "MetaHunt online. Go forth and make very confident tactical mistakes.", + "Dry sarcasm mode activated. Expectations lowered to safe operating levels.", + "Great news: you logged in. Even better news: your pet still believes in you.", + "You are absolutely prepared for this pull, in the same way a torch is prepared for rain.", + "Welcome back. Your cooldowns are ready and your judgment is on a short break.", + "If this run goes smoothly, we’ll all pretend it was intentional.", + "You bring precision, your pet brings enthusiasm, and reality brings pathing.", + "Today’s objective: do your best, then blame line of sight professionally.", + "Friendly reminder: overconfidence is a buff with a very short duration.", + "You’re not behind schedule; you’re operating on cinematic timing.", + "Your pet is doing its best, which is more than we can ask from stairs.", + "New day, new hunt, same wholesome chaos with extra snacks for the pet.", + "You’ve got this — and if not, you’ve got Feign Death, which is basically plan B.", + "Be kind, pull smart, and let your arrows do the confident talking.", + "Your healer appreciates communication. Your tank appreciates fewer surprises.", + "May your crits be high and your repair bill spiritually manageable.", + "Extra savage forecast: 100% chance of \"I meant to do that.\"", + "Pull like a legend, recover like a professional, explain like a politician.", + "If your trap catches nothing, call it zoning control and move on.", + "Your threat meter is not a suggestion box.", + "Go forth, ranged menace — be accurate, be humble, and be slightly less feral than your pet.", + "Welcome back, outlaw — your pet already blamed you in guild chat.", + "MetaHunt online. Good decisions remain optional.", + "You logged in with confidence and approximately zero accountability.", + "Your pull plan is bold, reckless, and legally fascinating.", + "Arrows loaded, ego loaded, consequences loading...", + "Hunter mode engaged: precision first, apologies later.", + "Your healer saw you log in and started pre-drinking mana potions.", + "Cooldowns ready. Morals unavailable in your region.", + "Pet is loyal, hungry, and deeply concerned about your strategy.", + "Welcome back, menace. Try not to speedrun another wipe.", + "You bring damage, drama, and highly questionable target priority.", + "If this goes smoothly, nobody will believe you.", + "Your threat meter is a warning, not a lifestyle choice.", + "Traps armed. Safety standards disarmed.", + "You are one bad pull away from becoming a cautionary tale.", + "Great posture, sharp aim, catastrophic impulse control.", + "MetaHunt loaded. The chaos now has proper tooling.", + "Your pet follows orders. Pathing follows dark magic.", + "Today’s objective: hit crits and dodge responsibility.", + "Go forth, badboy ranger — leave footprints, not explanations.", + } + + local welcomeIndex = 1 + local welcomeCount = table.getn(welcomeMessages) + if welcomeCount > 1 then + if type(random) == "function" then + welcomeIndex = random(1, welcomeCount) + elseif type(math) == "table" and type(math.random) == "function" then + welcomeIndex = math.random(1, welcomeCount) + end + end + self:Print(welcomeMessages[welcomeIndex]) + self._loadCompleteAnnounced = true +end + +function MTH:EnsureLoadCompleteAnnouncement() + if self:IsHardBlocked() then + return nil + end + + if self:IsClassGateBlocked() then + return nil + end + + if self._loadCompleteFrame then + return self._loadCompleteFrame + end + + local frame = CreateFrame("Frame", "MTH_LoadCompleteAnnounceFrame") + if not frame then + return nil + end + + frame:RegisterEvent("PLAYER_ENTERING_WORLD") + frame:SetScript("OnEvent", function(self, eventName) + self = self or this + eventName = eventName or event + if eventName ~= "PLAYER_ENTERING_WORLD" then + return + end + if MTH and MTH.AnnounceLoadComplete then + MTH:AnnounceLoadComplete() + end + if not self then return end + self:UnregisterAllEvents() + self:SetScript("OnEvent", nil) + end) + + self._loadCompleteFrame = frame + return frame +end + +MTH:EnsureModuleStateBootstrap() +MTH:EnsureLoadCompleteAnnouncement() + +function MTH:ShutdownForNonHunter(source) + if self:IsHardBlocked() then + return true + end + + self._hardBlocked = true + MTH_ClassGateTrace("ShutdownForNonHunter", "source=" .. tostring(source)) + + for name, module in pairs(self.modules or {}) do + if module and module.enabled then + self:_SetModuleEventSubscriptions(name, false) + end + + if module and module.setEnabled and type(module.setEnabled) == "function" then + pcall(function() + module:setEnabled(false) + end) + end + + if module and module.cleanup and type(module.cleanup) == "function" then + pcall(function() + module:cleanup() + end) + end + + if module then + module.enabled = false + end + end + + if self._eventRouter and self._eventRouter.frame then + self._eventRouter.frame:UnregisterAllEvents() + self._eventRouter.frame:SetScript("OnEvent", nil) + self._eventRouter.eventRefs = {} + end + + if self._moduleStateBootstrapFrame then + self._moduleStateBootstrapFrame:UnregisterAllEvents() + self._moduleStateBootstrapFrame:SetScript("OnEvent", nil) + self._moduleStateBootstrapFrame = nil + end + + if self._loadCompleteFrame then + self._loadCompleteFrame:UnregisterAllEvents() + self._loadCompleteFrame:SetScript("OnEvent", nil) + self._loadCompleteFrame = nil + end + + if type(MTH_TR_ShutdownService) == "function" then + pcall(function() + MTH_TR_ShutdownService("class-gate") + end) + elseif type(MTH_PT_ShutdownService) == "function" then + pcall(function() + MTH_PT_ShutdownService("class-gate") + end) + end + + if type(MTH_PS_ShutdownService) == "function" then + pcall(function() + MTH_PS_ShutdownService("class-gate") + end) + end + + if type(MTH_ST_ShutdownService) == "function" then + pcall(function() + MTH_ST_ShutdownService("class-gate") + end) + end + + return true +end diff --git a/api/core-scan-beasttraining.lua b/api/core-scan-beasttraining.lua new file mode 100644 index 0000000..fe41d1b --- /dev/null +++ b/api/core-scan-beasttraining.lua @@ -0,0 +1,912 @@ +if not MTH then + error("MetaHunt core framework missing: api/core-framework.lua must load before api/core-scan-beasttraining.lua") +end + +local function MTH_PT_SafeLower(text) + if text == nil then return "" end + return string.lower(tostring(text)) +end + +local function MTH_PT_GetGlobal(name) + if type(getglobal) == "function" then + return getglobal(name) + end + if _G then + return _G[name] + end + return nil +end + +local MTH_PT_ApplyScan +local MTH_PT_AbilityHasPositiveRanks +local MTH_PT_PromoteUnrankedKnownAliases + +local MTH_PT_VERBOSE_SCAN_LOGS = false + +local function MTH_PT_GetStore() + if not MTH_CharSavedVariables then MTH_CharSavedVariables = {} end + if not MTH_CharSavedVariables.trainScan and MTH_CharSavedVariables.petTraining then + MTH_CharSavedVariables.trainScan = MTH_CharSavedVariables.petTraining + end + if not MTH_CharSavedVariables.trainScan then + MTH_CharSavedVariables.trainScan = { + spellMap = {}, + hunterKnownMap = {}, + lastScan = 0, + lastPrompt = 0, + hasCompletedPetScan = nil, + } + end + if not MTH_CharSavedVariables.trainScan.spellMap then + MTH_CharSavedVariables.trainScan.spellMap = {} + end + if not MTH_CharSavedVariables.trainScan.hunterKnownMap then + MTH_CharSavedVariables.trainScan.hunterKnownMap = {} + end + if MTH_CharSavedVariables.trainScan.hasCompletedPetScan == nil then + if tonumber(MTH_CharSavedVariables.trainScan.lastScan) and tonumber(MTH_CharSavedVariables.trainScan.lastScan) > 0 then + MTH_CharSavedVariables.trainScan.hasCompletedPetScan = 1 + elseif MTH_CharSavedVariables.trainScan.spellMap then + for _ in pairs(MTH_CharSavedVariables.trainScan.spellMap) do + MTH_CharSavedVariables.trainScan.hasCompletedPetScan = 1 + break + end + end + end + MTH_CharSavedVariables.petTraining = MTH_CharSavedVariables.trainScan + MTH_PT_PromoteUnrankedKnownAliases(MTH_CharSavedVariables.trainScan) + return MTH_CharSavedVariables.trainScan +end + +function MTH_GetPetTrainingSnapshot() + return MTH_PT_GetStore() +end + +function MTH_GetKnownPetSpellMap() + local store = MTH_PT_GetStore() + if type(store) ~= "table" then + return nil + end + return store.spellMap +end + +local function MTH_PT_ParseRank(name, subText) + local rankNumber = nil + local sub = tostring(subText or "") + if sub ~= "" then + local _, _, subRank = string.find(sub, "(%d+)") + rankNumber = tonumber(subRank) + end + if not rankNumber then + local nm = tostring(name or "") + local _, _, nameRank = string.find(nm, "(%d+)") + rankNumber = tonumber(nameRank) + end + if not rankNumber then + local full = tostring(name or "") .. " " .. tostring(subText or "") + local _, _, fullRank = string.find(full, "(%d+)") + rankNumber = tonumber(fullRank) + end + if rankNumber then + return "Rank " .. tostring(rankNumber), rankNumber + end + return "", nil +end + +local function MTH_PT_NormalizeName(name) + local label = tostring(name or "") + label = string.gsub(label, "^%s+", "") + label = string.gsub(label, "%s+$", "") + return label +end + +local function MTH_PT_BaseAbility(name) + local base = MTH_PT_NormalizeName(name) + base = string.gsub(base, "%s*%([^%)]*%d+[^%)]*%)", "") + base = string.gsub(base, "%s*%([Rr]ank[^%)]*%)", "") + base = string.gsub(base, "%s+[Rr]ank%s+%d+", "") + base = string.gsub(base, "%s+%d+$", "") + base = string.gsub(base, "%s+$", "") + return base +end + +local function MTH_PT_MakeToken(name, subText) + local base = MTH_PT_BaseAbility(name) + local rankText, rankNumber = MTH_PT_ParseRank(name, subText) + if rankNumber and rankNumber > 0 and not MTH_PT_AbilityHasPositiveRanks(base) then + rankText = "" + rankNumber = nil + end + if rankText ~= "" then + return MTH_PT_SafeLower(base) .. "#" .. tostring(rankNumber), base, rankText, rankNumber + end + return MTH_PT_SafeLower(base), base, "", nil +end + +local function MTH_PT_HasAnyLearnedSpells() + local store = MTH_PT_GetStore() + local spellMap = store.spellMap + if not spellMap then return false end + for _ in pairs(spellMap) do + return true + end + return false +end + +local function MTH_PT_IsBeastTrainingContextActive() + local frameNames = { "BeastTrainingFrame", "PetTrainingFrame", "CraftFrame" } + for i = 1, table.getn(frameNames) do + local frameName = frameNames[i] + local frame = (type(getglobal) == "function" and getglobal(frameName)) or (_G and _G[frameName]) + if frame then + if frame.IsVisible and frame:IsVisible() then + return true + end + if frame.IsShown and frame:IsShown() then + return true + end + end + end + return false +end + +local function MTH_PT_GetBeastTrainingRowCount() + if not MTH_PT_IsBeastTrainingContextActive() then + return 0 + end + + local getNumCrafts = MTH_PT_GetGlobal("GetNumCrafts") + if type(getNumCrafts) ~= "function" then + return 0 + end + return tonumber(getNumCrafts()) or 0 +end + +function MTH_PT_HasBeastTrainingRows() + return MTH_PT_GetBeastTrainingRowCount() > 0 +end + +local function MTH_PT_DebugLog(line) + if not MTH_PT_VERBOSE_SCAN_LOGS then return end + local text = "[TRAINSCAN] " .. tostring(line or "") + MTH:Print(text, "debug") +end + +local function MTH_PT_MakeTokenFromAbilityRank(abilityName, rankNumber) + local ability = MTH_PT_NormalizeName(abilityName) + local key = MTH_PT_SafeLower(ability) + if key == "" then return "", ability, "", nil end + local rank = tonumber(rankNumber) + if rank and rank > 0 and not MTH_PT_AbilityHasPositiveRanks(ability) then + rank = nil + end + if rank and rank > 0 then + return key .. "#" .. tostring(rank), ability, "Rank " .. tostring(rank), rank + end + return key, ability, "", nil +end + +local function MTH_PT_AbilityExistsInData(baseAbility) + local ability = MTH_PT_NormalizeName(baseAbility) + if ability == "" then return false end + if not MTH_DS_PetSpells or not MTH_DS_PetSpells.byAbility then + return false + end + if MTH_DS_PetSpells.byAbility[ability] then + return true + end + for abilityName in pairs(MTH_DS_PetSpells.byAbility) do + if MTH_PT_SafeLower(abilityName) == MTH_PT_SafeLower(ability) then + return true + end + end + return false +end + +MTH_PT_AbilityHasPositiveRanks = function(baseAbility) + local ability = MTH_PT_NormalizeName(baseAbility) + if ability == "" then return false end + if not MTH_DS_PetSpells or not MTH_DS_PetSpells.byAbility then + return false + end + + local bucket = MTH_DS_PetSpells.byAbility[ability] + if not bucket then + for abilityName, row in pairs(MTH_DS_PetSpells.byAbility) do + if MTH_PT_SafeLower(abilityName) == MTH_PT_SafeLower(ability) then + bucket = row + break + end + end + end + + if not (bucket and bucket.spells) then + return false + end + + for i = 1, table.getn(bucket.spells) do + local spell = bucket.spells[i] + if spell then + local rank = tonumber(spell.rankNumber) + if not rank and spell.rank then + local _, _, parsed = string.find(tostring(spell.rank), "(%d+)") + rank = tonumber(parsed) + end + if rank and rank > 0 then + return true + end + end + end + + return false +end + +MTH_PT_PromoteUnrankedKnownAliases = function(store) + if not store or not store.hunterKnownMap or not store.spellMap then + return + end + + for token, isKnown in pairs(store.hunterKnownMap) do + if isKnown == true then + local _, _, baseToken = string.find(tostring(token), "^(.-)#%d+$") + if baseToken and baseToken ~= "" then + local baseRow = store.spellMap[baseToken] + if baseRow and ((tonumber(baseRow.rankNumber) or 0) <= 0) then + store.hunterKnownMap[baseToken] = true + if baseRow.isKnown ~= true then + baseRow.isKnown = true + end + end + end + end + end +end + +local function MTH_PT_MarkHunterKnownToken(token) + if token == nil or token == "" then + return false + end + + local store = MTH_PT_GetStore() + local markToken = token + local _, _, baseToken = string.find(tostring(token), "^(.-)#%d+$") + if baseToken and baseToken ~= "" then + local baseRow = store.spellMap and store.spellMap[baseToken] + if baseRow and ((tonumber(baseRow.rankNumber) or 0) <= 0) then + markToken = baseToken + end + end + local changed = false + if store.hunterKnownMap[markToken] ~= true then + store.hunterKnownMap[markToken] = true + changed = true + end + if markToken ~= token and store.hunterKnownMap[token] ~= true then + store.hunterKnownMap[token] = true + changed = true + end + + if store.spellMap and store.spellMap[markToken] and store.spellMap[markToken].isKnown ~= true then + store.spellMap[markToken].isKnown = true + changed = true + end + if markToken ~= token and store.spellMap and store.spellMap[token] and store.spellMap[token].isKnown ~= true then + store.spellMap[token].isKnown = true + changed = true + end + + if changed and MTH_PT_DebugLog then + MTH_PT_DebugLog("hunter-known token promoted: " .. tostring(token)) + end + + return changed +end + +local function MTH_PT_IsHunterTokenKnown(token) + if token == nil or token == "" then + return false + end + + local store = MTH_PT_GetStore() + if store.hunterKnownMap and store.hunterKnownMap[token] == true then + return true + end + + if store.spellMap and store.spellMap[token] and store.spellMap[token].isKnown == true then + return true + end + + local _, _, baseToken = string.find(tostring(token), "^(.-)#%d+$") + if baseToken and baseToken ~= "" then + if store.hunterKnownMap and store.hunterKnownMap[baseToken] == true then + return true + end + if store.spellMap and store.spellMap[baseToken] and store.spellMap[baseToken].isKnown == true then + return true + end + end + + return false +end + +local function MTH_PT_ProcessLearnSystemMessage(rawMessage) + local message = tostring(rawMessage or "") + if message == "" then return false end + message = string.gsub(message, "|c%x%x%x%x%x%x%x%x", "") + message = string.gsub(message, "|r", "") + message = string.gsub(message, "^%s+", "") + message = string.gsub(message, "%s+$", "") + + local _, _, abilityName, rankText = string.find(message, "^[Yy]ou have learned a new spell:%s*(.-)%s*%([Rr]ank%s*(%d+)%)%.?$") + if not abilityName or not rankText then + _, _, abilityName, rankText = string.find(message, "^[Yy]ou have learned a new spell:%s*(.-)%s*[Rr]ank%s*(%d+)%.?$") + end + if not abilityName or not rankText then + return false + end + + local rankNumber = tonumber(rankText) + if not rankNumber then + return false + end + + abilityName = MTH_PT_NormalizeName(abilityName) + if abilityName == "" then return false end + if not MTH_PT_AbilityExistsInData(abilityName) then + if MTH_PT_DebugLog then + MTH_PT_DebugLog("learn-msg ignored (not pet ability dataset): '" .. tostring(abilityName) .. "'") + end + return false + end + + local token = MTH_PT_MakeTokenFromAbilityRank(abilityName, rankNumber) + if token == "" then return false end + + local alreadyKnown = MTH_PT_IsHunterTokenKnown(token) + local changed = false + if not alreadyKnown then + changed = MTH_PT_MarkHunterKnownToken(token) + end + + local petRecorded = false + if type(MTH_PETS_RecordCurrentPetLearnedAbility) == "function" then + petRecorded = MTH_PETS_RecordCurrentPetLearnedAbility(abilityName, rankNumber, "beasttraining:learn-msg") and true or false + end + + if changed or petRecorded then + if MTH_PT_DebugLog then + MTH_PT_DebugLog("learn-msg captured: source='you have learned a new spell' ability='" .. tostring(abilityName) .. "' rank=" .. tostring(rankNumber) .. " token='" .. tostring(token) .. "' changed=" .. tostring(changed) .. " petRecorded=" .. tostring(petRecorded)) + end + local label = tostring(abilityName) + if rankNumber and rankNumber > 0 then + label = label .. " (Rank " .. tostring(rankNumber) .. ")" + end + if MTH and MTH.Print then + if changed then + MTH:Print("Pet ability learned and recorded: " .. label .. ". It is now marked as known in MetaHunt.") + else + MTH:Print("Pet ability learned for current pet: " .. label .. ".") + end + end + if changed then + MTH_PT_ApplyScan("learn-msg") + end + if type(MTH_PSP_RequestScan) == "function" then + MTH_PSP_RequestScan("train-learn-msg") + end + elseif alreadyKnown and MTH_PT_DebugLog then + MTH_PT_DebugLog("learn-msg ignored (already known and no active pet row update): token='" .. tostring(token) .. "'") + end + + return changed or petRecorded +end + +local function MTH_PT_CollectKnownBeastTrainingMap() + if not MTH_PT_IsBeastTrainingContextActive() then + if MTH_PT_DebugLog then + MTH_PT_DebugLog("collect beast-training known skipped: explicit beast training context not active") + end + return {} + end + + local getNumCrafts = MTH_PT_GetGlobal("GetNumCrafts") + local getCraftInfo = MTH_PT_GetGlobal("GetCraftInfo") + if type(getNumCrafts) ~= "function" or type(getCraftInfo) ~= "function" then + if MTH_PT_DebugLog then + MTH_PT_DebugLog("collect beast-training known unavailable: GetNumCrafts=" .. tostring(type(getNumCrafts)) .. ", GetCraftInfo=" .. tostring(type(getCraftInfo))) + end + return {} + end + + local totalCrafts = tonumber(getNumCrafts()) or 0 + local knownMap = {} + if totalCrafts <= 0 then + if MTH_PT_DebugLog then + MTH_PT_DebugLog("collect beast-training known empty: totalRows=0") + end + return knownMap + end + + if MTH_PT_DebugLog then + MTH_PT_DebugLog("collect beast-training known start: totalRows=" .. tostring(totalCrafts)) + end + for i = 1, totalCrafts do + local craftName, craftSubSpellName, craftType = getCraftInfo(i) + local typeText = tostring(craftType or "") + local token, base, parsedRankText, rankNumber = MTH_PT_MakeToken(craftName, craftSubSpellName) + + if token ~= "" then + if MTH_PT_DebugLog then + MTH_PT_DebugLog("beast-training row=" .. tostring(i) .. " token='" .. tostring(token) .. "' name='" .. tostring(base or "") .. "' rank='" .. tostring(parsedRankText or "") .. "' rankNumber=" .. tostring(rankNumber) .. " type='" .. typeText .. "'") + end + knownMap[token] = { + name = MTH_PT_NormalizeName(base), + rank = parsedRankText, + rankNumber = rankNumber, + fullName = MTH_PT_NormalizeName(craftName), + subText = tostring(craftSubSpellName or ""), + isKnown = true, + scannedAt = time(), + rowType = typeText, + } + end + end + if MTH_PT_DebugLog then + local total = 0 + for _ in pairs(knownMap) do total = total + 1 end + MTH_PT_DebugLog("collect beast-training known end: tokens=" .. tostring(total)) + end + return knownMap +end + +function MTH_PT_ApplyScan(triggerReason) + if MTH_PT_DebugLog then + MTH_PT_DebugLog("scan start: trigger=" .. tostring(triggerReason or "")) + end + + local store = MTH_PT_GetStore() + local oldMap = store.spellMap + if not oldMap then oldMap = {} end + local hunterKnownMap = store.hunterKnownMap or {} + + local knownMap = MTH_PT_CollectKnownBeastTrainingMap() + local knownCount = 0 + for _ in pairs(knownMap) do knownCount = knownCount + 1 end + if knownCount <= 0 then + if MTH_PT_DebugLog then + MTH_PT_DebugLog("scan abort: beast-training known count=0") + end + return false, 0, 0, 0 + end + for token in pairs(knownMap) do + hunterKnownMap[token] = true + end + for token, row in pairs(oldMap) do + if row and row.isKnown then + hunterKnownMap[token] = true + end + end + for token in pairs(knownMap) do + if hunterKnownMap[token] == nil then + hunterKnownMap[token] = false + end + end + store.hunterKnownMap = hunterKnownMap + + local newMap = {} + if MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility then + for abilityName, bucket in pairs(MTH_DS_PetSpells.byAbility) do + if bucket and bucket.spells then + for i = 1, table.getn(bucket.spells) do + local spell = bucket.spells[i] + if spell then + local rankNumber = tonumber(spell.rankNumber) + if not rankNumber and spell.rank then + local _, _, parsed = string.find(tostring(spell.rank), "(%d+)") + rankNumber = tonumber(parsed) + end + local token, baseName, rankText, parsedRankNumber = MTH_PT_MakeTokenFromAbilityRank(abilityName, rankNumber) + if token ~= "" then + local tpCost = tonumber(spell.trainingPointCost) + if not tpCost and spell.cost then + local _, _, parsedCost = string.find(tostring(spell.cost), "(%d+)") + tpCost = tonumber(parsedCost) + end + newMap[token] = { + name = baseName, + rank = rankText, + rankNumber = parsedRankNumber, + fullName = rankText ~= "" and (baseName .. " " .. rankText) or baseName, + subText = rankText, + serviceType = "datastore", + reqLevel = tonumber(spell.trainLevel), + isKnown = hunterKnownMap[token] and true or false, + isKnownPet = knownMap[token] and true or false, + scannedAt = time(), + trainingPointCost = tpCost, + learnMethod = spell.learnMethod, + icon = spell.icon, + } + end + end + end + end + end + end + + for token, row in pairs(knownMap) do + if newMap[token] then + newMap[token].isKnownPet = true + newMap[token].scannedAt = time() + else + newMap[token] = { + name = row.name, + rank = row.rank, + rankNumber = row.rankNumber, + fullName = row.fullName, + subText = row.subText, + serviceType = "beast-training", + reqLevel = nil, + isKnown = hunterKnownMap[token] and true or false, + isKnownPet = true, + scannedAt = time(), + trainingPointCost = nil, + } + end + end + + local discovered = 0 + for _ in pairs(newMap) do discovered = discovered + 1 end + if discovered <= 0 then + return false, 0, 0, 0 + end + + local added = 0 + local removed = 0 + local addedDetails = {} + for token in pairs(newMap) do + if not oldMap[token] then + added = added + 1 + local row = newMap[token] + local spellName = MTH_PT_NormalizeName((row and row.name) or "") + local rankText = tostring((row and row.rank) or "") + if spellName == "" then + spellName = MTH_PT_NormalizeName((row and row.fullName) or "") + end + if spellName ~= "" then + local label = spellName + if rankText ~= "" then + label = label .. " " .. rankText + end + table.insert(addedDetails, label) + end + end + end + for token in pairs(oldMap) do + if not newMap[token] then + removed = removed + 1 + end + end + + store.spellMap = newMap + store.lastScan = time() + store.hasCompletedPetScan = 1 + MTH_CharSavedVariables.trainScan = store + MTH_CharSavedVariables.petTraining = store + + local totalRanks = 0 + local baseSet = {} + for _, row in pairs(newMap) do + totalRanks = totalRanks + 1 + local base = MTH_PT_SafeLower(row and row.name) + if base ~= "" then baseSet[base] = true end + end + local totalBaselines = 0 + for _ in pairs(baseSet) do totalBaselines = totalBaselines + 1 end + + if triggerReason == "initial" then + MTH:Print("Initial train scan complete: " .. tostring(totalBaselines) .. " baselines / " .. tostring(totalRanks) .. " ranks recorded for this character.") + elseif added > 0 then + table.sort(addedDetails) + local detailsText = table.concat(addedDetails, ", ") + if detailsText ~= "" then + local msg = "New pet data recorded (" .. tostring(added) .. " new rank(s)): " .. detailsText .. ". Known totals for this character: " .. tostring(totalBaselines) .. " ability type(s), " .. tostring(totalRanks) .. " rank(s)." + MTH:Print(msg) + MTH:Print("INFO: " .. msg, "debug") + else + local msg = "New pet data recorded (" .. tostring(added) .. " new rank(s)). Known totals for this character: " .. tostring(totalBaselines) .. " ability type(s), " .. tostring(totalRanks) .. " rank(s)." + MTH:Print(msg) + MTH:Print("INFO: " .. msg, "debug") + end + end + + return true, added, totalBaselines, totalRanks +end + +local function MTH_PT_ShouldScanNow() + return MTH_PT_HasBeastTrainingRows() +end + +MTH_PT_IsPetBookVisible = function() + local frameNames = { "BeastTrainingFrame", "PetTrainingFrame", "CraftFrame" } + for i = 1, table.getn(frameNames) do + local frameName = frameNames[i] + local frame = (type(getglobal) == "function" and getglobal(frameName)) or (_G and _G[frameName]) + if frame then + if frame.IsVisible and frame:IsVisible() then + return true + end + if frame.IsShown and frame:IsShown() then + return true + end + end + end + return false +end + +local function MTH_PT_RunDoubleScan(triggerBase) + local triggerText = tostring(triggerBase or "") + local feedbackOnOpen = ( + triggerText == "petbook-open" + or triggerText == "craft-open" + or triggerText == "craft-update" + or triggerText == "beast-training-rows-open" + or triggerText == "petbook-visible-poll" + ) + if not MTH_PT_ShouldScanNow() then + if MTH_PT_DebugLog then + MTH_PT_DebugLog("scan skipped: no beast-training rows, trigger=" .. tostring(triggerBase or "")) + end + if feedbackOnOpen and MTH and MTH.Print then + MTH:Print("Beast Training opened, but scan did not run yet (no scannable rows detected).") + end + return false + end + + local t0 = time() + local ok, added, baselines, ranks = MTH_PT_ApplyScan(triggerText .. ":single") + local t1 = time() + + if MTH_PT_DebugLog then + MTH_PT_DebugLog("scan complete: trigger=" .. tostring(triggerBase or "") .. " ok=" .. tostring(ok and true or false) .. " dt=" .. tostring((t1 or 0) - (t0 or 0)) .. "s") + end + if feedbackOnOpen and MTH and MTH.Print then + if ok then + local addedCount = tonumber(added) or 0 + local msg = "Beast Training scan complete: " .. tostring(baselines or 0) .. " baselines / " .. tostring(ranks or 0) .. " ranks." + if addedCount > 0 then + msg = msg .. " Added +" .. tostring(addedCount) .. " new rank(s)." + end + MTH:Print(msg) + else + MTH:Print("Beast Training scan attempted, but no data was recorded.") + end + end + + return ok and true or false +end + +local MTH_PetTrainingFrame = nil +local MTH_PT_PetBookWasVisible = false +local MTH_PT_PetBookPollElapsed = 0 +local MTH_PT_TrainingRowsWerePresent = false +local MTH_PT_LastTrainingRowCount = 0 +local MTH_PT_LastAutoDoubleScanAt = 0 + +local function MTH_PT_RunDoubleScanThrottled(triggerBase, minIntervalSeconds) + local now = tonumber(time()) or 0 + local minInterval = tonumber(minIntervalSeconds) or 0 + if minInterval > 0 and (now - (MTH_PT_LastAutoDoubleScanAt or 0)) < minInterval then + return false + end + MTH_PT_LastAutoDoubleScanAt = now + return MTH_PT_RunDoubleScan(triggerBase) +end + +local function MTH_PT_OnEvent(_, evt, eventArg1) + evt = evt or event + eventArg1 = eventArg1 or arg1 + if not evt then + return + end + + if evt == "PLAYER_ENTERING_WORLD" then + if UnitClass then + local _, classToken = UnitClass("player") + if classToken ~= "HUNTER" then return end + end + if not MTH_PT_HasAnyLearnedSpells() then + local store = MTH_PT_GetStore() + local now = time() + if not store.lastPrompt or (now - store.lastPrompt) > 60 then + store.lastPrompt = now + MTH:Print("No Beast Training scan found for this character. Open Beast Training (right panel) to record pet spell ranks.") + end + end + return + end + + if evt == "LEARNED_SPELL_IN_TAB" then + if MTH_PT_ShouldScanNow() then + MTH_PT_RunDoubleScan("petbook-visible:" .. tostring(evt)) + end + return + end + + if evt == "PET_TRAINING_SHOW" then + MTH_PT_RunDoubleScan("petbook-open") + return + end + + if evt == "CRAFT_SHOW" then + MTH_PT_RunDoubleScanThrottled("craft-open", 1) + return + end + + if evt == "CRAFT_UPDATE" then + if MTH_PT_ShouldScanNow() then + MTH_PT_RunDoubleScanThrottled("craft-update", 1) + end + return + end + + if evt == "SPELLS_CHANGED" then + if MTH_PT_ShouldScanNow() then + MTH_PT_RunDoubleScan("petbook-visible:" .. tostring(evt)) + end + return + end + + if evt == "UNIT_PET" or evt == "PET_BAR_UPDATE" then + if evt == "UNIT_PET" then + local unit = eventArg1 + if unit ~= "player" then return end + end + if MTH_PT_ShouldScanNow() then + MTH_PT_RunDoubleScan("petbook-visible:" .. tostring(evt)) + end + return + end + + if evt == "CHAT_MSG_SYSTEM" then + MTH_PT_ProcessLearnSystemMessage(eventArg1) + return + end +end + +function MTH_PT_InitService() + if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate("trainscan-init") then + return + end + + if MTH_PetTrainingFrame then + return + end + MTH_PetTrainingFrame = CreateFrame("Frame", "MTHPetTrainingScanFrame") + MTH_PetTrainingFrame:RegisterEvent("PLAYER_ENTERING_WORLD") + MTH_PetTrainingFrame:RegisterEvent("LEARNED_SPELL_IN_TAB") + MTH_PetTrainingFrame:RegisterEvent("PET_TRAINING_SHOW") + MTH_PetTrainingFrame:RegisterEvent("SPELLS_CHANGED") + MTH_PetTrainingFrame:RegisterEvent("UNIT_PET") + MTH_PetTrainingFrame:RegisterEvent("PET_BAR_UPDATE") + MTH_PetTrainingFrame:RegisterEvent("CHAT_MSG_SYSTEM") + MTH_PetTrainingFrame:RegisterEvent("CRAFT_SHOW") + MTH_PetTrainingFrame:RegisterEvent("CRAFT_UPDATE") + MTH_PetTrainingFrame:SetScript("OnEvent", MTH_PT_OnEvent) + MTH_PetTrainingFrame:SetScript("OnUpdate", function(_, elapsed) + elapsed = elapsed or arg1 + MTH_PT_PetBookPollElapsed = (MTH_PT_PetBookPollElapsed or 0) + (elapsed or 0) + if MTH_PT_PetBookPollElapsed < 0.20 then + return + end + MTH_PT_PetBookPollElapsed = 0 + + local rowCount = MTH_PT_GetBeastTrainingRowCount() + local hasRows = rowCount > 0 + if hasRows and not MTH_PT_TrainingRowsWerePresent then + MTH_PT_RunDoubleScanThrottled("beast-training-rows-open", 1) + end + + if hasRows and rowCount ~= (MTH_PT_LastTrainingRowCount or 0) then + MTH_PT_RunDoubleScanThrottled("beast-training-rows-changed", 1) + end + + if hasRows then + MTH_PT_RunDoubleScanThrottled("beast-training-rows-present", 8) + end + + MTH_PT_TrainingRowsWerePresent = hasRows + MTH_PT_LastTrainingRowCount = rowCount + + local visible = MTH_PT_IsPetBookVisible() + if visible and not MTH_PT_PetBookWasVisible then + MTH_PT_RunDoubleScanThrottled("petbook-visible-poll", 1) + end + + MTH_PT_PetBookWasVisible = visible and true or false + end) +end + +function MTH_PT_ShutdownService(_reason) + if not MTH_PetTrainingFrame then + return + end + + MTH_PetTrainingFrame:UnregisterAllEvents() + MTH_PetTrainingFrame:SetScript("OnEvent", nil) + MTH_PetTrainingFrame:SetScript("OnUpdate", nil) + MTH_PetTrainingFrame = nil + + MTH_PT_PetBookWasVisible = false + MTH_PT_PetBookPollElapsed = 0 + MTH_PT_TrainingRowsWerePresent = false + MTH_PT_LastTrainingRowCount = 0 + MTH_PT_LastAutoDoubleScanAt = 0 +end + +function MTH_CommandPetScan() + local function requestBeastTrainingOpen() + if MTH_PT_IsPetBookVisible and MTH_PT_IsPetBookVisible() then + return true + end + + local bookTypePet = MTH_PT_GetGlobal("BOOKTYPE_PET") + local openSpellBook = MTH_PT_GetGlobal("OpenSpellBook") + if type(openSpellBook) == "function" and bookTypePet then + openSpellBook(bookTypePet) + end + + if MTH_PT_IsPetBookVisible and MTH_PT_IsPetBookVisible() then + return true + end + + local toggleSpellBook = MTH_PT_GetGlobal("ToggleSpellBook") + if type(toggleSpellBook) == "function" then + if bookTypePet then + toggleSpellBook(bookTypePet) + else + toggleSpellBook() + end + end + + return MTH_PT_IsPetBookVisible and MTH_PT_IsPetBookVisible() or false + end + + if UnitClass then + local _, classToken = UnitClass("player") + if classToken ~= "HUNTER" then + MTH:Print("Train scan is hunter-only.") + return + end + end + + if not MTH_PT_ShouldScanNow() then + local opened = requestBeastTrainingOpen() + if not MTH_PT_ShouldScanNow() then + if opened then + MTH:Print("Beast Training is open, but no scannable rows were detected yet. Try once more after the list fully loads.") + else + MTH:Print("Could not detect Beast Training rows. Open Beast Training (right panel) and run /mth trainscan again.") + end + return + end + end + + local ok, added, baselines, ranks = MTH_PT_ApplyScan("manual") + if not ok then + MTH:Print("Train scan failed. Keep Beast Training open and try /mth trainscan again.") + return + end + + MTH:Print("Train scan saved for this character: " .. tostring(baselines) .. " baselines / " .. tostring(ranks) .. " ranks.") + if (tonumber(added) or 0) > 0 then + MTH:Print("Newly recorded ranks: +" .. tostring(added)) + end +end + +function MTH_CommandTrainScan() + return MTH_CommandPetScan() +end + +MTH_TR_InitService = MTH_PT_InitService +MTH_TR_ShutdownService = MTH_PT_ShutdownService diff --git a/api/core-scan-spellbookpet.lua b/api/core-scan-spellbookpet.lua new file mode 100644 index 0000000..1d9dae2 --- /dev/null +++ b/api/core-scan-spellbookpet.lua @@ -0,0 +1,295 @@ +if not MTH then + error("MetaHunt core framework missing: api/core-framework.lua must load before api/core-scan-spellbookpet.lua") +end + +local MTH_PS_Frame = nil +local MTH_PS_LastScanAt = 0 +local MTH_PS_VERBOSE_LOGS = false +local MTH_PS_ForceScanUntil = 0 +local MTH_PS_ForceScanBudget = 0 + +local function MTH_PS_ClearForceWindow() + MTH_PS_ForceScanUntil = 0 + MTH_PS_ForceScanBudget = 0 +end + +local function MTH_PS_ArmForceWindow(durationSeconds, maxScans) + local now = tonumber(time()) or 0 + local duration = tonumber(durationSeconds) or 4 + local budget = tonumber(maxScans) or 6 + if duration < 1 then duration = 1 end + if budget < 1 then budget = 1 end + MTH_PS_ForceScanUntil = now + duration + MTH_PS_ForceScanBudget = budget +end + +local function MTH_PS_IsFollowupEventTrigger(trigger) + local t = tostring(trigger or "") + return t == "event:UNIT_PET" + or t == "event:PET_BAR_UPDATE" + or t == "event:SPELLS_CHANGED" + or t == "event:LEARNED_SPELL_IN_TAB" +end + +local function MTH_PS_IsPriorityTrigger(trigger) + local t = tostring(trigger or "") + return t == "unit-pet-current-change" or t == "refresh-current-change" +end + +local function MTH_PS_DebugPrint(message, force) + return +end + +local function MTH_PS_TraceTrigger(trigger, accepted, reason) + return +end + +local function MTH_PS_SafeLower(text) + if text == nil then return "" end + return string.lower(tostring(text)) +end + +local function MTH_PS_GetGlobal(name) + if type(getglobal) == "function" then + return getglobal(name) + end + if _G then + return _G[name] + end + return nil +end + +local function MTH_PS_GetStore() + if not MTH_CharSavedVariables then MTH_CharSavedVariables = {} end + if type(MTH_CharSavedVariables.petSpellScan) ~= "table" then + MTH_CharSavedVariables.petSpellScan = { + lastScan = 0, + lastTrigger = "", + lastCount = 0, + lastHash = "", + lastChangeAt = 0, + } + end + return MTH_CharSavedVariables.petSpellScan +end + +local function MTH_PS_ParseRank(name, subText) + local rankNumber = nil + local sub = tostring(subText or "") + if sub ~= "" then + local _, _, subRank = string.find(sub, "(%d+)") + rankNumber = tonumber(subRank) + end + if not rankNumber then + local _, _, nameRank = string.find(tostring(name or ""), "(%d+)") + rankNumber = tonumber(nameRank) + end + if rankNumber and rankNumber > 0 then + return rankNumber + end + return nil +end + +local function MTH_PS_MakeToken(name, rankNumber) + local base = MTH_PS_SafeLower(tostring(name or "")) + if base == "" then return "" end + local rank = tonumber(rankNumber) + if rank and rank > 0 then + return base .. "#" .. tostring(rank) + end + return base +end + +local function MTH_PS_CollectPetSpellbookRows() + local getSpellName = MTH_PS_GetGlobal("GetSpellName") + local getSpellTexture = MTH_PS_GetGlobal("GetSpellTexture") + local isPassiveSpell = MTH_PS_GetGlobal("IsPassiveSpell") + local bookTypePet = MTH_PS_GetGlobal("BOOKTYPE_PET") + if type(getSpellName) ~= "function" or not bookTypePet then + MTH_PS_DebugPrint("Collect rows aborted: GetSpellName=" .. tostring(type(getSpellName)) .. " BOOKTYPE_PET=" .. tostring(bookTypePet), true) + return {} + end + + local rows = {} + for slot = 1, 200 do + local name, subText = getSpellName(slot, bookTypePet) + if not name or name == "" then + if slot == 1 then + MTH_PS_DebugPrint("Slot 1 returned empty name (pet spellbook not ready/visible in this client context)", true) + end + break + end + local rankNumber = MTH_PS_ParseRank(name, subText) + local token = MTH_PS_MakeToken(name, rankNumber) + table.insert(rows, { + slot = slot, + name = tostring(name or ""), + rank = rankNumber, + icon = (type(getSpellTexture) == "function") and getSpellTexture(slot, bookTypePet) or nil, + isPassive = (type(isPassiveSpell) == "function") and (isPassiveSpell(slot, bookTypePet) and true or false) or false, + token = token, + }) + end + MTH_PS_DebugPrint("Collected rows: " .. tostring(table.getn(rows)), true) + return rows +end + +local function MTH_PS_HashRows(rows) + if type(rows) ~= "table" or table.getn(rows) == 0 then + return "" + end + local parts = {} + for i = 1, table.getn(rows) do + local row = rows[i] + parts[i] = tostring(row.token or "") .. ":" .. tostring(row.slot or "") + end + return table.concat(parts, "|") +end + +function MTH_PS_ScanNow(trigger) + if UnitClass then + local _, classToken = UnitClass("player") + if classToken ~= "HUNTER" then + return false, 0, false + end + end + + local rows = MTH_PS_CollectPetSpellbookRows() + local count = table.getn(rows) + local hash = MTH_PS_HashRows(rows) + local store = MTH_PS_GetStore() + local changed = (hash ~= tostring(store.lastHash or "")) + MTH_PS_DebugPrint("Scan trigger=" .. tostring(trigger or "auto") .. " count=" .. tostring(count) .. " changed=" .. tostring(changed and true or false), true) + + store.lastScan = time() + store.lastTrigger = tostring(trigger or "auto") + store.lastCount = count + if changed then + store.lastHash = hash + store.lastChangeAt = store.lastScan + end + + if type(MTH_PETS_RecordCurrentPetSpellbookSnapshot) == "function" then + local persistedCount, updated = MTH_PETS_RecordCurrentPetSpellbookSnapshot(rows, "petspellscan:" .. tostring(trigger or "auto")) + if updated == false then + MTH_PS_DebugPrint("Snapshot skipped; preserved existing count=" .. tostring(persistedCount or 0), true) + else + MTH_PS_DebugPrint("Snapshot persisted to current pet row", true) + end + else + MTH_PS_DebugPrint("Snapshot function missing: MTH_PETS_RecordCurrentPetSpellbookSnapshot", true) + end + + MTH_PS_LastScanAt = store.lastScan + return true, count, changed +end + +function MTH_PSP_RequestScan(trigger, minIntervalSeconds) + local triggerText = tostring(trigger or "request") + local now = tonumber(time()) or 0 + local minInterval = tonumber(minIntervalSeconds) or 1 + local bypassThrottle = false + local bypassReason = "" + + if MTH_PS_IsPriorityTrigger(triggerText) then + bypassThrottle = true + bypassReason = "priority" + MTH_PS_ArmForceWindow(4, 6) + elseif MTH_PS_IsFollowupEventTrigger(triggerText) + and MTH_PS_ForceScanBudget > 0 + and now <= (tonumber(MTH_PS_ForceScanUntil) or 0) + then + bypassThrottle = true + bypassReason = "followup" + MTH_PS_ForceScanBudget = MTH_PS_ForceScanBudget - 1 + end + + if (not bypassThrottle) and minInterval > 0 and (now - (MTH_PS_LastScanAt or 0)) < minInterval then + MTH_PS_TraceTrigger(triggerText, false, "throttled") + return false + end + + if bypassThrottle then + MTH_PS_TraceTrigger(triggerText, true, bypassReason) + else + MTH_PS_TraceTrigger(triggerText, true, "queued") + end + + local ok, count = MTH_PS_ScanNow(triggerText) + if ok and tonumber(count) and tonumber(count) > 0 then + MTH_PS_ClearForceWindow() + elseif MTH_PS_IsPriorityTrigger(triggerText) and type(UnitExists) == "function" and UnitExists("pet") then + MTH_PS_ArmForceWindow(4, 6) + end + if not ok then + MTH_PS_TraceTrigger(triggerText, false, "scan-failed") + end + return ok and true or false +end + +local function MTH_PS_OnEvent(_, evt, eventArg1) + evt = evt or event + eventArg1 = eventArg1 or arg1 + if not evt then return end + + if evt == "UNIT_PET" and eventArg1 ~= "player" then + return + end + + if evt == "PLAYER_ENTERING_WORLD" or evt == "UNIT_PET" or evt == "PET_BAR_UPDATE" or evt == "SPELLS_CHANGED" or evt == "LEARNED_SPELL_IN_TAB" then + MTH_PSP_RequestScan("event:" .. tostring(evt), 1) + end +end + +function MTH_PS_InitService() + if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate("petspellscan-init") then + return + end + if MTH_PS_Frame then + return + end + + MTH_PS_Frame = CreateFrame("Frame", "MTHPetSpellScanFrame") + MTH_PS_Frame:RegisterEvent("PLAYER_ENTERING_WORLD") + MTH_PS_Frame:RegisterEvent("UNIT_PET") + MTH_PS_Frame:RegisterEvent("PET_BAR_UPDATE") + MTH_PS_Frame:RegisterEvent("SPELLS_CHANGED") + MTH_PS_Frame:RegisterEvent("LEARNED_SPELL_IN_TAB") + MTH_PS_Frame:SetScript("OnEvent", MTH_PS_OnEvent) +end + +function MTH_PS_ShutdownService(_reason) + if not MTH_PS_Frame then + return + end + MTH_PS_Frame:UnregisterAllEvents() + MTH_PS_Frame:SetScript("OnEvent", nil) + MTH_PS_Frame = nil + MTH_PS_LastScanAt = 0 + MTH_PS_ClearForceWindow() +end + +function MTH_CommandPetSpellScan() + MTH_PS_VERBOSE_LOGS = true + local petName = UnitName and UnitName("pet") or nil + local hasPet = petName and petName ~= "" + local _, playerClass = UnitClass and UnitClass("player") or nil, nil + if UnitClass then + _, playerClass = UnitClass("player") + end + MTH_PS_DebugPrint("Manual command start: class=" .. tostring(playerClass) .. " pet=" .. tostring(petName or "") .. " hasPet=" .. tostring(hasPet and true or false), true) + local bookTypePet = MTH_PS_GetGlobal("BOOKTYPE_PET") + local getSpellName = MTH_PS_GetGlobal("GetSpellName") + MTH_PS_DebugPrint("API check: BOOKTYPE_PET=" .. tostring(bookTypePet) .. " GetSpellNameType=" .. tostring(type(getSpellName)), true) + + local ok, count, changed = MTH_PS_ScanNow("manual") + if not ok then + MTH:Print("Pet spellbook scan failed.") + MTH_PS_VERBOSE_LOGS = false + return + end + MTH:Print("Pet spellbook scan: " .. tostring(count or 0) .. " spell(s). changed=" .. tostring(changed and true or false)) + local store = MTH_PS_GetStore() + MTH_PS_DebugPrint("Store status: lastScan=" .. tostring(store.lastScan or 0) .. " lastCount=" .. tostring(store.lastCount or 0) .. " lastTrigger=" .. tostring(store.lastTrigger or "") .. " lastChangeAt=" .. tostring(store.lastChangeAt or 0), true) + MTH_PS_VERBOSE_LOGS = false +end diff --git a/api/core-scan-stablemaster.lua b/api/core-scan-stablemaster.lua new file mode 100644 index 0000000..86cae12 --- /dev/null +++ b/api/core-scan-stablemaster.lua @@ -0,0 +1,2934 @@ +if not MTH then + error("MetaHunt core framework missing: api/core-framework.lua must load before api/core-scan-stablemaster.lua") +end + +local MTH_PETS_SCHEMA_VERSION = 2 +local MTH_PETS_CORE_HOOK_BOUNDARY_KEY = "core-pet-rename-hook" +local MTH_PETS_TRACE_RUNAWAY = false +MTH_PETS_TRACE_CONSISTENCY = false + +local MTH_StableFrame = nil +local MTH_ST_LastAutoScanAt = 0 +local MTH_ST_LastUnitPetHadPet = false +local MTH_PETS_LastTameAttempt = nil +local MTH_PETS_LiveState = nil +local MTH_PETS_LiveStateSeq = 0 +local MTH_PETS_LiveStateSubscribers = {} +local MTH_PETS_LiveStateEventName = "MTH_PET_LIVE_STATE_CHANGED" + +MTH_PETS_CoreOriginal_PetRename = MTH_PETS_CoreOriginal_PetRename or nil + +local function MTH_ST_Log(line) + return +end + +local function MTH_PETS_LogConsistency(line) + return +end + +local function MTH_PETS_LogTame(line) + return +end + +local function MTH_PETS_FormatRowConsistency(row) + if type(row) ~= "table" then + return "" + end + return "id=" .. tostring(row.id) + .. " name='" .. tostring(row.name or "") .. "'" + .. " family='" .. tostring(row.family or "") .. "'" + .. " level='" .. tostring(row.level or "") .. "'" + .. " signature='" .. tostring(row.signature or "") .. "'" + .. " guid='" .. tostring(row.guid or "") .. "'" +end + +local function MTH_PETS_FormatCurrentConsistency(cp) + if type(cp) ~= "table" then + return "" + end + return "id=" .. tostring(cp.id) + .. " exists=" .. tostring(cp.exists) + .. " name='" .. tostring(cp.name or "") .. "'" + .. " family='" .. tostring(cp.family or "") .. "'" + .. " level='" .. tostring(cp.level or "") .. "'" + .. " signature='" .. tostring(cp.signature or "") .. "'" + .. " guid='" .. tostring(cp.guid or "") .. "'" +end + +local function MTH_PETS_RequestPetSpellScan(reason) + if type(MTH_PSP_RequestScan) == "function" then + MTH_PSP_RequestScan(tostring(reason or "stablemaster-current-change"), 1) + end +end + +local function MTH_PETS_CopyLiveState(state) + if type(state) ~= "table" then + return nil + end + local copy = {} + for key, value in pairs(state) do + copy[key] = value + end + return copy +end + +local function MTH_PETS_BuildLiveState(source) + local info = (type(MTH_GetCurrentPetInfo) == "function") and MTH_GetCurrentPetInfo() or nil + if type(info) ~= "table" then + info = {} + end + + return { + exists = info.exists and true or false, + liveExists = info.liveExists and true or false, + dead = info.dead and true or false, + suppressed = info.suppressed and true or false, + suppressedReason = tostring(info.suppressedReason or ""), + id = info.id, + name = info.name, + family = info.family, + level = tonumber(info.level) or nil, + happiness = tonumber(info.happiness) or nil, + loyalty = tonumber(info.loyalty) or nil, + loyaltyLevel = tonumber(info.loyaltyLevel) or nil, + health = tonumber(info.health) or nil, + healthMax = tonumber(info.healthMax) or nil, + xp = tonumber(info.xp) or nil, + xpMax = tonumber(info.xpMax) or nil, + xpPercent = tonumber(info.xpPercent) or nil, + lastSource = info.lastSource, + lastUpdated = tonumber(info.lastUpdated) or nil, + acquiredAt = tonumber(info.acquiredAt) or nil, + withMeSinceAt = tonumber(info.withMeSinceAt) or nil, + source = tostring(source or "stablemaster"), + } +end + +local function MTH_PETS_IsSameLiveStateField(a, b) + if a == b then + return true + end + if a == nil or b == nil then + return false + end + return tostring(a) == tostring(b) +end + +local function MTH_PETS_AreLiveStatesEqual(left, right) + if type(left) ~= "table" or type(right) ~= "table" then + return false + end + + local keys = { + "exists", "liveExists", "dead", "suppressed", "suppressedReason", + "id", "name", "family", "level", "happiness", "loyalty", "loyaltyLevel", + "health", "healthMax", "xp", "xpMax", "xpPercent", "lastSource", "lastUpdated", "acquiredAt", "withMeSinceAt", + } + + for _, key in ipairs(keys) do + if not MTH_PETS_IsSameLiveStateField(left[key], right[key]) then + return false + end + end + + return true +end + +local function MTH_PETS_NotifyLiveSubscribers(state, source) + for key, callback in pairs(MTH_PETS_LiveStateSubscribers) do + if type(callback) == "function" then + local ok, err = pcall(callback, MTH_PETS_CopyLiveState(state), source) + if not ok and MTH and MTH.Print then + MTH:Print("[PETS] live-state subscriber error ('" .. tostring(key) .. "'): " .. tostring(err), "error") + end + end + end +end + +function MTH_PETS_EmitLiveState(source, force) + local emittedSource = tostring(source or "stablemaster") + local nextState = MTH_PETS_BuildLiveState(emittedSource) + local changed = force and true or (not MTH_PETS_AreLiveStatesEqual(MTH_PETS_LiveState, nextState)) + if not changed then + return false + end + + MTH_PETS_LiveStateSeq = MTH_PETS_LiveStateSeq + 1 + nextState.seq = MTH_PETS_LiveStateSeq + nextState.changedAt = time() + MTH_PETS_LiveState = nextState + + if MTH and MTH.FireEvent then + MTH:FireEvent(MTH_PETS_LiveStateEventName, MTH_PETS_CopyLiveState(nextState), emittedSource) + end + MTH_PETS_NotifyLiveSubscribers(nextState, emittedSource) + return true +end + +function MTH_PETS_GetLiveState() + if type(MTH_PETS_LiveState) ~= "table" then + MTH_PETS_EmitLiveState("api:init", true) + end + return MTH_PETS_CopyLiveState(MTH_PETS_LiveState) +end + +function MTH_PETS_SubscribeLiveState(key, callback, emitCurrent) + if type(key) ~= "string" or key == "" then + return false, "invalid key" + end + if type(callback) ~= "function" then + return false, "callback must be function" + end + + MTH_PETS_LiveStateSubscribers[key] = callback + if emitCurrent then + local current = MTH_PETS_GetLiveState() + if type(current) == "table" then + pcall(callback, current, "subscribe:init") + end + end + return true +end + +function MTH_PETS_UnsubscribeLiveState(key) + if type(key) ~= "string" or key == "" then + return false + end + MTH_PETS_LiveStateSubscribers[key] = nil + return true +end + +local function MTH_PETS_SafeLower(text) + if text == nil then return "" end + return string.lower(tostring(text)) +end + +local function MTH_PETS_NormalizeText(text) + local normalized = tostring(text or "") + normalized = string.gsub(normalized, "^%s+", "") + normalized = string.gsub(normalized, "%s+$", "") + return normalized +end + +local function MTH_PETS_GetLoyaltyLevelNames() + local defaults = { + [1] = "Rebellious", + [2] = "Unruly", + [3] = "Submissive", + [4] = "Dependable", + [5] = "Faithful", + [6] = "Best Friend", + } + + local names = {} + for level = 1, 6 do + local key = "PET_LOYALTY" .. tostring(level) + local value = (_G and _G[key]) or (type(getglobal) == "function" and getglobal(key)) + value = MTH_PETS_NormalizeText(value) + if value ~= "" then + names[level] = value + else + names[level] = defaults[level] + end + end + + return names +end + +local function MTH_PETS_GetLoyaltyNameByLevel(level) + local resolvedLevel = tonumber(level) + if not resolvedLevel or resolvedLevel < 1 or resolvedLevel > 6 then + return nil + end + local names = MTH_PETS_GetLoyaltyLevelNames() + return names[resolvedLevel] +end + +local function MTH_PETS_ParseLoyaltyLevelFromText(text) + text = MTH_PETS_NormalizeText(text) + if text == "" then + return nil + end + + local _, _, levelText = string.find(text, "[Ll]oyalty%s*[Ll]evel%s*(%d+)") + local level = tonumber(levelText) + if level and level >= 1 and level <= 6 then + return level + end + + local lowerText = string.lower(text) + local names = MTH_PETS_GetLoyaltyLevelNames() + for index = 1, 6 do + local loyaltyName = MTH_PETS_NormalizeText(names[index]) + if loyaltyName ~= "" and string.find(lowerText, string.lower(loyaltyName), 1, true) then + return index + end + end + + return nil +end + +local function MTH_PETS_FormatLoyaltyDisplay(level, text) + local resolvedLevel = tonumber(level) + if (not resolvedLevel or resolvedLevel < 1 or resolvedLevel > 6) then + resolvedLevel = MTH_PETS_ParseLoyaltyLevelFromText(text) + end + local resolvedName = MTH_PETS_GetLoyaltyNameByLevel(resolvedLevel) + if resolvedLevel and resolvedName ~= "" then + return "Level " .. tostring(resolvedLevel) .. " " .. tostring(resolvedName) + end + if resolvedLevel then + return "Level " .. tostring(resolvedLevel) + end + return nil +end + +local function MTH_PETS_IsPlaceholderPetName(name) + local normalized = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(name)) + if normalized == "" then + return true + end + if normalized == "unknown" then + return true + end + local unknownObject = _G and _G["UNKNOWNOBJECT"] + if type(unknownObject) == "string" and unknownObject ~= "" then + local unknownNormalized = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(unknownObject)) + if unknownNormalized ~= "" and normalized == unknownNormalized then + return true + end + end + return false +end + +local function MTH_PETS_GetPlayerCoords() + if type(GetPlayerMapPosition) ~= "function" then + return nil, nil + end + local x, y = GetPlayerMapPosition("player") + x = tonumber(x) + y = tonumber(y) + if not x or not y then + return nil, nil + end + if x <= 0 and y <= 0 then + return nil, nil + end + return x, y +end + +local function MTH_PETS_CaptureContext() + local x, y = MTH_PETS_GetPlayerCoords() + local zoneName = (type(GetRealZoneText) == "function") and GetRealZoneText() or nil + if (not zoneName or zoneName == "") and type(GetZoneText) == "function" then + zoneName = GetZoneText() + end + local subZoneName = (type(GetSubZoneText) == "function") and GetSubZoneText() or nil + if subZoneName == "" then + subZoneName = nil + end + if zoneName == "" then + zoneName = nil + end + local hunterLevel = (type(UnitLevel) == "function") and UnitLevel("player") or nil + return { + timestamp = time(), + zone = zoneName, + subZone = subZoneName, + x = x, + y = y, + hunterLevel = hunterLevel, + } +end + +local function MTH_PETS_GetStableMasterName() + if type(UnitName) ~= "function" then + return nil + end + local name = nil + if type(UnitExists) == "function" and UnitExists("npc") then + name = UnitName("npc") + end + if (not name or name == "") and type(UnitExists) == "function" and UnitExists("target") then + if type(UnitIsPlayer) ~= "function" or not UnitIsPlayer("target") then + name = UnitName("target") + end + end + name = MTH_PETS_NormalizeText(name) + if name == "" then + return nil + end + return name +end + +local function MTH_PETS_NormalizeSystemMessage(rawMessage) + local message = tostring(rawMessage or "") + if message == "" then + return "" + end + message = string.gsub(message, "|c%x%x%x%x%x%x%x%x", "") + message = string.gsub(message, "|r", "") + message = string.gsub(message, "^%s+", "") + message = string.gsub(message, "%s+$", "") + return message +end + +local function MTH_PETS_IsRunawaySystemMessage(rawMessage) + local message = MTH_PETS_NormalizeSystemMessage(rawMessage) + if message == "" then + return false + end + + local lostPetMessage = _G and _G["PETTAME_LOSTPET"] + if type(lostPetMessage) == "string" and lostPetMessage ~= "" and message == lostPetMessage then + return true + end + + local lower = string.lower(message) + if string.find(lower, "pet has run away", 1, true) then return true end + if string.find(lower, "pet ran away", 1, true) then return true end + if string.find(lower, "pet has fled", 1, true) then return true end + if string.find(lower, "pet fled", 1, true) then return true end + + return false +end + +local function MTH_PETS_TraceRunawayEvent(evt, rawMessage, matched) + return +end + +local function MTH_PETS_ParseCreatureIdFromGuid(guid) + local guidText = tostring(guid or "") + if guidText == "" then + return nil + end + + local _, _, creatureIdText = string.find(guidText, "^Creature%-%d+%-%d+%-%d+%-%d+%-(%d+)%-%d+$") + if creatureIdText then + return tonumber(creatureIdText) + end + + return nil +end + +local function MTH_PETS_ParseBeastLevelBounds(levelField) + local levelText = tostring(levelField or "") + if levelText == "" then + return nil, nil + end + local exact = tonumber(levelText) + if exact then + return exact, exact + end + local _, _, minLevel, maxLevel = string.find(levelText, "(%d+)%s*%-%s*(%d+)") + if minLevel and maxLevel then + return tonumber(minLevel), tonumber(maxLevel) + end + local _, _, single = string.find(levelText, "(%d+)") + if single then + local parsed = tonumber(single) + return parsed, parsed + end + return nil, nil +end + +local function MTH_PETS_LevelMatchesBeast(levelField, targetLevel) + local numericTargetLevel = tonumber(targetLevel) + if not numericTargetLevel then + return false + end + local minLevel, maxLevel = MTH_PETS_ParseBeastLevelBounds(levelField) + if not minLevel and not maxLevel then + return false + end + if minLevel and maxLevel then + return numericTargetLevel >= minLevel and numericTargetLevel <= maxLevel + end + if minLevel then + return numericTargetLevel == minLevel + end + return false +end + +local function MTH_PETS_FindBeastIdByDataset(name, family, level) + if type(MTH_DS_Beasts) ~= "table" then + return nil + end + local normalizedName = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(name)) + if normalizedName == "" then + return nil + end + local normalizedFamily = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(family)) + local candidates = {} + for beastId, beast in pairs(MTH_DS_Beasts) do + if type(beast) == "table" then + local beastName = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(beast.name)) + if beastName == normalizedName then + table.insert(candidates, { id = tonumber(beastId), beast = beast }) + end + end + end + if table.getn(candidates) == 1 then + return candidates[1].id + end + if table.getn(candidates) <= 0 then + return nil + end + + local familyMatches = {} + if normalizedFamily ~= "" then + for i = 1, table.getn(candidates) do + local candidate = candidates[i] + local beastFamily = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(candidate.beast and candidate.beast.family)) + if beastFamily ~= "" and beastFamily == normalizedFamily then + table.insert(familyMatches, candidate) + end + end + end + if table.getn(familyMatches) == 1 then + return familyMatches[1].id + end + + local levelMatches = {} + local scanPool = candidates + if table.getn(familyMatches) > 0 then + scanPool = familyMatches + end + for i = 1, table.getn(scanPool) do + local candidate = scanPool[i] + if MTH_PETS_LevelMatchesBeast(candidate.beast and candidate.beast.lvl, level) then + table.insert(levelMatches, candidate) + end + end + if table.getn(levelMatches) == 1 then + return levelMatches[1].id + end + + if table.getn(scanPool) > 0 then + return scanPool[1].id + end + return candidates[1].id +end + +local function MTH_PETS_CaptureTargetSnapshot() + if type(UnitExists) ~= "function" or not UnitExists("target") then + return nil + end + local targetName = (type(UnitName) == "function") and UnitName("target") or nil + if MTH_PETS_IsPlaceholderPetName(targetName) then + return nil + end + local targetGuid = (type(UnitGUID) == "function") and UnitGUID("target") or nil + local beastId = MTH_PETS_ParseCreatureIdFromGuid(targetGuid) + local targetFamily = (type(UnitCreatureFamily) == "function") and UnitCreatureFamily("target") or nil + local targetLevel = (type(UnitLevel) == "function") and UnitLevel("target") or nil + if beastId == nil then + beastId = MTH_PETS_FindBeastIdByDataset(targetName, targetFamily, targetLevel) + end + return { + capturedAt = time(), + name = targetName, + family = targetFamily, + level = targetLevel, + guid = targetGuid, + beastId = beastId, + } +end + +local function MTH_PETS_IsTameBeastSpellName(spellName) + local normalized = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(spellName)) + if normalized == "" then + return false + end + if normalized == "tame beast" then + return true + end + local globalTameName = _G and _G["TAMEBEAST"] or nil + if type(globalTameName) == "string" then + local tameNormalized = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(globalTameName)) + if tameNormalized ~= "" then + if normalized == tameNormalized then + return true + end + if string.find(normalized, tameNormalized, 1, true) then + return true + end + end + end + if string.find(normalized, "tame beast", 1, true) then + return true + end + return false +end + +local function MTH_PETS_GetCurrentPlayerCastOrChannelName() + if type(UnitChannelInfo) == "function" then + local channelName = UnitChannelInfo("player") + if channelName and tostring(channelName) ~= "" then + return tostring(channelName), "UnitChannelInfo" + end + end + if type(UnitCastingInfo) == "function" then + local castName = UnitCastingInfo("player") + if castName and tostring(castName) ~= "" then + return tostring(castName), "UnitCastingInfo" + end + end + local castingBarText = _G and _G["CastingBarText"] + if castingBarText and type(castingBarText.GetText) == "function" then + local barText = castingBarText:GetText() + if barText and tostring(barText) ~= "" then + return tostring(barText), "CastingBarText" + end + end + return nil, nil +end + +local function MTH_PETS_IsTargetBeastCandidate() + if type(UnitExists) ~= "function" or not UnitExists("target") then + return false + end + if type(UnitIsPlayer) == "function" and UnitIsPlayer("target") then + return false + end + if type(UnitCreatureType) == "function" then + local creatureType = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(UnitCreatureType("target"))) + if creatureType ~= "" and creatureType ~= "beast" then + return false + end + end + return true +end + +local function MTH_PETS_RecordTameAttempt(source, spellName) + MTH_PETS_LogTame("Channel start source=" .. tostring(source) .. " spell='" .. tostring(spellName or "") .. "'") + local resolvedSpellName = spellName + if not MTH_PETS_IsTameBeastSpellName(resolvedSpellName) then + local numericArg = tonumber(resolvedSpellName) + if numericArg and type(GetSpellInfo) == "function" then + local byIdName = GetSpellInfo(numericArg) + if byIdName and MTH_PETS_IsTameBeastSpellName(byIdName) then + resolvedSpellName = byIdName + MTH_PETS_LogTame("Resolved channel spell via GetSpellInfo(" .. tostring(numericArg) .. ") => '" .. tostring(byIdName) .. "'") + end + end + end + if not MTH_PETS_IsTameBeastSpellName(resolvedSpellName) then + local fallbackName, fallbackSource = MTH_PETS_GetCurrentPlayerCastOrChannelName() + if fallbackName and MTH_PETS_IsTameBeastSpellName(fallbackName) then + resolvedSpellName = fallbackName + MTH_PETS_LogTame("Resolved channel spell via " .. tostring(fallbackSource) .. " => '" .. tostring(fallbackName) .. "'") + end + end + if not MTH_PETS_IsTameBeastSpellName(resolvedSpellName) then + local numericArg = tonumber(spellName) + local hasNoPet = (type(UnitExists) ~= "function") or (not UnitExists("pet")) + if numericArg and numericArg >= 18000 and numericArg <= 22000 and hasNoPet and MTH_PETS_IsTargetBeastCandidate() then + local tameLabel = (_G and _G["TAMEBEAST"]) or "Tame Beast" + resolvedSpellName = tameLabel + MTH_PETS_LogTame("Resolved channel as Tame Beast via duration heuristic arg=" .. tostring(numericArg)) + end + end + if not MTH_PETS_IsTameBeastSpellName(resolvedSpellName) then + MTH_PETS_LogTame("Ignored channel start: not Tame Beast") + local fallbackName, fallbackSource = MTH_PETS_GetCurrentPlayerCastOrChannelName() + if fallbackName then + MTH_PETS_LogTame("Fallback spell from " .. tostring(fallbackSource) .. " was '" .. tostring(fallbackName) .. "'") + end + return false + end + local targetSnapshot = MTH_PETS_CaptureTargetSnapshot() + if type(targetSnapshot) ~= "table" then + MTH_PETS_LogTame("Failed capture: no valid target snapshot") + return false + end + MTH_PETS_LastTameAttempt = { + source = tostring(source or "unknown"), + spellName = tostring(resolvedSpellName or ""), + capturedAt = targetSnapshot.capturedAt, + target = targetSnapshot, + } + MTH_PETS_LogTame("Captured target name='" .. tostring(targetSnapshot.name or "") + .. "' family='" .. tostring(targetSnapshot.family or "") + .. "' level='" .. tostring(targetSnapshot.level or "") + .. "' guid='" .. tostring(targetSnapshot.guid or "") + .. "' beastId='" .. tostring(targetSnapshot.beastId or "") .. "'") + MTH_PETS_LogConsistency("TameAttempt captured source=" .. tostring(source) + .. " target='" .. tostring(targetSnapshot.name or "") .. "'" + .. " family='" .. tostring(targetSnapshot.family or "") .. "'" + .. " level='" .. tostring(targetSnapshot.level or "") .. "'" + .. " beastId='" .. tostring(targetSnapshot.beastId or "") .. "'") + return true +end + +local function MTH_PETS_GetPendingTameBeastId(snapshot) + local pending = MTH_PETS_LastTameAttempt + if type(pending) ~= "table" or type(pending.target) ~= "table" then + MTH_PETS_LogTame("Resolve beastId: no pending tame attempt") + return nil + end + local pendingAt = tonumber(pending.capturedAt) or 0 + if pendingAt <= 0 or (time() - pendingAt) > 45 then + MTH_PETS_LogTame("Resolve beastId: pending tame expired age=" .. tostring((time() - pendingAt))) + return nil + end + local pendingTarget = pending.target + if type(snapshot) == "table" then + local targetFamily = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(pendingTarget.family)) + local petFamily = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(snapshot.family)) + if targetFamily ~= "" and petFamily ~= "" and targetFamily ~= petFamily then + MTH_PETS_LogTame("Resolve beastId: family mismatch target='" .. tostring(targetFamily) .. "' pet='" .. tostring(petFamily) .. "'") + return nil + end + local targetLevel = tonumber(pendingTarget.level) + local petLevel = tonumber(snapshot.level) + if targetLevel and petLevel and targetLevel ~= petLevel then + MTH_PETS_LogTame("Resolve beastId: level mismatch target='" .. tostring(targetLevel) .. "' pet='" .. tostring(petLevel) .. "'") + return nil + end + end + if tonumber(pendingTarget.beastId) and tonumber(pendingTarget.beastId) > 0 then + MTH_PETS_LogTame("Resolve beastId: using captured beastId=" .. tostring(pendingTarget.beastId)) + return tonumber(pendingTarget.beastId) + end + local fromTarget = MTH_PETS_FindBeastIdByDataset(pendingTarget.name, pendingTarget.family, pendingTarget.level) + if fromTarget then + MTH_PETS_LogTame("Resolve beastId: DS lookup matched beastId=" .. tostring(fromTarget)) + return fromTarget + end + MTH_PETS_LogTame("Resolve beastId: DS lookup failed for target='" .. tostring(pendingTarget.name or "") .. "'") + return nil +end + +local function MTH_PETS_MakeSignature(name, family, level) + local cleanName = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(name)) + local cleanFamily = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(family)) + local numericLevel = tonumber(level) or 0 + return cleanName .. "|" .. cleanFamily .. "|" .. tostring(numericLevel) +end + +local function MTH_PETS_EnsurePetStoreSchema(pets) + if type(pets.petStore) ~= "table" then + pets.petStore = {} + end + local petStore = pets.petStore + if type(petStore.activeById) ~= "table" then + petStore.activeById = {} + end + if type(petStore.historyById) ~= "table" then + petStore.historyById = {} + end + if type(petStore.signatureIndex) ~= "table" then + petStore.signatureIndex = {} + end + if type(petStore.guidIndex) ~= "table" then + petStore.guidIndex = {} + end + if type(petStore.stableSlotIndex) ~= "table" then + petStore.stableSlotIndex = {} + end + if tonumber(petStore.nextId) == nil or tonumber(petStore.nextId) < 1 then + petStore.nextId = 1 + end + if petStore.activeCurrentId == nil then + petStore.activeCurrentId = nil + end + + for _, row in pairs(petStore.activeById) do + if type(row) == "table" then + if row.loyaltyLevel == nil and type(row.loyalty) == "string" then + row.loyaltyLevel = MTH_PETS_ParseLoyaltyLevelFromText(row.loyalty) + end + if type(row.loyalty) == "string" then + row.loyalty = nil + end + row.loyaltyText = nil + row.loyaltyDisplay = nil + if type(row.stableInfo) == "table" then + if row.stableInfo.loyaltyLevel == nil then + row.stableInfo.loyaltyLevel = MTH_PETS_ParseLoyaltyLevelFromText(row.stableInfo.loyalty) + end + row.stableInfo.loyalty = nil + end + end + end +end + +local function MTH_PETS_EnsureCurrentPetSchema(pets) + if type(pets.currentPet) ~= "table" then + pets.currentPet = {} + end + local currentPet = pets.currentPet + if currentPet.id == nil then currentPet.id = nil end + if currentPet.guid == nil then currentPet.guid = nil end + if currentPet.signature == nil then currentPet.signature = nil end + if currentPet.beastId == nil then currentPet.beastId = nil end + if currentPet.name == nil then currentPet.name = nil end + if currentPet.family == nil then currentPet.family = nil end + if currentPet.level == nil then currentPet.level = nil end + if currentPet.happiness == nil then currentPet.happiness = nil end + if currentPet.loyalty == nil then currentPet.loyalty = nil end + if currentPet.loyaltyLevel == nil then currentPet.loyaltyLevel = nil end + if currentPet.loyaltyLevel == nil and type(currentPet.loyalty) == "string" then + currentPet.loyaltyLevel = MTH_PETS_ParseLoyaltyLevelFromText(currentPet.loyalty) + end + if type(currentPet.loyalty) == "string" then + currentPet.loyalty = nil + end + currentPet.loyaltyText = nil + currentPet.loyaltyDisplay = nil + if currentPet.xp == nil then currentPet.xp = nil end + if currentPet.xpMax == nil then currentPet.xpMax = nil end + if currentPet.xpPercent == nil then currentPet.xpPercent = nil end + if currentPet.exists == nil then currentPet.exists = false end + if currentPet.lastSeen == nil then currentPet.lastSeen = 0 end + if currentPet.lastUpdated == nil then currentPet.lastUpdated = 0 end + if currentPet.acquiredAt == nil then currentPet.acquiredAt = 0 end + if currentPet.lastSource == nil then currentPet.lastSource = "" end +end + +local function MTH_PETS_NormalizePetId(petId) + if petId == nil then + return nil + end + local value = tostring(petId) + if value == "" then + return nil + end + return value +end + +local function MTH_PETS_SetCurrentPetId(pets, petId) + if type(pets) ~= "table" then + return nil + end + local normalized = MTH_PETS_NormalizePetId(petId) + pets.currentPetId = normalized + if type(pets.petStore) == "table" then + pets.petStore.activeCurrentId = normalized + end + return normalized +end + +local function MTH_PETS_SetCurrentPetSuppressed(pets, suppressed, reason) + if type(pets) ~= "table" then + return + end + pets.currentPetSuppressed = suppressed and true or false + pets.currentPetSuppressedAwaitNoLive = suppressed and true or false + pets.currentPetSuppressedReason = suppressed and tostring(reason or "") or "" + pets.currentPetSuppressedAt = time() +end + +local function MTH_PETS_ResolveNoLiveCurrentRow(pets, previousCurrentId) + if type(pets) ~= "table" then + return nil, nil + end + if pets.currentPetSuppressed == true then + return nil, nil + end + local petStore = (type(pets.petStore) == "table") and pets.petStore or nil + if type(petStore) ~= "table" or type(petStore.activeById) ~= "table" then + return nil, nil + end + + local cp = pets.currentPet + local candidates = { + previousCurrentId, + cp and cp.id or nil, + pets.currentPetId, + petStore.activeCurrentId, + } + + local seen = {} + for i = 1, table.getn(candidates) do + local candidateId = candidates[i] + if candidateId ~= nil then + local key = tostring(candidateId) + if not seen[key] then + seen[key] = true + local row = petStore.activeById[candidateId] + if type(row) == "table" then + local stableSlot = tonumber(row.stableSlot) + if stableSlot and stableSlot > 0 then + return nil, nil + end + return tostring(candidateId), row + end + end + end + end + + return nil, nil +end + +local function MTH_PETS_ApplyCurrentPetFromActiveRow(cp, petId, row) + if type(cp) ~= "table" or petId == nil or type(row) ~= "table" then + return + end + cp.id = tostring(petId) + cp.guid = row.guid + cp.signature = row.signature + cp.beastId = row.beastId + cp.name = row.name + cp.family = row.family + cp.level = row.level + cp.happiness = row.happiness + cp.loyalty = row.loyalty + cp.loyaltyLevel = row.loyaltyLevel + cp.xp = row.xp + cp.xpMax = row.xpMax + cp.xpPercent = row.xpPercent + if cp.acquiredAt == nil or tonumber(cp.acquiredAt) == nil or tonumber(cp.acquiredAt) <= 0 then + cp.acquiredAt = tonumber(row.createdAt) or tonumber(row.lastSeen) or tonumber(row.lastUpdated) or time() + end + if tonumber(cp.lastSeen) == nil or tonumber(cp.lastSeen) <= 0 then + cp.lastSeen = tonumber(row.lastSeen) or tonumber(row.lastUpdated) or time() + end +end + +local function MTH_PETS_EnsureSchema(pets) + local now = time() + if pets.schemaVersion ~= MTH_PETS_SCHEMA_VERSION then + pets.schemaVersion = MTH_PETS_SCHEMA_VERSION + pets.schemaMigratedAt = now + if type(pets.schemaMigration) ~= "table" then + pets.schemaMigration = {} + end + pets.schemaMigration.version = MTH_PETS_SCHEMA_VERSION + pets.schemaMigration.at = now + pets.schemaMigration.note = "beta canonical store" + end + if pets.updatedAt == nil then + pets.updatedAt = 0 + end + if pets.currentPetId == nil then + local cpId = (type(pets.currentPet) == "table") and pets.currentPet.id or nil + pets.currentPetId = MTH_PETS_NormalizePetId(cpId) + end + if pets.currentPetSuppressed == nil then + pets.currentPetSuppressed = false + end + if pets.currentPetSuppressedAwaitNoLive == nil then + pets.currentPetSuppressedAwaitNoLive = false + end + if pets.currentPetSuppressedReason == nil then + pets.currentPetSuppressedReason = "" + end + if pets.hasVisitedStableOnce == nil then + pets.hasVisitedStableOnce = false + end + if pets.stableVisitPromptLastAt == nil then + pets.stableVisitPromptLastAt = 0 + end + MTH_PETS_EnsureCurrentPetSchema(pets) + MTH_PETS_EnsurePetStoreSchema(pets) +end + +local function MTH_PETS_MarkStableVisited(pets, source) + if type(pets) ~= "table" then + return + end + if pets.hasVisitedStableOnce == true then + return + end + pets.hasVisitedStableOnce = true + pets.stableVisitedAt = time() + pets.stableVisitedSource = source or "stable-scan" + pets.updatedAt = time() +end + +local function MTH_PETS_MaybePromptStableVisit(pets) + if type(pets) ~= "table" then + return + end + if pets.hasVisitedStableOnce == true then + return + end + local playerLevel = (type(UnitLevel) == "function") and tonumber(UnitLevel("player")) or nil + if not playerLevel or playerLevel <= 12 then + return + end + local now = time() + local lastPrompt = tonumber(pets.stableVisitPromptLastAt) or 0 + if lastPrompt > 0 and (now - lastPrompt) <= 60 then + return + end + pets.stableVisitPromptLastAt = now + if MTH and MTH.Print then + MTH:Print("No Stable scan found for this character. Visit a Stable Master once to record stable pets and slots.") + end +end + +local function MTH_PETS_EnsurePetTrainingCompatibility(pets) + if type(pets.trainScan) ~= "table" and type(pets.petTraining) == "table" then + pets.trainScan = pets.petTraining + end + if type(pets.trainScan) ~= "table" then + pets.trainScan = {} + end + if type(pets.petTraining) ~= "table" then + pets.petTraining = pets.trainScan + end + if pets.petTraining ~= pets.trainScan then + pets.petTraining = pets.trainScan + end + local pt = pets.trainScan + if type(pt.spellMap) ~= "table" then + pt.spellMap = {} + end + if type(pt.hunterKnownMap) ~= "table" then + pt.hunterKnownMap = {} + end + if pt.lastScan == nil then + pt.lastScan = 0 + end + if pt.lastPrompt == nil then + pt.lastPrompt = 0 + end + + if pt.hasCompletedPetScan == nil then + if (tonumber(pt.lastScan) or 0) > 0 then + pt.hasCompletedPetScan = 1 + else + for _ in pairs(pt.spellMap) do + pt.hasCompletedPetScan = 1 + break + end + end + end +end + +local function MTH_PETS_GetStoreTables(pets) + if type(pets) ~= "table" then + return nil + end + MTH_PETS_EnsurePetStoreSchema(pets) + return pets.petStore +end + +local function MTH_PETS_AddSignatureIndex(store, signature, petId) + if signature == nil or signature == "" or petId == nil then + return + end + if type(store.signatureIndex[signature]) ~= "table" then + store.signatureIndex[signature] = {} + end + local list = store.signatureIndex[signature] + for _, existingId in pairs(list) do + if existingId == petId then + return + end + end + table.insert(list, petId) +end + +local function MTH_PETS_RemoveSignatureIndex(store, signature, petId) + if signature == nil or signature == "" or petId == nil then + return + end + local list = store.signatureIndex[signature] + if type(list) ~= "table" then + return + end + for i = table.getn(list), 1, -1 do + if list[i] == petId then + table.remove(list, i) + end + end + if table.getn(list) <= 0 then + store.signatureIndex[signature] = nil + end +end + +local function MTH_PETS_GetPetById(pets, petId) + if petId == nil or type(pets) ~= "table" then + return nil + end + local store = MTH_PETS_GetStoreTables(pets) + if not store then + return nil + end + return store.activeById[petId] +end + +local function MTH_PETS_CreatePetId(store) + local nextId = tonumber(store.nextId) or 1 + store.nextId = nextId + 1 + return "pet-" .. tostring(nextId) +end + +local function MTH_PETS_MakeSnapshotFromLivePet() + local exists = (type(UnitExists) == "function") and UnitExists("pet") + if not exists then + return nil + end + local petName = (type(UnitName) == "function") and UnitName("pet") or nil + if MTH_PETS_IsPlaceholderPetName(petName) then + MTH_PETS_LogConsistency("MakeSnapshotFromLivePet ignored placeholder name='" .. tostring(petName or "") .. "'") + return nil + end + local petFamily = (type(UnitCreatureFamily) == "function") and UnitCreatureFamily("pet") or nil + local petLevel = (type(UnitLevel) == "function") and UnitLevel("pet") or nil + local happiness = nil + local loyalty = nil + local loyaltyLevel = nil + local xp = nil + local xpMax = nil + local xpPercent = nil + if type(GetPetHappiness) == "function" then + local h, _, l = GetPetHappiness() + happiness = h + loyalty = l + end + local getPetLoyalty = (type(getglobal) == "function" and getglobal("GetPetLoyalty")) or (_G and _G["GetPetLoyalty"]) + if type(getPetLoyalty) == "function" then + local lLevel, lText = getPetLoyalty() + if lLevel ~= nil then + loyaltyLevel = tonumber(lLevel) or nil + end + if lText ~= nil and tostring(lText) ~= "" then + local lTextValue = tostring(lText) + local parsedFromText = MTH_PETS_ParseLoyaltyLevelFromText(lTextValue) + if loyaltyLevel == nil and parsedFromText ~= nil then + loyaltyLevel = parsedFromText + end + end + end + local petFrameHappiness = (_G and _G["PetFrameHappiness"]) or (type(getglobal) == "function" and getglobal("PetFrameHappiness")) + if type(petFrameHappiness) == "table" and type(petFrameHappiness.tooltipLoyalty) == "string" and petFrameHappiness.tooltipLoyalty ~= "" then + local tooltipText = tostring(petFrameHappiness.tooltipLoyalty) + local parsedFromTooltip = MTH_PETS_ParseLoyaltyLevelFromText(tooltipText) + if loyaltyLevel == nil and parsedFromTooltip ~= nil then + loyaltyLevel = parsedFromTooltip + end + end + local petLoyaltyText = (_G and _G["PetLoyaltyText"]) or (type(getglobal) == "function" and getglobal("PetLoyaltyText")) + if type(petLoyaltyText) == "table" and type(petLoyaltyText.GetText) == "function" then + local uiLoyaltyText = MTH_PETS_NormalizeText(petLoyaltyText:GetText()) + if uiLoyaltyText ~= "" then + local parsedFromUi = MTH_PETS_ParseLoyaltyLevelFromText(uiLoyaltyText) + if loyaltyLevel == nil and parsedFromUi ~= nil then + loyaltyLevel = parsedFromUi + end + end + end + local getPetExperience = (type(getglobal) == "function" and getglobal("GetPetExperience")) or (_G and _G["GetPetExperience"]) + if type(getPetExperience) == "function" then + local pxp, pxpMax = getPetExperience() + xp = tonumber(pxp) + xpMax = tonumber(pxpMax) + if xp and xpMax and xpMax > 0 then + xpPercent = math.floor((xp / xpMax) * 1000 + 0.5) / 10 + end + end + local petGuid = (type(UnitGUID) == "function") and UnitGUID("pet") or nil + local beastId = MTH_PETS_ParseCreatureIdFromGuid(petGuid) + if beastId == nil and type(UnitGUID) == "function" then + beastId = MTH_PETS_ParseCreatureIdFromGuid(UnitGUID("target")) + end + local signature = MTH_PETS_MakeSignature(petName, petFamily, petLevel) + return { + name = petName, + family = petFamily, + level = petLevel, + happiness = happiness, + loyalty = loyalty, + loyaltyLevel = loyaltyLevel, + xp = xp, + xpMax = xpMax, + xpPercent = xpPercent, + guid = petGuid, + beastId = beastId, + signature = signature, + } +end + +local function MTH_PETS_SelectPetIdBySnapshot(store, snapshot, previousPetId) + if type(store) ~= "table" or type(snapshot) ~= "table" then + return nil + end + + if previousPetId and store.activeById[previousPetId] then + local previousRow = store.activeById[previousPetId] + if previousRow.guid and snapshot.guid and previousRow.guid == snapshot.guid then + return previousPetId + end + if previousRow.signature and snapshot.signature and previousRow.signature == snapshot.signature then + return previousPetId + end + end + + if snapshot.guid and store.guidIndex[snapshot.guid] and store.activeById[store.guidIndex[snapshot.guid]] then + return store.guidIndex[snapshot.guid] + end + + if snapshot.signature and snapshot.signature ~= "" then + local list = store.signatureIndex[snapshot.signature] + if type(list) == "table" then + local count = 0 + local candidateId = nil + for _, listedId in pairs(list) do + if listedId and store.activeById[listedId] then + count = count + 1 + candidateId = listedId + end + end + if count == 1 then + return candidateId + end + end + end + + return nil +end + +local function MTH_PETS_ApplySnapshotToRow(row, snapshot, source, context) + if type(row) ~= "table" or type(snapshot) ~= "table" then + return + end + local isStableScan = (source == "stable-scan") + MTH_PETS_LogConsistency("ApplySnapshotToRow(before) source=" .. tostring(source) + .. " row=" .. MTH_PETS_FormatRowConsistency(row) + .. " snapshotName='" .. tostring(snapshot.name or "") .. "'") + local now = time() + row.name = snapshot.name + row.family = snapshot.family + row.level = snapshot.level + if snapshot.happiness ~= nil then + row.happiness = snapshot.happiness + elseif not isStableScan and row.happiness == nil then + row.happiness = nil + end + if not isStableScan then + if snapshot.loyalty ~= nil then + row.loyalty = snapshot.loyalty + end + if snapshot.loyaltyLevel ~= nil then + row.loyaltyLevel = snapshot.loyaltyLevel + end + if snapshot.xp ~= nil then + row.xp = snapshot.xp + end + if snapshot.xpMax ~= nil then + row.xpMax = snapshot.xpMax + end + if snapshot.xpPercent ~= nil then + row.xpPercent = snapshot.xpPercent + end + else + if (row.loyalty == nil or tostring(row.loyalty) == "") and snapshot.loyalty ~= nil and tostring(snapshot.loyalty) ~= "" then + row.loyalty = snapshot.loyalty + end + if row.loyaltyLevel == nil and snapshot.loyaltyLevel ~= nil then + row.loyaltyLevel = snapshot.loyaltyLevel + end + end + row.guid = snapshot.guid or row.guid + row.beastId = snapshot.beastId or row.beastId + local hasPendingTame = type(MTH_PETS_LastTameAttempt) == "table" + if hasPendingTame and snapshot.beastId and (row.tameBeastId == nil or tonumber(row.tameBeastId) == nil) then + row.tameBeastId = snapshot.beastId + end + if hasPendingTame and (source == "unit-pet-acquire" or source == "refresh-current-pet") and type(context) == "table" then + row.tameRecorded = true + row.tamedAt = row.tamedAt or context.timestamp + row.tameHunterLevel = row.tameHunterLevel or context.hunterLevel + row.tameZone = row.tameZone or context.zone + row.tameSubZone = row.tameSubZone or context.subZone + row.tameX = row.tameX or context.x + row.tameY = row.tameY or context.y + end + row.signature = snapshot.signature + row.lastUpdated = now + if not isStableScan then + row.lastSeen = now + end + row.status = "active" + row.lastSource = source or row.lastSource + if source ~= "stable-scan" then + if row.stabledAt then + row.lastUnstabledAt = now + end + row.stabledAt = nil + end + if type(context) == "table" then + row.lastContext = context + if not isStableScan then + row.zone = context.zone or row.zone + row.subZone = context.subZone or row.subZone + row.x = context.x or row.x + row.y = context.y or row.y + row.hunterLevel = context.hunterLevel or row.hunterLevel + end + end + if not isStableScan and type(context) == "table" then + local hasTameRecord = (row.tameRecorded == true) + or tonumber(row.tamedAt) ~= nil + or tonumber(row.tameBeastId) ~= nil + or (type(row.tameZone) == "string" and row.tameZone ~= "") + if not hasTameRecord then + local sourceText = tostring(source or "") + if sourceText == "unit-pet-acquire" or sourceText == "refresh-current-pet" then + row.tameRecorded = true + row.tamedAt = row.tamedAt or context.timestamp + row.tameHunterLevel = row.tameHunterLevel or context.hunterLevel + row.tameZone = row.tameZone or context.zone + row.tameSubZone = row.tameSubZone or context.subZone + row.tameX = row.tameX or context.x + row.tameY = row.tameY or context.y + if snapshot.beastId and tonumber(row.tameBeastId) == nil then + row.tameBeastId = snapshot.beastId + end + end + end + end + if type(row.abilities) ~= "table" then + row.abilities = {} + end + MTH_PETS_LogConsistency("ApplySnapshotToRow(after) source=" .. tostring(source) .. " row=" .. MTH_PETS_FormatRowConsistency(row)) +end + +local function MTH_PETS_UpsertActivePetFromSnapshot(pets, snapshot, source, options) + if type(pets) ~= "table" or type(snapshot) ~= "table" then + return nil, false + end + local petStore = MTH_PETS_GetStoreTables(pets) + if not petStore then + return nil, false + end + local currentPet = pets.currentPet + local previousPetId = currentPet and currentPet.id or nil + local petId = MTH_PETS_SelectPetIdBySnapshot(petStore, snapshot, previousPetId) + local created = false + if not petId then + petId = MTH_PETS_CreatePetId(petStore) + created = true + end + MTH_PETS_LogConsistency("UpsertActivePet select source=" .. tostring(source) + .. " previousCurrentId=" .. tostring(previousPetId) + .. " selectedId=" .. tostring(petId) + .. " created=" .. tostring(created) + .. " snapshotName='" .. tostring(snapshot.name or "") .. "'") + + local row = petStore.activeById[petId] + local wasKnownRow = type(row) == "table" + if type(row) ~= "table" then + row = { + id = petId, + createdAt = time(), + firstSeen = time(), + abilities = {}, + events = {}, + status = "active", + } + petStore.activeById[petId] = row + created = true + end + + if row.signature and row.signature ~= snapshot.signature then + MTH_PETS_RemoveSignatureIndex(petStore, row.signature, petId) + end + if row.stableSlot and petStore.stableSlotIndex[row.stableSlot] == petId then + petStore.stableSlotIndex[row.stableSlot] = nil + end + row.stableSlot = nil + if row.guid and row.guid ~= snapshot.guid and petStore.guidIndex[row.guid] == petId then + petStore.guidIndex[row.guid] = nil + end + + local context = MTH_PETS_CaptureContext() + MTH_PETS_ApplySnapshotToRow(row, snapshot, source, context) + row.firstSeen = row.firstSeen or time() + + if snapshot.guid and snapshot.guid ~= "" then + petStore.guidIndex[snapshot.guid] = petId + end + if snapshot.signature and snapshot.signature ~= "" then + MTH_PETS_AddSignatureIndex(petStore, snapshot.signature, petId) + end + + if created then + row.origin = source or "unknown" + row.originContext = context + local hasPendingTame = type(MTH_PETS_LastTameAttempt) == "table" + local shouldRecordTame = hasPendingTame and (source == "unit-pet-acquire" or source == "refresh-current-pet") + if shouldRecordTame then + row.tamedAt = context.timestamp + row.tameHunterLevel = context.hunterLevel + row.tameZone = context.zone + row.tameSubZone = context.subZone + row.tameX = context.x + row.tameY = context.y + row.tameBeastId = snapshot.beastId + row.tameRecorded = true + end + if type(options) == "table" and options.stableSlot then + row.origin = "stable-slot" + end + end + + MTH_PETS_SetCurrentPetId(pets, petId) + pets.updatedAt = time() + MTH_PETS_LogConsistency("UpsertActivePet finalized activeCurrentId=" .. tostring(petStore.activeCurrentId) + .. " row=" .. MTH_PETS_FormatRowConsistency(row)) + + return petId, created +end + +local function MTH_PETS_RecordStableSlot(pets, slot, raw1, raw2, raw3, raw4, raw5, raw6, raw7, raw8, scanContext, scanStableMasterName) + if type(pets) ~= "table" then + return + end + local slotNumber = tonumber(slot) + if not slotNumber or slotNumber <= 0 then + return + end + local petStore = MTH_PETS_GetStoreTables(pets) + if not petStore then + return + end + + if raw2 == nil or raw2 == "" then + petStore.stableSlotIndex[slotNumber] = nil + MTH_PETS_LogConsistency("RecordStableSlot cleared slot=" .. tostring(slotNumber) .. " because name is empty") + return + end + MTH_PETS_LogConsistency("RecordStableSlot slot=" .. tostring(slotNumber) + .. " name='" .. tostring(raw2 or "") .. "'" + .. " family='" .. tostring(raw4 or "") .. "'" + .. " level='" .. tostring(raw3 or "") .. "'") + + local snapshot = { + name = raw2, + family = raw4, + level = tonumber(raw3) or 0, + happiness = nil, + loyalty = nil, + loyaltyLevel = MTH_PETS_ParseLoyaltyLevelFromText(raw5), + xp = nil, + xpMax = nil, + xpPercent = nil, + guid = nil, + beastId = nil, + signature = MTH_PETS_MakeSignature(raw2, raw4, raw3), + } + + local existingId = petStore.stableSlotIndex[slotNumber] + local petId = nil + if existingId and petStore.activeById[existingId] then + local existingRow = petStore.activeById[existingId] + local existingSignature = existingRow and existingRow.signature or nil + local snapshotSignature = snapshot and snapshot.signature or nil + local existingGuid = existingRow and existingRow.guid or nil + local snapshotGuid = snapshot and snapshot.guid or nil + local signatureMatches = (existingSignature and snapshotSignature and existingSignature == snapshotSignature) and true or false + local guidMatches = (existingGuid and snapshotGuid and existingGuid ~= "" and existingGuid == snapshotGuid) and true or false + + if signatureMatches or guidMatches then + petId = existingId + else + MTH_PETS_LogConsistency("RecordStableSlot remapping stale slot=" .. tostring(slotNumber) + .. " existingId=" .. tostring(existingId) + .. " existingSignature='" .. tostring(existingSignature or "") .. "'" + .. " snapshotSignature='" .. tostring(snapshotSignature or "") .. "'") + petStore.stableSlotIndex[slotNumber] = nil + end + end + + if not petId then + petId = MTH_PETS_SelectPetIdBySnapshot(petStore, snapshot, nil) + end + + if not petId then + petId = MTH_PETS_CreatePetId(petStore) + end + + local row = petStore.activeById[petId] + local wasKnownRow = type(row) == "table" + if type(row) ~= "table" then + row = { + id = petId, + createdAt = time(), + firstSeen = time(), + abilities = {}, + events = {}, + status = "active", + } + petStore.activeById[petId] = row + end + + if row.signature and row.signature ~= snapshot.signature then + MTH_PETS_RemoveSignatureIndex(petStore, row.signature, petId) + end + + local previousStableSlot = tonumber(row.stableSlot) + local wasPreviouslyStabled = previousStableSlot ~= nil and previousStableSlot > 0 + local lastSourceLower = MTH_PETS_SafeLower(row.lastSource) + local didEnterStableFromActive = wasKnownRow and not wasPreviouslyStabled and lastSourceLower ~= "stable-scan" + + local stableContext = type(scanContext) == "table" and scanContext or MTH_PETS_CaptureContext() + local previousStableInfo = (type(row.stableInfo) == "table") and row.stableInfo or nil + local stableMasterName = previousStableInfo and tostring(previousStableInfo.stableMasterName or "") or "" + local stableZone = previousStableInfo and previousStableInfo.stableZone or nil + local stableSubZone = previousStableInfo and previousStableInfo.stableSubZone or nil + if didEnterStableFromActive then + stableMasterName = MTH_PETS_NormalizeText(scanStableMasterName) + if stableMasterName == "" then + stableMasterName = MTH_PETS_GetStableMasterName() + end + if stableMasterName == "" then + stableMasterName = "" + end + stableZone = stableContext and stableContext.zone or nil + stableSubZone = stableContext and stableContext.subZone or nil + end + if stableSubZone == "" then stableSubZone = nil end + if stableZone == "" then stableZone = nil end + MTH_PETS_ApplySnapshotToRow(row, snapshot, "stable-scan", stableContext) + if tonumber(row.stableSlot) ~= slotNumber then + if didEnterStableFromActive then + row.stabledAt = time() + else + row.stableFirstSeenAt = row.stableFirstSeenAt or time() + end + end + row.icon = raw1 + row.stableSlot = slotNumber + row.stableRaw = { raw1, raw2, raw3, raw4, raw5, raw6, raw7, raw8 } + row.stableInfo = { + icon = raw1, + name = raw2, + level = raw3, + family = raw4, + loyaltyLevel = MTH_PETS_ParseLoyaltyLevelFromText(raw5), + stabledAt = row.stabledAt, + stableFirstSeenAt = row.stableFirstSeenAt, + stableZone = stableZone, + stableSubZone = stableSubZone, + stableMasterName = stableMasterName, + r6 = raw6, + r7 = raw7, + r8 = raw8, + updatedAt = (didEnterStableFromActive and time()) or (previousStableInfo and previousStableInfo.updatedAt) or time(), + } + + if snapshot.signature and snapshot.signature ~= "" then + MTH_PETS_AddSignatureIndex(petStore, snapshot.signature, petId) + end + petStore.stableSlotIndex[slotNumber] = petId + pets.updatedAt = time() +end + +function MTH_PETS_RecordCurrentPetLearnedAbility(abilityName, rankNumber, source) + local pets = MTH_PETS_GetRootStore() + if type(pets) ~= "table" then + return false + end + local cp = pets.currentPet + if type(cp) ~= "table" or cp.exists ~= true or not cp.id then + return false + end + local row = MTH_PETS_GetPetById(pets, cp.id) + if type(row) ~= "table" then + return false + end + if type(row.abilities) ~= "table" then + row.abilities = {} + end + local nameText = MTH_PETS_NormalizeText(abilityName) + if nameText == "" then + return false + end + local token = MTH_PETS_SafeLower(nameText) + local numericRank = tonumber(rankNumber) + if numericRank and numericRank > 0 then + token = token .. "#" .. tostring(numericRank) + end + row.abilities[token] = { + name = nameText, + rank = numericRank, + source = source or "unknown", + recordedAt = time(), + } + row.lastUpdated = time() + pets.updatedAt = time() + return true +end + +function MTH_PETS_RecordCurrentPetSpellbookSnapshot(spells, source) + local pets = MTH_PETS_GetRootStore() + if type(pets) ~= "table" then + return 0, false + end + local cp = pets.currentPet + if type(cp) ~= "table" or cp.exists ~= true or not cp.id then + return 0, false + end + local row = MTH_PETS_GetPetById(pets, cp.id) + if type(row) ~= "table" then + return 0, false + end + if type(spells) ~= "table" then + spells = {} + end + + local stored = {} + local count = 0 + for i = 1, table.getn(spells) do + local spell = spells[i] + if type(spell) == "table" and tostring(spell.name or "") ~= "" then + count = count + 1 + stored[count] = { + name = tostring(spell.name or ""), + rank = spell.rank, + icon = spell.icon, + isPassive = spell.isPassive and true or false, + slot = tonumber(spell.slot) or count, + token = spell.token, + } + end + end + + local existingSpellbook = row.petSpellbook + local existingCount = 0 + if type(existingSpellbook) == "table" then + existingCount = tonumber(existingSpellbook.count) or 0 + end + if count <= 0 and existingCount > 0 then + row.lastPetSpellbookSkipAt = time() + row.lastPetSpellbookSkipSource = source or "unknown" + row.lastPetSpellbookSkipReason = "empty-scan-preserved-existing" + row.lastUpdated = time() + pets.updatedAt = time() + return existingCount, false + end + + row.petSpellbook = { + updatedAt = time(), + source = source or "unknown", + count = count, + spells = stored, + } + row.lastUpdated = time() + pets.updatedAt = time() + return count, true +end + +local function MTH_PETS_MoveToHistory(pets, petId, source, context, reason) + if type(pets) ~= "table" or not petId then + return false + end + local petStore = MTH_PETS_GetStoreTables(pets) + if type(petStore) ~= "table" then + return false + end + local resolvedPetId = petId + local row = petStore and petStore.activeById and petStore.activeById[resolvedPetId] + if type(row) ~= "table" then + local normalizedPetId = MTH_PETS_NormalizePetId(petId) + if normalizedPetId ~= nil then + resolvedPetId = normalizedPetId + row = petStore and petStore.activeById and petStore.activeById[resolvedPetId] + end + end + if type(row) ~= "table" then + return false + end + + if row.signature then + MTH_PETS_RemoveSignatureIndex(petStore, row.signature, resolvedPetId) + end + if row.guid and petStore.guidIndex[row.guid] == resolvedPetId then + petStore.guidIndex[row.guid] = nil + end + if row.stableSlot and petStore.stableSlotIndex[row.stableSlot] == resolvedPetId then + petStore.stableSlotIndex[row.stableSlot] = nil + end + + row.status = "abandoned" + row.abandonedAt = (context and context.timestamp) or time() + row.abandonContext = context or MTH_PETS_CaptureContext() + row.abandonSource = source or "unknown" + row.abandonReason = reason or "abandon" + row.lastUpdated = time() + + petStore.historyById[resolvedPetId] = row + petStore.activeById[resolvedPetId] = nil + if tostring(petStore.activeCurrentId or "") == tostring(resolvedPetId) + or tostring(pets.currentPetId or "") == tostring(resolvedPetId) then + MTH_PETS_SetCurrentPetId(pets, nil) + end + pets.updatedAt = time() + return true +end + +function MTH_PETS_RecordPetAbandon(source, explicitName) + local pets = MTH_PETS_GetRootStore() + if type(pets) ~= "table" then + return false + end + local cp = pets.currentPet + local petId = cp and cp.id or nil + if not petId then + local petStore = MTH_PETS_GetStoreTables(pets) + if type(petStore) ~= "table" then + return false + end + local petName = MTH_PETS_NormalizeText(explicitName or (cp and cp.name) or "") + local petFamily = cp and cp.family or nil + local petLevel = cp and cp.level or nil + local signature = MTH_PETS_MakeSignature(petName, petFamily, petLevel) + local list = petStore.signatureIndex[signature] + if type(list) == "table" and table.getn(list) == 1 then + petId = list[1] + end + end + + local context = MTH_PETS_CaptureContext() + local ok = MTH_PETS_MoveToHistory(pets, petId, source or "abandon", context, "pet-abandon") + + if type(cp) == "table" then + cp.exists = false + cp.id = nil + cp.guid = nil + cp.signature = nil + cp.beastId = nil + cp.name = nil + cp.family = nil + cp.level = nil + cp.happiness = nil + cp.loyalty = nil + cp.loyaltyLevel = nil + cp.xp = nil + cp.xpMax = nil + cp.xpPercent = nil + cp.lastUpdated = time() + cp.lastSource = source or "abandon" + end + MTH_PETS_SetCurrentPetId(pets, nil) + MTH_PETS_SetCurrentPetSuppressed(pets, true, "abandon") + MTH_PETS_LastTameAttempt = nil + + return ok +end + +function MTH_PETS_RecordPetRunaway(source, rawMessage) + local pets = MTH_PETS_GetRootStore() + if type(pets) ~= "table" then + return false + end + + local cp = pets.currentPet + local petId = cp and cp.id or nil + local petName = cp and cp.name or nil + local context = MTH_PETS_CaptureContext() + context.systemMessage = MTH_PETS_NormalizeSystemMessage(rawMessage) + + local ok = MTH_PETS_MoveToHistory(pets, petId, source or "runaway", context, "pet-runaway") + if not ok then + return false + end + + if type(cp) == "table" then + cp.exists = false + cp.id = nil + cp.guid = nil + cp.signature = nil + cp.beastId = nil + cp.name = nil + cp.family = nil + cp.level = nil + cp.happiness = nil + cp.loyalty = nil + cp.loyaltyLevel = nil + cp.xp = nil + cp.xpMax = nil + cp.xpPercent = nil + cp.lastUpdated = time() + cp.lastSource = source or "runaway" + end + MTH_PETS_SetCurrentPetId(pets, nil) + MTH_PETS_SetCurrentPetSuppressed(pets, true, "runaway") + MTH_PETS_LastTameAttempt = nil + + MTH_ST_LastUnitPetHadPet = false + pets.updatedAt = time() + + if MTH and MTH.Print then + local displayName = MTH_PETS_NormalizeText(petName) + if displayName == "" then displayName = "Unknown" end + MTH:Print("YOUR PET " .. tostring(displayName) .. " HAS RUNAWAY FOREVER :-( Try to feed it better next time.") + end + + return true +end + +function MTH_PETS_RecordPetRename(oldName, newName, source) + local pets = MTH_PETS_GetRootStore() + if type(pets) ~= "table" then + return false + end + local cp = pets.currentPet + local petId = cp and cp.id or nil + local petStore = MTH_PETS_GetStoreTables(pets) + local petRow = MTH_PETS_GetPetById(pets, petId) + if type(petRow) ~= "table" then + return false + end + + local normalizedNewName = MTH_PETS_NormalizeText(newName) + if normalizedNewName == "" then + return false + end + local oldSignature = petRow.signature + petRow.previousName = MTH_PETS_NormalizeText(oldName) + petRow.name = normalizedNewName + petRow.signature = MTH_PETS_MakeSignature(petRow.name, petRow.family, petRow.level) + MTH_PETS_RemoveSignatureIndex(petStore, oldSignature, petId) + MTH_PETS_AddSignatureIndex(petStore, petRow.signature, petId) + petRow.lastUpdated = time() + petRow.lastSource = source or "rename" + + if type(petRow.events) ~= "table" then + petRow.events = {} + end + table.insert(petRow.events, { + type = "pet-rename", + from = MTH_PETS_NormalizeText(oldName), + to = normalizedNewName, + source = source or "rename", + at = time(), + context = MTH_PETS_CaptureContext(), + }) + + cp.name = normalizedNewName + cp.signature = petRow.signature + cp.lastUpdated = time() + cp.lastSource = source or "rename" + pets.updatedAt = time() + return true +end + +function MTH_PETS_CorePetRenameHook(newName) + local oldName = nil + if type(UnitName) == "function" then + oldName = UnitName("pet") + end + if (not oldName or oldName == "") and MTH_CharSavedVariables and type(MTH_CharSavedVariables.MTH_Pets) == "table" then + local cp = MTH_CharSavedVariables.MTH_Pets.currentPet + oldName = cp and cp.name or oldName + end + + local newNameNormalized = MTH_PETS_NormalizeText(newName) + if newNameNormalized ~= "" then + if type(MTH_PETS_RecordPetRename) == "function" then + MTH_PETS_RecordPetRename(oldName, newNameNormalized, "core:PetRename") + end + + if type(FOM_FoodQuality) == "table" and type(FOM_RealmPlayer) == "string" and FOM_RealmPlayer ~= "" then + if type(FOM_FoodQuality[FOM_RealmPlayer]) == "table" then + local realmMap = FOM_FoodQuality[FOM_RealmPlayer] + local oldKey = MTH_PETS_NormalizeText(oldName) + if oldKey ~= "" and oldKey ~= newNameNormalized and realmMap[oldKey] ~= nil then + realmMap[newNameNormalized] = realmMap[oldKey] + realmMap[oldKey] = nil + end + end + end + end + + if type(MTH_PETS_CoreOriginal_PetRename) == "function" and MTH_PETS_CoreOriginal_PetRename ~= MTH_PETS_CorePetRenameHook then + return MTH_PETS_CoreOriginal_PetRename(newName) + end + return nil +end + +local function MTH_PETS_InstallCoreRenameHook() + if type(PetRename) ~= "function" then + return false + end + if PetRename == MTH_PETS_CorePetRenameHook then + if MTH and MTH.CaptureHookBoundary then + MTH:CaptureHookBoundary(MTH_PETS_CORE_HOOK_BOUNDARY_KEY, { + { globalName = "PetRename", originalName = "MTH_PETS_CoreOriginal_PetRename" }, + }) + end + return true + end + + if type(MTH_PETS_CoreOriginal_PetRename) ~= "function" or MTH_PETS_CoreOriginal_PetRename == MTH_PETS_CorePetRenameHook then + MTH_PETS_CoreOriginal_PetRename = PetRename + end + + if MTH_PETS_CoreOriginal_PetRename == MTH_PETS_CorePetRenameHook then + return false + end + + PetRename = MTH_PETS_CorePetRenameHook + if MTH and MTH.CaptureHookBoundary then + MTH:CaptureHookBoundary(MTH_PETS_CORE_HOOK_BOUNDARY_KEY, { + { globalName = "PetRename", originalName = "MTH_PETS_CoreOriginal_PetRename" }, + }) + end + return true +end + +function MTH_PETS_GetRootStore() + if not MTH_CharSavedVariables then + MTH_CharSavedVariables = {} + end + + if type(MTH_CharSavedVariables.MTH_Pets) ~= "table" then + MTH_CharSavedVariables.MTH_Pets = { + petTraining = {}, + trainScan = {}, + stableScan = {}, + petStore = {}, + } + end + + local pets = MTH_CharSavedVariables.MTH_Pets + if pets._betaFreshStoreApplied ~= 1 then + pets.petTraining = {} + pets.trainScan = {} + pets.stableScan = {} + pets.currentPet = {} + pets.petStore = {} + pets._betaFreshStoreApplied = 1 + pets.updatedAt = time() + end + if type(pets.petTraining) ~= "table" then + pets.petTraining = {} + end + if type(pets.trainScan) ~= "table" then + pets.trainScan = pets.petTraining + end + if type(pets.stableScan) ~= "table" then + pets.stableScan = {} + end + if type(pets.petStore) ~= "table" then + pets.petStore = {} + end + + MTH_PETS_EnsureSchema(pets) + MTH_PETS_EnsurePetTrainingCompatibility(pets) + + MTH_CharSavedVariables.petTraining = pets.petTraining + MTH_CharSavedVariables.trainScan = pets.trainScan + MTH_CharSavedVariables.stableScan = pets.stableScan + MTH_CharSavedVariables.petStore = pets.petStore + + return pets +end + +function MTH_PETS_RefreshCurrentPet() + local pets = type(MTH_PETS_GetRootStore) == "function" and MTH_PETS_GetRootStore() or nil + if type(pets) ~= "table" then + return + end + MTH_PETS_EnsureCurrentPetSchema(pets) + + local cp = pets.currentPet + local previousCurrentId = cp and cp.id or nil + local now = time() + local snapshot = MTH_PETS_MakeSnapshotFromLivePet() + if pets.currentPetSuppressed == true and pets.currentPetSuppressedAwaitNoLive == true then + if type(snapshot) == "table" then + snapshot = nil + else + pets.currentPetSuppressedAwaitNoLive = false + end + end + local hadPendingTame = type(MTH_PETS_LastTameAttempt) == "table" + if hadPendingTame then + MTH_PETS_LogTame("RefreshCurrentPet with pending tame attempt previousCurrentId=" .. tostring(previousCurrentId) + .. " liveSnapshot=" .. tostring(type(snapshot) == "table")) + end + MTH_PETS_LogConsistency("RefreshCurrentPet start current=" .. MTH_PETS_FormatCurrentConsistency(cp) + .. " previousCurrentId=" .. tostring(previousCurrentId) + .. " hasLiveSnapshot=" .. tostring(type(snapshot) == "table")) + if not snapshot then + cp.exists = false + local keepCurrentId, keepCurrentRow = MTH_PETS_ResolveNoLiveCurrentRow(pets, previousCurrentId) + if keepCurrentId ~= nil and type(keepCurrentRow) == "table" then + MTH_PETS_ApplyCurrentPetFromActiveRow(cp, keepCurrentId, keepCurrentRow) + else + cp.id = nil + cp.guid = nil + cp.signature = nil + cp.beastId = nil + cp.name = nil + cp.family = nil + cp.level = nil + cp.happiness = nil + cp.loyalty = nil + cp.loyaltyLevel = nil + cp.xp = nil + cp.xpMax = nil + cp.xpPercent = nil + end + cp.lastUpdated = now + cp.lastSource = "no-pet" + local petStore = MTH_PETS_GetStoreTables(pets) + MTH_PETS_SetCurrentPetId(pets, keepCurrentId) + MTH_PETS_LogConsistency("RefreshCurrentPet no-live result current=" .. MTH_PETS_FormatCurrentConsistency(cp) + .. " activeCurrentId=" .. tostring(petStore and petStore.activeCurrentId or nil)) + pets.updatedAt = now + return + end + + if snapshot.beastId == nil and hadPendingTame then + snapshot.beastId = MTH_PETS_GetPendingTameBeastId(snapshot) + MTH_PETS_LogTame("RefreshCurrentPet resolved beastId from pending tame => " .. tostring(snapshot.beastId or "nil")) + end + + local petId, created = MTH_PETS_UpsertActivePetFromSnapshot(pets, snapshot, "refresh-current-pet", nil) + MTH_PETS_SetCurrentPetSuppressed(pets, false) + cp.exists = true + cp.id = petId + cp.guid = snapshot.guid + cp.signature = snapshot.signature + cp.beastId = snapshot.beastId + cp.name = snapshot.name + cp.family = snapshot.family + cp.level = snapshot.level + cp.happiness = snapshot.happiness + cp.loyalty = snapshot.loyalty + cp.loyaltyLevel = snapshot.loyaltyLevel + cp.xp = snapshot.xp + cp.xpMax = snapshot.xpMax + cp.xpPercent = snapshot.xpPercent + cp.lastUpdated = now + cp.lastSeen = now + if created then + cp.acquiredAt = now + cp.lastSource = "acquired" + else + cp.lastSource = "refresh" + end + if cp.id and cp.id ~= previousCurrentId then + MTH_PETS_RequestPetSpellScan("refresh-current-change") + end + if hadPendingTame and cp.id and cp.id ~= previousCurrentId then + MTH_PETS_LogTame("RefreshCurrentPet acquire/change completed; clearing pending tame attempt") + MTH_PETS_LastTameAttempt = nil + end + MTH_PETS_LogConsistency("RefreshCurrentPet live result current=" .. MTH_PETS_FormatCurrentConsistency(cp)) + pets.updatedAt = now +end + +function MTH_GetPetsStore() + return MTH_PETS_GetRootStore() +end + +function MTH_GetCurrentPetSnapshot() + local pets = MTH_PETS_GetRootStore() + MTH_PETS_RefreshCurrentPet() + return pets and pets.currentPet or nil +end + +function MTH_GetCurrentPetInfo() + local pets = MTH_PETS_GetRootStore() + MTH_PETS_RefreshCurrentPet() + local cp = pets and pets.currentPet or nil + + local info = { + exists = false, + liveExists = false, + dead = false, + suppressed = false, + suppressedReason = "", + id = nil, + name = nil, + family = nil, + level = nil, + happiness = nil, + loyalty = nil, + loyaltyLevel = nil, + loyaltyText = nil, + loyaltyDisplay = nil, + xp = nil, + xpMax = nil, + xpPercent = nil, + lastSource = nil, + lastUpdated = nil, + acquiredAt = nil, + tamedAt = nil, + stabledAt = nil, + stableFirstSeenAt = nil, + lastUnstabledAt = nil, + withMeSinceAt = nil, + } + + if type(cp) == "table" then + info.id = cp.id or pets.currentPetId + info.name = cp.name + info.family = cp.family + info.level = cp.level + info.happiness = cp.happiness + info.loyalty = cp.loyalty + info.loyaltyLevel = tonumber(cp.loyaltyLevel) or nil + info.lastSource = cp.lastSource + info.lastUpdated = cp.lastUpdated + info.acquiredAt = tonumber(cp.acquiredAt) or nil + info.exists = info.id ~= nil and tostring(info.id) ~= "" + end + + local activeRow = nil + if type(pets) == "table" and type(pets.petStore) == "table" and type(pets.petStore.activeById) == "table" and info.id ~= nil then + activeRow = pets.petStore.activeById[info.id] or pets.petStore.activeById[tostring(info.id)] + end + if type(activeRow) == "table" then + info.tamedAt = tonumber(activeRow.tamedAt) or nil + info.stabledAt = tonumber(activeRow.stabledAt) or nil + info.stableFirstSeenAt = tonumber(activeRow.stableFirstSeenAt) or nil + info.lastUnstabledAt = tonumber(activeRow.lastUnstabledAt) or nil + if info.acquiredAt == nil then + info.acquiredAt = tonumber(activeRow.createdAt) or tonumber(activeRow.lastSeen) or tonumber(activeRow.lastUpdated) or nil + end + if info.loyaltyLevel == nil then + info.loyaltyLevel = tonumber(activeRow.loyaltyLevel) or nil + end + end + + info.withMeSinceAt = info.lastUnstabledAt or info.stabledAt or info.stableFirstSeenAt or info.tamedAt or nil + + info.suppressed = (pets and pets.currentPetSuppressed) and true or false + info.suppressedReason = tostring((pets and pets.currentPetSuppressedReason) or "") + + local hasLivePet = (type(UnitExists) == "function") and UnitExists("pet") and true or false + info.liveExists = hasLivePet + + if hasLivePet then + if type(UnitName) == "function" then + local liveName = UnitName("pet") + if liveName and tostring(liveName) ~= "" then + info.name = liveName + end + end + if type(UnitLevel) == "function" then + local liveLevel = tonumber(UnitLevel("pet")) + if liveLevel and liveLevel > 0 then + info.level = liveLevel + end + end + if type(UnitIsDead) == "function" then + info.dead = UnitIsDead("pet") and true or false + end + if type(GetPetHappiness) == "function" then + local h, _, l = GetPetHappiness() + if h ~= nil then + info.happiness = h + end + if l ~= nil then + info.loyalty = l + end + end + local getPetLoyalty = (type(getglobal) == "function" and getglobal("GetPetLoyalty")) or (_G and _G["GetPetLoyalty"]) + if type(getPetLoyalty) == "function" then + local loyaltyLevel, loyaltyText = getPetLoyalty() + if loyaltyLevel ~= nil then + info.loyaltyLevel = tonumber(loyaltyLevel) or info.loyaltyLevel + end + if loyaltyText ~= nil and tostring(loyaltyText) ~= "" then + local loyaltyTextValue = tostring(loyaltyText) + local parsedFromText = MTH_PETS_ParseLoyaltyLevelFromText(loyaltyTextValue) + if info.loyaltyLevel == nil and parsedFromText ~= nil then + info.loyaltyLevel = parsedFromText + end + end + end + local petFrameHappiness = (_G and _G["PetFrameHappiness"]) or (type(getglobal) == "function" and getglobal("PetFrameHappiness")) + if type(petFrameHappiness) == "table" and type(petFrameHappiness.tooltipLoyalty) == "string" and petFrameHappiness.tooltipLoyalty ~= "" then + local tooltipText = tostring(petFrameHappiness.tooltipLoyalty) + local parsedFromTooltip = MTH_PETS_ParseLoyaltyLevelFromText(tooltipText) + if info.loyaltyLevel == nil and parsedFromTooltip ~= nil then + info.loyaltyLevel = parsedFromTooltip + end + end + local petLoyaltyText = (_G and _G["PetLoyaltyText"]) or (type(getglobal) == "function" and getglobal("PetLoyaltyText")) + if type(petLoyaltyText) == "table" and type(petLoyaltyText.GetText) == "function" then + local uiLoyaltyText = MTH_PETS_NormalizeText(petLoyaltyText:GetText()) + if uiLoyaltyText ~= "" then + local parsedFromUi = MTH_PETS_ParseLoyaltyLevelFromText(uiLoyaltyText) + if info.loyaltyLevel == nil and parsedFromUi ~= nil then + info.loyaltyLevel = parsedFromUi + end + end + end + local getPetExperience = (type(getglobal) == "function" and getglobal("GetPetExperience")) or (_G and _G["GetPetExperience"]) + if type(getPetExperience) == "function" then + local xp, xpMax = getPetExperience() + xp = tonumber(xp) + xpMax = tonumber(xpMax) + if xp then + info.xp = xp + end + if xpMax and xpMax > 0 then + info.xpMax = xpMax + if xp then + info.xpPercent = math.floor((xp / xpMax) * 1000 + 0.5) / 10 + end + end + end + if type(UnitHealth) == "function" then + info.health = tonumber(UnitHealth("pet")) or nil + end + if type(UnitHealthMax) == "function" then + info.healthMax = tonumber(UnitHealthMax("pet")) or nil + end + end + + if info.loyaltyLevel ~= nil then + info.loyaltyText = MTH_PETS_GetLoyaltyNameByLevel(info.loyaltyLevel) + info.loyaltyDisplay = MTH_PETS_FormatLoyaltyDisplay(info.loyaltyLevel) + else + info.loyaltyText = nil + info.loyaltyDisplay = nil + end + + if info.liveExists and info.id and type(cp) == "table" then + local changed = false + local function assignIfChanged(target, key, value) + if type(target) ~= "table" then + return + end + if not MTH_PETS_IsSameLiveStateField(target[key], value) then + target[key] = value + changed = true + end + end + + assignIfChanged(cp, "happiness", info.happiness) + assignIfChanged(cp, "loyalty", info.loyalty) + if info.loyaltyLevel ~= nil then + assignIfChanged(cp, "loyaltyLevel", info.loyaltyLevel) + end + assignIfChanged(cp, "xp", info.xp) + assignIfChanged(cp, "xpMax", info.xpMax) + assignIfChanged(cp, "xpPercent", info.xpPercent) + + if type(activeRow) == "table" then + assignIfChanged(activeRow, "happiness", info.happiness) + assignIfChanged(activeRow, "loyalty", info.loyalty) + if info.loyaltyLevel ~= nil then + assignIfChanged(activeRow, "loyaltyLevel", info.loyaltyLevel) + end + assignIfChanged(activeRow, "xp", info.xp) + assignIfChanged(activeRow, "xpMax", info.xpMax) + assignIfChanged(activeRow, "xpPercent", info.xpPercent) + if changed then + activeRow.lastUpdated = time() + end + end + + if changed then + cp.lastUpdated = time() + pets.updatedAt = cp.lastUpdated + end + end + + return info +end + +function MTH_GetPetDatastoreSnapshot() + local pets = MTH_PETS_GetRootStore() + if type(pets) ~= "table" or type(pets.petStore) ~= "table" then + return nil + end + return pets.petStore +end + +function MTH_GetPetHistorySnapshot() + local store = MTH_GetPetDatastoreSnapshot() + if type(store) ~= "table" then + return nil + end + return store.historyById +end + +local function MTH_ST_GetStore() + local pets = MTH_PETS_GetRootStore() + if type(pets.stableScan) ~= "table" then + pets.stableScan = { + lastScan = 0, + lastEvent = "", + slotCount = 0, + slots = {}, + } + end + if type(pets.stableScan.slots) ~= "table" then + pets.stableScan.slots = {} + end + MTH_CharSavedVariables.stableScan = pets.stableScan + return pets.stableScan +end + +function MTH_ST_Scan(reason) + if UnitClass then + local _, classToken = UnitClass("player") + if classToken ~= "HUNTER" then + return false + end + end + + if type(GetNumStableSlots) ~= "function" or type(GetStablePetInfo) ~= "function" then + MTH_ST_Log("scan skipped: stable APIs unavailable") + return false + end + + local slotCount = tonumber(GetNumStableSlots()) or 0 + local store = MTH_ST_GetStore() + local petsRoot = MTH_PETS_GetRootStore() + local scanContext = MTH_PETS_CaptureContext() + local scanStableMasterName = MTH_PETS_GetStableMasterName() + store.lastScan = time() + store.lastEvent = tostring(reason or "manual") + store.slotCount = slotCount + store.zone = scanContext and scanContext.zone or store.zone + store.subZone = scanContext and scanContext.subZone or store.subZone + if scanStableMasterName and scanStableMasterName ~= "" then + store.stableMasterName = scanStableMasterName + end + store.slots = {} + + for slot = 1, slotCount do + local r1, r2, r3, r4, r5, r6, r7, r8 = GetStablePetInfo(slot) + MTH_PETS_RecordStableSlot(petsRoot, slot, r1, r2, r3, r4, r5, r6, r7, r8, scanContext, scanStableMasterName or store.stableMasterName) + local petId = nil + if type(petsRoot) == "table" and type(petsRoot.petStore) == "table" and type(petsRoot.petStore.stableSlotIndex) == "table" then + petId = petsRoot.petStore.stableSlotIndex[slot] + end + store.slots[slot] = { + petId = petId, + icon = r1, + name = r2, + level = r3, + family = r4, + loyalty = r5, + raw = { r1, r2, r3, r4, r5, r6, r7, r8 }, + r1, r2, r3, r4, r5, r6, r7, r8, + } + end + + MTH_PETS_MarkStableVisited(petsRoot, tostring(reason or "stable-scan")) + + MTH_PETS_RefreshCurrentPet() + + return true +end + +function MTH_GetStableScanSnapshot() + local pets = MTH_PETS_GetRootStore() + if not pets then + return nil + end + if type(pets.stableScan) ~= "table" then + return nil + end + MTH_CharSavedVariables.stableScan = pets.stableScan + return pets.stableScan +end + +function MTH_RunStableScan(reason) + if type(MTH_ST_Scan) ~= "function" then + return false + end + return MTH_ST_Scan(reason or "external") and true or false +end + +local function MTH_ST_RunScanThrottled(reason, minIntervalSeconds) + local now = tonumber(time()) or 0 + local minInterval = tonumber(minIntervalSeconds) or 0 + if minInterval > 0 and (now - (MTH_ST_LastAutoScanAt or 0)) < minInterval then + return false + end + MTH_ST_LastAutoScanAt = now + return MTH_ST_Scan(reason) +end + +local function MTH_PETS_HandleUnitPetTransition(source) + local pets = MTH_PETS_GetRootStore() + if type(pets) ~= "table" then + return + end + MTH_PETS_EnsureCurrentPetSchema(pets) + + local hadPet = MTH_ST_LastUnitPetHadPet and true or false + local previousCurrentId = (type(pets.currentPet) == "table") and pets.currentPet.id or nil + local snapshot = MTH_PETS_MakeSnapshotFromLivePet() + if pets.currentPetSuppressed == true and pets.currentPetSuppressedAwaitNoLive == true then + if type(snapshot) == "table" then + snapshot = nil + else + pets.currentPetSuppressedAwaitNoLive = false + end + end + local hasPet = snapshot and true or false + local pending = MTH_PETS_LastTameAttempt + local pendingTarget = (type(pending) == "table" and type(pending.target) == "table") and pending.target or nil + local hasPendingTame = type(pendingTarget) == "table" + if hasPendingTame then + MTH_PETS_LogTame("UNIT_PET source=" .. tostring(source) + .. " hadPet=" .. tostring(hadPet) + .. " hasLiveSnapshot=" .. tostring(type(snapshot) == "table") + .. " pendingTarget='" .. tostring((pendingTarget and pendingTarget["name"]) or "") + .. "' pendingBeastId='" .. tostring((pendingTarget and pendingTarget["beastId"]) or "") .. "'") + end + MTH_PETS_LogConsistency("HandleUnitPetTransition source=" .. tostring(source) + .. " previousCurrentId=" .. tostring(previousCurrentId) + .. " hadPet=" .. tostring(hadPet) + .. " hasLiveSnapshot=" .. tostring(type(snapshot) == "table")) + + if type(snapshot) == "table" then + local transitionSource = "unit-pet-refresh" + if not hadPet then + transitionSource = "unit-pet-acquire" + end + if not hadPet and snapshot.beastId == nil then + snapshot.beastId = MTH_PETS_GetPendingTameBeastId(snapshot) + MTH_PETS_LogTame("UNIT_PET acquire beastId resolved to " .. tostring(snapshot.beastId or "nil")) + end + + local petId, created = MTH_PETS_UpsertActivePetFromSnapshot(pets, snapshot, transitionSource, nil) + MTH_PETS_SetCurrentPetSuppressed(pets, false) + local cp = pets.currentPet + cp.exists = true + cp.id = petId + cp.guid = snapshot.guid + cp.signature = snapshot.signature + cp.beastId = snapshot.beastId + cp.name = snapshot.name + cp.family = snapshot.family + cp.level = snapshot.level + cp.happiness = snapshot.happiness + cp.loyalty = snapshot.loyalty + cp.loyaltyLevel = snapshot.loyaltyLevel + cp.xp = snapshot.xp + cp.xpMax = snapshot.xpMax + cp.xpPercent = snapshot.xpPercent + cp.lastUpdated = time() + cp.lastSeen = time() + cp.lastSource = source or transitionSource + if not hadPet or created then + cp.acquiredAt = time() + end + if cp.id and cp.id ~= previousCurrentId then + MTH_PETS_RequestPetSpellScan("unit-pet-current-change") + end + + local row = MTH_PETS_GetPetById(pets, petId) + if type(row) == "table" then + if type(row.events) ~= "table" then + row.events = {} + end + table.insert(row.events, { + type = (not hadPet) and "pet-acquired" or "pet-updated", + source = source or transitionSource, + at = time(), + context = MTH_PETS_CaptureContext(), + }) + end + if not hadPet and hasPendingTame then + MTH_PETS_LogTame("Acquire completed; clearing pending tame attempt") + MTH_PETS_LastTameAttempt = nil + end + else + local cp = pets.currentPet + cp.exists = false + local keepCurrentId, keepCurrentRow = MTH_PETS_ResolveNoLiveCurrentRow(pets, previousCurrentId) + if keepCurrentId ~= nil and type(keepCurrentRow) == "table" then + MTH_PETS_ApplyCurrentPetFromActiveRow(cp, keepCurrentId, keepCurrentRow) + else + cp.id = nil + cp.guid = nil + cp.signature = nil + cp.beastId = nil + cp.name = nil + cp.family = nil + cp.level = nil + cp.happiness = nil + cp.loyalty = nil + cp.loyaltyLevel = nil + cp.xp = nil + cp.xpMax = nil + cp.xpPercent = nil + end + cp.lastUpdated = time() + cp.lastSource = source or "unit-pet-none" + MTH_PETS_SetCurrentPetId(pets, keepCurrentId) + MTH_PETS_LogConsistency("HandleUnitPetTransition no-live result current=" .. MTH_PETS_FormatCurrentConsistency(cp) + .. " activeCurrentId=" .. tostring(type(pets.petStore) == "table" and pets.petStore.activeCurrentId or nil)) + if hasPendingTame then + MTH_PETS_LogTame("UNIT_PET no-live while tame pending; keeping pending attempt") + end + end + + MTH_ST_LastUnitPetHadPet = hasPet + pets.updatedAt = time() +end + +function MTH_GetStablePetNames() + MTH_PETS_RefreshCurrentPet() + local names = {} + local seen = {} + local snapshot = MTH_GetStableScanSnapshot() + + if snapshot and type(snapshot.slots) == "table" then + for _, row in pairs(snapshot.slots) do + local petName = nil + if type(row) == "table" then + petName = row.name or row[2] + end + if petName and petName ~= "" and not seen[petName] then + seen[petName] = true + table.insert(names, petName) + end + end + end + + if type(UnitName) == "function" then + local activePetName = UnitName("pet") + if activePetName and activePetName ~= "" and not seen[activePetName] then + seen[activePetName] = true + table.insert(names, activePetName) + end + end + + return names +end + +local function MTH_ST_CountStableSlots(snapshot) + if type(snapshot) ~= "table" then + return 0 + end + local count = tonumber(snapshot.slotCount) or 0 + if count > 0 then + return count + end + if type(snapshot.slots) ~= "table" then + return 0 + end + for _ in pairs(snapshot.slots) do + count = count + 1 + end + return count +end + +local function MTH_ST_DebugDumpLine(line) + if MTH and MTH.Print then + MTH:Print(tostring(line or ""), "debug") + end +end + +local function MTH_ST_DebugSortKeys(tbl) + local keys = {} + for k in pairs(tbl or {}) do + table.insert(keys, k) + end + table.sort(keys, function(a, b) + return tostring(a) < tostring(b) + end) + return keys +end + +local function MTH_ST_DebugDumpValue(prefix, value, depth, maxDepth, seen) + depth = tonumber(depth) or 0 + maxDepth = tonumber(maxDepth) or 4 + seen = seen or {} + + if type(value) ~= "table" then + MTH_ST_DebugDumpLine(prefix .. " = " .. tostring(value)) + return + end + + if seen[value] then + MTH_ST_DebugDumpLine(prefix .. " = ") + return + end + seen[value] = true + + if depth >= maxDepth then + MTH_ST_DebugDumpLine(prefix .. " = ") + return + end + + local keys = MTH_ST_DebugSortKeys(value) + if table.getn(keys) == 0 then + MTH_ST_DebugDumpLine(prefix .. " = {}") + return + end + + for i = 1, table.getn(keys) do + local key = keys[i] + local child = value[key] + local childPrefix = prefix .. "." .. tostring(key) + if type(child) == "table" then + MTH_ST_DebugDumpLine(childPrefix .. " = {") + MTH_ST_DebugDumpValue(childPrefix, child, depth + 1, maxDepth, seen) + MTH_ST_DebugDumpLine(childPrefix .. " = }") + else + MTH_ST_DebugDumpLine(childPrefix .. " = " .. tostring(child)) + end + end +end + +function MTH_CommandPetsState() + local pets = MTH_PETS_GetRootStore() + MTH_PETS_RefreshCurrentPet() + + local currentPet = pets and pets.currentPet or nil + local stableScan = pets and pets.stableScan or nil + local petTraining = pets and (pets.trainScan or pets.petTraining) or nil + local petStore = pets and pets.petStore or nil + local activeCount = 0 + local historyCount = 0 + if type(petStore) == "table" and type(petStore.activeById) == "table" then + for _ in pairs(petStore.activeById) do activeCount = activeCount + 1 end + end + if type(petStore) == "table" and type(petStore.historyById) == "table" then + for _ in pairs(petStore.historyById) do historyCount = historyCount + 1 end + end + + local currentPetText = "none" + if type(currentPet) == "table" and currentPet.exists then + local petName = tostring(currentPet.name or "") + local petLevel = tonumber(currentPet.level) or 0 + if petName ~= "" then + if petLevel > 0 then + currentPetText = petName .. " (" .. tostring(petLevel) .. ")" + else + currentPetText = petName + end + end + end + + local summary = "MTH_Pets: schemaVersion=" .. tostring(pets and pets.schemaVersion or 0) + .. " currentPet=" .. currentPetText + .. " activePets=" .. tostring(activeCount) + .. " petHistory=" .. tostring(historyCount) + .. " stableSlots=" .. tostring(MTH_ST_CountStableSlots(stableScan)) + .. " stableLastScan=" .. tostring(stableScan and stableScan.lastScan or 0) + .. " petTrainingLastScan=" .. tostring(petTraining and petTraining.lastScan or 0) + .. " hasCompletedPetScan=" .. tostring(petTraining and petTraining.hasCompletedPetScan or nil) + MTH:Print(summary) + MTH:Print("[PETSSTATE] " .. summary, "debug") +end + +function MTH_CommandPetsReset() + local pets = MTH_PETS_GetRootStore() + if type(pets) ~= "table" then + MTH:Print("Pets reset failed: datastore unavailable.") + return false + end + + MTH_PETS_EnsureSchema(pets) + local store = MTH_PETS_GetStoreTables(pets) + if type(store) ~= "table" then + MTH:Print("Pets reset failed: petStore unavailable.") + return false + end + + local activeCount = 0 + if type(store.activeById) == "table" then + for _ in pairs(store.activeById) do + activeCount = activeCount + 1 + end + end + + local historyCount = 0 + if type(store.historyById) == "table" then + for _ in pairs(store.historyById) do + historyCount = historyCount + 1 + end + else + store.historyById = {} + end + + store.activeById = {} + store.signatureIndex = {} + store.guidIndex = {} + store.stableSlotIndex = {} + store.activeCurrentId = nil + pets.currentPetId = nil + pets.currentPetSuppressed = false + pets.currentPetSuppressedReason = "" + store.nextId = 1 + + MTH_PETS_EnsureCurrentPetSchema(pets) + local cp = pets.currentPet + cp.exists = false + cp.id = nil + cp.guid = nil + cp.signature = nil + cp.beastId = nil + cp.name = nil + cp.family = nil + cp.level = nil + cp.happiness = nil + cp.loyalty = nil + cp.loyaltyLevel = nil + cp.xp = nil + cp.xpMax = nil + cp.xpPercent = nil + cp.acquiredAt = 0 + cp.lastSeen = time() + cp.lastUpdated = time() + cp.lastSource = "pets-reset" + + if type(pets.stableScan) ~= "table" then + pets.stableScan = {} + end + pets.stableScan.lastScan = 0 + pets.stableScan.lastEvent = "reset:/mth pets reset" + pets.stableScan.slotCount = 0 + pets.stableScan.slots = {} + + MTH_ST_LastUnitPetHadPet = false + pets.updatedAt = time() + + if type(MTH_CharSavedVariables) == "table" then + MTH_CharSavedVariables.MTH_Pets = pets + MTH_CharSavedVariables.petStore = pets.petStore + MTH_CharSavedVariables.stableScan = pets.stableScan + end + + local line = "Pets reset complete: clearedActive=" .. tostring(activeCount) + .. " preservedHistory=" .. tostring(historyCount) + .. " nextId=" .. tostring(store.nextId) + MTH:Print(line) + MTH:Print("[PETSRESET] " .. line, "debug") + return true +end + +function MTH_CommandPetsDump() + local pets = MTH_PETS_GetRootStore() + MTH_PETS_RefreshCurrentPet() + + if type(pets) ~= "table" or type(pets.currentPet) ~= "table" or not pets.currentPet.exists or not pets.currentPet.id then + MTH:Print("Current pet dump: no active pet.") + return + end + + local cp = pets.currentPet + local row = nil + if type(pets.petStore) == "table" and type(pets.petStore.activeById) == "table" then + row = pets.petStore.activeById[cp.id] + end + if type(row) ~= "table" then + MTH:Print("Current pet dump: active row missing for id=" .. tostring(cp.id)) + return + end + + MTH_ST_DebugDumpLine("[PETDUMP] BEGIN currentPet") + MTH_ST_DebugDumpValue("[PETDUMP].currentPet", cp, 0, 3, {}) + MTH_ST_DebugDumpLine("[PETDUMP] END currentPet") + + MTH_ST_DebugDumpLine("[PETDUMP] BEGIN activeRow[" .. tostring(cp.id) .. "]") + MTH_ST_DebugDumpValue("[PETDUMP].activeRow", row, 0, 5, {}) + MTH_ST_DebugDumpLine("[PETDUMP] END activeRow") + + local petStore = pets.petStore + if type(petStore) == "table" and type(petStore.stableSlotIndex) == "table" then + local slotKeys = MTH_ST_DebugSortKeys(petStore.stableSlotIndex) + for i = 1, table.getn(slotKeys) do + local slot = slotKeys[i] + local petId = petStore.stableSlotIndex[slot] + MTH_ST_DebugDumpLine("[PETDUMP] stableSlotIndex." .. tostring(slot) .. " = " .. tostring(petId)) + if petId and type(petStore.activeById) == "table" and type(petStore.activeById[petId]) == "table" then + MTH_ST_DebugDumpLine("[PETDUMP] BEGIN stableRow slot=" .. tostring(slot) .. " id=" .. tostring(petId)) + MTH_ST_DebugDumpValue("[PETDUMP].stableRow[" .. tostring(slot) .. "]", petStore.activeById[petId], 0, 5, {}) + MTH_ST_DebugDumpLine("[PETDUMP] END stableRow slot=" .. tostring(slot)) + end + end + end + + if type(pets.stableScan) == "table" then + MTH_ST_DebugDumpLine("[PETDUMP] BEGIN stableScan") + MTH_ST_DebugDumpValue("[PETDUMP].stableScan", pets.stableScan, 0, 4, {}) + MTH_ST_DebugDumpLine("[PETDUMP] END stableScan") + end +end + +function MTH_CommandStableScan() + if UnitClass then + local _, classToken = UnitClass("player") + if classToken ~= "HUNTER" then + MTH:Print("Stable scan is hunter-only.") + return + end + end + + local ok = MTH_ST_Scan("manual:/mth stablescan") + if not ok then + MTH:Print("Stable scan failed or no stable data available.") + return + end + + local store = MTH_CharSavedVariables and MTH_CharSavedVariables.stableScan + local slotCount = store and store.slotCount or 0 + MTH:Print("Stable scan captured " .. tostring(slotCount) .. " slot(s).") +end + +local function MTH_ST_OnEvent(_, evt, eventArg1) + evt = evt or event + eventArg1 = eventArg1 or arg1 + if not evt then + return + end + + if evt == "PLAYER_ENTERING_WORLD" then + local pets = MTH_PETS_GetRootStore() + MTH_PETS_MaybePromptStableVisit(pets) + MTH_PETS_RefreshCurrentPet() + MTH_PETS_EmitLiveState("event:PLAYER_ENTERING_WORLD", true) + return + end + + if evt == "SPELLCAST_CHANNEL_START" then + MTH_PETS_RecordTameAttempt("event:" .. tostring(evt), eventArg1) + return + end + + if evt == "SPELLCAST_STOP" or evt == "SPELLCAST_CHANNEL_STOP" or evt == "SPELLCAST_FAILED" or evt == "SPELLCAST_INTERRUPTED" then + MTH_PETS_LogTame("Spell terminal evt=" .. tostring(evt) .. " spell='" .. tostring(eventArg1 or "") .. "' pending=" .. tostring(type(MTH_PETS_LastTameAttempt) == "table")) + local terminalIsTame = MTH_PETS_IsTameBeastSpellName(eventArg1) + if not terminalIsTame and type(MTH_PETS_LastTameAttempt) == "table" then + terminalIsTame = true + MTH_PETS_LogTame("Treating terminal event as tame because pending attempt exists") + end + if not terminalIsTame then + local fallbackName, fallbackSource = MTH_PETS_GetCurrentPlayerCastOrChannelName() + if fallbackName and MTH_PETS_IsTameBeastSpellName(fallbackName) then + terminalIsTame = true + MTH_PETS_LogTame("Resolved terminal spell via " .. tostring(fallbackSource) .. " => '" .. tostring(fallbackName) .. "'") + end + end + if terminalIsTame then + if evt == "SPELLCAST_FAILED" or evt == "SPELLCAST_INTERRUPTED" then + MTH_PETS_LogTame("Tame ended with failure/interruption; clearing pending attempt") + MTH_PETS_LastTameAttempt = nil + elseif evt == "SPELLCAST_CHANNEL_STOP" or evt == "SPELLCAST_STOP" then + MTH_PETS_LogTame("Tame channel ended; awaiting UNIT_PET to confirm acquire") + end + end + return + end + + if evt == "UNIT_PET" then + if eventArg1 ~= "player" then + return + end + MTH_PETS_HandleUnitPetTransition("event:UNIT_PET") + MTH_PETS_EmitLiveState("event:UNIT_PET") + return + end + + if evt == "PET_BAR_UPDATE" then + MTH_PETS_RefreshCurrentPet() + MTH_PETS_EmitLiveState("event:PET_BAR_UPDATE") + return + end + + if evt == "UNIT_HAPPINESS" then + if eventArg1 and eventArg1 ~= "pet" then + return + end + MTH_PETS_RefreshCurrentPet() + MTH_PETS_EmitLiveState("event:UNIT_HAPPINESS") + return + end + + if evt == "CHAT_MSG_COMBAT_XP_GAIN" or evt == "PLAYER_XP_UPDATE" then + MTH_PETS_RefreshCurrentPet() + MTH_PETS_EmitLiveState("event:" .. tostring(evt)) + return + end + + if evt == "CHAT_MSG_SYSTEM" or evt == "UI_ERROR_MESSAGE" then + local matchedRunaway = MTH_PETS_IsRunawaySystemMessage(eventArg1) + MTH_PETS_TraceRunawayEvent(evt, eventArg1, matchedRunaway) + if matchedRunaway then + MTH_PETS_RecordPetRunaway("event:" .. tostring(evt), eventArg1) + MTH_PETS_RefreshCurrentPet() + MTH_PETS_EmitLiveState("event:" .. tostring(evt)) + end + return + end + + if evt == "PET_STABLE_SHOW" then + MTH_ST_RunScanThrottled("event:" .. tostring(evt), 1) + return + end + + if evt == "PET_STABLE_UPDATE" then + MTH_ST_Scan("event:PET_STABLE_UPDATE") + MTH_PETS_RefreshCurrentPet() + MTH_PETS_EmitLiveState("event:PET_STABLE_UPDATE") + return + end + + if evt == "PET_STABLE_CLOSED" then + MTH_PETS_RefreshCurrentPet() + MTH_PETS_EmitLiveState("event:PET_STABLE_CLOSED") + return + end +end + +function MTH_ST_InitService() + if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate("stablescan-init") then + return + end + + if MTH_StableFrame then + return + end + + MTH_StableFrame = CreateFrame("Frame", "MTHStableScanFrame") + MTH_StableFrame:RegisterEvent("PLAYER_ENTERING_WORLD") + MTH_StableFrame:RegisterEvent("SPELLCAST_CHANNEL_START") + MTH_StableFrame:RegisterEvent("SPELLCAST_CHANNEL_STOP") + MTH_StableFrame:RegisterEvent("SPELLCAST_FAILED") + MTH_StableFrame:RegisterEvent("SPELLCAST_INTERRUPTED") + MTH_StableFrame:RegisterEvent("UNIT_PET") + MTH_StableFrame:RegisterEvent("PET_BAR_UPDATE") + MTH_StableFrame:RegisterEvent("UNIT_HAPPINESS") + MTH_StableFrame:RegisterEvent("CHAT_MSG_COMBAT_XP_GAIN") + MTH_StableFrame:RegisterEvent("PLAYER_XP_UPDATE") + MTH_StableFrame:RegisterEvent("CHAT_MSG_SYSTEM") + MTH_StableFrame:RegisterEvent("UI_ERROR_MESSAGE") + MTH_StableFrame:RegisterEvent("PET_STABLE_SHOW") + MTH_StableFrame:RegisterEvent("PET_STABLE_UPDATE") + MTH_StableFrame:RegisterEvent("PET_STABLE_CLOSED") + MTH_StableFrame:SetScript("OnEvent", MTH_ST_OnEvent) + MTH_ST_LastUnitPetHadPet = ((type(UnitExists) == "function") and UnitExists("pet")) and true or false + MTH_PETS_InstallCoreRenameHook() + MTH_PETS_EmitLiveState("service:init", true) + if MTH_PETS_TRACE_CONSISTENCY and MTH and MTH.Print then + MTH:Print("[PETCONSIST] trace enabled", "debug") + end +end + +function MTH_ST_ShutdownService(_reason) + if not MTH_StableFrame then + return + end + + MTH_StableFrame:UnregisterAllEvents() + MTH_StableFrame:SetScript("OnEvent", nil) + MTH_StableFrame = nil + MTH_ST_LastAutoScanAt = 0 + MTH_ST_LastUnitPetHadPet = false + MTH_PETS_LiveState = nil + MTH_PETS_LiveStateSeq = 0 + + if type(PetRename) == "function" and PetRename == MTH_PETS_CorePetRenameHook and type(MTH_PETS_CoreOriginal_PetRename) == "function" then + PetRename = MTH_PETS_CoreOriginal_PetRename + end + if MTH and MTH.RestoreHookBoundary then + MTH:RestoreHookBoundary(MTH_PETS_CORE_HOOK_BOUNDARY_KEY) + end +end + +MTH_PETS_GetRootStore() diff --git a/api/core.lua b/api/core.lua new file mode 100644 index 0000000..84c15bd --- /dev/null +++ b/api/core.lua @@ -0,0 +1,264 @@ +if not MTH then + error("MetaHunt core framework missing: api/core-framework.lua must load before api/core.lua") +end + +local function MTH_PT_GetGlobal(name) + if type(getglobal) == "function" then + return getglobal(name) + end + if _G then + return _G[name] + end + return nil +end + +local MTH_ItemLinkCacheTooltip = nil + +local function MTH_GetQualityHexColor(quality) + local q = tonumber(quality) + if type(GetItemQualityColor) == "function" and q then + local _, _, _, colorCode = GetItemQualityColor(q) + if colorCode and colorCode ~= "" then + local hex = string.gsub(tostring(colorCode), "|c", "") + hex = string.gsub(hex, "|r", "") + hex = string.gsub(hex, "^#", "") + if string.len(hex) == 8 then return hex end + if string.len(hex) == 6 then return "ff" .. hex end + end + end + return "ffffffff" +end + +function MTH_PrimeItemCache(itemId) + local id = tonumber(itemId) + if not id or id <= 0 then return false end + if type(GetItemInfo) == "function" and GetItemInfo(id) then return true end + + if not MTH_ItemLinkCacheTooltip then + MTH_ItemLinkCacheTooltip = CreateFrame("GameTooltip", "MTH_ItemLinkCacheTooltip", UIParent, "GameTooltipTemplate") + if not MTH_ItemLinkCacheTooltip then return false end + MTH_ItemLinkCacheTooltip:SetOwner(UIParent, "ANCHOR_NONE") + end + + MTH_ItemLinkCacheTooltip:ClearLines() + MTH_ItemLinkCacheTooltip:SetHyperlink("item:" .. tostring(id) .. ":0:0:0") + return type(GetItemInfo) == "function" and (GetItemInfo(id) ~= nil) or false +end + +function MTH_GetClickableItemLink(itemId, fallbackName, tryPrimeCache) + local id = tonumber(itemId) + if not id then return nil end + + local itemName = nil + local itemLink = nil + local itemQuality = nil + if type(GetItemInfo) == "function" then + itemName, itemLink, itemQuality = GetItemInfo(id) + if (not itemName or itemName == "") and tryPrimeCache then + MTH_PrimeItemCache(id) + itemName, itemLink, itemQuality = GetItemInfo(id) + end + end + + local displayName = tostring(itemName or fallbackName or ("Item " .. tostring(id))) + local coreLink = nil + if itemLink and itemLink ~= "" then + local _, _, parsedCore = string.find(tostring(itemLink), "|H(item:[^|]+)|h") + if parsedCore and parsedCore ~= "" then + coreLink = parsedCore + elseif string.find(tostring(itemLink), "^item:", 1, true) then + coreLink = tostring(itemLink) + end + end + if not coreLink then + coreLink = "item:" .. tostring(id) .. ":0:0:0" + end + + local colorHex = MTH_GetQualityHexColor(itemQuality) + return "|c" .. tostring(colorHex) .. "|H" .. tostring(coreLink) .. "|h[" .. tostring(displayName) .. "]|h|r" +end + +function MTH_InsertLinkToChat(link) + if type(link) ~= "string" or link == "" then return false end + + local wimEditBox = MTH_PT_GetGlobal("WIM_EditBoxInFocus") + if wimEditBox and type(wimEditBox.Insert) == "function" then + wimEditBox:Insert(link) + return true + end + + local chatEditBox = MTH_PT_GetGlobal("ChatFrameEditBox") + if not chatEditBox then return false end + + if type(chatEditBox.IsVisible) == "function" and chatEditBox:IsVisible() and type(chatEditBox.Insert) == "function" then + chatEditBox:Insert(link) + return true + end + + if type(chatEditBox.Show) == "function" then chatEditBox:Show() end + if type(chatEditBox.Insert) == "function" then + chatEditBox:Insert(link) + elseif type(chatEditBox.SetText) == "function" then + chatEditBox:SetText(link) + if type(chatEditBox.HighlightText) == "function" then + chatEditBox:HighlightText() + end + end + + return true +end + +-- Slash command handler (original, from backup) +MTH:RegisterSlashAliases("MTH") + +local function MTH_CommandBook() + if type(MTH_OpenHunterBook) == "function" then + MTH_OpenHunterBook() + elseif type(MTH_ToggleHunterBook) == "function" then + MTH_ToggleHunterBook() + else + MTH:Print("Hunter Book is not available yet") + end +end + +local function MTH_CommandOptions() + if MTH_OpenOptions then + MTH_OpenOptions("General") + else + MTH:Print("Options window is not available yet") + end +end + +local function MTH_CommandPetSpellScanFallback() + local getSpellName = (type(getglobal) == "function" and getglobal("GetSpellName")) or (_G and _G["GetSpellName"]) + local getSpellTexture = (type(getglobal) == "function" and getglobal("GetSpellTexture")) or (_G and _G["GetSpellTexture"]) + local isPassiveSpell = (type(getglobal) == "function" and getglobal("IsPassiveSpell")) or (_G and _G["IsPassiveSpell"]) + local bookTypePet = (type(getglobal) == "function" and getglobal("BOOKTYPE_PET")) or (_G and _G["BOOKTYPE_PET"]) + + local petName = UnitName and UnitName("pet") or nil + local pets = (type(MTH_PETS_GetRootStore) == "function") and MTH_PETS_GetRootStore() or nil + local currentPetId = (type(pets) == "table" and type(pets.currentPet) == "table") and pets.currentPet.id or nil + + if type(getSpellName) ~= "function" or not bookTypePet then + return false, 0 + end + + local rows = {} + for slot = 1, 200 do + local name, subText = getSpellName(slot, bookTypePet) + if not name or name == "" then + break + end + local rankNumber = nil + if tostring(subText or "") ~= "" then + local _, _, r = string.find(tostring(subText), "(%d+)") + rankNumber = tonumber(r) + end + if not rankNumber then + local _, _, r2 = string.find(tostring(name), "(%d+)") + rankNumber = tonumber(r2) + end + local token = string.lower(tostring(name or "")) + if rankNumber and rankNumber > 0 then + token = token .. "#" .. tostring(rankNumber) + end + table.insert(rows, { + slot = slot, + name = tostring(name or ""), + rank = rankNumber, + icon = (type(getSpellTexture) == "function") and getSpellTexture(slot, bookTypePet) or nil, + isPassive = (type(isPassiveSpell) == "function") and (isPassiveSpell(slot, bookTypePet) and true or false) or false, + token = token, + }) + end + + local count = table.getn(rows) + if count > 0 then + local preview = {} + local maxPreview = math.min(6, count) + for i = 1, maxPreview do + local row = rows[i] + if type(row) == "table" then + local line = tostring(row.name or "?") + local rank = tonumber(row.rank) + if rank and rank > 0 then + line = line .. " R" .. tostring(rank) + end + table.insert(preview, line) + end + end + end + + if type(MTH_PETS_RecordCurrentPetSpellbookSnapshot) == "function" then + local persisted, updated = MTH_PETS_RecordCurrentPetSpellbookSnapshot(rows, "petspellscan:core-fallback") + return true, tonumber(persisted) or count + else + return false, count + end +end + +if type(MTH_PS_ScanNow) ~= "function" then + function MTH_PS_ScanNow(trigger) + local ok, count = MTH_CommandPetSpellScanFallback() + return ok and true or false, tonumber(count) or 0, false + end +end + +if type(MTH_PSP_RequestScan) ~= "function" then + local mthPsFallbackLastScan = 0 + function MTH_PSP_RequestScan(trigger, minIntervalSeconds) + local now = tonumber(time()) or 0 + local minInterval = tonumber(minIntervalSeconds) or 1 + local triggerText = tostring(trigger or "request") + if minInterval > 0 and (now - mthPsFallbackLastScan) < minInterval then + return false + end + local ok = MTH_PS_ScanNow(triggerText) + if ok then + mthPsFallbackLastScan = now + end + return ok and true or false + end +end + +if type(MTH_CommandPetSpellScan) ~= "function" then + function MTH_CommandPetSpellScan() + local ok, count = MTH_PS_ScanNow("manual-core-fallback") + MTH:Print("Pet spellbook scan: " .. tostring(count or 0) .. " spell(s). ok=" .. tostring(ok and true or false)) + end +end + +function SlashCmdList.MTH(msg, editbox) + if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate("slash") then + return + end + + msg = msg or "" + msg = string.gsub(tostring(msg), "^%s+", "") + msg = string.gsub(msg, "%s+$", "") + local lowerMsg = string.lower(msg) + if msg == "" then + MTH:Print("Available: /mth options, /mth book") + elseif lowerMsg == "options" then + MTH_CommandOptions() + elseif lowerMsg == "book" or lowerMsg == "hunterbook" then + MTH_CommandBook() + else + MTH:Print("Unknown command: " .. tostring(msg)) + MTH:Print("Available: /mth options, /mth book") + end +end + +-- Initialize on load +MTH:InitSavedVariables() +if type(MTH_ST_InitService) == "function" then + MTH_ST_InitService() +end +if type(MTH_TR_InitService) == "function" then + MTH_TR_InitService() +elseif type(MTH_PT_InitService) == "function" then + MTH_PT_InitService() +end +if type(MTH_PS_InitService) == "function" then + MTH_PS_InitService() +end diff --git a/api/debug-frame.lua b/api/debug-frame.lua new file mode 100644 index 0000000..3219faa --- /dev/null +++ b/api/debug-frame.lua @@ -0,0 +1,374 @@ +------------------------------------------------------ +-- MetaHunt: Debug Frame +-- Displays errors and debug messages in a scrollable frame +------------------------------------------------------ + +MTH_DebugFrame = { + frame = nil, + scrollFrame = nil, + scrollText = nil, + errors = {}, + maxErrors = 500, + isVisible = false, + initialized = false, + captureCount = 0, + infoCount = 0 +} + +function MTH_DebugFrame:Initialize() + if self.initialized then + return + end + self.initialized = true + + -- Create main frame + self.frame = CreateFrame("Frame", "MTH_DebugFrameWindow", UIParent) + self.frame:SetWidth(800) + self.frame:SetHeight(400) + self.frame:SetPoint("CENTER", UIParent, "CENTER") + self.frame:SetFrameStrata("DIALOG") + self.frame:SetBackdrop({ + bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background", + edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", + tile = true, + tileSize = 32, + edgeSize = 32, + insets = { left = 11, right = 12, top = 12, bottom = 11 } + }) + self.frame:SetBackdropColor(0, 0, 0, 0.8) + self.frame:SetMovable(true) + self.frame:EnableMouse(true) + self.frame:RegisterForDrag("LeftButton") + self.frame:SetScript("OnDragStart", function() self.frame:StartMoving() end) + self.frame:SetScript("OnDragStop", function() self.frame:StopMovingOrSizing() end) + if UISpecialFrames then + local already = false + for i = 1, table.getn(UISpecialFrames) do + if UISpecialFrames[i] == "MTH_DebugFrameWindow" then + already = true + break + end + end + if not already then + table.insert(UISpecialFrames, "MTH_DebugFrameWindow") + end + end + + -- Title + local title = self.frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + title:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 20, -15) + title:SetText("MetaHunt Debug - Errors & Messages") + + -- Close button + local closeButton = CreateFrame("Button", nil, self.frame, "UIPanelButtonTemplate") + closeButton:SetPoint("TOPRIGHT", self.frame, "TOPRIGHT", -8, -8) + closeButton:SetWidth(22) + closeButton:SetHeight(22) + closeButton:SetText("X") + closeButton:SetScript("OnClick", function() MTH_DebugFrame:Hide() end) + + -- Clear button + local clearButton = CreateFrame("Button", nil, self.frame, "GameMenuButtonTemplate") + clearButton:SetPoint("BOTTOMLEFT", self.frame, "BOTTOMLEFT", 10, 10) + clearButton:SetWidth(100) + clearButton:SetHeight(25) + clearButton:SetText("Clear") + clearButton:SetScript("OnClick", function() MTH_DebugFrame:Clear() end) + + -- Select-all button + local copyButton = CreateFrame("Button", nil, self.frame, "GameMenuButtonTemplate") + copyButton:SetPoint("LEFT", clearButton, "RIGHT", 10, 0) + copyButton:SetWidth(100) + copyButton:SetHeight(25) + copyButton:SetText("Select All") + copyButton:SetScript("OnClick", function() MTH_DebugFrame:SelectAllText() end) + + -- Scroll frame + text content with proper vertical scrollbar + self.scrollFrame = CreateFrame("ScrollFrame", "MTH_DebugScrollFrame", self.frame, "UIPanelScrollFrameTemplate") + self.scrollFrame:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 15, -50) + self.scrollFrame:SetPoint("BOTTOMRIGHT", self.frame, "BOTTOMRIGHT", -35, 45) + + self.scrollText = CreateFrame("EditBox", "MTH_DebugScrollText", self.scrollFrame) + self.scrollText:SetMultiLine(true) + self.scrollText:SetMaxLetters(999999) + self.scrollText:SetFont("Fonts\\FRIZQT__.TTF", 11, "") + self.scrollText:SetTextColor(0.8, 0.8, 0.8, 1) + self.scrollText:SetWidth(730) + self.scrollText:SetHeight(1) + self.scrollText:EnableMouse(true) + self.scrollText:SetScript("OnCursorChanged", function() + if not MTH_DebugFrame.scrollFrame then + return + end + local width = MTH_DebugFrame.scrollFrame:GetWidth() + if width and width > 20 then + MTH_DebugFrame.scrollText:SetWidth(width - 20) + end + end) + self.scrollText:SetScript("OnEscapePressed", function() self.scrollText:ClearFocus() end) + self.scrollText:SetAutoFocus(false) + self.scrollText:EnableMouseWheel(true) + self.scrollText:SetScript("OnMouseWheel", function() + if not MTH_DebugFrame.scrollFrame then + return + end + local curr = MTH_DebugFrame.scrollFrame:GetVerticalScroll() + local step = 24 + if arg1 and arg1 > 0 then + MTH_DebugFrame.scrollFrame:SetVerticalScroll(curr - step) + else + MTH_DebugFrame.scrollFrame:SetVerticalScroll(curr + step) + end + end) + self.scrollText:SetScript("OnTextChanged", nil) + + self.scrollFrame:SetScrollChild(self.scrollText) + + -- Hide by default + self.frame:Hide() + self.isVisible = false +end + +function MTH_DebugFrame:AddError(message) + if not message then return end + + self.captureCount = self.captureCount + 1 + + if string.len(message) > 1000 then + message = string.sub(message, 1, 1000) .. "..." + end + + table.insert(self.errors, 1, { + time = date("%H:%M:%S"), + message = message, + level = "error" + }) + + -- Limit stored errors + if table.getn(self.errors) > self.maxErrors then + table.remove(self.errors, self.maxErrors + 1) + end + + self:UpdateDisplay() +end + +function MTH_DebugFrame:AddInfo(message) + if not message then return end + + self.infoCount = self.infoCount + 1 + + if string.len(message) > 1000 then + message = string.sub(message, 1, 1000) .. "..." + end + + table.insert(self.errors, 1, { + time = date("%H:%M:%S"), + message = message, + level = "info" + }) + + if table.getn(self.errors) > self.maxErrors then + table.remove(self.errors, self.maxErrors + 1) + end + + self:UpdateDisplay() +end + +function MTH_DebugFrame:UpdateDisplay() + if not self.scrollText or not self.initialized then + return + end + + local text = "=== Errors Captured: " .. self.captureCount .. " ===\n" + text = text .. "=== Info Captured: " .. (self.infoCount or 0) .. " ===\n" + text = text .. "=== Total in queue: " .. table.getn(self.errors) .. " ===\n\n" + + for i = 1, table.getn(self.errors) do + local err = self.errors[i] + text = text .. "[" .. err.time .. "] " .. err.message .. "\n" + end + + local _, lineCount = string.gsub(text, "\n", "\n") + local lineHeight = 13 + local contentHeight = (lineCount + 1) * lineHeight + 8 + if contentHeight < 1 then + contentHeight = 1 + end + self.scrollText:SetHeight(contentHeight) + self.scrollText:SetText(text) + if self.scrollFrame then + self.scrollFrame:SetVerticalScroll(0) + end +end + +function MTH_DebugFrame:Toggle() + if self.isVisible then + self.frame:Hide() + self.isVisible = false + else + if not self.initialized then + self:Initialize() + end + self.frame:Show() + self.isVisible = true + self:UpdateDisplay() + end +end + +function MTH_DebugFrame:Show() + if not self.initialized then + self:Initialize() + end + self.frame:Show() + self.isVisible = true + self:UpdateDisplay() +end + +function MTH_DebugFrame:Hide() + if self.frame then + self.frame:Hide() + end + self.isVisible = false +end + +function MTH_DebugFrame:Clear() + self.errors = {} + self.captureCount = 0 + self.infoCount = 0 + self:UpdateDisplay() + if self.initialized and self.isVisible then + if MTH and MTH.Print then + MTH:Print("Debug frame cleared") + end + end +end + +function MTH_DebugFrame:SelectAllText() + if not self.scrollText or not self.initialized then + return + end + -- Select all text + self.scrollText:HighlightText(0, -1) + self.scrollText:SetFocus() + if MTH and MTH.Print then + MTH:Print("All text selected - press Ctrl+C to copy") + end +end + +------------------------------------------------------ +-- Error Capture Hooks +------------------------------------------------------ +local function MTH_DF_AddMessageCapture(msg) + if msg then + local msgStr = tostring(msg) + if string.find(msgStr, "%.lua:%d+:") then + MTH_DebugFrame:AddError(msgStr) + end + end +end + +local function MTH_DF_IsGlobalCaptureEnabled() + if MTH and MTH.GetConfig then + return MTH:GetConfig("debug", "globalErrorCapture", false) and true or false + end + return false +end + +local function MTH_DF_SetGlobalCaptureEnabled(enabled) + if MTH and MTH.SetConfig then + MTH:SetConfig("debug", "globalErrorCapture", enabled and true or false) + end +end + +local function MTH_DF_InstallHooks() + if MTH_DebugFrame.hooksInstalled then + return + end + if not MTH_DF_IsGlobalCaptureEnabled() then + return + end + + if DEFAULT_CHAT_FRAME and type(DEFAULT_CHAT_FRAME.AddMessage) == "function" then + MTH_DebugFrame.originalAddMessage = DEFAULT_CHAT_FRAME.AddMessage + MTH_DebugFrame.addMessageHook = function(self, msg, arg1, arg2, arg3, arg4, arg5) + MTH_DF_AddMessageCapture(msg) + if MTH_DebugFrame.originalAddMessage then + return MTH_DebugFrame.originalAddMessage(self, msg, arg1, arg2, arg3, arg4, arg5) + end + end + DEFAULT_CHAT_FRAME.AddMessage = MTH_DebugFrame.addMessageHook + end + + if _G and type(_G["ScriptErrors"]) == "function" then + MTH_DebugFrame.originalScriptErrors = _G["ScriptErrors"] + MTH_DebugFrame.scriptErrorsHook = function(msg) + if msg then + MTH_DebugFrame:AddError(tostring(msg)) + end + if MTH_DebugFrame.originalScriptErrors then + return MTH_DebugFrame.originalScriptErrors(msg) + end + end + _G["ScriptErrors"] = MTH_DebugFrame.scriptErrorsHook + end + + MTH_DebugFrame.hooksInstalled = true +end + +local function MTH_DF_UninstallHooks() + if not MTH_DebugFrame.hooksInstalled then + return + end + + if DEFAULT_CHAT_FRAME + and MTH_DebugFrame.addMessageHook + and DEFAULT_CHAT_FRAME.AddMessage == MTH_DebugFrame.addMessageHook + and MTH_DebugFrame.originalAddMessage + then + DEFAULT_CHAT_FRAME.AddMessage = MTH_DebugFrame.originalAddMessage + end + + if _G + and MTH_DebugFrame.scriptErrorsHook + and _G["ScriptErrors"] == MTH_DebugFrame.scriptErrorsHook + and MTH_DebugFrame.originalScriptErrors + then + _G["ScriptErrors"] = MTH_DebugFrame.originalScriptErrors + end + + MTH_DebugFrame.hooksInstalled = false +end + +function MTH_DebugFrame:IsGlobalCaptureEnabled() + return MTH_DF_IsGlobalCaptureEnabled() +end + +function MTH_DebugFrame:SetGlobalCaptureEnabled(enabled) + local want = enabled and true or false + MTH_DF_SetGlobalCaptureEnabled(want) + if want then + MTH_DF_InstallHooks() + else + MTH_DF_UninstallHooks() + end +end + +MTH_DebugFrame.InstallHooks = MTH_DF_InstallHooks +MTH_DebugFrame.UninstallHooks = MTH_DF_UninstallHooks + +-- Event listener for addon load +local eventFrame = CreateFrame("Frame") +eventFrame:RegisterEvent("ADDON_LOADED") +eventFrame:SetScript("OnEvent", function(self, event, addonName) + if event == "ADDON_LOADED" and addonName == "MetaHunt" then + if MTH and MTH.GetConfig then + MTH_DF_SetGlobalCaptureEnabled(MTH:GetConfig("debug", "globalErrorCapture", false)) + end + MTH_DF_InstallHooks() + MTH_DebugFrame:Initialize() + if MTH and MTH.Print then + local mode = MTH_DF_IsGlobalCaptureEnabled() and "legacy-global" or "safe-local" + MTH:Print("=== MetaHunt Debug Frame Initialized (" .. mode .. ") ===", "debug") + end + end +end) diff --git a/api/hunterbook-tab-beastlore.lua b/api/hunterbook-tab-beastlore.lua new file mode 100644 index 0000000..896352c --- /dev/null +++ b/api/hunterbook-tab-beastlore.lua @@ -0,0 +1,390 @@ +if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end + +MTH_HUNTERBOOK_TABS.pets = { + headerLabel = "Beasts", + columnLabels = { "ID", "Lvl", "Family", "Name", "Abilities", "Zone", "R", "E", "U" }, + columnLayout = { + { x = 8, width = 28, align = "LEFT" }, + { x = 38, width = 30, align = "LEFT" }, + { x = 68, width = 74, align = "LEFT" }, + { x = 142, width = 130, align = "LEFT" }, + { x = 272, width = 180, align = "LEFT" }, + { x = 452, width = 68, align = "LEFT" }, + { x = 520, width = 10, align = "CENTER" }, + { x = 532, width = 10, align = "CENTER" }, + { x = 544, width = 10, align = "CENTER" }, + }, +} + +function MTH_BOOK_GetBeastAbilitiesSummary(beast) + if not beast or not beast.abilities or beast.abilities == "" or beast.abilities == "None" then + return "-" + end + return tostring(beast.abilities) +end + +function MTH_BOOK_GetBeastZoneSummary(beast) + if not beast or not beast.coords or table.getn(beast.coords) == 0 then + return "-" + end + + local seen = {} + local zoneNames = {} + for i = 1, table.getn(beast.coords) do + local c = beast.coords[i] + if c and c[3] and not seen[c[3]] then + seen[c[3]] = true + table.insert(zoneNames, MTH_BOOK_GetZoneName(c[3])) + end + end + + if table.getn(zoneNames) == 0 then return "-" end + if table.getn(zoneNames) == 1 then return zoneNames[1] end + return zoneNames[1] .. " +" .. tostring(table.getn(zoneNames) - 1) +end + +function MTH_BOOK_SplitAbilities(text) + local out = {} + if not text or text == "" or text == "None" then return out end + local startPos = 1 + local textLen = string.len(text) + while startPos <= textLen do + local commaPos = string.find(text, ",", startPos, true) + local token + if commaPos then + token = string.sub(text, startPos, commaPos - 1) + startPos = commaPos + 1 + else + token = string.sub(text, startPos) + startPos = textLen + 1 + end + token = string.gsub(token, "^%s+", "") + token = string.gsub(token, "%s+$", "") + if token ~= "" then + table.insert(out, token) + end + end + return out +end + +function MTH_BOOK_HasUsableAbilities(beast) + if not beast then return false end + local abilities = MTH_BOOK_SplitAbilities(beast.abilities) + if table.getn(abilities) == 0 then return false end + for i = 1, table.getn(abilities) do + local token = MTH_BOOK_SafeLower(abilities[i]) + if token ~= "" and token ~= "none" and token ~= "unknown" then + return true + end + end + return false +end + +function MTH_BOOK_HasKnownLevel(beast) + if not beast then return false end + if MTH_BOOK_IsUnknownText(beast.lvl) then return false end + return MTH_BOOK_ParseLevel(beast.lvl) ~= nil +end + +function MTH_BOOK_HasKnownZone(beast) + if not beast or not beast.coords or table.getn(beast.coords) == 0 then + return false + end + + for i = 1, table.getn(beast.coords) do + local c = beast.coords[i] + if c and c[3] then + local zoneName = MTH_BOOK_SafeLower(MTH_BOOK_GetZoneName(c[3])) + if zoneName ~= "" and zoneName ~= "unknown" and string.sub(zoneName, 1, 5) ~= "zone " then + return true + end + end + end + + return false +end + +function MTH_BOOK_ParseAbilityToken(token) + local text = tostring(token or "") + text = string.gsub(text, "^%s+", "") + text = string.gsub(text, "%s+$", "") + if text == "" then return "", "", nil end + + local _, _, ability, rank = string.find(text, "^(.-)%s*(%d+)$") + if ability and ability ~= "" then + ability = string.gsub(ability, "^%s+", "") + ability = string.gsub(ability, "%s+$", "") + if ability ~= "" then + return MTH_BOOK_SafeLower(ability), ability, tonumber(rank) + end + end + + return MTH_BOOK_SafeLower(text), text, nil +end + +function MTH_BOOK_BeastHasAbilityRank(beast, abilityValue, rankValue) + if not beast then return false end + local wantedAbility = MTH_BOOK_SafeLower(abilityValue) + if wantedAbility == "" or wantedAbility == "all" then return true end + + local wantedRank = tonumber(rankValue) + local abilities = MTH_BOOK_SplitAbilities(beast.abilities) + for i = 1, table.getn(abilities) do + local abilityLower, _, rank = MTH_BOOK_ParseAbilityToken(abilities[i]) + if abilityLower == wantedAbility then + if not wantedRank then + return true + end + if rank and rank == wantedRank then + return true + end + end + end + + return false +end + +function MTH_BOOK_ParseBeastTraits(beast) + local traits = { + rare = beast and beast.rare and true or false, + elite = beast and (beast.elite == true or beast.isElite == true) or false, + unique = beast and (beast.unique == true or beast.isUnique == true) or false, + } + + if not beast then return traits end + + local rank = MTH_BOOK_SafeLower(beast.rank) + if rank ~= "" then + if string.find(rank, "rare", 1, true) then traits.rare = true end + if string.find(rank, "elite", 1, true) then traits.elite = true end + if string.find(rank, "unique", 1, true) then traits.unique = true end + end + + local cls = MTH_BOOK_SafeLower(beast.classification) + if cls ~= "" then + if string.find(cls, "rare", 1, true) then traits.rare = true end + if string.find(cls, "elite", 1, true) then traits.elite = true end + if string.find(cls, "unique", 1, true) then traits.unique = true end + end + + local name = MTH_BOOK_SafeLower(beast.name) + if name ~= "" then + local scanPos = 1 + while true do + local startPos, endPos = string.find(name, "%b()", scanPos) + if not startPos then break end + local token = string.sub(name, startPos, endPos) + scanPos = endPos + 1 + + local inner = string.sub(token, 2, -2) + inner = string.gsub(inner, "^%s+", "") + inner = string.gsub(inner, "%s+$", "") + local normalized = string.gsub(inner, "[%s%-_/]", "") + + if inner == "rare" or normalized == "r" then traits.rare = true end + if inner == "elite" or normalized == "e" then traits.elite = true end + if inner == "unique" or normalized == "u" then traits.unique = true end + + if string.find(inner, "rare", 1, true) then traits.rare = true end + if string.find(inner, "elite", 1, true) then traits.elite = true end + if string.find(inner, "unique", 1, true) then traits.unique = true end + + if normalized ~= "" and string.find(normalized, "[^reu]", 1) == nil then + if string.find(normalized, "r", 1, true) then traits.rare = true end + if string.find(normalized, "e", 1, true) then traits.elite = true end + if string.find(normalized, "u", 1, true) then traits.unique = true end + end + end + end + + return traits +end + +function MTH_BOOK_BeastMatches(beastId, beast) + if not beast then return false end + local forcedBeastId = MTH_BOOK_STATE and tonumber(MTH_BOOK_STATE.forcedBeastId) or nil + if forcedBeastId and tonumber(beastId) ~= forcedBeastId then + return false + end + local beastLevel = MTH_BOOK_ParseLevel(beast.lvl) + local traits = MTH_BOOK_ParseBeastTraits(beast) + local familyLower = MTH_BOOK_SafeLower(beast.family or "") + + if MTH_BOOK_STATE.minLevel and (not beastLevel or beastLevel < MTH_BOOK_STATE.minLevel) then return false end + if MTH_BOOK_STATE.maxLevel and (not beastLevel or beastLevel > MTH_BOOK_STATE.maxLevel) then return false end + + if MTH_BOOK_STATE.flag1 and not traits.rare then return false end + if MTH_BOOK_STATE.flag2 and not traits.elite then return false end + if MTH_BOOK_STATE.flag3 and not traits.unique then return false end + if not MTH_BOOK_BeastHasAbilityRank(beast, MTH_BOOK_STATE.petAbility, MTH_BOOK_STATE.petRank) then return false end + if MTH_BOOK_STATE.petHideNoAbilities and not MTH_BOOK_HasUsableAbilities(beast) then return false end + if MTH_BOOK_STATE.petHideUnknown then + if familyLower == "unknown" then return false end + if not MTH_BOOK_HasKnownLevel(beast) then return false end + if not MTH_BOOK_HasKnownZone(beast) then return false end + end + + if MTH_BOOK_STATE.quick ~= "all" then + if familyLower ~= MTH_BOOK_STATE.quick then + return false + end + end + + if MTH_BOOK_STATE.petInZoneOnly then + local currentZoneId = MTH_BOOK_GetCurrentZoneId() + if currentZoneId and not MTH_BOOK_BeastHasZoneId(beast, currentZoneId) then + return false + end + end + + if MTH_BOOK_STATE.search ~= "" then + local name = MTH_BOOK_SafeLower(beast.name) + local family = MTH_BOOK_SafeLower(beast.family) + local idText = tostring(beastId) + if string.find(name, MTH_BOOK_STATE.search, 1, true) == nil + and string.find(family, MTH_BOOK_STATE.search, 1, true) == nil + and string.find(idText, MTH_BOOK_STATE.search, 1, true) == nil + then + return false + end + end + + return true +end + +function MTH_BOOK_BeastSort(a, b) + local beasts = MTH_DS_Beasts + local ba = beasts and beasts[a] + local bb = beasts and beasts[b] + if not ba or not bb then return a < b end + local la = MTH_BOOK_ParseLevel(ba.lvl) or 0 + local lb = MTH_BOOK_ParseLevel(bb.lvl) or 0 + if la ~= lb then return la < lb end + local na = ba.name or "" + local nb = bb.name or "" + if na ~= nb then return na < nb end + return a < b +end + +local function MTH_BOOKTAB_BeastLoreTrim(value) + local text = tostring(value or "") + text = string.gsub(text, "^%s+", "") + text = string.gsub(text, "%s+$", "") + return text +end + +local function MTH_BOOKTAB_BeastLoreFindAbilityBundle(abilityName) + if not (MTH_DS_PetSpells and type(MTH_DS_PetSpells.byAbility) == "table") then + return nil + end + local direct = MTH_DS_PetSpells.byAbility[abilityName] + if type(direct) == "table" then + return direct + end + local wanted = MTH_BOOK_SafeLower(abilityName) + for key, bundle in pairs(MTH_DS_PetSpells.byAbility) do + if MTH_BOOK_SafeLower(key) == wanted then + return bundle + end + end + return nil +end + +function MTH_BOOKTAB_BuildBeastAbilityEntries(beast) + local entries = {} + if type(beast) ~= "table" then + return entries + end + + local tokens = MTH_BOOK_SplitAbilities(beast.abilities) + local seen = {} + for i = 1, table.getn(tokens) do + local rawToken = MTH_BOOKTAB_BeastLoreTrim(tokens[i]) + if rawToken ~= "" then + local tokenKey = MTH_BOOK_SafeLower(rawToken) + if tokenKey ~= "" and not seen[tokenKey] then + seen[tokenKey] = true + + local _, abilityLabel, requestedRank = MTH_BOOK_ParseAbilityToken(rawToken) + local bundle = MTH_BOOKTAB_BeastLoreFindAbilityBundle(abilityLabel) + local canonical = abilityLabel + if type(bundle) == "table" and type(bundle.ability) == "string" and bundle.ability ~= "" then + canonical = bundle.ability + end + + local spellRows = (type(bundle) == "table" and type(bundle.spells) == "table") and bundle.spells or {} + local preferredSpell = nil + local firstBeastSpell = nil + local rankMin = nil + local rankMax = nil + local rankMap = {} + for s = 1, table.getn(spellRows) do + local spell = spellRows[s] + if type(spell) == "table" and MTH_BOOK_SafeLower(spell.learnMethod or "beast") ~= "trainer" then + if not firstBeastSpell then + firstBeastSpell = spell + end + local rank = tonumber(spell.rankNumber) + if rank and rank > 0 then + rankMap[rank] = true + if not rankMin or rank < rankMin then rankMin = rank end + if not rankMax or rank > rankMax then rankMax = rank end + if requestedRank and requestedRank == rank then + preferredSpell = spell + end + end + end + end + + local rankCount = 0 + for _ in pairs(rankMap) do + rankCount = rankCount + 1 + end + + if not preferredSpell then + preferredSpell = firstBeastSpell + end + + local displayName = canonical + if requestedRank then + displayName = canonical .. " " .. tostring(requestedRank) + end + + table.insert(entries, { + name = displayName, + tooltipName = canonical, + requestedRank = requestedRank, + icon = preferredSpell and preferredSpell.icon or nil, + description = preferredSpell and preferredSpell.description or "", + rankMin = rankMin, + rankMax = rankMax, + rankCount = rankCount, + }) + end + end + end + + return entries +end + +function MTH_BOOKTAB_ShowBeastAbilityTooltip(anchor, abilityEntry) + if not anchor or type(abilityEntry) ~= "table" then return end + local tooltip = MTH_BOOK_GetSpellTooltip and MTH_BOOK_GetSpellTooltip() or GameTooltip + if not tooltip then return end + + tooltip:SetOwner(anchor, "ANCHOR_RIGHT") + tooltip:ClearLines() + if MTH_BOOK_SetSpellTooltipIcon then + MTH_BOOK_SetSpellTooltipIcon(tooltip, abilityEntry.icon) + end + local title = tostring(abilityEntry.tooltipName or abilityEntry.name or "Unknown") + if abilityEntry.requestedRank then + title = title .. " " .. tostring(abilityEntry.requestedRank) + end + tooltip:AddLine(title, 1.00, 0.82, 0.00) + if abilityEntry.description and abilityEntry.description ~= "" then + tooltip:AddLine(" ") + tooltip:AddLine(tostring(abilityEntry.description), 0.90, 0.90, 0.90, true) + end + tooltip:Show() +end diff --git a/api/hunterbook-tab-families.lua b/api/hunterbook-tab-families.lua new file mode 100644 index 0000000..95c5222 --- /dev/null +++ b/api/hunterbook-tab-families.lua @@ -0,0 +1,398 @@ +if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end + +MTH_HUNTERBOOK_TABS.families = { + headerLabel = "Families", + columnLabels = { "Family", "Named", "Coords", "Abilities", "Diet" }, + columnLayout = { + { x = 10, width = 106, align = "LEFT" }, + { x = 122, width = 52, align = "LEFT" }, + { x = 176, width = 52, align = "LEFT" }, + { x = 232, width = 292, align = "LEFT" }, + { x = 460, width = 184, align = "LEFT" }, + }, +} + +local function MTH_BOOKTAB_FamiliesTrace(message) + return +end + +local function MTH_BOOKTAB_FamiliesSafeLower(value) + return string.lower(tostring(value or "")) +end + +local function MTH_BOOKTAB_FamiliesTrim(value) + local text = tostring(value or "") + text = string.gsub(text, "^%s+", "") + text = string.gsub(text, "%s+$", "") + return text +end + +local function MTH_BOOKTAB_FindAbilityBundle(abilityName) + if not (MTH_DS_PetSpells and type(MTH_DS_PetSpells.byAbility) == "table") then + return nil + end + local direct = MTH_DS_PetSpells.byAbility[abilityName] + if type(direct) == "table" then + return direct + end + local wanted = MTH_BOOKTAB_FamiliesSafeLower(abilityName) + for key, bundle in pairs(MTH_DS_PetSpells.byAbility) do + if MTH_BOOKTAB_FamiliesSafeLower(key) == wanted then + return bundle + end + end + return nil +end + +function MTH_BOOKTAB_BuildFamiliesRows() + local results = {} + local families = MTH_DS_Families + if type(families) ~= "table" then + return results + end + + local abilityCounts = {} + local canonicalMap = {} + + for _, familyRow in pairs(families) do + if type(familyRow) == "table" and type(familyRow.abilities) == "table" then + local seenInFamily = {} + for i = 1, table.getn(familyRow.abilities) do + local rawName = MTH_BOOKTAB_FamiliesTrim(familyRow.abilities[i]) + if rawName ~= "" then + local bundle = MTH_BOOKTAB_FindAbilityBundle(rawName) + local canonical = rawName + if type(bundle) == "table" and type(bundle.ability) == "string" and bundle.ability ~= "" then + canonical = bundle.ability + end + local token = MTH_BOOKTAB_FamiliesSafeLower(canonical) + if token ~= "" and not seenInFamily[token] then + seenInFamily[token] = true + abilityCounts[token] = (abilityCounts[token] or 0) + 1 + canonicalMap[token] = canonical + end + end + end + end + end + + for familyName, familyRow in pairs(families) do + if type(familyRow) == "table" then + local abilities = {} + local seen = {} + + if type(familyRow.abilities) == "table" then + for i = 1, table.getn(familyRow.abilities) do + local rawName = MTH_BOOKTAB_FamiliesTrim(familyRow.abilities[i]) + if rawName ~= "" then + local bundle = MTH_BOOKTAB_FindAbilityBundle(rawName) + local canonical = rawName + if type(bundle) == "table" and type(bundle.ability) == "string" and bundle.ability ~= "" then + canonical = bundle.ability + end + + local token = MTH_BOOKTAB_FamiliesSafeLower(canonical) + if token ~= "" and not seen[token] then + seen[token] = true + local spellRows = (type(bundle) == "table" and type(bundle.spells) == "table") and bundle.spells or {} + local firstBeastSpell = nil + local rankMin = nil + local rankMax = nil + local rankMap = {} + for s = 1, table.getn(spellRows) do + local spell = spellRows[s] + if type(spell) == "table" and MTH_BOOKTAB_FamiliesSafeLower(spell.learnMethod or "beast") ~= "trainer" then + if not firstBeastSpell then + firstBeastSpell = spell + end + local rank = tonumber(spell.rankNumber) + if rank and rank > 0 then + rankMap[rank] = true + if not rankMin or rank < rankMin then rankMin = rank end + if not rankMax or rank > rankMax then rankMax = rank end + end + end + end + + local rankCount = 0 + for _ in pairs(rankMap) do + rankCount = rankCount + 1 + end + + table.insert(abilities, { + name = canonicalMap[token] or canonical, + token = token, + isUnique = (abilityCounts[token] or 0) == 1, + icon = firstBeastSpell and firstBeastSpell.icon or nil, + description = firstBeastSpell and firstBeastSpell.description or "", + rankMin = rankMin, + rankMax = rankMax, + rankCount = rankCount, + }) + end + end + end + end + + table.sort(abilities, function(a, b) + if a.isUnique ~= b.isUnique then + return a.isUnique and true or false + end + return MTH_BOOKTAB_FamiliesSafeLower(a.name) < MTH_BOOKTAB_FamiliesSafeLower(b.name) + end) + + local dietItems = {} + if type(familyRow.food) == "table" then + for i = 1, table.getn(familyRow.food) do + local food = MTH_BOOKTAB_FamiliesTrim(familyRow.food[i]) + if food ~= "" then + table.insert(dietItems, string.upper(string.sub(food, 1, 1)) .. string.sub(food, 2)) + end + end + table.sort(dietItems, function(a, b) + return MTH_BOOKTAB_FamiliesSafeLower(a) < MTH_BOOKTAB_FamiliesSafeLower(b) + end) + end + + table.insert(results, { + family = tostring(familyName), + named = tonumber(familyRow.named) or 0, + coords = tonumber(familyRow.coords) or 0, + abilities = abilities, + dietText = table.concat(dietItems, ", "), + }) + end + end + + table.sort(results, function(a, b) + return MTH_BOOKTAB_FamiliesSafeLower(a.family) < MTH_BOOKTAB_FamiliesSafeLower(b.family) + end) + + return results +end + +function MTH_BOOKTAB_ShowFamilyAbilityTooltip(anchor, abilityEntry) + if not anchor or type(abilityEntry) ~= "table" then return end + local tooltip = MTH_BOOK_GetSpellTooltip and MTH_BOOK_GetSpellTooltip() or GameTooltip + if not tooltip then return end + + tooltip:SetOwner(anchor, "ANCHOR_RIGHT") + tooltip:ClearLines() + if MTH_BOOK_SetSpellTooltipIcon then + MTH_BOOK_SetSpellTooltipIcon(tooltip, abilityEntry.icon) + end + tooltip:AddLine(tostring(abilityEntry.name or "Unknown"), 1.00, 0.82, 0.00) + tooltip:AddLine(" ") + tooltip:AddLine("Baseline", 1.00, 0.82, 0.00) + if abilityEntry.rankCount and abilityEntry.rankCount > 0 then + if abilityEntry.rankMin and abilityEntry.rankMax then + if abilityEntry.rankMin == abilityEntry.rankMax then + tooltip:AddDoubleLine("Ranks", tostring(abilityEntry.rankMin), 0.85, 0.85, 0.85, 1, 1, 1) + else + tooltip:AddDoubleLine("Ranks", tostring(abilityEntry.rankMin) .. "-" .. tostring(abilityEntry.rankMax), 0.85, 0.85, 0.85, 1, 1, 1) + end + end + else + tooltip:AddDoubleLine("Ranks", "Unranked", 0.85, 0.85, 0.85, 1, 1, 1) + end + tooltip:AddDoubleLine("Family-Unique", abilityEntry.isUnique and "Yes" or "No", 0.85, 0.85, 0.85, 1, 1, 1) + if abilityEntry.description and abilityEntry.description ~= "" then + tooltip:AddLine(" ") + tooltip:AddLine(tostring(abilityEntry.description), 0.90, 0.90, 0.90, true) + end + tooltip:Show() +end + +function MTH_BOOKTAB_EnsureFamiliesUI() + if MTH_BOOK_STATE.familiesUI then return end + local listParent = getglobal("MTH_BOOK_ListBackdrop") + if not listParent then + MTH_BOOKTAB_FamiliesTrace("EnsureFamiliesUI failed: missing MTH_BOOK_ListBackdrop") + return + end + + local ui = {} + ui.frame = CreateFrame("Frame", nil, listParent) + ui.frame:SetPoint("TOPLEFT", listParent, "TOPLEFT", 6, -6) + ui.frame:SetPoint("BOTTOMRIGHT", listParent, "BOTTOMRIGHT", -6, 6) + ui.frame:Hide() + + ui.headerFamily = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.headerFamily:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 4, -2) + ui.headerFamily:SetTextColor(1.00, 0.82, 0.00) + ui.headerFamily:SetText("Family") + + ui.headerNamed = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.headerNamed:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 122, -2) + ui.headerNamed:SetTextColor(1.00, 0.82, 0.00) + ui.headerNamed:SetText("Named") + + ui.headerCoords = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.headerCoords:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 176, -2) + ui.headerCoords:SetTextColor(1.00, 0.82, 0.00) + ui.headerCoords:SetText("Coords") + + ui.headerAbilities = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.headerAbilities:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 232, -2) + ui.headerAbilities:SetTextColor(1.00, 0.82, 0.00) + ui.headerAbilities:SetText("Abilities") + + ui.headerDiet = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.headerDiet:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 460, -2) + ui.headerDiet:SetTextColor(1.00, 0.82, 0.00) + ui.headerDiet:SetText("Diet") + + ui.rows = {} + for i = 1, 28 do + local row = CreateFrame("Frame", nil, ui.frame) + row:SetHeight(18) + if i == 1 then + row:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 0, -18) + row:SetPoint("TOPRIGHT", ui.frame, "TOPRIGHT", 0, -18) + else + row:SetPoint("TOPLEFT", ui.rows[i - 1], "BOTTOMLEFT", 0, -1) + row:SetPoint("TOPRIGHT", ui.rows[i - 1], "BOTTOMRIGHT", 0, -1) + end + + row.family = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + row.family:SetPoint("LEFT", row, "LEFT", 4, 0) + row.family:SetWidth(106) + row.family:SetJustifyH("LEFT") + + row.named = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + row.named:SetPoint("LEFT", row, "LEFT", 122, 0) + row.named:SetWidth(52) + row.named:SetJustifyH("LEFT") + + row.coords = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + row.coords:SetPoint("LEFT", row, "LEFT", 176, 0) + row.coords:SetWidth(52) + row.coords:SetJustifyH("LEFT") + + row.diet = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + row.diet:SetPoint("LEFT", row, "LEFT", 460, 0) + row.diet:SetWidth(184) + row.diet:SetJustifyH("LEFT") + + row.abilityButtons = {} + for b = 1, 16 do + local button = CreateFrame("Button", nil, row) + button:SetHeight(14) + button.icon = button:CreateTexture(nil, "ARTWORK") + button.icon:SetPoint("LEFT", button, "LEFT", 0, 0) + button.icon:SetWidth(12) + button.icon:SetHeight(12) + button.text = button:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + button.text:SetPoint("LEFT", button.icon, "RIGHT", 2, 0) + button.text:SetJustifyH("LEFT") + button.text:SetTextColor(0.90, 0.90, 0.90) + button.entry = nil + button:SetScript("OnEnter", function() + if this and this.entry then + MTH_BOOKTAB_ShowFamilyAbilityTooltip(this, this.entry) + end + end) + button:SetScript("OnLeave", function() + if type(MTH_BOOK_HideSpellTooltip) == "function" then + MTH_BOOK_HideSpellTooltip() + end + end) + button:Hide() + row.abilityButtons[b] = button + end + + row.divider = row:CreateTexture(nil, "BORDER") + row.divider:SetTexture("Interface\\Buttons\\WHITE8X8") + row.divider:SetVertexColor(0.28, 0.28, 0.28, 0.45) + row.divider:SetPoint("BOTTOMLEFT", row, "BOTTOMLEFT", 0, 0) + row.divider:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", 0, 0) + row.divider:SetHeight(1) + + ui.rows[i] = row + end + + MTH_BOOK_STATE.familiesUI = ui + MTH_BOOKTAB_FamiliesTrace("EnsureFamiliesUI created rows=" .. tostring(table.getn(ui.rows or {}))) +end + +function MTH_BOOKTAB_SetFamiliesUIVisible(visible) + local ui = MTH_BOOK_STATE.familiesUI + if visible then + if not ui then + MTH_BOOKTAB_EnsureFamiliesUI() + ui = MTH_BOOK_STATE.familiesUI + end + if not ui then return end + else + if not ui then return end + end + + local listParent = getglobal("MTH_BOOK_ListBackdrop") + local detailParent = getglobal("MTH_BOOK_DetailBackdrop") + local detailText = getglobal("MTH_BOOK_DetailBackdropDetailText") + local openMapButton = getglobal("MTH_BOOK_OpenMapButton") + + if visible then + if listParent then listParent:Show() end + if detailParent then detailParent:Hide() end + if detailText then detailText:Hide() end + if openMapButton then openMapButton:Hide() end + ui.frame:Show() + else + ui.frame:Hide() + if listParent then listParent:Show() end + if detailParent then detailParent:Show() end + if detailText then detailText:Show() end + end +end + +function MTH_BOOKTAB_RenderFamiliesList() + local ui = MTH_BOOK_STATE.familiesUI + if not ui then return end + local rows = MTH_BOOK_STATE.results or {} + MTH_BOOKTAB_FamiliesTrace("RenderFamiliesList rows=" .. tostring(table.getn(rows))) + + for i = 1, table.getn(ui.rows) do + local rowFrame = ui.rows[i] + local row = rows[i] + if row then + rowFrame:Show() + rowFrame.family:SetText(tostring(row.family or "-")) + rowFrame.named:SetText(tostring(row.named or 0)) + rowFrame.coords:SetText(tostring(row.coords or 0)) + rowFrame.diet:SetText(tostring(row.dietText or "-")) + + local offsetX = 232 + for b = 1, table.getn(rowFrame.abilityButtons) do + local button = rowFrame.abilityButtons[b] + local ability = row.abilities and row.abilities[b] or nil + if ability then + local nameText = tostring(ability.name or "") + if b < table.getn(row.abilities) then + nameText = nameText .. "," + end + button.entry = ability + button.text:SetText(nameText) + local textW = button.text:GetStringWidth() or 40 + if textW < 24 then textW = 24 end + button:SetWidth(14 + textW) + button:ClearAllPoints() + button:SetPoint("LEFT", rowFrame, "LEFT", offsetX, 0) + local iconPath = ability.icon and MTH_BOOK_ResolveIconPath and MTH_BOOK_ResolveIconPath(ability.icon) or nil + if iconPath then + button.icon:SetTexture(iconPath) + else + button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") + end + button:Show() + offsetX = offsetX + button:GetWidth() + 6 + else + button.entry = nil + button:Hide() + end + end + else + rowFrame:Hide() + end + end +end \ No newline at end of file diff --git a/api/hunterbook-tab-npcfinder.lua b/api/hunterbook-tab-npcfinder.lua new file mode 100644 index 0000000..1803e9c --- /dev/null +++ b/api/hunterbook-tab-npcfinder.lua @@ -0,0 +1,189 @@ +if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end + +MTH_HUNTERBOOK_TABS.npcs = { + headerLabel = "NPC Finder", + columnLabels = { "ID", "Name", "React", "Function", "Zone" }, + columnLayout = { + { x = 8, width = 52, align = "LEFT" }, + { x = 60, width = 140, align = "LEFT" }, + { x = 200, width = 40, align = "LEFT" }, + { x = 242, width = 160, align = "LEFT" }, + { x = 404, width = 148, align = "LEFT" }, + }, +} + +function MTH_BOOKTAB_GetNPCReactBucket(fac) + local f = tostring(fac or "") + if f == "A" then return "A" end + if f == "H" then return "H" end + return "AH" +end +MTH_BOOK_GetNPCReactBucket = MTH_BOOKTAB_GetNPCReactBucket + +function MTH_BOOKTAB_GetNPCFunctionLabel(key) + local map = { + vendor = "Ammo Vendor", + repair = "Repair", + stablemaster = "Stable Master", + huntertrainer = "Hunter Trainer", + pettrainer = "Pet Trainer", + } + return map[key] or (string.upper(string.sub(key, 1, 1)) .. string.sub(key, 2)) +end +MTH_BOOK_GetNPCFunctionLabel = MTH_BOOKTAB_GetNPCFunctionLabel + +function MTH_BOOKTAB_GetNPCFunctions(vendor) + local out = {} + local seen = {} + local meta = vendor and vendor.meta + if meta then + for key, value in pairs(meta) do + if type(key) == "string" and value and tostring(value) ~= "" then + local normalizedKey = MTH_BOOK_SafeLower(key) + if normalizedKey ~= "" and not seen[normalizedKey] then + seen[normalizedKey] = true + table.insert(out, normalizedKey) + end + end + end + end + table.sort(out, function(a, b) + return MTH_BOOK_SafeLower(MTH_BOOKTAB_GetNPCFunctionLabel(a)) < MTH_BOOK_SafeLower(MTH_BOOKTAB_GetNPCFunctionLabel(b)) + end) + return out +end +MTH_BOOK_GetNPCFunctions = MTH_BOOKTAB_GetNPCFunctions + +function MTH_BOOKTAB_GetNPCFunctionSummary(vendor) + local keys = MTH_BOOKTAB_GetNPCFunctions(vendor) + if table.getn(keys) == 0 then return "-", {} end + local labels = {} + for i = 1, table.getn(keys) do + table.insert(labels, MTH_BOOKTAB_GetNPCFunctionLabel(keys[i])) + end + return table.concat(labels, ", "), keys +end +MTH_BOOK_GetNPCFunctionSummary = MTH_BOOKTAB_GetNPCFunctionSummary + +local function MTH_BOOKTAB_GetZoneAndSubzone(zoneId) + local zid = tonumber(zoneId) + if not zid then return "-", "-" end + local zoneName = MTH_BOOK_GetZoneName(zid) + local row = MTH_DS_Zones and (MTH_DS_Zones[zid] or MTH_DS_Zones[tostring(zid)]) + if row and row.parent and tonumber(row.parent) and tonumber(row.parent) ~= zid then + local parentName = MTH_BOOK_GetZoneName(row.parent) + if parentName and string.sub(parentName, 1, 5) ~= "Zone " then + return parentName, zoneName + end + end + return zoneName, "-" +end + +function MTH_BOOKTAB_GetNPCZoneSummary(vendor) + if not vendor or not vendor.coords or table.getn(vendor.coords) == 0 then + return "-", "-", {} + end + local zones = {} + local zoneSet = {} + local firstZone = "-" + local firstSubzone = "-" + for i = 1, table.getn(vendor.coords) do + local c = vendor.coords[i] + if c and c[3] then + local zoneName, subzoneName = MTH_BOOKTAB_GetZoneAndSubzone(c[3]) + if firstZone == "-" then + firstZone = zoneName + firstSubzone = subzoneName + end + if zoneName ~= "-" and not zoneSet[zoneName] then + zoneSet[zoneName] = true + table.insert(zones, zoneName) + end + end + end + return firstZone, firstSubzone, zones +end +MTH_BOOK_GetNPCZoneSummary = MTH_BOOKTAB_GetNPCZoneSummary + +function MTH_BOOK_NPCHasZoneId(vendor, zoneId) + local zid = tonumber(zoneId) + if not zid or not vendor or not vendor.coords then + return false + end + for i = 1, table.getn(vendor.coords) do + local c = vendor.coords[i] + if c and tonumber(c[3]) == zid then + return true + end + end + return false +end + +function MTH_BOOK_NPCMatches(npcId, vendor) + if not vendor then return false end + local react = MTH_BOOK_GetNPCReactBucket(vendor.fac) + if react == "A" and not MTH_BOOK_STATE.flag1 then return false end + if react == "H" and not MTH_BOOK_STATE.flag2 then return false end + if react == "AH" and not MTH_BOOK_STATE.flag3 then return false end + + if MTH_BOOK_STATE.npcHideNoZone then + local firstZone = MTH_BOOK_GetNPCZoneSummary(vendor) + local zoneText = MTH_BOOK_SafeLower(firstZone) + if firstZone == "-" or zoneText == "" or zoneText == "unknown" or string.sub(zoneText, 1, 5) == "zone " then + return false + end + end + + if MTH_BOOK_STATE.npcFunction and MTH_BOOK_STATE.npcFunction ~= "all" then + local hasFunction = false + local functions = MTH_BOOK_GetNPCFunctions(vendor) + for i = 1, table.getn(functions) do + if functions[i] == MTH_BOOK_STATE.npcFunction then + hasFunction = true + break + end + end + if not hasFunction then return false end + end + + if MTH_BOOK_STATE.npcZone and MTH_BOOK_STATE.npcZone ~= "all" then + local _, _, zones = MTH_BOOK_GetNPCZoneSummary(vendor) + local hasZone = false + for i = 1, table.getn(zones) do + if zones[i] == MTH_BOOK_STATE.npcZone then + hasZone = true + break + end + end + if not hasZone then return false end + end + + if MTH_BOOK_STATE.npcInZoneOnly then + local currentZoneId = MTH_BOOK_GetCurrentZoneId() + if currentZoneId and not MTH_BOOK_NPCHasZoneId(vendor, currentZoneId) then + return false + end + end + + if MTH_BOOK_STATE.search ~= "" then + local functionSummary = MTH_BOOK_GetNPCFunctionSummary(vendor) + local firstZone, firstSubzone = MTH_BOOK_GetNPCZoneSummary(vendor) + local hay = MTH_BOOK_SafeLower(tostring(vendor.name or "") .. " " .. tostring(functionSummary or "") .. " " .. tostring(firstZone or "") .. " " .. tostring(firstSubzone or "") .. " " .. tostring(npcId)) + if string.find(hay, MTH_BOOK_STATE.search, 1, true) == nil then + return false + end + end + + return true +end + +function MTH_BOOK_NPCSort(a, b) + local vendors = MTH_DS_Vendors + local va = vendors and vendors[a] + local vb = vendors and vendors[b] + if not va or not vb then return a < b end + local na = MTH_BOOK_SafeLower(va.name) + local nb = MTH_BOOK_SafeLower(vb.name) + if na ~= nb then return na < nb end + return a < b +end diff --git a/api/hunterbook-tab-petabilities.lua b/api/hunterbook-tab-petabilities.lua new file mode 100644 index 0000000..a2d416e --- /dev/null +++ b/api/hunterbook-tab-petabilities.lua @@ -0,0 +1,903 @@ +if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end + +MTH_HUNTERBOOK_TABS.petabilities = { + headerLabel = "Pet Abilities", + columnLabels = { "Ability Name", "Ranks", "Families" }, + columnLayout = { + { x = 8, width = 160, align = "LEFT" }, + { x = 170, width = 52, align = "LEFT" }, + { x = 224, width = 328, align = "LEFT" }, + }, +} + +MTH_HUNTERBOOK_TABS.abilities = { + headerLabel = "Pet Abilities", + columnLabels = { "Ability", "Lvl", "Family", "Beast", "Rare" }, + columnLayout = { + { x = 8, width = 112, align = "LEFT" }, + { x = 120, width = 40, align = "LEFT" }, + { x = 160, width = 78, align = "LEFT" }, + { x = 238, width = 142, align = "LEFT" }, + { x = 380, width = 38, align = "LEFT" }, + }, +} + +function MTH_BOOK_AbilitySort(a, b) + if a.ability ~= b.ability then return a.ability < b.ability end + if a.level ~= b.level then return a.level < b.level end + if a.name ~= b.name then return a.name < b.name end + return a.beastId < b.beastId +end + +function MTH_BOOK_PetAbilitySort(a, b) + local aa = MTH_BOOK_SafeLower(a and a.ability) + local ab = MTH_BOOK_SafeLower(b and b.ability) + if aa ~= ab then return aa < ab end + local ra = tonumber(a and a.rankCount) or 0 + local rb = tonumber(b and b.rankCount) or 0 + if ra ~= rb then return ra > rb end + local fa = tonumber(a and a.familyCount) or 0 + local fb = tonumber(b and b.familyCount) or 0 + if fa ~= fb then return fa > fb end + return tostring(a and a.familyList or "") < tostring(b and b.familyList or "") +end + +function MTH_BOOK_GetSpellRowsForAbility(abilityName) + if not abilityName or abilityName == "" then return 0 end + if not (MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility) then return 0 end + local row = MTH_DS_PetSpells.byAbility[abilityName] + if not row or not row.spells then return 0 end + return table.getn(row.spells) +end + +function MTH_BOOK_GetSpellCoverageForAbility(abilityName) + local coverage = { + ability = abilityName, + icon = nil, + spellRows = 0, + hasBeastLearned = false, + hasTrainerLearned = false, + rankCount = 0, + minRank = nil, + maxRank = nil, + minTrainLevel = nil, + maxTrainLevel = nil, + } + + if not abilityName or abilityName == "" then return coverage end + if not (MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility) then return coverage end + + local row = MTH_DS_PetSpells.byAbility[abilityName] + if not row or not row.spells then return coverage end + + local rankMap = {} + local hasPositiveRank = false + for i = 1, table.getn(row.spells) do + local spell = row.spells[i] + if spell then + local rankNumber = tonumber(spell.rankNumber) + if rankNumber and rankNumber > 0 then + hasPositiveRank = true + break + end + end + end + + coverage.spellRows = 0 + for i = 1, table.getn(row.spells) do + local spell = row.spells[i] + if spell then + local rankNumber = tonumber(spell.rankNumber) + if hasPositiveRank and ((not rankNumber) or rankNumber <= 0) then + -- Ignore legacy/unranked rows when proper ranked variants exist. + else + coverage.spellRows = coverage.spellRows + 1 + local learnMethod = MTH_BOOK_SafeLower(spell.learnMethod) + if learnMethod == "trainer" then + coverage.hasTrainerLearned = true + else + coverage.hasBeastLearned = true + end + if not coverage.icon and spell.icon and spell.icon ~= "" then + coverage.icon = spell.icon + end + if rankNumber and rankNumber > 0 then + rankMap[rankNumber] = true + if not coverage.minRank or rankNumber < coverage.minRank then coverage.minRank = rankNumber end + if not coverage.maxRank or rankNumber > coverage.maxRank then coverage.maxRank = rankNumber end + end + + local trainLevel = tonumber(spell.trainLevel) + if trainLevel then + if not coverage.minTrainLevel or trainLevel < coverage.minTrainLevel then coverage.minTrainLevel = trainLevel end + if not coverage.maxTrainLevel or trainLevel > coverage.maxTrainLevel then coverage.maxTrainLevel = trainLevel end + end + end + end + end + + for _ in pairs(rankMap) do + coverage.rankCount = coverage.rankCount + 1 + end + if coverage.rankCount == 0 and coverage.spellRows > 0 then + coverage.rankCount = 1 + end + + return coverage +end + +function MTH_BOOK_GetKnownSpellMap() + if not MTH_CharSavedVariables then return nil end + if not MTH_CharSavedVariables.petTraining then return nil end + local petTraining = MTH_CharSavedVariables.petTraining + if petTraining.hunterKnownMap and petTraining.spellMap then + for token, isKnown in pairs(petTraining.hunterKnownMap) do + if isKnown == true then + local _, _, baseToken = string.find(tostring(token), "^(.-)#%d+$") + if baseToken and baseToken ~= "" then + local baseRow = petTraining.spellMap[baseToken] + if baseRow and ((tonumber(baseRow.rankNumber) or 0) <= 0) then + baseRow.isKnown = true + end + end + end + end + end + return MTH_CharSavedVariables.petTraining.spellMap +end + +function MTH_BOOK_GetKnownRankCountForAbility(abilityName) + local spellMap = MTH_BOOK_GetKnownSpellMap() + if not spellMap or not abilityName or abilityName == "" then return 0 end + + local abilityLower = MTH_BOOK_SafeLower(abilityName) + if abilityLower == "" then return 0 end + + local knownRanks = {} + for _, row in pairs(spellMap) do + if row and row.name and (row.isKnown ~= false) then + if MTH_BOOK_SafeLower(row.name) == abilityLower then + local rankNumber = tonumber(row.rankNumber) or 0 + knownRanks[rankNumber] = true + end + end + end + + local count = 0 + for _ in pairs(knownRanks) do + count = count + 1 + end + return count +end + +function MTH_BOOK_IsKnownRankForAbility(abilityName, rankNumber) + local spellMap = MTH_BOOK_GetKnownSpellMap() + if not spellMap then return false end + if not abilityName or abilityName == "" then return false end + local rankValue = tonumber(rankNumber) + + local abilityLower = MTH_BOOK_SafeLower(abilityName) + for _, row in pairs(spellMap) do + if row and row.name and (row.isKnown ~= false) then + if MTH_BOOK_SafeLower(row.name) == abilityLower then + local rowRank = tonumber(row.rankNumber) + if rankValue and rankValue > 0 then + if rowRank == rankValue then + return true + end + else + return true + end + end + end + end + + return false +end + +function MTH_BOOK_BuildPetAbilitiesRows(ignoreSearch) + local spellRows = {} + + if MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility then + for abilityName in pairs(MTH_DS_PetSpells.byAbility) do + local ability = tostring(abilityName or "") + if ability ~= "" then + local key = MTH_BOOK_SafeLower(ability) + if key ~= "" then + spellRows[key] = { + ability = ability, + familiesMap = {}, + } + end + end + end + end + + local function ensureRow(abilityName) + local ability = tostring(abilityName or "") + if ability == "" then return nil end + local key = MTH_BOOK_SafeLower(ability) + if key == "" then return nil end + return spellRows[key], key + end + + if MTH_DS_Families then + for familyName, row in pairs(MTH_DS_Families) do + if familyName and familyName ~= "" and row and row.abilities then + for i = 1, table.getn(row.abilities) do + local ability = row.abilities[i] + local spellRow = ensureRow(ability) + if spellRow then + spellRow.familiesMap[familyName] = true + end + end + end + end + end + + if MTH_DS_Beasts then + for _, beast in pairs(MTH_DS_Beasts) do + if beast and beast.family and beast.family ~= "" then + local tokens = MTH_BOOK_SplitAbilities(beast.abilities) + for i = 1, table.getn(tokens) do + local _, abilityName = MTH_BOOK_ParseAbilityToken(tokens[i]) + local spellRow = ensureRow(abilityName) + if spellRow then + spellRow.familiesMap[beast.family] = true + end + end + end + end + end + + local results = {} + for _, row in pairs(spellRows) do + local coverage = MTH_BOOK_GetSpellCoverageForAbility(row.ability) + local families = {} + for familyName in pairs(row.familiesMap or {}) do + table.insert(families, familyName) + end + table.sort(families, function(a, b) + return MTH_BOOK_SafeLower(a) < MTH_BOOK_SafeLower(b) + end) + + local rankCount = tonumber(coverage.rankCount) or 0 + local knownRankCount = MTH_BOOK_GetKnownRankCountForAbility(row.ability) + local minRank = coverage.minRank + local maxRank = coverage.maxRank + local minTrainLevel = coverage.minTrainLevel + local maxTrainLevel = coverage.maxTrainLevel + if MTH_BOOK_STATE.petOnlyMyLevel then + knownRankCount, rankCount, minRank, maxRank, minTrainLevel, maxTrainLevel = MTH_BOOK_GetScopedRankSummary(row.ability) + end + if knownRankCount > rankCount then knownRankCount = rankCount end + local rankText = tostring(knownRankCount) .. "/" .. tostring(rankCount) + + local sourceFilter = tostring(MTH_BOOK_STATE.petLearnSource or "all") + local sourceMatch = true + if sourceFilter == "beast" then + sourceMatch = coverage.hasBeastLearned and true or false + elseif sourceFilter == "trainer" then + sourceMatch = coverage.hasTrainerLearned and true or false + end + + local familyList = table.concat(families, ", ") + local searchable = MTH_BOOK_SafeLower(tostring(row.ability or "") .. " " .. familyList) + if sourceMatch and rankCount > 0 and (ignoreSearch or MTH_BOOK_STATE.search == "" or string.find(searchable, MTH_BOOK_STATE.search, 1, true) ~= nil) then + table.insert(results, { + ability = row.ability, + icon = coverage.icon, + knownRankCount = knownRankCount, + rankCount = rankCount, + rankText = rankText, + families = families, + familyCount = table.getn(families), + familyList = familyList, + spellRows = coverage.spellRows or 0, + minRank = minRank, + maxRank = maxRank, + minTrainLevel = minTrainLevel, + maxTrainLevel = maxTrainLevel, + hasBeastLearned = coverage.hasBeastLearned, + hasTrainerLearned = coverage.hasTrainerLearned, + }) + end + end + + table.sort(results, MTH_BOOK_PetAbilitySort) + return results +end + +function MTH_BOOK_BuildPetAbilityRankRows(entry) + local rows = {} + if not entry or not entry.ability then return rows end + if not (MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility) then return rows end + + local spellBucket = MTH_DS_PetSpells.byAbility[entry.ability] + if not spellBucket or not spellBucket.spells then return rows end + + local hasPositiveRank = false + for i = 1, table.getn(spellBucket.spells) do + local spell = spellBucket.spells[i] + if spell then + local rankNumber = tonumber(spell.rankNumber) + if rankNumber and rankNumber > 0 then + hasPositiveRank = true + break + end + end + end + + for i = 1, table.getn(spellBucket.spells) do + local spell = spellBucket.spells[i] + if spell then + local rankNumber = tonumber(spell.rankNumber) + if not rankNumber and spell.rank then + local _, _, parsed = string.find(tostring(spell.rank), "(%d+)") + rankNumber = tonumber(parsed) + end + if hasPositiveRank and ((not rankNumber) or rankNumber <= 0) then + -- Ignore legacy/unranked rows when proper ranked variants exist. + elseif MTH_BOOK_IsSpellInLevelScope(tonumber(spell.trainLevel)) then + table.insert(rows, { + id = spell.id, + ability = entry.ability, + icon = spell.icon or spell.iconName or spell.texture or spell.iconTexture or entry.icon, + rankNumber = rankNumber, + isKnown = MTH_BOOK_IsKnownRankForAbility(entry.ability, rankNumber), + trainLevel = tonumber(spell.trainLevel), + description = spell.description, + cost = spell.cost, + castTime = spell.castTime, + range = spell.range, + school = spell.school, + effects = spell.effects, + sourceUrl = spell.sourceUrl, + }) + end + end + end + + table.sort(rows, function(a, b) + local ra = a.rankNumber or 0 + local rb = b.rankNumber or 0 + if ra ~= rb then return ra < rb end + local la = a.trainLevel or 0 + local lb = b.trainLevel or 0 + if la ~= lb then return la < lb end + return (tonumber(a.id) or 0) < (tonumber(b.id) or 0) + end) + + return rows +end + +function MTH_BOOK_GetRankLabel(rankNumber, includePrefix) + local rankValue = tonumber(rankNumber) + if rankValue and rankValue > 0 then + if includePrefix then + return "Rank " .. tostring(rankValue) + end + return tostring(rankValue) + end + if includePrefix then + return "Unranked" + end + return "None" +end + +function MTH_BOOKTAB_EnsurePetAbilitiesUI(listParent, detailParent) + if MTH_BOOK_STATE.petUI then return end + if not listParent or not detailParent then return end + + local ui = {} + + ui.leftPane = CreateFrame("Frame", nil, listParent) + ui.leftPane:SetPoint("TOPLEFT", listParent, "TOPLEFT", 4, -4) + ui.leftPane:SetWidth(162) + ui.leftPane:SetHeight(346) + + ui.rightPane = CreateFrame("Frame", nil, listParent) + ui.rightPane:SetPoint("TOPLEFT", ui.leftPane, "TOPRIGHT", 8, 0) + ui.rightPane:SetWidth(390) + ui.rightPane:SetHeight(346) + + ui.vSplit = listParent:CreateTexture(nil, "BORDER") + ui.vSplit:SetTexture("Interface\\Buttons\\WHITE8X8") + ui.vSplit:SetVertexColor(0.30, 0.30, 0.30, 0.60) + ui.vSplit:SetPoint("TOPLEFT", ui.leftPane, "TOPRIGHT", 4, -2) + ui.vSplit:SetPoint("BOTTOMLEFT", ui.leftPane, "BOTTOMRIGHT", 4, 2) + ui.vSplit:SetWidth(1) + + ui.leftHeaderAbility = ui.leftPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.leftHeaderAbility:SetPoint("TOPLEFT", ui.leftPane, "TOPLEFT", 24, -6) + ui.leftHeaderAbility:SetWidth(102) + ui.leftHeaderAbility:SetJustifyH("LEFT") + ui.leftHeaderAbility:SetTextColor(1.00, 0.82, 0.00) + ui.leftHeaderAbility:SetText("Ability") + + ui.leftHeaderRank = ui.leftPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.leftHeaderRank:SetPoint("TOPLEFT", ui.leftPane, "TOPLEFT", 126, -6) + ui.leftHeaderRank:SetWidth(28) + ui.leftHeaderRank:SetJustifyH("LEFT") + ui.leftHeaderRank:SetTextColor(1.00, 0.82, 0.00) + ui.leftHeaderRank:SetText("K/T") + + ui.rightHeaderRank = ui.rightPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.rightHeaderRank:SetPoint("TOPLEFT", ui.rightPane, "TOPLEFT", 2, -6) + ui.rightHeaderRank:SetWidth(36) + ui.rightHeaderRank:SetJustifyH("LEFT") + ui.rightHeaderRank:SetTextColor(1.00, 0.82, 0.00) + ui.rightHeaderRank:SetText("Rank") + + ui.rightHeaderLevel = ui.rightPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.rightHeaderKnown = ui.rightPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.rightHeaderKnown:SetPoint("TOPLEFT", ui.rightPane, "TOPLEFT", 34, -6) + ui.rightHeaderKnown:SetWidth(22) + ui.rightHeaderKnown:SetJustifyH("LEFT") + ui.rightHeaderKnown:SetTextColor(1.00, 0.82, 0.00) + ui.rightHeaderKnown:SetText("K?") + + ui.rightHeaderLevel:SetPoint("TOPLEFT", ui.rightPane, "TOPLEFT", 58, -6) + ui.rightHeaderLevel:SetWidth(40) + ui.rightHeaderLevel:SetJustifyH("LEFT") + ui.rightHeaderLevel:SetTextColor(1.00, 0.82, 0.00) + ui.rightHeaderLevel:SetText("Lvl") + + ui.rightHeaderDesc = ui.rightPane:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.rightHeaderDesc:SetPoint("TOPLEFT", ui.rightPane, "TOPLEFT", 98, -6) + ui.rightHeaderDesc:SetWidth(286) + ui.rightHeaderDesc:SetJustifyH("LEFT") + ui.rightHeaderDesc:SetTextColor(1.00, 0.82, 0.00) + ui.rightHeaderDesc:SetText("Description") + + ui.leftRowsVisible = 20 + + ui.leftScroll = CreateFrame("Slider", "MTH_BOOK_PetLeftScroll", ui.leftPane, "UIPanelScrollBarTemplate") + ui.leftScroll:SetPoint("TOPRIGHT", ui.leftPane, "TOPRIGHT", -2, -22) + ui.leftScroll:SetPoint("BOTTOMRIGHT", ui.leftPane, "BOTTOMRIGHT", -2, 20) + ui.leftScroll:SetWidth(16) + if not getglobal("MTH_BOOK_PetLeftScrollScrollFrame") then + local leftScrollShim = CreateFrame("ScrollFrame", "MTH_BOOK_PetLeftScrollScrollFrame", ui.leftPane) + leftScrollShim:SetPoint("TOPLEFT", ui.leftPane, "TOPLEFT", 0, 0) + leftScrollShim:SetWidth(1) + leftScrollShim:SetHeight(1) + leftScrollShim:Hide() + end + ui.leftScroll:SetScript("OnValueChanged", function() + local val = this:GetValue() or 0 + val = math.floor(val + 0.5) + if val ~= MTH_BOOK_STATE.petLeftOffset then + MTH_BOOK_STATE.petLeftOffset = val + MTH_BOOK_UpdatePetAbilitiesLists() + end + end) + ui.leftScroll:SetMinMaxValues(0, 0) + ui.leftScroll:SetValueStep(1) + ui.leftScroll:SetValue(0) + + ui.leftPane:EnableMouseWheel(true) + ui.leftPane:SetScript("OnMouseWheel", function() + local delta = arg1 or 0 + if delta == 0 then return end + local current = tonumber(MTH_BOOK_STATE.petLeftOffset or 0) or 0 + if delta < 0 then + current = current + 1 + else + current = current - 1 + end + if current < 0 then current = 0 end + local maxOffset = 0 + if MTH_BOOK_STATE.results then + maxOffset = math.max(0, table.getn(MTH_BOOK_STATE.results) - table.getn(ui.leftButtons or {})) + end + if current > maxOffset then current = maxOffset end + MTH_BOOK_STATE.petLeftOffset = current + if ui.leftScroll then + ui.leftScroll:SetValue(current) + end + MTH_BOOK_UpdatePetAbilitiesLists() + end) + + ui.leftButtons = {} + local leftY = -24 + for i = 1, ui.leftRowsVisible do + local btn = CreateFrame("Button", nil, ui.leftPane) + btn:SetPoint("TOPLEFT", ui.leftPane, "TOPLEFT", 0, leftY) + btn:SetWidth(144) + btn:SetHeight(19) + btn.entry = nil + + btn.selected = btn:CreateTexture(nil, "BACKGROUND") + btn.selected:SetAllPoints(btn) + btn.selected:SetTexture("Interface\\Buttons\\WHITE8X8") + btn.selected:SetVertexColor(0.18, 0.35, 0.55, 0.35) + btn.selected:Hide() + + local hl = btn:CreateTexture(nil, "HIGHLIGHT") + hl:SetAllPoints(btn) + hl:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + hl:SetBlendMode("ADD") + hl:SetAlpha(0.30) + + btn.ability = btn:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + btn.ability:SetPoint("LEFT", btn, "LEFT", 24, 0) + btn.ability:SetWidth(100) + btn.ability:SetJustifyH("LEFT") + + btn.rank = btn:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + btn.rank:SetPoint("LEFT", btn, "LEFT", 126, 0) + btn.rank:SetWidth(26) + btn.rank:SetJustifyH("LEFT") + + btn.icon = btn:CreateTexture(nil, "ARTWORK") + btn.icon:SetPoint("LEFT", btn, "LEFT", 2, 0) + btn.icon:SetWidth(16) + btn.icon:SetHeight(16) + + btn:SetScript("OnClick", function() + if not this.entry then return end + MTH_BOOK_STATE.selectedEntry = this.entry + MTH_BOOK_STATE.selectedPetRankEntry = nil + MTH_BOOK_STATE.petRankRows = MTH_BOOK_BuildPetAbilityRankRows(this.entry) + MTH_BOOK_UpdateResults() + end) + btn:SetScript("OnEnter", function() + if this and this.entry then + MTH_BOOK_ShowBaselineTooltip(this, this.entry) + end + end) + btn:SetScript("OnLeave", function() + MTH_BOOK_HideSpellTooltip() + end) + + ui.leftButtons[i] = btn + leftY = leftY - 20 + end + + local footerParent = listParent:GetParent() or listParent + ui.recapText = footerParent:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.recapText:SetPoint("TOPLEFT", footerParent, "TOPLEFT", 20, -530) + ui.recapText:SetWidth(190) + ui.recapText:SetJustifyH("LEFT") + ui.recapText:SetTextColor(1.00, 0.82, 0.00) + ui.recapText:SetText("Abilities 0 | Known 0/0") + + ui.progressBackdrop = CreateFrame("Frame", nil, footerParent) + ui.progressBackdrop:SetPoint("LEFT", ui.recapText, "RIGHT", 8, 0) + ui.progressBackdrop:SetWidth(320) + ui.progressBackdrop:SetHeight(14) + ui.progressBackdrop:SetFrameLevel(footerParent:GetFrameLevel() + 3) + ui.progressBackdrop:SetBackdrop({ + bgFile = "Interface\\Buttons\\WHITE8X8", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, + tileSize = 8, + edgeSize = 8, + insets = { left = 2, right = 2, top = 2, bottom = 2 }, + }) + ui.progressBackdrop:SetBackdropColor(0.08, 0.08, 0.08, 0.95) + ui.progressBackdrop:SetBackdropBorderColor(0.24, 0.24, 0.24, 0.90) + + ui.progressBar = CreateFrame("StatusBar", nil, ui.progressBackdrop) + ui.progressBar:SetPoint("TOPLEFT", ui.progressBackdrop, "TOPLEFT", 2, -2) + ui.progressBar:SetPoint("BOTTOMRIGHT", ui.progressBackdrop, "BOTTOMRIGHT", -2, 2) + ui.progressBar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8") + ui.progressBar:SetStatusBarColor(0.18, 0.75, 0.22, 0.9) + ui.progressBar:SetMinMaxValues(0, 1) + ui.progressBar:SetValue(0) + + ui.progressText = ui.progressBackdrop:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + ui.progressText:SetPoint("CENTER", ui.progressBackdrop, "CENTER", 0, 0) + ui.progressText:SetTextColor(1.00, 1.00, 1.00) + ui.progressText:SetText("0%") + + ui.rightButtons = {} + local rightRowsVisible = 10 + local rightRowHeight = 36 + local rightRowStep = 37 + local rightY = -24 + for i = 1, rightRowsVisible do + local btn = CreateFrame("Button", nil, ui.rightPane) + btn:SetPoint("TOPLEFT", ui.rightPane, "TOPLEFT", 0, rightY) + btn:SetWidth(388) + btn:SetHeight(rightRowHeight) + btn.entry = nil + + btn.selected = btn:CreateTexture(nil, "BACKGROUND") + btn.selected:SetAllPoints(btn) + btn.selected:SetTexture("Interface\\Buttons\\WHITE8X8") + btn.selected:SetVertexColor(0.18, 0.35, 0.55, 0.35) + btn.selected:Hide() + + local hl = btn:CreateTexture(nil, "HIGHLIGHT") + hl:SetAllPoints(btn) + hl:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + hl:SetBlendMode("ADD") + hl:SetAlpha(0.28) + + btn.rank = btn:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + btn.rank:SetPoint("LEFT", btn, "LEFT", 2, 1) + btn.rank:SetWidth(30) + btn.rank:SetJustifyH("LEFT") + + btn.knownIcon = btn:CreateTexture(nil, "ARTWORK") + btn.knownIcon:SetPoint("LEFT", btn, "LEFT", 38, 1) + btn.knownIcon:SetWidth(12) + btn.knownIcon:SetHeight(12) + btn.knownIcon:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") + btn.knownIcon:SetVertexColor(0.18, 0.90, 0.18, 1.00) + btn.knownIcon:Hide() + + btn.level = btn:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + btn.level:SetPoint("LEFT", btn, "LEFT", 58, 1) + btn.level:SetWidth(40) + btn.level:SetJustifyH("LEFT") + + btn.desc = btn:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + btn.desc:SetPoint("LEFT", btn, "LEFT", 98, 1) + btn.desc:SetWidth(286) + btn.desc:SetJustifyH("LEFT") + + btn:SetScript("OnClick", function() + if not this.entry then return end + MTH_BOOK_STATE.selectedPetRankEntry = this.entry + MTH_BOOK_UpdateDetail() + MTH_BOOK_UpdateResults() + end) + btn:SetScript("OnEnter", function() + if this and this.entry then + MTH_BOOK_ShowRankTooltip(this, this.entry) + end + end) + btn:SetScript("OnLeave", function() + MTH_BOOK_HideSpellTooltip() + end) + + ui.rightButtons[i] = btn + rightY = rightY - rightRowStep + end + + ui.detailTitleTop = detailParent:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.detailTitleTop:SetPoint("TOPLEFT", detailParent, "TOPLEFT", 8, -8) + ui.detailTitleTop:SetWidth(120) + ui.detailTitleTop:SetJustifyH("LEFT") + ui.detailTitleTop:SetTextColor(1.00, 0.82, 0.00) + ui.detailTitleTop:SetText("Spell baseline") + + ui.detailTop = CreateFrame("EditBox", nil, detailParent) + ui.detailTop:SetMultiLine(true) + ui.detailTop:SetAutoFocus(false) + ui.detailTop:SetPoint("TOPLEFT", ui.detailTitleTop, "BOTTOMLEFT", 0, -4) + ui.detailTop:SetWidth(120) + ui.detailTop:SetHeight(132) + + ui.detailTopIcon = detailParent:CreateTexture(nil, "ARTWORK") + ui.detailTopIcon:SetPoint("TOPRIGHT", ui.detailTop, "TOPRIGHT", -4, -4) + ui.detailTopIcon:SetWidth(16) + ui.detailTopIcon:SetHeight(16) + ui.detailTopIcon:Hide() + + ui.detailTitleBottom = detailParent:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + ui.detailTitleBottom:SetPoint("TOPLEFT", ui.detailTop, "BOTTOMLEFT", 0, -8) + ui.detailTitleBottom:SetWidth(120) + ui.detailTitleBottom:SetJustifyH("LEFT") + ui.detailTitleBottom:SetTextColor(1.00, 0.82, 0.00) + ui.detailTitleBottom:SetText("Current Rank") + + ui.detailBottom = CreateFrame("EditBox", nil, detailParent) + ui.detailBottom:SetMultiLine(true) + ui.detailBottom:SetAutoFocus(false) + ui.detailBottom:SetPoint("TOPLEFT", ui.detailTitleBottom, "BOTTOMLEFT", 0, -4) + ui.detailBottom:SetWidth(120) + ui.detailBottom:SetHeight(116) + + MTH_BOOK_ConfigureReadOnlyEditBox(ui.detailTop) + MTH_BOOK_ConfigureReadOnlyEditBox(ui.detailBottom) + if ui.detailTop.SetTextColor then ui.detailTop:SetTextColor(1.00, 1.00, 1.00) end + if ui.detailBottom.SetTextColor then ui.detailBottom:SetTextColor(1.00, 1.00, 1.00) end + + MTH_BOOK_STATE.petUI = ui +end + +function MTH_BOOKTAB_SetPetAbilitiesUIVisible(visible) + local ui = MTH_BOOK_STATE.petUI + if not ui then return end + + local detail = getglobal("MTH_BOOK_DetailBackdropDetailText") + if detail then + if visible then detail:Hide() else detail:Show() end + end + + local widgets = { + ui.leftPane, + ui.leftScroll, + ui.rightPane, + ui.vSplit, + ui.leftHeaderAbility, + ui.leftHeaderRank, + ui.rightHeaderRank, + ui.rightHeaderKnown, + ui.rightHeaderLevel, + ui.rightHeaderDesc, + ui.recapText, + ui.progressBackdrop, + ui.detailTop, + ui.detailBottom, + ui.detailTitleTop, + ui.detailTitleBottom, + } + + for i = 1, table.getn(widgets) do + local widget = widgets[i] + if widget and widget.Show and widget.Hide then + if visible then widget:Show() else widget:Hide() end + end + end + + if ui.detailTopIcon and not visible then + ui.detailTopIcon:Hide() + end + + if not visible then + MTH_BOOK_HideSpellTooltip() + end +end + +function MTH_BOOKTAB_UpdatePetAbilitiesLists() + local ui = MTH_BOOK_STATE.petUI + if not ui then return end + + local resultCount = table.getn(MTH_BOOK_STATE.results) + local visibleRows = table.getn(ui.leftButtons) + local maxOffset = math.max(0, resultCount - visibleRows) + if MTH_BOOK_STATE.petLeftOffset < 0 then MTH_BOOK_STATE.petLeftOffset = 0 end + if MTH_BOOK_STATE.petLeftOffset > maxOffset then MTH_BOOK_STATE.petLeftOffset = maxOffset end + if ui.leftScroll then + ui.leftScroll:SetMinMaxValues(0, maxOffset) + ui.leftScroll:SetValueStep(1) + ui.leftScroll:SetValue(MTH_BOOK_STATE.petLeftOffset) + local upButton = getglobal("MTH_BOOK_PetLeftScrollScrollUpButton") + local downButton = getglobal("MTH_BOOK_PetLeftScrollScrollDownButton") + if upButton then + if MTH_BOOK_STATE.petLeftOffset > 0 then + upButton:EnableMouse(true) + upButton:SetAlpha(1) + else + upButton:EnableMouse(false) + upButton:SetAlpha(0.35) + end + end + if downButton then + if MTH_BOOK_STATE.petLeftOffset < maxOffset then + downButton:EnableMouse(true) + downButton:SetAlpha(1) + else + downButton:EnableMouse(false) + downButton:SetAlpha(0.35) + end + end + if maxOffset > 0 then + ui.leftScroll:SetAlpha(1) + ui.leftScroll:Show() + else + ui.leftScroll:SetAlpha(1) + ui.leftScroll:Hide() + end + end + + local startIndex = MTH_BOOK_STATE.petLeftOffset + 1 + for i = 1, visibleRows do + local btn = ui.leftButtons[i] + if btn then + local rowIndex = startIndex + i - 1 + local entry = MTH_BOOK_STATE.results[rowIndex] + btn.entry = entry + if entry then + if btn.icon then + if entry.icon and entry.icon ~= "" then + if string.find(entry.icon, "\\", 1, true) then + btn.icon:SetTexture(entry.icon) + else + btn.icon:SetTexture("Interface\\Icons\\" .. tostring(entry.icon)) + end + btn.icon:Show() + else + btn.icon:Hide() + end + end + btn.ability:SetText(tostring(entry.ability or "?")) + btn.rank:SetText(tostring(entry.rankText or "")) + btn.ability:SetTextColor(0.92, 0.92, 0.92) + btn.rank:SetTextColor(0.92, 0.92, 0.92) + if btn.icon then btn.icon:SetVertexColor(1, 1, 1, 1) end + if MTH_BOOK_STATE.selectedEntry and MTH_BOOK_STATE.selectedEntry.ability == entry.ability then + btn.selected:Show() + else + btn.selected:Hide() + end + btn:Show() + else + if btn.icon then btn.icon:Hide() end + btn.ability:SetText("") + btn.rank:SetText("") + btn.selected:Hide() + btn:Hide() + end + end + end + + local totalKnown = 0 + local totalRanks = 0 + for i = 1, resultCount do + local entry = MTH_BOOK_STATE.results[i] + if entry then + totalKnown = totalKnown + (tonumber(entry.knownRankCount) or 0) + totalRanks = totalRanks + (tonumber(entry.rankCount) or 0) + end + end + if ui.recapText then + ui.recapText:SetText("Abilities " .. tostring(resultCount) .. " | Known " .. tostring(totalKnown) .. "/" .. tostring(totalRanks)) + end + if ui.progressBar then + ui.progressBar:SetMinMaxValues(0, math.max(1, totalRanks)) + ui.progressBar:SetValue(totalKnown) + end + if ui.progressText then + local pct = 0 + if totalRanks > 0 then + pct = math.floor((totalKnown / totalRanks) * 100 + 0.5) + end + ui.progressText:SetText(tostring(pct) .. "%") + end + + if MTH_BOOK_STATE.selectedEntry then + local keep = false + for i = 1, table.getn(MTH_BOOK_STATE.results) do + local row = MTH_BOOK_STATE.results[i] + if row and row.ability == MTH_BOOK_STATE.selectedEntry.ability then + MTH_BOOK_STATE.selectedEntry = row + keep = true + break + end + end + if not keep then + MTH_BOOK_STATE.selectedEntry = nil + MTH_BOOK_STATE.selectedPetRankEntry = nil + end + end + + MTH_BOOK_STATE.petRankRows = MTH_BOOK_BuildPetAbilityRankRows(MTH_BOOK_STATE.selectedEntry) + + for i = 1, table.getn(ui.rightButtons) do + local btn = ui.rightButtons[i] + if btn then + local entry = MTH_BOOK_STATE.petRankRows[i] + btn.entry = entry + if entry then + btn.rank:SetText(MTH_BOOK_GetRankLabel(entry.rankNumber, false)) + if btn.knownIcon then + if entry.isKnown then + btn.knownIcon:Show() + else + btn.knownIcon:Hide() + end + end + btn.level:SetText(entry.trainLevel and tostring(entry.trainLevel) or "-") + btn.desc:SetText(tostring(entry.description or "-")) + if MTH_BOOK_STATE.selectedPetRankEntry and MTH_BOOK_STATE.selectedPetRankEntry.id == entry.id then + btn.selected:Show() + else + btn.selected:Hide() + end + btn:Show() + else + btn.rank:SetText("") + if btn.knownIcon then btn.knownIcon:Hide() end + btn.level:SetText("") + btn.desc:SetText("") + btn.selected:Hide() + btn:Hide() + end + end + end +end diff --git a/api/hunterbook-tab-pethistory.lua b/api/hunterbook-tab-pethistory.lua new file mode 100644 index 0000000..5607a8b --- /dev/null +++ b/api/hunterbook-tab-pethistory.lua @@ -0,0 +1,29 @@ +if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end + +MTH_HUNTERBOOK_TABS.pethistory = { + headerLabel = "Pet History", + columnLabels = { "Pet ID", "Name", "Family", "Lvl", "Lost Date/Time", "Lost Cause" }, + columnLayout = { + { x = 8, width = 66, align = "LEFT" }, + { x = 76, width = 104, align = "LEFT" }, + { x = 182, width = 84, align = "LEFT" }, + { x = 268, width = 30, align = "LEFT" }, + { x = 300, width = 128, align = "LEFT" }, + { x = 430, width = 78, align = "LEFT" }, + }, +} + +function MTH_BOOK_PetHistorySort(a, b) + local ra = MTH_BOOK_GetPetStoreRow(a) + local rb = MTH_BOOK_GetPetStoreRow(b) + if not ra and not rb then return tostring(a) < tostring(b) end + if not ra then return false end + if not rb then return true end + local ta = tonumber(ra.abandonedAt) or 0 + local tb = tonumber(rb.abandonedAt) or 0 + if ta ~= tb then return ta > tb end + local nameA = MTH_BOOK_SafeLower(ra.name) + local nameB = MTH_BOOK_SafeLower(rb.name) + if nameA ~= nameB then return nameA < nameB end + return tostring(a) < tostring(b) +end diff --git a/api/hunterbook-tab-stable.lua b/api/hunterbook-tab-stable.lua new file mode 100644 index 0000000..63aebd0 --- /dev/null +++ b/api/hunterbook-tab-stable.lua @@ -0,0 +1,1801 @@ +if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end + +MTH_HUNTERBOOK_TABS.stable = { + headerLabel = "Stable", + columnLabels = { "Pet ID", "Slot", "Name", "Family", "Lvl", "Loyalty", "Seen" }, + columnLayout = { + { x = 8, width = 78, align = "LEFT" }, + { x = 88, width = 34, align = "LEFT" }, + { x = 124, width = 118, align = "LEFT" }, + { x = 244, width = 92, align = "LEFT" }, + { x = 338, width = 34, align = "LEFT" }, + { x = 374, width = 114, align = "LEFT" }, + { x = 490, width = 52, align = "LEFT" }, + }, +} + +function MTH_BOOK_GetLoyaltyHeaderText(loyaltyValue) + local text = tostring(loyaltyValue or "") + text = string.gsub(text, "^%s+", "") + text = string.gsub(text, "%s+$", "") + if text == "" then + return "-" + end + + local closePos = string.find(text, "%)") + if closePos and closePos < string.len(text) then + local tail = string.sub(text, closePos + 1) + tail = string.gsub(tail, "^%s+", "") + tail = string.gsub(tail, "%s+$", "") + if tail ~= "" then + return tail + end + end + + return text +end + +local function MTH_BOOK_GetLoyaltyNameByLevel(level) + level = tonumber(level) + if not level then + return nil + end + local names = { + [1] = "Rebellious", + [2] = "Unruly", + [3] = "Submissive", + [4] = "Dependable", + [5] = "Faithful", + [6] = "Best Friend", + } + local globalName = _G and _G["PET_LOYALTY" .. tostring(level)] or nil + if type(globalName) == "string" and globalName ~= "" then + return globalName + end + return names[level] +end + +function MTH_BOOK_CountPetAbilitiesMap(map) + if type(map) ~= "table" then return 0 end + local total = 0 + for _ in pairs(map) do + total = total + 1 + end + return total +end + +function MTH_BOOK_GetStableAbilitySummary(map) + if type(map) ~= "table" then return "-" end + local entries = {} + for _, ability in pairs(map) do + if type(ability) == "table" then + local nameText = tostring(ability.name or "") + local rankValue = tonumber(ability.rank) + if nameText ~= "" then + if rankValue and rankValue > 0 then + table.insert(entries, nameText .. " R" .. tostring(rankValue)) + else + table.insert(entries, nameText) + end + end + end + end + table.sort(entries) + if table.getn(entries) == 0 then return "-" end + return table.concat(entries, ", ") +end + +function MTH_BOOK_GetStablePetSpellbookSummary(spellbook) + if type(spellbook) ~= "table" or type(spellbook.spells) ~= "table" then + return "Abilities (-)" + end + + local entries = {} + for i = 1, table.getn(spellbook.spells) do + local spell = spellbook.spells[i] + if type(spell) == "table" and tostring(spell.name or "") ~= "" then + local line = tostring(spell.name) + local rankValue = tonumber(spell.rank) + if rankValue and rankValue > 0 then + line = line .. " R" .. tostring(rankValue) + end + if spell.isPassive then + line = line .. " (P)" + end + table.insert(entries, line) + end + end + + if table.getn(entries) == 0 then + return "Abilities (0)" + end + + return "Abilities (" .. tostring(table.getn(entries)) .. ")\n" .. table.concat(entries, "\n") +end + +function MTH_BOOK_NormalizeAbilityDisplayName(nameText) + local name = tostring(nameText or "") + name = string.gsub(name, "^%s+", "") + name = string.gsub(name, "%s+$", "") + name = string.gsub(name, "%s+[Rr]ank%s+%d+", "") + name = string.gsub(name, "%s*%([^%)]*[Rr]ank%s*%d+[^%)]*%)", "") + name = string.gsub(name, "%s+$", "") + return name +end + +function MTH_BOOK_IsResistanceAbilityName(nameText) + local name = string.lower(tostring(nameText or "")) + if name == "" then return false end + if string.find(name, "resistance", 1, true) then + return true + end + if name == "arcane resistance" or name == "nature resistance" or name == "fire resistance" or name == "frost resistance" or name == "shadow resistance" then + return true + end + return false +end + +function MTH_BOOK_GetResistanceShortName(nameText) + local lowerName = string.lower(tostring(nameText or "")) + if string.find(lowerName, "arcane", 1, true) then return "AR" end + if string.find(lowerName, "nature", 1, true) then return "NR" end + if string.find(lowerName, "fire", 1, true) then return "FR" end + if string.find(lowerName, "frost", 1, true) then return "FR" end + if string.find(lowerName, "shadow", 1, true) then return "SR" end + return tostring(nameText or "-") +end + +function MTH_BOOK_FindPetAbilityDSRow(abilityName, rankNumber) + if not (MTH_DS_PetSpells and type(MTH_DS_PetSpells.byAbility) == "table") then + return nil + end + local normalized = MTH_BOOK_NormalizeAbilityDisplayName(abilityName) + if normalized == "" then + return nil + end + + local bundle = MTH_DS_PetSpells.byAbility[normalized] + if type(bundle) ~= "table" then + for candidateName, candidateBundle in pairs(MTH_DS_PetSpells.byAbility) do + if string.lower(tostring(candidateName or "")) == string.lower(normalized) then + bundle = candidateBundle + break + end + end + end + if type(bundle) ~= "table" or type(bundle.spells) ~= "table" then + return nil + end + + local wantedRank = tonumber(rankNumber) + if wantedRank and wantedRank > 0 then + for i = 1, table.getn(bundle.spells) do + local row = bundle.spells[i] + if type(row) == "table" and tonumber(row.rankNumber) == wantedRank then + return row + end + end + end + + return bundle.spells[1] +end + +function MTH_BOOK_BuildStablePetAbilityEntries(row) + if type(row) ~= "table" then + return {}, {} + end + + local source = {} + if type(row.petSpellbook) == "table" and type(row.petSpellbook.spells) == "table" and table.getn(row.petSpellbook.spells) > 0 then + source = row.petSpellbook.spells + elseif type(row.abilities) == "table" then + local i = 0 + for _, ability in pairs(row.abilities) do + if type(ability) == "table" and tostring(ability.name or "") ~= "" then + i = i + 1 + source[i] = { + name = tostring(ability.name or ""), + rank = tonumber(ability.rank), + icon = ability.icon, + } + end + end + end + + local sourceCount = table.getn(source) + if sourceCount == 0 then + return {}, {} + end + + local hideAvoidance = sourceCount > 8 + local leftEntries = {} + local rightEntries = {} + + for i = 1, sourceCount do + local spell = source[i] + if type(spell) == "table" and tostring(spell.name or "") ~= "" then + local fullName = MTH_BOOK_NormalizeAbilityDisplayName(spell.name) + if fullName ~= "" then + local isAvoidance = string.lower(fullName) == "avoidance" + if not (hideAvoidance and isAvoidance) then + local rankNumber = tonumber(spell.rank) + local dsRow = MTH_BOOK_FindPetAbilityDSRow(fullName, rankNumber) + local icon = (dsRow and dsRow.icon) or spell.icon + local description = (dsRow and dsRow.description) or "" + local displayName = fullName + if MTH_BOOK_IsResistanceAbilityName(fullName) then + displayName = MTH_BOOK_GetResistanceShortName(fullName) + end + if rankNumber and rankNumber > 0 then + displayName = displayName .. " R" .. tostring(rankNumber) + end + + local entry = { + name = fullName, + display = displayName, + rank = rankNumber, + description = tostring(description or ""), + icon = icon, + } + + if MTH_BOOK_IsResistanceAbilityName(fullName) then + table.insert(rightEntries, entry) + else + table.insert(leftEntries, entry) + end + end + end + end + end + + return leftEntries, rightEntries +end + +MTH_BOOK_ShowStableAbilityTooltip = function(anchor, entry) + if not anchor or type(entry) ~= "table" then return end + local tooltip = MTH_BOOK_GetSpellTooltip() + if not tooltip then return end + + tooltip:SetOwner(anchor, "ANCHOR_RIGHT") + tooltip:ClearLines() + MTH_BOOK_SetSpellTooltipIcon(tooltip, entry.icon) + tooltip:AddLine(tostring(entry.name or "Unknown"), 1.00, 0.82, 0.00) + if tonumber(entry.rank) and tonumber(entry.rank) > 0 then + tooltip:AddLine("Rank " .. tostring(entry.rank), 0.85, 0.85, 0.85) + end + if entry.description and entry.description ~= "" then + tooltip:AddLine(" ") + tooltip:AddLine(tostring(entry.description), 0.90, 0.90, 0.90, true) + end + tooltip:Show() +end + +function MTH_BOOK_GetContextSummary(context) + if type(context) ~= "table" then + return "-" + end + local parts = {} + if context.zone and context.zone ~= "" then table.insert(parts, tostring(context.zone)) end + if context.subZone and context.subZone ~= "" then table.insert(parts, tostring(context.subZone)) end + if context.x and context.y then + table.insert(parts, string.format("%.2f,%.2f", tonumber(context.x) or 0, tonumber(context.y) or 0)) + elseif context.coordinate and context.coordinate ~= "" then + table.insert(parts, tostring(context.coordinate)) + end + if context.hunterLevel then table.insert(parts, "L" .. tostring(context.hunterLevel)) end + if context.timestamp then table.insert(parts, "t=" .. tostring(context.timestamp)) end + if table.getn(parts) == 0 then return "-" end + return table.concat(parts, " | ") +end + +function MTH_BOOK_FormatTimestamp(timestamp) + local value = tonumber(timestamp) + if not value or value <= 0 then return "-" end + if type(date) ~= "function" then return tostring(value) end + return date("%Y-%m-%d %H:%M:%S", value) +end + +function MTH_BOOK_FormatElapsedFromTimestamp(timestamp) + local value = tonumber(timestamp) + if not value or value <= 0 then return "-" end + local now = tonumber(time()) or 0 + local elapsed = now - value + if elapsed < 0 then elapsed = 0 end + local days = math.floor(elapsed / 86400) + local elapsedAfterDays = elapsed - (days * 86400) + local hours = math.floor(elapsedAfterDays / 3600) + local elapsedAfterHours = elapsedAfterDays - (hours * 3600) + local mins = math.floor(elapsedAfterHours / 60) + if days > 0 then + return tostring(days) .. "d " .. tostring(hours) .. "h" + end + if hours > 0 then + return tostring(hours) .. "h " .. tostring(mins) .. "m" + end + return tostring(mins) .. "m" +end + +local function MTH_BOOK_FormatElapsedStableInfo(timestamp) + local value = tonumber(timestamp) + if not value or value <= 0 then return "-" end + local now = tonumber(time()) or 0 + local elapsed = now - value + if elapsed < 0 then elapsed = 0 end + local days = math.floor(elapsed / 86400) + local remAfterDays = elapsed - (days * 86400) + local hours = math.floor(remAfterDays / 3600) + local remAfterHours = remAfterDays - (hours * 3600) + local mins = math.floor(remAfterHours / 60) + if days > 0 then + if hours > 0 then + return tostring(days) .. " days " .. tostring(hours) .. " hours" + end + return tostring(days) .. " days" + end + if hours > 0 then + if mins > 0 then + return tostring(hours) .. " hours " .. tostring(mins) .. " minutes" + end + return tostring(hours) .. " hours" + end + return tostring(mins) .. " minutes" +end + +local function MTH_BOOK_NormalizeMapCoordValue(value) + local numeric = tonumber(value) + if not numeric then + return nil + end + if numeric > 0 and numeric <= 1 then + return numeric * 100 + end + return numeric +end + +local function MTH_BOOK_StableDetailKV(title, value) + local key = tostring(title or "") + if key == "" then + return tostring(value or "-") + end + return "|cff9a9a9a" .. key .. " :|r " .. tostring(value or "-") +end + +local function MTH_BOOK_EnsureStableColumnLines(card, fieldName, lineCount) + if type(card[fieldName]) ~= "table" then + card[fieldName] = {} + end + for i = 1, lineCount do + if not card[fieldName][i] then + local fs = card:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + fs:SetJustifyH("LEFT") + fs:SetJustifyV("TOP") + fs:SetTextColor(1.00, 1.00, 1.00) + if fs.SetWordWrap then fs:SetWordWrap(false) end + if fs.SetNonSpaceWrap then fs:SetNonSpaceWrap(false) end + if fs.SetMaxLines then fs:SetMaxLines(1) end + card[fieldName][i] = fs + end + end + return card[fieldName] +end + +local function MTH_BOOK_ResolveStableTameZoneId(zoneName, beast, tameX, tameY) + local normalizedZone = MTH_BOOK_NormalizeZoneLookupName and MTH_BOOK_NormalizeZoneLookupName(zoneName) or "" + if zoneName and zoneName ~= "" and MTH_Map and type(MTH_Map.GetMapIDByName) == "function" then + local mapped = MTH_Map:GetMapIDByName(zoneName) + if mapped then + return tonumber(mapped) or mapped + end + end + + if normalizedZone ~= "" and type(MTH_DS_Zones) == "table" then + for zoneId, zoneRow in pairs(MTH_DS_Zones) do + if type(zoneRow) == "table" and type(zoneRow.names) == "table" then + for _, zoneValue in pairs(zoneRow.names) do + if MTH_BOOK_NormalizeZoneLookupName and MTH_BOOK_NormalizeZoneLookupName(zoneValue) == normalizedZone then + return tonumber(zoneId) or zoneId + end + end + end + end + end + + if type(beast) == "table" and type(beast.coords) == "table" and table.getn(beast.coords) > 0 then + local bestZoneId = nil + local bestDistance = nil + for i = 1, table.getn(beast.coords) do + local c = beast.coords[i] + if c and c[3] then + local zid = tonumber(c[3]) + if zid and not bestZoneId then + bestZoneId = zid + end + if zid and tameX and tameY then + local cx = tonumber(c[1]) + local cy = tonumber(c[2]) + if cx and cy then + local dx = cx - tameX + local dy = cy - tameY + local dist = (dx * dx) + (dy * dy) + if not bestDistance or dist < bestDistance then + bestDistance = dist + bestZoneId = zid + end + end + end + end + end + if bestZoneId then + return bestZoneId + end + end + + return nil +end + +local MTH_BOOK_NormalizeStableFoodLink + +local function MTH_BOOK_GetStableFeedSummary(petId) + local totalFed = 0 + local topFoods = {} + if type(MTH_FEED_GetPetFeedStats) ~= "function" then + return totalFed, topFoods + end + + local stats = MTH_FEED_GetPetFeedStats(petId) + if type(stats) ~= "table" then + return totalFed, topFoods + end + + totalFed = tonumber(stats.totals and stats.totals.accepted) or 0 + if totalFed < 0 then totalFed = 0 end + + local recentNameByItemId = {} + if type(stats.recent) == "table" then + for i = 1, table.getn(stats.recent) do + local recent = stats.recent[i] + if type(recent) == "table" then + local itemId = tonumber(recent.itemId) + local itemName = tostring(recent.itemName or "") + if itemId and itemName ~= "" and recentNameByItemId[itemId] == nil then + recentNameByItemId[itemId] = itemName + end + end + end + end + + if type(stats.byItemId) == "table" then + for key, itemRow in pairs(stats.byItemId) do + local acceptedCount = tonumber(type(itemRow) == "table" and itemRow.accepted) or 0 + local itemId = tonumber(key) + if acceptedCount > 0 and itemId then + local itemName = type(itemRow) == "table" and tostring(itemRow.itemName or "") or "" + local itemLink = type(itemRow) == "table" and tostring(itemRow.itemLink or "") or "" + itemLink = MTH_BOOK_NormalizeStableFoodLink(itemId, itemLink, itemName) + local itemTexture = type(itemRow) == "table" and itemRow.itemIcon or nil + if type(MTH_PrimeItemCache) == "function" then + MTH_PrimeItemCache(itemId) + end + if type(GetItemInfo) == "function" then + local infoName, infoLink, _, _, _, _, _, _, _, infoTexture = GetItemInfo(itemId) + if (not itemName or itemName == "") and infoName and infoName ~= "" then + itemName = infoName + end + if (not itemLink or itemLink == "") and infoLink and infoLink ~= "" then + itemLink = infoLink + end + if (not itemTexture or itemTexture == "") and infoTexture and infoTexture ~= "" then + itemTexture = infoTexture + end + if (not itemTexture or itemTexture == "") and itemLink and itemLink ~= "" then + local _, _, _, _, _, _, _, _, _, linkTexture = GetItemInfo(itemLink) + if linkTexture and linkTexture ~= "" then + itemTexture = linkTexture + end + end + end + if (not itemName or itemName == "") and recentNameByItemId[itemId] then + itemName = recentNameByItemId[itemId] + end + itemLink = MTH_BOOK_NormalizeStableFoodLink(itemId, itemLink, itemName) + if not itemName or itemName == "" then + itemName = "Item " .. tostring(itemId) + end + table.insert(topFoods, { + itemId = itemId, + name = itemName, + link = itemLink, + icon = itemTexture or (type(GetItemIcon) == "function" and GetItemIcon(itemId) or nil), + count = acceptedCount, + }) + end + end + end + + table.sort(topFoods, function(a, b) + if a.count ~= b.count then + return a.count > b.count + end + return tostring(a.name or "") < tostring(b.name or "") + end) + + while table.getn(topFoods) > 3 do + table.remove(topFoods) + end + + return totalFed, topFoods +end + +MTH_BOOK_NormalizeStableFoodLink = function(itemId, itemLink, itemName) + local link = tostring(itemLink or "") + if link ~= "" then + if string.find(link, "|Hitem:", 1, true) or string.find(link, "^item:%d+") then + return link + end + end + + local numericItemId = tonumber(itemId) + if numericItemId and type(MTH_GetClickableItemLink) == "function" then + local clickable = MTH_GetClickableItemLink(numericItemId, itemName, false) + if clickable and clickable ~= "" then + return tostring(clickable) + end + end + if numericItemId then + return "item:" .. tostring(numericItemId) .. ":0:0:0" + end + return nil +end + +local function MTH_BOOK_ResolveStableFoodIcon(itemId, iconHint, itemName, itemLink) + local linkText = tostring(itemLink or "") + local linkItemId = nil + if linkText ~= "" then + local _, _, parsedId = string.find(linkText, "item:(%d+)") + if parsedId then + linkItemId = tonumber(parsedId) + end + end + + local numericItemId = tonumber(itemId) or linkItemId + + if linkText ~= "" and type(GetItemInfo) == "function" then + local _, _, _, _, _, _, _, _, _, linkTexture = GetItemInfo(linkText) + if linkTexture and linkTexture ~= "" then + if type(linkTexture) == "number" then + return linkTexture + end + if string.find(tostring(linkTexture), "\\", 1, true) then + return tostring(linkTexture) + end + local asNumber = tonumber(tostring(linkTexture)) + if asNumber then + return asNumber + end + return "Interface\\Icons\\" .. tostring(linkTexture) + end + end + + if type(iconHint) == "number" then + return iconHint + end + + local iconHintText = tostring(iconHint or "") + if iconHintText ~= "" then + local numericHint = tonumber(iconHintText) + if numericHint then + return numericHint + end + end + + local iconPath = iconHintText + if iconPath ~= "" then + if string.find(iconPath, "\\", 1, true) then + return iconPath + end + return "Interface\\Icons\\" .. iconPath + end + + if numericItemId and type(GetItemInfo) == "function" then + local _, _, _, _, _, _, _, _, _, infoTexture = GetItemInfo(numericItemId) + if infoTexture and infoTexture ~= "" then + if type(infoTexture) == "number" then + return infoTexture + end + if string.find(tostring(infoTexture), "\\", 1, true) then + return tostring(infoTexture) + end + local asNumber = tonumber(tostring(infoTexture)) + if asNumber then + return asNumber + end + return "Interface\\Icons\\" .. tostring(infoTexture) + end + end + + if numericItemId and type(GetItemIcon) == "function" then + local directIcon = GetItemIcon(numericItemId) + if directIcon and directIcon ~= "" then + if type(directIcon) == "number" then + return directIcon + end + if string.find(tostring(directIcon), "\\", 1, true) then + return tostring(directIcon) + end + local asNumber = tonumber(tostring(directIcon)) + if asNumber then + return asNumber + end + return "Interface\\Icons\\" .. tostring(directIcon) + end + end + + if itemName and itemName ~= "" and type(GetItemIcon) == "function" then + local namedIcon = GetItemIcon(itemName) + if namedIcon and namedIcon ~= "" then + if type(namedIcon) == "number" then + return namedIcon + end + if string.find(tostring(namedIcon), "\\", 1, true) then + return tostring(namedIcon) + end + local asNumber = tonumber(tostring(namedIcon)) + if asNumber then + return asNumber + end + return "Interface\\Icons\\" .. tostring(namedIcon) + end + end + + if numericItemId and type(MTH_DS_Items) == "table" and type(MTH_DS_Items[numericItemId]) == "table" then + local dsIcon = tostring(MTH_DS_Items[numericItemId].icon or "") + if dsIcon ~= "" then + if string.find(dsIcon, "\\", 1, true) then + return dsIcon + end + return "Interface\\Icons\\" .. dsIcon + end + end + + return nil +end + +local function MTH_BOOK_UpdateStableFoodRowIcon(foodRow) + if not foodRow or not foodRow.icon then + return + end + local texturePath = MTH_BOOK_ResolveStableFoodIcon(foodRow.itemId, foodRow.iconHint, foodRow.itemName, foodRow.itemLink) + if texturePath and texturePath ~= "" then + foodRow.icon:SetTexture(texturePath) + else + foodRow.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") + end +end + +function MTH_BOOK_GetLoyaltyLevel(loyaltyValue) + local text = tostring(loyaltyValue or "") + local _, _, levelText = string.find(text, "[Ll]oyalty%s*[Ll]evel%s*(%d+)") + local level = tonumber(levelText) + if level then + return level + end + + if tonumber(loyaltyValue) then + local numeric = tonumber(loyaltyValue) + if numeric and numeric >= 1 and numeric <= 6 then + return numeric + end + end + + return nil +end + +function MTH_BOOK_GetLoyaltyHeaderColor(loyaltyValue) + local level = MTH_BOOK_GetLoyaltyLevel(loyaltyValue) + if not level then + return 0.84, 0.84, 0.84 + end + if level < 1 then level = 1 end + if level > 6 then level = 6 end + local t = (level - 1) / 5 + local red = 1 - t + local green = t + return red, green, 0 +end + +function MTH_BOOK_GetVisibleStableSlotCount() + local maxStableSlots = 4 + local detected = nil + + if type(MTH_PETS_GetRootStore) == "function" then + local pets = MTH_PETS_GetRootStore() + if type(pets) == "table" and type(pets.stableScan) == "table" then + detected = tonumber(pets.stableScan.slotCount) + end + end + + local getNumStableSlots = nil + if type(getglobal) == "function" then + getNumStableSlots = getglobal("GetNumStableSlots") + end + if (not detected or detected < 0) and type(getNumStableSlots) == "function" then + detected = tonumber(getNumStableSlots()) + end + + if detected and detected >= 0 then + if detected > 4 then detected = 4 end + maxStableSlots = detected + end + + if maxStableSlots < 0 then maxStableSlots = 0 end + return maxStableSlots +end + +function MTH_BOOK_GetStableRenderCardCount(maxCards) + local cards = 1 + MTH_BOOK_GetVisibleStableSlotCount() + if cards < 1 then cards = 1 end + if maxCards and cards > maxCards then cards = maxCards end + return cards +end + +function MTH_BOOK_GetStableRenderCardHeight(frameHeight, cardCount, topPad, bottomPad, bigGap, smallGap) + local usable = frameHeight - topPad - bottomPad + local gapCount = cardCount - 1 + if gapCount > 0 then + usable = usable - bigGap - (smallGap * (gapCount - 1)) + end + local cardHeight = math.floor(usable / cardCount) + if cardHeight < 70 then cardHeight = 70 end + return cardHeight +end + +function MTH_BOOKTAB_StableSort(a, b) + local store = MTH_BOOK_GetPetDatastore() + local ra = MTH_BOOK_GetPetStoreRow(a) + local rb = MTH_BOOK_GetPetStoreRow(b) + if not ra and not rb then return tostring(a) < tostring(b) end + if not ra then return false end + if not rb then return true end + local slotA = MTH_BOOK_GetStableDisplaySlot(a, ra, store) or 9999 + local slotB = MTH_BOOK_GetStableDisplaySlot(b, rb, store) or 9999 + if slotA ~= slotB then return slotA < slotB end + local nameA = MTH_BOOK_SafeLower(ra.name) + local nameB = MTH_BOOK_SafeLower(rb.name) + if nameA ~= nameB then return nameA < nameB end + return tostring(a) < tostring(b) +end + +function MTH_BOOKTAB_EnsureStableUI() + if MTH_BOOK_STATE.stableUI then return end + local listParent = getglobal("MTH_BOOK_ListBackdrop") + local parent = listParent + local parentSource = "ListBackdrop" + if not parent then + parent = getglobal("MTH_BOOK_TopAreaBackdrop") + parentSource = "TopAreaBackdrop" + end + if not parent and listParent and listParent.GetParent then + parent = listParent:GetParent() + parentSource = "ListBackdropParent" + end + if not parent then + parent = MTH_BOOK_Browser + parentSource = "Browser" + end + if not parent then + MTH_BOOK_DebugTrace("EnsureStableUI failed: no parent") + return + end + MTH_BOOK_DebugTrace("EnsureStableUI parent=" .. tostring(parentSource) .. " name=" .. tostring(parent.GetName and parent:GetName() or "")) + + local ui = {} + ui.frame = CreateFrame("Frame", nil, parent) + ui.frame:SetPoint("TOPLEFT", parent, "TOPLEFT", 4, -4) + ui.frame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", -4, 4) + ui.frame:SetFrameStrata("DIALOG") + if parent.GetFrameLevel and ui.frame.SetFrameLevel then + ui.frame:SetFrameLevel(parent:GetFrameLevel() + 5) + end + ui.frame:SetAlpha(1) + ui.frame:Hide() + + ui.cards = {} + for i = 1, 5 do + local card = CreateFrame("Button", nil, ui.frame) + card:SetFrameStrata("DIALOG") + card:SetFrameLevel((ui.frame.GetFrameLevel and ui.frame:GetFrameLevel() or 1) + 1) + card:SetAlpha(1) + + card.bg = card:CreateTexture(nil, "BACKGROUND") + card.bg:SetAllPoints(card) + card.bg:SetTexture("Interface\\Buttons\\WHITE8X8") + card.bg:SetVertexColor(0.08, 0.08, 0.08, 0.72) + + card.borderTop = card:CreateTexture(nil, "BORDER") + card.borderTop:SetTexture("Interface\\Buttons\\WHITE8X8") + card.borderTop:SetVertexColor(0.35, 0.35, 0.35, 0.75) + card.borderTop:SetPoint("TOPLEFT", card, "TOPLEFT", 0, 0) + card.borderTop:SetPoint("TOPRIGHT", card, "TOPRIGHT", 0, 0) + card.borderTop:SetHeight(1) + + card.borderBottom = card:CreateTexture(nil, "BORDER") + card.borderBottom:SetTexture("Interface\\Buttons\\WHITE8X8") + card.borderBottom:SetVertexColor(0.35, 0.35, 0.35, 0.75) + card.borderBottom:SetPoint("BOTTOMLEFT", card, "BOTTOMLEFT", 0, 0) + card.borderBottom:SetPoint("BOTTOMRIGHT", card, "BOTTOMRIGHT", 0, 0) + card.borderBottom:SetHeight(1) + + card.icon = card:CreateTexture(nil, "ARTWORK") + card.icon:SetPoint("TOPLEFT", card, "TOPLEFT", 12, -10) + card.icon:SetWidth(44) + card.icon:SetHeight(44) + card.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") + + card.name = card:CreateFontString(nil, "ARTWORK", "GameFontHighlightLarge") + card.name:SetPoint("TOPLEFT", card.icon, "TOPRIGHT", 10, 2) + card.name:SetPoint("TOPRIGHT", card, "TOPRIGHT", -10, -8) + card.name:SetJustifyH("LEFT") + card.name:SetTextColor(1.00, 0.82, 0.00) + + card.meta = card:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + card.meta:SetPoint("TOPLEFT", card.icon, "TOPRIGHT", 10, -18) + card.meta:SetPoint("TOPRIGHT", card, "TOPRIGHT", -10, -24) + card.meta:SetJustifyH("LEFT") + card.meta:SetJustifyV("TOP") + + card.metaLoyalty = card:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + card.metaLoyalty:SetJustifyH("LEFT") + card.metaLoyalty:SetJustifyV("TOP") + card.metaLoyalty:SetTextColor(0.84, 0.84, 0.84) + do + local fontName, fontHeight, fontFlags = card.meta:GetFont() + if fontName then + local loyaltySize = fontHeight + if loyaltySize and loyaltySize > 6 then + loyaltySize = loyaltySize - 1 + end + card.metaLoyalty:SetFont(fontName, loyaltySize, fontFlags) + end + end + + card.headerXpBar = CreateFrame("StatusBar", nil, card) + card.headerXpBar:SetFrameLevel((card.GetFrameLevel and card:GetFrameLevel() or 1) + 2) + card.headerXpBar:SetStatusBarTexture("Interface\\Buttons\\WHITE8X8") + card.headerXpBar:SetStatusBarColor(0.22, 0.62, 0.96, 0.95) + card.headerXpBar:SetMinMaxValues(0, 1) + card.headerXpBar:SetValue(0) + card.headerXpBar:Hide() + card.headerXpBarBg = card.headerXpBar:CreateTexture(nil, "BACKGROUND") + card.headerXpBarBg:SetAllPoints(card.headerXpBar) + card.headerXpBarBg:SetTexture("Interface\\Buttons\\WHITE8X8") + card.headerXpBarBg:SetVertexColor(0.22, 0.22, 0.22, 0.95) + card.headerXpBarBorderTop = card.headerXpBar:CreateTexture(nil, "BORDER") + card.headerXpBarBorderTop:SetTexture("Interface\\Buttons\\WHITE8X8") + card.headerXpBarBorderTop:SetVertexColor(0.45, 0.45, 0.45, 0.85) + card.headerXpBarBorderTop:SetPoint("TOPLEFT", card.headerXpBar, "TOPLEFT", 0, 0) + card.headerXpBarBorderTop:SetPoint("TOPRIGHT", card.headerXpBar, "TOPRIGHT", 0, 0) + card.headerXpBarBorderTop:SetHeight(1) + card.headerXpBarBorderBottom = card.headerXpBar:CreateTexture(nil, "BORDER") + card.headerXpBarBorderBottom:SetTexture("Interface\\Buttons\\WHITE8X8") + card.headerXpBarBorderBottom:SetVertexColor(0.45, 0.45, 0.45, 0.85) + card.headerXpBarBorderBottom:SetPoint("BOTTOMLEFT", card.headerXpBar, "BOTTOMLEFT", 0, 0) + card.headerXpBarBorderBottom:SetPoint("BOTTOMRIGHT", card.headerXpBar, "BOTTOMRIGHT", 0, 0) + card.headerXpBarBorderBottom:SetHeight(1) + card.headerXpBarBorderLeft = card.headerXpBar:CreateTexture(nil, "BORDER") + card.headerXpBarBorderLeft:SetTexture("Interface\\Buttons\\WHITE8X8") + card.headerXpBarBorderLeft:SetVertexColor(0.45, 0.45, 0.45, 0.85) + card.headerXpBarBorderLeft:SetPoint("TOPLEFT", card.headerXpBar, "TOPLEFT", 0, 0) + card.headerXpBarBorderLeft:SetPoint("BOTTOMLEFT", card.headerXpBar, "BOTTOMLEFT", 0, 0) + card.headerXpBarBorderLeft:SetWidth(1) + card.headerXpBarBorderRight = card.headerXpBar:CreateTexture(nil, "BORDER") + card.headerXpBarBorderRight:SetTexture("Interface\\Buttons\\WHITE8X8") + card.headerXpBarBorderRight:SetVertexColor(0.45, 0.45, 0.45, 0.85) + card.headerXpBarBorderRight:SetPoint("TOPRIGHT", card.headerXpBar, "TOPRIGHT", 0, 0) + card.headerXpBarBorderRight:SetPoint("BOTTOMRIGHT", card.headerXpBar, "BOTTOMRIGHT", 0, 0) + card.headerXpBarBorderRight:SetWidth(1) + card.headerXpText = card:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + card.headerXpText:SetJustifyH("CENTER") + card.headerXpText:SetJustifyV("MIDDLE") + card.headerXpText:SetTextColor(0.92, 0.92, 0.92) + card.headerXpText:SetText("") + card.headerXpText:Hide() + + card.sepAB = card:CreateTexture(nil, "BORDER") + card.sepAB:SetTexture("Interface\\Buttons\\WHITE8X8") + card.sepAB:SetVertexColor(0.36, 0.36, 0.36, 0.70) + card.sepAB:SetWidth(1) + + card.sepHA = card:CreateTexture(nil, "BORDER") + card.sepHA:SetTexture("Interface\\Buttons\\WHITE8X8") + card.sepHA:SetVertexColor(0.36, 0.36, 0.36, 0.70) + card.sepHA:SetWidth(1) + + card.sepBC = card:CreateTexture(nil, "BORDER") + card.sepBC:SetTexture("Interface\\Buttons\\WHITE8X8") + card.sepBC:SetVertexColor(0.36, 0.36, 0.36, 0.70) + card.sepBC:SetWidth(1) + + card.sepCD = card:CreateTexture(nil, "BORDER") + card.sepCD:SetTexture("Interface\\Buttons\\WHITE8X8") + card.sepCD:SetVertexColor(0.36, 0.36, 0.36, 0.70) + card.sepCD:SetWidth(1) + + card.sectionLabelHeader = card:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + card.sectionLabelHeader:SetPoint("TOPLEFT", card, "TOPLEFT", 10, -8) + card.sectionLabelHeader:SetJustifyH("LEFT") + card.sectionLabelHeader:SetTextColor(1.00, 0.82, 0.00) + card.sectionLabelHeader:SetText("") + + card.sectionLabelA = card:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + card.sectionLabelA:SetJustifyH("LEFT") + card.sectionLabelA:SetTextColor(1.00, 0.82, 0.00) + card.sectionLabelA:SetText("") + + card.sectionLabelB = card:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + card.sectionLabelB:SetJustifyH("LEFT") + card.sectionLabelB:SetTextColor(1.00, 0.82, 0.00) + card.sectionLabelB:SetText("B") + + card.sectionLabelC = card:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + card.sectionLabelC:SetJustifyH("LEFT") + card.sectionLabelC:SetTextColor(1.00, 0.82, 0.00) + card.sectionLabelC:SetText("C") + + card.sectionLabelD = card:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + card.sectionLabelD:SetJustifyH("LEFT") + card.sectionLabelD:SetTextColor(1.00, 0.82, 0.00) + card.sectionLabelD:SetText("D") + + card.detailA = card:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + card.detailA:SetJustifyH("LEFT") + card.detailA:SetJustifyV("TOP") + + card.detailB = card:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + card.detailB:SetJustifyH("LEFT") + card.detailB:SetJustifyV("TOP") + card.detailB:SetTextColor(1.00, 1.00, 1.00) + if card.detailB.SetWordWrap then card.detailB:SetWordWrap(false) end + if card.detailB.SetNonSpaceWrap then card.detailB:SetNonSpaceWrap(false) end + + card.detailC = card:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + card.detailC:SetJustifyH("LEFT") + card.detailC:SetJustifyV("TOP") + card.detailC:SetTextColor(1.00, 1.00, 1.00) + if card.detailC.SetWordWrap then card.detailC:SetWordWrap(false) end + if card.detailC.SetNonSpaceWrap then card.detailC:SetNonSpaceWrap(false) end + + card.detailD = card:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + card.detailD:SetJustifyH("LEFT") + card.detailD:SetJustifyV("TOP") + card.detailD:SetTextColor(1.00, 1.00, 1.00) + if card.detailD.SetWordWrap then card.detailD:SetWordWrap(false) end + if card.detailD.SetNonSpaceWrap then card.detailD:SetNonSpaceWrap(false) end + + card.tameBeastLinkButton = CreateFrame("Button", nil, card) + card.tameBeastLinkButton:SetHeight(12) + card.tameBeastLinkButton:SetWidth(96) + card.tameBeastLinkButton.text = card.tameBeastLinkButton:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + card.tameBeastLinkButton.text:SetAllPoints(card.tameBeastLinkButton) + card.tameBeastLinkButton.text:SetJustifyH("LEFT") + card.tameBeastLinkButton.text:SetTextColor(0.35, 0.75, 1.00) + if card.tameBeastLinkButton.text.SetWordWrap then card.tameBeastLinkButton.text:SetWordWrap(false) end + if card.tameBeastLinkButton.text.SetNonSpaceWrap then card.tameBeastLinkButton.text:SetNonSpaceWrap(false) end + card.tameBeastLinkButton:SetScript("OnClick", function() + if not this then return end + if this.beastId and type(MTH_BOOK_JumpToBeastById) == "function" then + MTH_BOOK_JumpToBeastById(this.beastId) + end + end) + card.tameBeastLinkButton:SetScript("OnEnter", function() + if this and this.text then + this.text:SetTextColor(0.50, 0.85, 1.00) + end + end) + card.tameBeastLinkButton:SetScript("OnLeave", function() + if this and this.text then + this.text:SetTextColor(0.35, 0.75, 1.00) + end + end) + card.tameBeastLinkButton:Hide() + + card.tameMapButton = CreateFrame("Button", nil, card, "UIPanelButtonTemplate") + card.tameMapButton:SetHeight(16) + card.tameMapButton:SetWidth(106) + card.tameMapButton:SetText("Show Taming Loc") + card.tameMapButton:SetScript("OnClick", function() + if not this then return end + if type(MTH_BOOK_OpenTamePointOnMap) ~= "function" then return end + local zoneId = tonumber(this.zoneId) + local x = tonumber(this.coordX) + local y = tonumber(this.coordY) + if zoneId and x and y then + MTH_BOOK_OpenTamePointOnMap(zoneId, x, y, this.pointTitle, this.pointDetail) + end + end) + card.tameMapButton:Hide() + + card.abilitySlots = {} + for slotIndex = 1, 8 do + local slotButton = CreateFrame("Button", nil, card) + slotButton:SetFrameStrata("DIALOG") + slotButton:SetFrameLevel((card.GetFrameLevel and card:GetFrameLevel() or 1) + 2) + slotButton:SetHeight(12) + + slotButton.icon = slotButton:CreateTexture(nil, "ARTWORK") + slotButton.icon:SetPoint("LEFT", slotButton, "LEFT", 0, 0) + slotButton.icon:SetWidth(12) + slotButton.icon:SetHeight(12) + + slotButton.text = slotButton:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + slotButton.text:SetPoint("LEFT", slotButton.icon, "RIGHT", 4, 0) + slotButton.text:SetPoint("RIGHT", slotButton, "RIGHT", 0, 0) + slotButton.text:SetJustifyH("LEFT") + slotButton.text:SetTextColor(1.00, 1.00, 1.00) + + slotButton.entry = nil + slotButton:SetScript("OnEnter", function() + if this and this.entry then + MTH_BOOK_ShowStableAbilityTooltip(this, this.entry) + end + end) + slotButton:SetScript("OnLeave", function() + MTH_BOOK_HideSpellTooltip() + end) + slotButton:Hide() + card.abilitySlots[slotIndex] = slotButton + end + + card.feedFoodRows = {} + for foodRowIndex = 1, 3 do + local foodRow = CreateFrame("Button", nil, card) + foodRow:SetFrameStrata("DIALOG") + foodRow:SetFrameLevel((card.GetFrameLevel and card:GetFrameLevel() or 1) + 2) + foodRow:SetHeight(12) + foodRow:SetWidth(10) + foodRow.itemId = nil + foodRow.itemLink = nil + foodRow.itemName = nil + + foodRow.countText = foodRow:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + foodRow.countText:SetPoint("LEFT", foodRow, "LEFT", 0, 0) + foodRow.countText:SetWidth(14) + foodRow.countText:SetJustifyH("LEFT") + foodRow.countText:SetTextColor(1.00, 0.82, 0.00) + + foodRow.icon = foodRow:CreateTexture(nil, "ARTWORK") + foodRow.icon:SetPoint("LEFT", foodRow.countText, "RIGHT", 0, 0) + foodRow.icon:SetWidth(12) + foodRow.icon:SetHeight(12) + + foodRow.nameButton = CreateFrame("Button", nil, foodRow) + foodRow.nameButton:SetHeight(12) + foodRow.nameButton:SetPoint("LEFT", foodRow.icon, "RIGHT", 2, 0) + foodRow.nameButton:SetPoint("RIGHT", foodRow, "RIGHT", 0, 0) + foodRow.nameButton.itemId = nil + foodRow.nameButton.itemLink = nil + foodRow.nameButton.itemName = nil + + foodRow.nameText = foodRow.nameButton:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + foodRow.nameText:SetAllPoints(foodRow.nameButton) + foodRow.nameText:SetJustifyH("LEFT") + foodRow.nameText:SetTextColor(1.00, 1.00, 1.00) + + foodRow.nameButton:SetScript("OnEnter", function() + if not this then return end + if not GameTooltip then return end + if this.itemId then + this.itemLink = MTH_BOOK_NormalizeStableFoodLink(this.itemId, this.itemLink, this.itemName) + end + if type(MTH_PrimeItemCache) == "function" and this.itemId then + MTH_PrimeItemCache(this.itemId) + end + if this.itemLink and this.itemLink ~= "" then + GameTooltip:SetOwner(this, "ANCHOR_RIGHT") + local ok, err = pcall(GameTooltip.SetHyperlink, GameTooltip, this.itemLink) + if not ok then + if this.itemId then + local fallbackLink = "item:" .. tostring(this.itemId) .. ":0:0:0" + local fallbackOk, fallbackErr = pcall(GameTooltip.SetHyperlink, GameTooltip, fallbackLink) + if not fallbackOk then + GameTooltip:ClearLines() + GameTooltip:AddLine(tostring(this.itemName or ("Item #" .. tostring(this.itemId))), 1.00, 1.00, 1.00) + end + else + GameTooltip:ClearLines() + GameTooltip:AddLine(tostring(this.itemName or "Unknown Food"), 1.00, 1.00, 1.00) + end + end + GameTooltip:Show() + local rowFrame = this:GetParent() + if rowFrame then + if type(GetItemInfo) == "function" and this.itemId then + local _, _, _, _, _, _, _, _, _, itemTexture = GetItemInfo(this.itemId) + if itemTexture and itemTexture ~= "" then + rowFrame.iconHint = itemTexture + end + end + MTH_BOOK_UpdateStableFoodRowIcon(rowFrame) + end + return + end + if this.itemId then + GameTooltip:SetOwner(this, "ANCHOR_RIGHT") + local itemLink = "item:" .. tostring(this.itemId) .. ":0:0:0" + local ok, err = pcall(GameTooltip.SetHyperlink, GameTooltip, itemLink) + if not ok then + GameTooltip:ClearLines() + GameTooltip:AddLine(tostring(this.itemName or ("Item #" .. tostring(this.itemId))), 1.00, 1.00, 1.00) + end + GameTooltip:Show() + local rowFrame = this:GetParent() + if rowFrame then + if type(GetItemInfo) == "function" then + local _, _, _, _, _, _, _, _, _, itemTexture = GetItemInfo(this.itemId) + if itemTexture and itemTexture ~= "" then + rowFrame.iconHint = itemTexture + end + end + MTH_BOOK_UpdateStableFoodRowIcon(rowFrame) + end + return + end + if this.itemName and this.itemName ~= "" then + GameTooltip:SetOwner(this, "ANCHOR_RIGHT") + GameTooltip:ClearLines() + GameTooltip:AddLine(tostring(this.itemName), 1.00, 1.00, 1.00) + GameTooltip:Show() + end + end) + foodRow.nameButton:SetScript("OnLeave", function() + if GameTooltip then GameTooltip:Hide() end + end) + + foodRow:Hide() + card.feedFoodRows[foodRowIndex] = foodRow + end + + ui.cards[i] = card + end + + MTH_BOOK_STATE.stableUI = ui + MTH_BOOK_DebugTrace("EnsureStableUI created cards=" .. tostring(table.getn(ui.cards))) +end + +function MTH_BOOKTAB_SetStableUIVisible(visible) + MTH_BOOKTAB_EnsureStableUI() + local ui = MTH_BOOK_STATE.stableUI + if not ui then return end + + local listParent = getglobal("MTH_BOOK_ListBackdrop") + local detailParent = getglobal("MTH_BOOK_DetailBackdrop") + local detailText = getglobal("MTH_BOOK_DetailBackdropDetailText") + local openMapButton = getglobal("MTH_BOOK_OpenMapButton") + if visible then + if listParent then listParent:Show() end + if detailParent then detailParent:Hide() end + if detailText then detailText:Hide() end + if openMapButton then openMapButton:Hide() end + ui.frame:SetAlpha(1) + ui.frame:Show() + else + ui.frame:Hide() + if listParent then listParent:Show() end + if detailParent then detailParent:Show() end + if detailText then detailText:Show() end + end +end + +function MTH_BOOKTAB_RenderStableCards() + MTH_BOOKTAB_EnsureStableUI() + local ui = MTH_BOOK_STATE.stableUI + if not ui then + MTH_BOOK_DebugTrace("RenderStableCards aborted: stableUI nil") + return + end + + local results = MTH_BOOK_STATE.results or {} + local count = table.getn(ui.cards) + local visibleCardCount = MTH_BOOK_GetStableRenderCardCount(count) + MTH_BOOK_DebugTrace("RenderStableCards results=" .. tostring(table.getn(results)) .. " cards=" .. tostring(count)) + local topPad = 8 + local bottomPad = 2 + local smallGap = 7 + local bigGap = 13 + local cardHeight = MTH_BOOK_GetStableRenderCardHeight((ui.frame:GetHeight() or 380), visibleCardCount, topPad, bottomPad, bigGap, smallGap) + cardHeight = cardHeight + 6 + + local slotEntries = {} + for r = 1, table.getn(results) do + local resultPetId = results[r] + local resultRow = resultPetId and MTH_BOOK_GetPetStoreRow(resultPetId) or nil + local slotNumber = MTH_BOOK_GetStableDisplaySlot(resultPetId, resultRow) + if resultRow and slotNumber and slotNumber >= 0 and slotNumber <= 4 then + slotEntries[slotNumber] = { petId = resultPetId, row = resultRow } + end + end + + local y = -topPad + for i = 1, count do + local card = ui.cards[i] + if i > visibleCardCount then + card:Hide() + else + local slotIndex = i - 1 + local slotEntry = slotEntries[slotIndex] + local petId = slotEntry and slotEntry.petId or nil + local row = slotEntry and slotEntry.row or nil + MTH_BOOK_DebugTrace("Render card#" .. tostring(i) .. " petId=" .. tostring(petId) .. " hasRow=" .. tostring(type(row) == "table")) + + card:ClearAllPoints() + card:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 8, y) + card:SetPoint("TOPRIGHT", ui.frame, "TOPRIGHT", -8, y) + card:SetHeight(cardHeight) + local cardWidth = 0 + if card.GetRight and card.GetLeft then + local right = tonumber(card:GetRight()) + local left = tonumber(card:GetLeft()) + if right and left and right > left then + cardWidth = math.floor(right - left) + end + end + if cardWidth <= 0 then + local frameWidth = math.floor(tonumber(ui.frame:GetWidth()) or 0) + if frameWidth <= 0 and ui.frame.GetRight and ui.frame.GetLeft then + local right = tonumber(ui.frame:GetRight()) + local left = tonumber(ui.frame:GetLeft()) + if right and left and right > left then + frameWidth = math.floor(right - left) + end + end + if frameWidth <= 0 then frameWidth = 900 end + cardWidth = frameWidth - 16 + end + if cardWidth < 260 then cardWidth = 260 end + local topY = -6 + local bottomY = 4 + local headerWidth = 139 + local bodyRight = cardWidth - 10 + local bodyWidth = bodyRight - headerWidth + if bodyWidth < 4 then bodyWidth = 4 end + local columnWidth = math.floor(bodyWidth / 4) + local remainder = bodyWidth - (columnWidth * 4) + local widthA = columnWidth + local widthB = columnWidth + local widthC = columnWidth + local widthD = columnWidth + if remainder > 0 then widthA = widthA + 1 remainder = remainder - 1 end + if remainder > 0 then widthB = widthB + 1 remainder = remainder - 1 end + if remainder > 0 then widthC = widthC + 1 remainder = remainder - 1 end + if remainder > 0 then widthD = widthD + 1 end + local xHA = headerWidth + local xAB = xHA + widthA + local xBC = xAB + widthB + local xCD = xBC + widthC + local xDD = xCD + widthD + + card.sepHA:ClearAllPoints() + card.sepHA:SetPoint("TOPLEFT", card, "TOPLEFT", xHA, topY) + card.sepHA:SetPoint("BOTTOMLEFT", card, "BOTTOMLEFT", xHA, bottomY) + card.sepAB:ClearAllPoints() + card.sepAB:SetPoint("TOPLEFT", card, "TOPLEFT", xAB, topY) + card.sepAB:SetPoint("BOTTOMLEFT", card, "BOTTOMLEFT", xAB, bottomY) + card.sepBC:ClearAllPoints() + card.sepBC:SetPoint("TOPLEFT", card, "TOPLEFT", xBC, topY) + card.sepBC:SetPoint("BOTTOMLEFT", card, "BOTTOMLEFT", xBC, bottomY) + card.sepCD:ClearAllPoints() + card.sepCD:SetPoint("TOPLEFT", card, "TOPLEFT", xCD, topY) + card.sepCD:SetPoint("BOTTOMLEFT", card, "BOTTOMLEFT", xCD, bottomY) + + card.sectionLabelHeader:ClearAllPoints() + card.sectionLabelHeader:SetPoint("TOPLEFT", card, "TOPLEFT", 10, topY) + card.sectionLabelHeader:SetTextColor(0.75, 0.75, 0.75) + card.sectionLabelA:ClearAllPoints() + card.sectionLabelA:SetPoint("TOPLEFT", card, "TOPLEFT", xHA + 8, topY) + card.sectionLabelA:SetTextColor(1.00, 0.82, 0.00) + card.sectionLabelA:SetText("Training") + card.sectionLabelB:ClearAllPoints() + card.sectionLabelB:SetPoint("TOPLEFT", card, "TOPLEFT", xAB + 8, topY) + card.sectionLabelB:SetTextColor(1.00, 0.82, 0.00) + card.sectionLabelB:SetText("Stable info") + card.sectionLabelC:ClearAllPoints() + card.sectionLabelC:SetPoint("TOPLEFT", card, "TOPLEFT", xBC + 8, topY) + card.sectionLabelC:SetTextColor(1.00, 0.82, 0.00) + card.sectionLabelC:SetText("Taming info") + card.sectionLabelD:ClearAllPoints() + card.sectionLabelD:SetPoint("TOPLEFT", card, "TOPLEFT", xCD + 8, topY) + card.sectionLabelD:SetTextColor(1.00, 0.82, 0.00) + card.sectionLabelD:SetText("Feeding") + + card.icon:ClearAllPoints() + card.icon:SetPoint("TOPLEFT", card, "TOPLEFT", 10, topY - 14) + card.icon:SetWidth(36) + card.icon:SetHeight(36) + card.name:ClearAllPoints() + card.name:SetPoint("TOPLEFT", card.icon, "TOPRIGHT", 8, 2) + card.name:SetPoint("TOPRIGHT", card, "TOPLEFT", xHA - 8, topY - 14) + card.meta:ClearAllPoints() + card.meta:SetPoint("TOPLEFT", card.icon, "TOPRIGHT", 8, -15) + card.meta:SetPoint("TOPRIGHT", card, "BOTTOMLEFT", xHA - 8, topY - 24) + card.metaLoyalty:ClearAllPoints() + card.metaLoyalty:SetPoint("TOPLEFT", card.meta, "BOTTOMLEFT", 0, 0) + card.metaLoyalty:SetPoint("TOPRIGHT", card, "BOTTOMLEFT", xHA - 8, topY - 36) + if card.headerXpBar then + card.headerXpBar:ClearAllPoints() + card.headerXpBar:SetPoint("BOTTOMLEFT", card, "BOTTOMLEFT", 10, 8) + card.headerXpBar:SetPoint("BOTTOMRIGHT", card, "BOTTOMLEFT", xHA - 8, 8) + card.headerXpBar:SetHeight(10) + end + if card.headerXpText then + card.headerXpText:ClearAllPoints() + card.headerXpText:SetPoint("CENTER", card.headerXpBar or card, "CENTER", 0, 0) + end + + card.detailA:ClearAllPoints() + card.detailA:SetPoint("TOPLEFT", card, "TOPLEFT", xHA + 8, topY - 14) + card.detailA:SetPoint("BOTTOMRIGHT", card, "BOTTOMLEFT", xAB - 8, bottomY) + card.detailA:SetText("") + + card.detailB:ClearAllPoints() + card.detailB:SetPoint("TOPLEFT", card, "TOPLEFT", xAB + 8, topY - 14) + card.detailB:SetPoint("BOTTOMRIGHT", card, "BOTTOMLEFT", xBC - 8, bottomY) + + card.detailC:ClearAllPoints() + card.detailC:SetPoint("TOPLEFT", card, "TOPLEFT", xBC + 8, topY - 14) + card.detailC:SetPoint("BOTTOMRIGHT", card, "BOTTOMLEFT", xCD - 8, bottomY) + + card.detailD:ClearAllPoints() + card.detailD:SetPoint("TOPLEFT", card, "TOPLEFT", xCD + 8, topY - 14) + card.detailD:SetPoint("BOTTOMRIGHT", card, "BOTTOMLEFT", xDD - 8, bottomY) + card.detailB:SetText("") + card.detailC:SetText("") + card.detailD:SetText("") + + local bLines = MTH_BOOK_EnsureStableColumnLines(card, "detailBLines", 5) + local cLines = MTH_BOOK_EnsureStableColumnLines(card, "detailCLines", 5) + local dLines = MTH_BOOK_EnsureStableColumnLines(card, "detailDLines", 5) + local lineTop = topY - 14 + local lineStep = 12 + for iLine = 1, 5 do + local line = bLines[iLine] + line:ClearAllPoints() + line:SetPoint("TOPLEFT", card, "TOPLEFT", xAB + 8, lineTop - ((iLine - 1) * lineStep)) + line:SetPoint("RIGHT", card, "BOTTOMLEFT", xBC - 8, lineTop - ((iLine - 1) * lineStep)) + line:SetText("") + end + for iLine = 1, 5 do + local line = cLines[iLine] + line:ClearAllPoints() + line:SetPoint("TOPLEFT", card, "TOPLEFT", xBC + 8, lineTop - ((iLine - 1) * lineStep)) + line:SetPoint("RIGHT", card, "BOTTOMLEFT", xCD - 8, lineTop - ((iLine - 1) * lineStep)) + line:SetTextColor(1.00, 1.00, 1.00) + line:SetText("") + end + for iLine = 1, 5 do + local line = dLines[iLine] + line:ClearAllPoints() + line:SetPoint("TOPLEFT", card, "TOPLEFT", xCD + 8, lineTop - ((iLine - 1) * lineStep)) + line:SetPoint("RIGHT", card, "BOTTOMLEFT", xDD - 8, lineTop - ((iLine - 1) * lineStep)) + line:SetText("") + end + + for foodRowIndex = 1, table.getn(card.feedFoodRows or {}) do + local foodRow = card.feedFoodRows[foodRowIndex] + if foodRow then + foodRow:ClearAllPoints() + foodRow:SetPoint("TOPLEFT", card, "TOPLEFT", xCD + 8, lineTop - (foodRowIndex * lineStep)) + foodRow:SetWidth((xDD - xCD) - 16) + foodRow.itemId = nil + foodRow.itemLink = nil + foodRow.itemName = nil + foodRow.iconHint = nil + if foodRow.countText then foodRow.countText:SetText("") end + if foodRow.icon then foodRow.icon:SetTexture(nil) end + if foodRow.nameText then foodRow.nameText:SetText("") end + if foodRow.nameButton then + foodRow.nameButton.itemId = nil + foodRow.nameButton.itemLink = nil + foodRow.nameButton.itemName = nil + foodRow.nameButton:SetWidth(10) + end + foodRow:Hide() + end + end + + local aLeft = xHA + 8 + local aRight = xAB - 8 + local aTop = lineTop + local aWidth = aRight - aLeft + if aWidth < 20 then aWidth = 20 end + local colGap = 4 + local innerWidth = aWidth - colGap + if innerWidth < 16 then innerWidth = 16 end + local leftColWidth = math.floor(innerWidth * 0.62) + local rightColWidth = innerWidth - leftColWidth + if leftColWidth < 8 then leftColWidth = 8 end + if rightColWidth < 8 then rightColWidth = 8 end + local rowHeight = 12 + local rowStep = 13 + + for slotButtonIndex = 1, table.getn(card.abilitySlots or {}) do + local slotButton = card.abilitySlots[slotButtonIndex] + if slotButton then + local rowIndex = math.floor((slotButtonIndex - 1) / 2) + 1 + local remainder = (slotButtonIndex - 1) - (math.floor((slotButtonIndex - 1) / 2) * 2) + local colIndex = remainder + 1 + local xOffset = aLeft + local slotWidth = leftColWidth + if colIndex == 2 then + xOffset = aLeft + leftColWidth + colGap + 4 + slotWidth = rightColWidth + end + local yOffset = aTop - ((rowIndex - 1) * rowStep) + slotButton:ClearAllPoints() + slotButton:SetPoint("TOPLEFT", card, "TOPLEFT", xOffset, yOffset) + slotButton:SetWidth(slotWidth) + slotButton:SetHeight(rowHeight) + slotButton.entry = nil + slotButton:Hide() + end + end + + if card.tameBeastLinkButton then + card.tameBeastLinkButton:ClearAllPoints() + card.tameBeastLinkButton:SetPoint("TOPLEFT", card, "TOPLEFT", xBC + 64, lineTop - lineStep + 1) + local tameLinkWidth = (xCD - xBC) - 82 + if tameLinkWidth < 24 then tameLinkWidth = 24 end + card.tameBeastLinkButton:SetWidth(tameLinkWidth) + card.tameBeastLinkButton.beastId = nil + card.tameBeastLinkButton.text:SetText("") + card.tameBeastLinkButton:Hide() + end + if card.tameMapButton then + card.tameMapButton:ClearAllPoints() + card.tameMapButton:SetPoint("TOP", card, "TOPLEFT", math.floor((xBC + xCD) / 2), topY - 50) + card.tameMapButton.zoneId = nil + card.tameMapButton.coordX = nil + card.tameMapButton.coordY = nil + card.tameMapButton.pointTitle = nil + card.tameMapButton.pointDetail = nil + card.tameMapButton:Hide() + end + + if row then + local slot = slotIndex + if slot == 0 then + card.sectionLabelB:SetText("Last stable info") + else + card.sectionLabelB:SetText("Stable info") + end + local nameText = tostring(row.name or "Unknown") + local loyaltyLevelValue = tonumber(row.loyaltyLevel) + if not loyaltyLevelValue and row.stableInfo then + loyaltyLevelValue = tonumber(row.stableInfo.loyaltyLevel) + end + local loyaltyTextValue = nil + if loyaltyLevelValue then + loyaltyTextValue = MTH_BOOK_GetLoyaltyNameByLevel(loyaltyLevelValue) + end + if not loyaltyTextValue or loyaltyTextValue == "" then + loyaltyTextValue = "-" + end + local loyaltyColorValue = loyaltyTextValue + if loyaltyLevelValue then + loyaltyColorValue = "Loyalty Level " .. tostring(loyaltyLevelValue) + end + card.name:SetText(nameText) + + card.sectionLabelHeader:SetText("") + card.meta:SetText(tostring(row.family or "-") .. " " .. tostring(row.level or "-")) + card.metaLoyalty:SetTextColor(MTH_BOOK_GetLoyaltyHeaderColor(loyaltyColorValue)) + card.metaLoyalty:SetText(tostring(loyaltyTextValue or "-")) + + if card.headerXpBar then + local petLevel = tonumber(row.level) + local xpCur = tonumber(row.xp) + local xpMax = tonumber(row.xpMax) + local xpPercent = tonumber(row.xpPercent) + if (not xpCur or not xpMax or xpMax <= 0) and type(row.stableInfo) == "table" then + xpCur = xpCur or tonumber(row.stableInfo.r6) + xpMax = xpMax or tonumber(row.stableInfo.r7) + xpPercent = xpPercent or tonumber(row.stableInfo.r8) + end + if (not xpCur or not xpMax or xpMax <= 0) and type(row.stableRaw) == "table" then + xpCur = xpCur or tonumber(row.stableRaw[6]) + xpMax = xpMax or tonumber(row.stableRaw[7]) + xpPercent = xpPercent or tonumber(row.stableRaw[8]) + end + + if petLevel and petLevel < 60 then + local barMin = 0 + local barMax = 1 + local barValue = 0 + + if xpCur and xpMax and xpMax > 0 then + if xpCur < 0 then xpCur = 0 end + if xpCur > xpMax then xpCur = xpMax end + barMax = xpMax + barValue = xpCur + elseif xpPercent and xpPercent >= 0 then + if xpPercent > 100 then xpPercent = 100 end + barMax = 100 + barValue = xpPercent + end + + card.headerXpBar:SetMinMaxValues(barMin, barMax) + card.headerXpBar:SetValue(barValue) + card.headerXpBar:Show() + if card.headerXpText then + card.headerXpText:SetText("") + card.headerXpText:Hide() + end + else + card.headerXpBar:Hide() + if card.headerXpText then + card.headerXpText:SetText("") + card.headerXpText:Hide() + end + end + end + + local iconPath = tostring(row.icon or "") + if slot == 0 then + local lowerIconPath = string.lower(iconPath) + if iconPath == "" or lowerIconPath == "ability_hunter_beasttaming" then + local fallbackIcon = (type(row.stableInfo) == "table") and tostring(row.stableInfo.icon or "") or "" + if fallbackIcon == "" and type(row.stableRaw) == "table" then + fallbackIcon = tostring(row.stableRaw[1] or "") + end + if fallbackIcon ~= "" then + iconPath = fallbackIcon + end + end + end + + card.icon:Show() + if iconPath ~= "" then + if string.find(iconPath, "\\", 1, true) then + card.icon:SetTexture(iconPath) + else + card.icon:SetTexture("Interface\\Icons\\" .. iconPath) + end + else + card.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") + end + + local leftAbilities, rightAbilities = MTH_BOOK_BuildStablePetAbilityEntries(row) + local abilitiesCount = MTH_BOOK_CountPetAbilitiesMap(row.abilities) + local abilitiesText = MTH_BOOK_GetStableAbilitySummary(row.abilities) + local legacyTameContext = type(row.tameContext) == "table" and row.tameContext or nil + local hasRecordedTame = row.tameRecorded == true + if not hasRecordedTame and legacyTameContext then + if legacyTameContext.name or legacyTameContext.zone or legacyTameContext.timestamp then + hasRecordedTame = true + end + end + local tameZone = hasRecordedTame and tostring(row.tameZone or (legacyTameContext and legacyTameContext.zone) or "-") or "-" + local tameX = hasRecordedTame and (MTH_BOOK_NormalizeMapCoordValue(row.tameX) or (legacyTameContext and MTH_BOOK_NormalizeMapCoordValue(legacyTameContext.x) or nil)) or nil + local tameY = hasRecordedTame and (MTH_BOOK_NormalizeMapCoordValue(row.tameY) or (legacyTameContext and MTH_BOOK_NormalizeMapCoordValue(legacyTameContext.y) or nil)) or nil + local tameCoords = "-" + if tameX and tameY then + tameCoords = string.format("%.1f, %.1f", tameX, tameY) + end + local tamedAt = hasRecordedTame and (tonumber(row.tamedAt) or (legacyTameContext and tonumber(legacyTameContext.timestamp) or nil)) or nil + local stableInfo = (type(row.stableInfo) == "table") and row.stableInfo or nil + local stabledAt = tonumber(row.stabledAt) or tonumber(row.stableFirstSeenAt) or (stableInfo and tonumber(stableInfo.stabledAt) or nil) + local stabledSince = MTH_BOOK_FormatElapsedStableInfo(stabledAt) + local pulledAt = tonumber(row.lastUnstabledAt) or stabledAt + local withMeSince = MTH_BOOK_FormatElapsedStableInfo(pulledAt) + local stableZone = stableInfo and tostring(stableInfo.stableZone or "") or "" + local stableSubZone = stableInfo and tostring(stableInfo.stableSubZone or "") or "" + local stableMasterName = stableInfo and tostring(stableInfo.stableMasterName or "") or "" + local stableLocation = "-" + if stableSubZone ~= "" and stableZone ~= "" then + stableLocation = stableSubZone .. " (" .. stableZone .. ")" + elseif stableSubZone ~= "" then + stableLocation = stableSubZone + elseif stableZone ~= "" then + stableLocation = stableZone + end + if stableMasterName == "" then + stableMasterName = "-" + end + local rowOrigin = tostring(row.origin or row.lastSource or "-") + local rowLastUpdateAt = tonumber(row.lastUpdated) or tonumber(row.updatedAt) or tonumber(row.lastSeen) or nil + local tameBeastId = hasRecordedTame and (tonumber(row.tameBeastId) or (legacyTameContext and tonumber(legacyTameContext.beastId) or nil)) or nil + local tameBeast = tameBeastId and MTH_DS_Beasts and MTH_DS_Beasts[tameBeastId] or nil + local tameBeastName = tostring((tameBeast and tameBeast.name) or (legacyTameContext and legacyTameContext.name) or "-") + local tameZoneId = MTH_BOOK_ResolveStableTameZoneId(tameZone ~= "-" and tameZone or nil, tameBeast, tameX, tameY) + for rowIndex = 1, 4 do + local leftSlot = ((rowIndex - 1) * 2) + 1 + local rightSlot = leftSlot + 1 + local leftEntry = leftAbilities[rowIndex] + local rightEntry = rightAbilities[rowIndex] + + local leftButton = card.abilitySlots and card.abilitySlots[leftSlot] or nil + if leftButton then + if leftEntry then + leftButton.entry = leftEntry + leftButton.text:SetText(tostring(leftEntry.display or "-")) + local leftIconPath = MTH_BOOK_ResolveIconPath(leftEntry.icon) + if leftIconPath then + leftButton.icon:SetTexture(leftIconPath) + else + leftButton.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") + end + leftButton:Show() + else + leftButton.entry = nil + leftButton:Hide() + end + end + + local rightButton = card.abilitySlots and card.abilitySlots[rightSlot] or nil + if rightButton then + if rightEntry then + rightButton.entry = rightEntry + rightButton.text:SetText(tostring(rightEntry.display or "-")) + local rightIconPath = MTH_BOOK_ResolveIconPath(rightEntry.icon) + if rightIconPath then + rightButton.icon:SetTexture(rightIconPath) + else + rightButton.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") + end + rightButton:Show() + else + rightButton.entry = nil + rightButton:Hide() + end + end + end + if slot == 0 then + bLines[1]:SetText(MTH_BOOK_StableDetailKV("Pulled", MTH_BOOK_FormatTimestamp(pulledAt))) + bLines[2]:SetText(MTH_BOOK_StableDetailKV("For", tostring(withMeSince))) + bLines[3]:SetText(MTH_BOOK_StableDetailKV("Master", tostring(stableMasterName))) + bLines[4]:SetText(tostring(stableLocation)) + bLines[5]:SetText("") + else + bLines[1]:SetText(MTH_BOOK_StableDetailKV("Stabled", MTH_BOOK_FormatTimestamp(stabledAt))) + bLines[2]:SetText(MTH_BOOK_StableDetailKV("For", tostring(stabledSince))) + bLines[3]:SetText(MTH_BOOK_StableDetailKV("Master", tostring(stableMasterName))) + bLines[4]:SetText(tostring(stableLocation)) + bLines[5]:SetText("") + end + if hasRecordedTame then + cLines[1]:SetText(MTH_BOOK_StableDetailKV("Tamed on", MTH_BOOK_FormatTimestamp(tamedAt))) + cLines[2]:SetText("") + cLines[3]:SetText(MTH_BOOK_StableDetailKV("Taming Loc", tostring(tameZone))) + cLines[4]:SetText("") + cLines[5]:SetText("") + if card.tameBeastLinkButton and tameBeastId and tameBeastName ~= "-" then + cLines[2]:SetText(MTH_BOOK_StableDetailKV("Tamed Beast", "")) + card.tameBeastLinkButton.beastId = tameBeastId + card.tameBeastLinkButton.text:SetText("|cff59bfff" .. tostring(tameBeastName) .. "|r") + card.tameBeastLinkButton:Show() + else + cLines[2]:SetText(MTH_BOOK_StableDetailKV("Tamed Beast", tostring(tameBeastName))) + end + else + cLines[1]:SetText("") + cLines[2]:SetText("") + cLines[3]:SetTextColor(0.72, 0.72, 0.72) + cLines[3]:SetText("No info") + cLines[4]:SetText("") + cLines[5]:SetText("") + end + if card.tameMapButton and tameZoneId and tameX and tameY then + card.tameMapButton.zoneId = tameZoneId + card.tameMapButton.coordX = tameX + card.tameMapButton.coordY = tameY + card.tameMapButton.pointTitle = "Tame: " .. tostring(tameBeastName) + card.tameMapButton.pointDetail = "Zone: " .. tostring(tameZone) .. "\nCoords: " .. tostring(tameCoords) + card.tameMapButton:Show() + end + local totalFed, topFoods = MTH_BOOK_GetStableFeedSummary(petId) + local totalFedText = tostring(totalFed) + if tonumber(totalFed) == 1 then + dLines[1]:SetText("You fed it " .. totalFedText .. " time") + else + dLines[1]:SetText("You fed it " .. totalFedText .. " times") + end + dLines[2]:SetText("") + dLines[3]:SetText("") + dLines[4]:SetText("") + dLines[5]:SetText("") + for foodRowIndex = 1, table.getn(card.feedFoodRows or {}) do + local foodRow = card.feedFoodRows[foodRowIndex] + local foodEntry = topFoods[foodRowIndex] + if foodRow and foodEntry then + if type(MTH_PrimeItemCache) == "function" and foodEntry.itemId then + MTH_PrimeItemCache(foodEntry.itemId) + end + foodRow.itemId = foodEntry.itemId + foodRow.itemLink = foodEntry.link + foodRow.itemName = foodEntry.name + foodRow.iconHint = foodEntry.icon + if foodRow.countText then + foodRow.countText:SetText(tostring(foodEntry.count or 0)) + end + if foodRow.icon then + MTH_BOOK_UpdateStableFoodRowIcon(foodRow) + end + if foodRow.nameText then + foodRow.nameText:SetText(tostring(foodEntry.name or "-")) + end + if foodRow.nameButton then + foodRow.nameButton.itemId = foodEntry.itemId + foodRow.nameButton.itemLink = foodEntry.link + foodRow.nameButton.itemName = foodEntry.name + end + foodRow:Show() + elseif foodRow then + foodRow:Hide() + end + end + + if slot == 0 then + card.bg:SetVertexColor(0.16, 0.14, 0.08, 0.82) + else + card.bg:SetVertexColor(0.08, 0.08, 0.08, 0.72) + end + card:Show() + else + card.sectionLabelHeader:SetText("") + if slotIndex == 0 then + card.icon:Hide() + card.name:SetText("") + card.meta:SetText("No current pet") + card.detailA:SetText("") + card.detailB:SetText("") + card.detailC:SetText("") + card.detailD:SetText("") + else + card.icon:Hide() + card.name:SetText("") + card.meta:SetText("Free slot") + card.detailA:SetText("") + card.detailB:SetText("") + card.detailC:SetText("") + card.detailD:SetText("") + end + for iLine = 1, 5 do + if bLines and bLines[iLine] then bLines[iLine]:SetText("") end + if dLines and dLines[iLine] then dLines[iLine]:SetText("") end + end + for iLine = 1, 5 do + if cLines and cLines[iLine] then cLines[iLine]:SetText("") end + end + for foodRowIndex = 1, table.getn(card.feedFoodRows or {}) do + local foodRow = card.feedFoodRows[foodRowIndex] + if foodRow then + foodRow:Hide() + end + end + card.metaLoyalty:SetTextColor(0.84, 0.84, 0.84) + card.metaLoyalty:SetText("") + if card.headerXpBar then + card.headerXpBar:Hide() + end + if card.headerXpText then + card.headerXpText:SetText("") + card.headerXpText:Hide() + end + for slotButtonIndex = 1, table.getn(card.abilitySlots or {}) do + local slotButton = card.abilitySlots[slotButtonIndex] + if slotButton then + slotButton.entry = nil + slotButton:Hide() + end + end + card.bg:SetVertexColor(0.05, 0.05, 0.05, 0.55) + card:Show() + end + + y = y - cardHeight + if i == 1 and visibleCardCount > 1 then + y = y - bigGap + elseif i < visibleCardCount then + y = y - smallGap + end + end + end +end diff --git a/api/hunterbook-tab-weapons.lua b/api/hunterbook-tab-weapons.lua new file mode 100644 index 0000000..835c1e1 --- /dev/null +++ b/api/hunterbook-tab-weapons.lua @@ -0,0 +1,73 @@ +if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end + +MTH_HUNTERBOOK_TABS.items = { + headerLabel = "Ranged Weapons", + columnLabels = { "ID", "Req", "Lvl", "Type", "Name", "DPS", "Speed", "Source" }, + columnLayout = { + { x = 8, width = 36, align = "LEFT" }, + { x = 44, width = 30, align = "LEFT" }, + { x = 74, width = 30, align = "LEFT" }, + { x = 104, width = 62, align = "LEFT" }, + { x = 166, width = 188, align = "LEFT" }, + { x = 356, width = 44, align = "LEFT" }, + { x = 402, width = 40, align = "LEFT" }, + { x = 444, width = 108, align = "LEFT" }, + }, +} + +function MTH_BOOK_NormalizeItemSubtype(value) + local subtype = MTH_BOOK_SafeLower(value) + if subtype == "arrows" then return "arrow" end + if subtype == "bullets" then return "bullet" end + if subtype == "bows" then return "bow" end + if subtype == "rifle" or subtype == "rifles" then return "gun" end + if subtype == "cross bow" or subtype == "cross-bow" or subtype == "crossbows" or subtype == "xbow" or subtype == "xbows" then return "crossbow" end + return subtype +end + +function MTH_BOOK_IsRangedWeaponSubtype(subtype) + return subtype == "bow" or subtype == "gun" or subtype == "crossbow" +end + +function MTH_BOOK_ItemMatches(itemId, item) + if not item then return false end + if item.notingame then return false end + local itemSubtype = MTH_BOOK_NormalizeItemSubtype(item.subtype) + if not MTH_BOOK_IsRangedWeaponSubtype(itemSubtype) then return false end + local selectedSubtype = MTH_BOOK_NormalizeItemSubtype(MTH_BOOK_STATE.itemSubtype) + if selectedSubtype ~= "all" and itemSubtype ~= selectedSubtype then return false end + if MTH_BOOK_STATE.itemOnlyMyLevel then + local playerLevel = MTH_BOOK_GetPlayerLevelValue() + local reqLevel = tonumber(item.reqlevel) + if playerLevel and reqLevel and reqLevel > playerLevel then + return false + end + end + if MTH_BOOK_STATE.flag1 and not MTH_BOOK_HasEntries(item.vendors) then return false end + if MTH_BOOK_STATE.flag2 and not MTH_BOOK_HasEntries(item.drops) then return false end + if MTH_BOOK_STATE.flag3 and not MTH_BOOK_HasEntries(item.objects) then return false end + + if MTH_BOOK_STATE.search ~= "" then + local name = MTH_BOOK_SafeLower(item.name) + local idText = tostring(itemId) + if string.find(name, MTH_BOOK_STATE.search, 1, true) == nil and string.find(idText, MTH_BOOK_STATE.search, 1, true) == nil then + return false + end + end + + return true +end + +function MTH_BOOK_ItemSort(a, b) + local items = MTH_BOOK_GetItemsTable() + local ia = items and items[a] + local ib = items and items[b] + if not ia or not ib then return a < b end + local la = ia.level or 0 + local lb = ib.level or 0 + if la ~= lb then return la < lb end + local na = ia.name or "" + local nb = ib.name or "" + if na ~= nb then return na < nb end + return a < b +end diff --git a/api/hunterbook.lua b/api/hunterbook.lua new file mode 100644 index 0000000..927d420 --- /dev/null +++ b/api/hunterbook.lua @@ -0,0 +1,4946 @@ +-- MetaHunt standalone Hunter Book UI (XML + Lua) + +MTH_HUNTERBOOK_LOADED = true +MTH_HUNTERBOOK_TABS = MTH_HUNTERBOOK_TABS or {} + +local function MTH_BOOK_GetTabDefinition(mode) + if type(MTH_HUNTERBOOK_TABS) ~= "table" then return nil end + return MTH_HUNTERBOOK_TABS[tostring(mode or "")] +end + +MTH_BOOK_Browser = nil +local MTH_BOOK_RefreshFilter +local MTH_BOOK_UpdateResults +local MTH_BOOK_GetZoneName +local MTH_BOOK_GetPlayerLevelValue +local MTH_BOOK_IsSpellInLevelScope +local MTH_BOOK_GetScopedRankSummary +local MTH_BOOK_BEAST_DATABASE_URL = "https://database.turtlecraft.gg/?npc=%d" +local MTH_BOOK_NPC_DATABASE_URL = "https://database.turtlecraft.gg/?npc=%d" +local MTH_BOOK_ITEM_DATABASE_URL = "https://database.turtlecraft.gg/?item=%d" +local MTH_BOOK_STABLE_CURRENT_ID_CACHE = nil + +local function MTH_BOOK_FamiliesTrace(message) + return +end + +local MTH_BOOK_STATE = { + mode = "stable", + search = "", + quick = "all", + minLevel = nil, + maxLevel = nil, + flag1 = false, + flag2 = false, + flag3 = false, + petAbility = "all", + petRank = "all", + petLearnSource = "beast", + petOnlyMyLevel = true, + itemOnlyMyLevel = false, + petInZoneOnly = false, + itemSubtype = "all", + npcFunction = "all", + npcZone = "all", + npcInZoneOnly = false, + npcHideNoZone = true, + petHideNoAbilities = true, + petHideUnknown = true, + forcedBeastId = nil, + page = 1, + pageSize = 16, + petLeftOffset = 0, + results = {}, + selectedEntry = nil, + selectedPetRankEntry = nil, + petRankRows = {}, + petUI = nil, + stableUI = nil, + buttons = {}, + headerCols = {}, + headerButtons = {}, + sortByMode = { + pets = { col = nil, asc = true }, + families = { col = nil, asc = true }, + abilities = { col = nil, asc = true }, + items = { col = nil, asc = true }, + npcs = { col = nil, asc = true }, + stable = { col = nil, asc = true }, + pethistory = { col = 5, asc = false }, + }, + sliderDragging = false, +} + +_G.MTH_BOOK_STATE = MTH_BOOK_STATE + +local function MTH_HB_ReportGuard(message) + local text = "[MTH HUNTERBOOK] " .. tostring(message) + if MTH and MTH.Print then + MTH:Print(text, "error") + elseif type(MTH_Log) == "function" then + MTH_Log(text, "error") + elseif DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + DEFAULT_CHAT_FRAME:AddMessage(text) + end +end + +function MTH_BOOK_DebugTrace(message) + return +end + +local function MTH_BOOK_GetLoyaltyNameByLevel(level) + local resolvedLevel = tonumber(level) + if not resolvedLevel or resolvedLevel < 1 or resolvedLevel > 6 then + return "-" + end + local key = "PET_LOYALTY" .. tostring(resolvedLevel) + local name = (_G and _G[key]) or (type(getglobal) == "function" and getglobal(key)) + name = tostring(name or "") + if name == "" then + local defaults = { + [1] = "Rebellious", + [2] = "Unruly", + [3] = "Submissive", + [4] = "Dependable", + [5] = "Faithful", + [6] = "Best Friend", + } + name = defaults[resolvedLevel] or "-" + end + return tostring(name) +end + +local function MTH_HB_RequireOpenDeps() + if type(MTH_BOOK_RefreshFilter) ~= "function" then + MTH_HB_ReportGuard("Missing dependency: MTH_BOOK_RefreshFilter") + return false + end + return true +end + +local MTH_BOOK_MAX_COLS = 9 +local MTH_BOOK_MAX_ROWS = 20 + +local MTH_BOOK_PetFamilyOptions = { + { value = "all", label = "All Families" }, +} + +local MTH_BOOK_PetAbilityOptions = { + { value = "all", label = "All Abilities" }, +} + +local MTH_BOOK_PetRankOptions = { + { value = "all", label = "All Ranks" }, +} + +local MTH_BOOK_PetLearnSourceOptions = { + { value = "all", label = "All Abilities" }, + { value = "beast", label = "Beast Learned" }, + { value = "trainer", label = "Trainer Learned" }, +} + +local MTH_BOOK_NPCFunctionOptions = { + { value = "all", label = "All Hunter Friends" }, + { value = "huntertrainer", label = "Hunter Trainer" }, + { value = "pettrainer", label = "Pet Trainer" }, + { value = "vendor", label = "Ammo Vendor" }, +} + +local MTH_BOOK_NPCZoneOptions = { + { value = "all", label = "All Zones" }, +} + +local MTH_BOOK_ItemSubtypeOptions = { + { value = "all", label = "All Ranged" }, + { value = "bow", label = "Bow" }, + { value = "gun", label = "Gun" }, + { value = "crossbow", label = "Crossbow" }, +} + +local MTH_BOOK_PetAbilityRanks = {} + +local MTH_BOOK_CONTENT_LAYOUT_DEFAULT = { + listX = 16, + listY = -166, + listW = 564, + listH = 354, + detailX = 608, + detailY = -166, + detailW = 136, + detailH = 324, +} + +local MTH_BOOK_CONTENT_LAYOUT_ITEMS = { + listX = 16, + listY = -124, + listW = 564, + listH = 396, + detailX = 608, + detailY = -124, + detailW = 136, + detailH = 366, +} + +local MTH_BOOK_CONTENT_LAYOUT_PETABILITIES = { + listX = 16, + listY = -106, + listW = 564, + listH = 424, + detailX = 608, + detailY = -106, + detailW = 136, + detailH = 424, +} + +local MTH_BOOK_CONTENT_LAYOUT_NPCS = { + listX = 16, + listY = -142, + listW = 564, + listH = 378, + detailX = 608, + detailY = -142, + detailW = 136, + detailH = 348, +} + +local MTH_BOOK_CONTENT_LAYOUT_STABLE = { + listX = 16, + listY = -74, + listW = 728, + listH = 456, + detailX = 748, + detailY = -74, + detailW = 1, + detailH = 1, +} + +local MTH_BOOK_CONTENT_LAYOUT_FAMILIES = { + listX = 16, + listY = -74, + listW = 728, + listH = 376, + detailX = 748, + detailY = -74, + detailW = 1, + detailH = 1, +} + +local MTH_BOOK_CONTENT_LAYOUT_HISTORY = { + listX = 16, + listY = -74, + listW = 520, + listH = 446, + detailX = 564, + detailY = -74, + detailW = 180, + detailH = 446, +} + +local function MTH_BOOK_ApplyContentLayoutForMode() + local listParent = getglobal("MTH_BOOK_ListBackdrop") + local detailParent = getglobal("MTH_BOOK_DetailBackdrop") + local detailText = getglobal("MTH_BOOK_DetailBackdropDetailText") + local sliderBackdrop = getglobal("MTH_BOOK_ListSliderBackdrop") + local listSlider = getglobal("MTH_BOOK_ListSlider") + if not (listParent and detailParent) then return end + + local layout = MTH_BOOK_CONTENT_LAYOUT_DEFAULT + if MTH_BOOK_STATE.mode == "petabilities" then + layout = MTH_BOOK_CONTENT_LAYOUT_PETABILITIES + elseif MTH_BOOK_STATE.mode == "items" then + layout = MTH_BOOK_CONTENT_LAYOUT_ITEMS + elseif MTH_BOOK_STATE.mode == "npcs" then + layout = MTH_BOOK_CONTENT_LAYOUT_NPCS + elseif MTH_BOOK_STATE.mode == "families" then + layout = MTH_BOOK_CONTENT_LAYOUT_FAMILIES + elseif MTH_BOOK_STATE.mode == "stable" then + layout = MTH_BOOK_CONTENT_LAYOUT_STABLE + elseif MTH_BOOK_STATE.mode == "pethistory" then + layout = MTH_BOOK_CONTENT_LAYOUT_HISTORY + end + + local browserFrame = listParent:GetParent() + listParent:ClearAllPoints() + listParent:SetPoint("TOPLEFT", browserFrame, "TOPLEFT", layout.listX, layout.listY) + listParent:SetWidth(layout.listW) + listParent:SetHeight(layout.listH) + + detailParent:ClearAllPoints() + detailParent:SetPoint("TOPLEFT", browserFrame, "TOPLEFT", layout.detailX, layout.detailY) + detailParent:SetWidth(layout.detailW) + detailParent:SetHeight(layout.detailH) + if detailText then + detailText:ClearAllPoints() + detailText:SetPoint("TOPLEFT", detailParent, "TOPLEFT", 6, -6) + detailText:SetWidth(layout.detailW - 12) + detailText:SetHeight(layout.detailH - 12) + end + + local sliderHeight = layout.listH - 32 + if sliderHeight < 120 then sliderHeight = 120 end + if sliderBackdrop then + sliderBackdrop:SetHeight(sliderHeight) + end + if listSlider then + listSlider:SetHeight(sliderHeight) + end + + if MTH_BOOK_STATE.petUI then + local paneHeight = layout.listH - 8 + if paneHeight < 200 then paneHeight = 200 end + if MTH_BOOK_STATE.petUI.leftPane then MTH_BOOK_STATE.petUI.leftPane:SetHeight(paneHeight) end + if MTH_BOOK_STATE.petUI.rightPane then MTH_BOOK_STATE.petUI.rightPane:SetHeight(paneHeight) end + + local detailAvailable = layout.detailH - 60 + if detailAvailable < 120 then detailAvailable = 120 end + local topHeight = math.floor(detailAvailable * 0.54) + local bottomHeight = detailAvailable - topHeight + if MTH_BOOK_STATE.petUI.detailTop then MTH_BOOK_STATE.petUI.detailTop:SetHeight(topHeight) end + if MTH_BOOK_STATE.petUI.detailBottom then MTH_BOOK_STATE.petUI.detailBottom:SetHeight(bottomHeight) end + end +end + +local function MTH_BOOK_UpdateCheckboxLayoutByMode() + local controls = { + getglobal("MTH_BOOK_RequireVendor"), + getglobal("MTH_BOOK_RequireDrop"), + getglobal("MTH_BOOK_RequireObject"), + getglobal("MTH_BOOK_HideNoAbilities"), + getglobal("MTH_BOOK_HideUnknown"), + } + local xOffsets = { 18, 86, 154, 232, 372 } + local yOffset = -140 + if MTH_BOOK_STATE.mode == "npcs" then + yOffset = -116 + end + for i = 1, table.getn(controls) do + local control = controls[i] + if control then + control:ClearAllPoints() + control:SetPoint("TOPLEFT", control:GetParent(), "TOPLEFT", xOffsets[i], yOffset) + end + end +end + +local function MTH_BOOK_DropdownSetText(frame, text) + local value = text or "" + if frame and frame.GetName then + local frameName = frame:GetName() + local txt = getglobal(frameName .. "Text") + if txt and txt.SetText then + txt:SetText(value) + end + local button = getglobal(frameName .. "Button") + if button and button.GetFontString and button:GetFontString() and button:GetFontString().SetText then + button:GetFontString():SetText(value) + end + local buttonText = getglobal(frameName .. "ButtonNormalText") + if buttonText and buttonText.SetText then + buttonText:SetText(value) + end + end + if frame and frame.Text and frame.Text.SetText then + frame.Text:SetText(value) + end +end + +local function MTH_BOOK_DropdownCreateInfo() + local createInfo = _G["UIDropDownMenu_CreateInfo"] + if createInfo then return createInfo() end + return {} +end + +local function MTH_BOOK_SetSliderFromCursor(slider) + if not slider then return end + local minValue, maxValue = slider:GetMinMaxValues() + if not minValue or not maxValue then return end + if maxValue <= minValue then + slider:SetValue(minValue) + return + end + local cursorX, cursorY = GetCursorPosition() + local scale = slider:GetEffectiveScale() or 1 + local y = cursorY / scale + local bottom = slider:GetBottom() + local top = slider:GetTop() + if not bottom or not top or top <= bottom then return end + local pct = (y - bottom) / (top - bottom) + if pct < 0 then pct = 0 end + if pct > 1 then pct = 1 end + local value = minValue + (maxValue - minValue) * (1 - pct) + slider:SetValue(value) +end + +local function MTH_BOOK_UpdateSliderMarker(slider) + if not slider then return end + local lane = getglobal("MTH_BOOK_ListSliderBackdrop") + if not lane then return end + + if not lane._mthMarker then + lane._mthMarker = lane:CreateTexture(nil, "ARTWORK") + lane._mthMarker:SetTexture("Interface\\Buttons\\WHITE8X8") + lane._mthMarker:SetWidth(14) + lane._mthMarker:SetHeight(3) + lane._mthMarker:SetVertexColor(1.00, 0.82, 0.00, 0.90) + end + + local marker = lane._mthMarker + local minValue, maxValue = slider:GetMinMaxValues() + local value = slider:GetValue() or minValue or 1 + if not minValue or not maxValue or maxValue <= minValue then + marker:ClearAllPoints() + marker:SetPoint("TOP", lane, "TOP", 0, -4) + marker:Show() + return + end + + local pct = (value - minValue) / (maxValue - minValue) + if pct < 0 then pct = 0 end + if pct > 1 then pct = 1 end + + local travel = lane:GetHeight() - 8 - 8 + if travel < 0 then travel = 0 end + + marker:ClearAllPoints() + marker:SetPoint("BOTTOM", lane, "BOTTOM", 0, 4 + (travel * (1 - pct))) + marker:Show() +end + +local function MTH_BOOK_HideSliderTemplateTexts() + local sliderLow = getglobal("MTH_BOOK_ListSliderLow") + local sliderHigh = getglobal("MTH_BOOK_ListSliderHigh") + local sliderText = getglobal("MTH_BOOK_ListSliderText") + if sliderLow then sliderLow:Hide() end + if sliderHigh then sliderHigh:Hide() end + if sliderText then sliderText:Hide() end +end + +local function MTH_BOOK_SetFontSizeDelta(fontString, delta) + if not (fontString and fontString.GetFont and fontString.SetFont) then return end + local baseFont = fontString._mthBaseFont + local baseSize = fontString._mthBaseSize + local baseFlags = fontString._mthBaseFlags + if not baseFont or not baseSize then + local currentFont, currentSize, currentFlags = fontString:GetFont() + if not currentFont or not currentSize then return end + baseFont = currentFont + baseSize = currentSize + baseFlags = currentFlags + fontString._mthBaseFont = baseFont + fontString._mthBaseSize = baseSize + fontString._mthBaseFlags = baseFlags + end + fontString:SetFont(baseFont, baseSize + (delta or 0), baseFlags) +end + +local function MTH_BOOK_SetFamiliesListVisualScale(enabled) + local rowHeight = enabled and 20 or 19 + local textHeight = enabled and 15 or 14 + local iconSize = enabled and 15 or 14 + local chipHeight = enabled and 15 or 14 + local chipIcon = enabled and 13 or 12 + local fontDelta = enabled and 1 or 0 + + for i = 1, MTH_BOOK_MAX_COLS do + local header = MTH_BOOK_STATE.headerCols and MTH_BOOK_STATE.headerCols[i] or nil + if header then + header:SetHeight(textHeight) + MTH_BOOK_SetFontSizeDelta(header, fontDelta) + end + end + + for i = 1, table.getn(MTH_BOOK_STATE.buttons or {}) do + local button = MTH_BOOK_STATE.buttons[i] + if button then + button:SetHeight(rowHeight) + if button.bookLinkButton then button.bookLinkButton:SetHeight(rowHeight) end + if button.nameHoverButton then button.nameHoverButton:SetHeight(rowHeight) end + if button.itemLinkButton then button.itemLinkButton:SetHeight(rowHeight) end + if button.itemIcon then + button.itemIcon:SetWidth(iconSize) + button.itemIcon:SetHeight(iconSize) + end + for c = 1, table.getn(button.cols or {}) do + local col = button.cols[c] + if col then + col:SetHeight(textHeight) + MTH_BOOK_SetFontSizeDelta(col, fontDelta) + end + end + for chipIndex = 1, table.getn(button.familyAbilityButtons or {}) do + local chip = button.familyAbilityButtons[chipIndex] + if chip then + chip:SetHeight(chipHeight) + if chip.icon then + chip.icon:SetWidth(chipIcon) + chip.icon:SetHeight(chipIcon) + end + if chip.text then + MTH_BOOK_SetFontSizeDelta(chip.text, fontDelta) + end + end + end + end + end +end + + +local function MTH_BOOK_OpenNPCLink(npcId) + local id = tonumber(npcId) + if not id then return end + local url = string.format(MTH_BOOK_NPC_DATABASE_URL, id) + local chatEditBox = _G["ChatFrameEditBox"] + + if chatEditBox then + chatEditBox:Show() + chatEditBox:SetText(url) + chatEditBox:HighlightText() + end + + if DEFAULT_CHAT_FRAME then + MTH:Print("[MetaHunt Book] " .. url) + end +end + +local function MTH_BOOK_OpenItemDatabaseLink(itemId) + local id = tonumber(itemId) + if not id then return end + local url = string.format(MTH_BOOK_ITEM_DATABASE_URL, id) + local chatEditBox = _G["ChatFrameEditBox"] + + if chatEditBox then + chatEditBox:Show() + chatEditBox:SetText(url) + chatEditBox:HighlightText() + end + + if DEFAULT_CHAT_FRAME then + MTH:Print("[MetaHunt Book] " .. url) + end +end + +local function MTH_BOOK_InsertItemLinkToChat(itemId) + local id = tonumber(itemId) + if not id then return false end + + local itemName = nil + local items = _G["MTH_DS_Items"] or MTH_DS_AmmoItems + if items and items[id] and items[id].name then + itemName = items[id].name + end + + local itemLink = nil + if type(MTH_GetClickableItemLink) == "function" then + itemLink = MTH_GetClickableItemLink(id, itemName, true) + end + if not itemLink or itemLink == "" then return false end + + if type(MTH_InsertLinkToChat) == "function" then + return MTH_InsertLinkToChat(itemLink) + end + + local chatEditBox = _G["ChatFrameEditBox"] + if chatEditBox then + chatEditBox:Show() + chatEditBox:SetText(itemLink) + chatEditBox:HighlightText() + return true + end + + return false +end + +local function MTH_BOOK_ShowItemTooltip(anchorFrame, itemId) + local id = tonumber(itemId) + if not id then return end + if not GameTooltip then return end + if type(MTH_PrimeItemCache) == "function" then + MTH_PrimeItemCache(id) + end + GameTooltip:SetOwner(anchorFrame or UIParent, "ANCHOR_CURSOR") + GameTooltip:SetHyperlink("item:" .. tostring(id) .. ":0:0:0") + GameTooltip:Show() +end + +local function MTH_BOOK_HideItemTooltip() + if GameTooltip then + GameTooltip:Hide() + end +end + +local function MTH_BOOK_GetSelectedBeastForMap() + if MTH_BOOK_STATE.mode ~= "pets" then return nil, nil end + local selected = MTH_BOOK_STATE.selectedEntry + if not selected or not selected.beastId then return nil, nil end + local beastId = tonumber(selected.beastId) + if not beastId then return nil, nil end + local beast = MTH_DS_Beasts and MTH_DS_Beasts[beastId] + if not beast then return nil, nil end + local coords = beast and beast.coords + if not coords or table.getn(coords) == 0 then return nil, nil end + return beastId, beast +end + +local function MTH_BOOK_GetSelectedNPCForMap() + if MTH_BOOK_STATE.mode ~= "npcs" then return nil, nil end + local selected = MTH_BOOK_STATE.selectedEntry + if not selected or not selected.npcId then return nil, nil end + local npcId = tonumber(selected.npcId) + if not npcId then return nil, nil end + local npc = MTH_DS_Vendors and MTH_DS_Vendors[npcId] + if not npc then return nil, nil end + local coords = npc.coords + if not coords or table.getn(coords) == 0 then return nil, nil end + return npcId, npc +end + +local function MTH_BOOK_UpdateOpenMapButton() + local openMapButton = getglobal("MTH_BOOK_OpenMapButton") + if not openMapButton then return end + local beastId = MTH_BOOK_GetSelectedBeastForMap() + local npcId = MTH_BOOK_GetSelectedNPCForMap() + if beastId or npcId then + openMapButton:Show() + openMapButton:Enable() + else + openMapButton:Hide() + end +end + +local function MTH_BOOK_OpenSelectedBeastOnMap() + local beastId, beast = MTH_BOOK_GetSelectedBeastForMap() + if not beastId then + local npcId, npc = MTH_BOOK_GetSelectedNPCForMap() + if not npcId then return end + if not MTH_Map or not MTH_Map.FocusVendor then return end + local okNpc = MTH_Map:FocusVendor(npcId, npc) + if not okNpc then return end + if DEFAULT_CHAT_FRAME then + local npcName = npc and npc.name or "Unknown" + MTH:Print("Focused map markers for " .. tostring(npcName) .. " (" .. tostring(npcId) .. ")") + end + return + end + if not MTH_Map or not MTH_Map.FocusBeast then + return + end + + local ok = MTH_Map:FocusBeast(beastId, beast) + if not ok then + return + end + + if DEFAULT_CHAT_FRAME then + local beastName = beast and beast.name or "Unknown" + MTH:Print("Focused map markers for " .. tostring(beastName) .. " (" .. tostring(beastId) .. ")") + end +end + +function MTH_BOOK_OpenTamePointOnMap(zoneId, x, y, title, detail) + local zid = tonumber(zoneId) + local nx = tonumber(x) + local ny = tonumber(y) + if not (zid and nx and ny) then + return false + end + if nx > 0 and nx <= 1 then + nx = nx * 100 + end + if ny > 0 and ny <= 1 then + ny = ny * 100 + end + if not (MTH_Map and MTH_Map.SetSource and MTH_Map.UpdateWorldMap and MTH_Map.UpdateMinimap) then + return false + end + + MTH_Map.focusNodes = { + { + zoneId = zid, + x = nx, + y = ny, + title = tostring(title or "Tame Location"), + detail = tostring(detail or ""), + color = { 0.95, 0.82, 0.10 }, + } + } + + if not MTH_Map:SetSource("focus") then + return false + end + if MTH_Map.OpenWorldMapForZone then + MTH_Map:OpenWorldMapForZone(zid) + end + MTH_Map:UpdateWorldMap() + MTH_Map:UpdateMinimap() + return true +end + +local function MTH_BOOK_ShouldShowAllOnMapButton() + return MTH_BOOK_STATE.mode == "pets" and MTH_BOOK_STATE.petInZoneOnly and true or false +end + +local function MTH_BOOK_UpdateShowAllOnMapButton() + local showAllButton = getglobal("MTH_BOOK_ShowAllOnMapButton") + if not showAllButton then return end + if MTH_BOOK_ShouldShowAllOnMapButton() then + showAllButton:Show() + else + showAllButton:Hide() + end +end + +local function MTH_BOOK_ShowAllFilteredBeastsOnMap() + if not MTH_BOOK_ShouldShowAllOnMapButton() then + return + end + if not (MTH_Map and MTH_Map.SetSource and MTH_Map.UpdateWorldMap and MTH_Map.UpdateMinimap) then + return + end + + local nodes = {} + local firstZoneId = nil + for i = 1, table.getn(MTH_BOOK_STATE.results or {}) do + local beastId = tonumber(MTH_BOOK_STATE.results[i]) + local beast = beastId and MTH_DS_Beasts and MTH_DS_Beasts[beastId] + if beast and beast.coords then + for j = 1, table.getn(beast.coords) do + local c = beast.coords[j] + if c and c[1] and c[2] and c[3] then + local x = tonumber(c[1]) + local y = tonumber(c[2]) + local zoneId = tonumber(c[3]) + if x and y and zoneId then + if not firstZoneId then + firstZoneId = zoneId + end + local details = string.format("Family: %s\nLevel: %s\nAbilities: %s", beast.family or "?", beast.lvl or "?", beast.abilities or "None") + table.insert(nodes, { + zoneId = zoneId, + x = x, + y = y, + title = string.format("%s (%d)", beast.name or "Unknown", beastId), + detail = details, + beastId = beastId, + color = { 0.95, 0.82, 0.10 }, + }) + end + end + end + end + end + + if table.getn(nodes) == 0 then + return + end + + MTH_Map.focusNodes = nodes + if not MTH_Map:SetSource("focus") then + return + end + if firstZoneId and MTH_Map.OpenWorldMapForZone then + MTH_Map:OpenWorldMapForZone(firstZoneId) + end + MTH_Map:UpdateWorldMap() + MTH_Map:UpdateMinimap() + + if MTH and MTH.Print then + MTH:Print("Focused map markers for " .. tostring(table.getn(nodes)) .. " beasts in this zone.") + end +end + +local function MTH_BOOK_TryTargetSelectedEntry() + local entry = MTH_BOOK_STATE and MTH_BOOK_STATE.selectedEntry + if not entry then return false end + local name = entry.name + if not name or name == "" then return false end + local targetByName = _G["TargetByName"] + if type(targetByName) == "function" then + targetByName(name, true) + return true + end + return false +end + +local function MTH_BOOK_SyncTopAreaColorWithSelectedTab() + local topAreaBackdrop = getglobal("MTH_BOOK_TopAreaBackdrop") + if not topAreaBackdrop then return end + topAreaBackdrop:SetBackdropColor(0.10, 0.10, 0.10, 1.00) + topAreaBackdrop:SetBackdropBorderColor(0.28, 0.28, 0.28, 1.00) +end + +function MTH_BOOK_GetItemsTable() + local items = _G["MTH_DS_Items"] + if items then return items end + return MTH_DS_AmmoItems +end + +local function MTH_BOOK_CountMap(map) + local count = 0 + if not map then return 0 end + for _ in pairs(map) do count = count + 1 end + return count +end + +function MTH_BOOK_HasEntries(map) + if not map then return false end + for _ in pairs(map) do return true end + return false +end + +function MTH_BOOK_SafeLower(text) + if not text then return "" end + return string.lower(text) +end + +local function MTH_BOOK_ParseBoxNumber(name) + local box = getglobal(name) + if not box then return nil end + local txt = box:GetText() or "" + txt = string.gsub(txt, "^%s+", "") + txt = string.gsub(txt, "%s+$", "") + if txt == "" then return nil end + return tonumber(txt) +end + +function MTH_BOOK_ParseLevel(value) + if not value then return nil end + if type(value) == "number" then return value end + if type(value) ~= "string" then return nil end + local _, _, first = string.find(value, "(%d+)") + if not first then return nil end + return tonumber(first) +end + +local function MTH_BOOK_GetItemQualityColorCode(quality) + local q = tonumber(quality) + if q == 2 then return "FFAA66FF" end + if q == 3 then return "FF0070DD" end + if q == 4 then return "FF1EFF00" end + if q == 5 then return "FFFFFFFF" end + if q == 6 then return "FF9D9D9D" end + return "FFFFFFFF" +end + +local function MTH_BOOK_GetPlayerReactDefaults() + local faction = UnitFactionGroup and UnitFactionGroup("player") + if faction == "Alliance" then + return true, false, true + end + if faction == "Horde" then + return false, true, true + end + return true, true, true +end + +local function MTH_BOOK_GetNPCReactBucket(fac) + if type(MTH_BOOKTAB_GetNPCReactBucket) == "function" then + return MTH_BOOKTAB_GetNPCReactBucket(fac) + end + return "AH" +end + +local function MTH_BOOK_GetNPCFunctionLabel(key) + if type(MTH_BOOKTAB_GetNPCFunctionLabel) == "function" then + return MTH_BOOKTAB_GetNPCFunctionLabel(key) + end + return tostring(key or "") +end + +local function MTH_BOOK_GetNPCFunctions(vendor) + if type(MTH_BOOKTAB_GetNPCFunctions) == "function" then + return MTH_BOOKTAB_GetNPCFunctions(vendor) + end + return {} +end + +local function MTH_BOOK_GetNPCFunctionSummary(vendor) + if type(MTH_BOOKTAB_GetNPCFunctionSummary) == "function" then + return MTH_BOOKTAB_GetNPCFunctionSummary(vendor) + end + return "-", {} +end + +local function MTH_BOOK_GetNPCZoneSummary(vendor) + if type(MTH_BOOKTAB_GetNPCZoneSummary) == "function" then + return MTH_BOOKTAB_GetNPCZoneSummary(vendor) + end + return "-", "-", {} +end + +MTH_BOOK_GetZoneName = function(zoneId) + local normalizedId = tonumber(zoneId) or zoneId + if not normalizedId then return "Zone " .. tostring(zoneId or "?") end + if MTH_DS_ZoneNamesFallback and MTH_DS_ZoneNamesFallback[normalizedId] then + return MTH_DS_ZoneNamesFallback[normalizedId] + end + if not MTH_DS_Zones then return "Zone " .. tostring(zoneId or "?") end + local row = MTH_DS_Zones[normalizedId] or MTH_DS_Zones[tostring(normalizedId)] + if not row then return "Zone " .. tostring(normalizedId) end + local names = row.names + if not names then return "Zone " .. tostring(normalizedId) end + return names.enUS or names[GetLocale()] or names.deDE or names.frFR or ("Zone " .. tostring(normalizedId)) +end +_G.MTH_BOOK_GetZoneName = MTH_BOOK_GetZoneName + +function MTH_BOOK_NormalizeZoneLookupName(name) + if not name then return "" end + local lowered = string.lower(tostring(name)) + return string.gsub(lowered, "[^%w]", "") +end + +function MTH_BOOK_GetCurrentZoneId() + local zoneName = nil + if type(GetRealZoneText) == "function" then + zoneName = GetRealZoneText() + end + if (not zoneName or zoneName == "") and type(GetZoneText) == "function" then + zoneName = GetZoneText() + end + if not zoneName or zoneName == "" then + return nil + end + + if MTH_Map and type(MTH_Map.GetMapIDByName) == "function" then + local mapId = MTH_Map:GetMapIDByName(zoneName) + if mapId then + return tonumber(mapId) or mapId + end + end + + local normalized = MTH_BOOK_NormalizeZoneLookupName(zoneName) + if normalized == "" then + return nil + end + + if MTH_DS_Zones then + for zoneId, row in pairs(MTH_DS_Zones) do + if row and row.names then + for _, value in pairs(row.names) do + if MTH_BOOK_NormalizeZoneLookupName(value) == normalized then + return tonumber(zoneId) or zoneId + end + end + end + end + end + + if MTH_DS_ZoneNamesFallback then + for zoneId, value in pairs(MTH_DS_ZoneNamesFallback) do + if MTH_BOOK_NormalizeZoneLookupName(value) == normalized then + return tonumber(zoneId) or zoneId + end + end + end + + return nil +end + +function MTH_BOOK_BeastHasZoneId(beast, zoneId) + local zid = tonumber(zoneId) + if not zid or not beast or not beast.coords then + return false + end + for i = 1, table.getn(beast.coords) do + local c = beast.coords[i] + if c and tonumber(c[3]) == zid then + return true + end + end + return false +end + +function MTH_BOOK_IsUnknownText(value) + local text = MTH_BOOK_SafeLower(value) + if text == "" then return true end + if text == "unknown" then return true end + if text == "?" or text == "??" or text == "???" then return true end + return false +end + + +local function MTH_BOOK_GetSortState() + local mode = MTH_BOOK_STATE.mode or "pets" + if not MTH_BOOK_STATE.sortByMode[mode] then + MTH_BOOK_STATE.sortByMode[mode] = { col = nil, asc = true } + end + return MTH_BOOK_STATE.sortByMode[mode] +end + +function MTH_BOOK_GetPetDatastore() + if type(MTH_GetPetDatastoreSnapshot) == "function" then + local snapshot = MTH_GetPetDatastoreSnapshot() + if type(snapshot) == "table" then + return snapshot + end + end + if type(MTH_PETS_GetRootStore) == "function" then + local pets = MTH_PETS_GetRootStore() + if type(pets) == "table" and type(pets.petStore) == "table" then + return pets.petStore + end + end + return nil +end + +function MTH_BOOK_GetPetStoreRow(petId) + local store = MTH_BOOK_GetPetDatastore() + if type(store) ~= "table" then return nil end + if MTH_BOOK_STATE.mode == "stable" and type(store.activeById) == "table" then + return store.activeById[petId] + end + if MTH_BOOK_STATE.mode == "pethistory" and type(store.historyById) == "table" then + return store.historyById[petId] + end + return nil +end + +function MTH_BOOK_IsCurrentActivePetId(store, petId) + if type(store) ~= "table" then return false end + if petId == nil then return false end + if MTH_BOOK_STABLE_CURRENT_ID_CACHE and tostring(petId) == tostring(MTH_BOOK_STABLE_CURRENT_ID_CACHE) then + return true + end + return tostring(store.activeCurrentId or "") == tostring(petId) +end + +function MTH_BOOK_ResolveCurrentPetId(store) + if type(store) == "table" and store.activeCurrentId ~= nil and tostring(store.activeCurrentId) ~= "" then + local activeId = tostring(store.activeCurrentId) + if type(store.activeById) == "table" and type(store.activeById[activeId]) == "table" then + return activeId + end + end + + if type(MTH_PETS_GetRootStore) == "function" then + local pets = MTH_PETS_GetRootStore() + if type(pets) == "table" and pets.currentPetSuppressed == true then + return nil + end + local persistedCurrentId = pets and pets.currentPetId + if persistedCurrentId ~= nil and tostring(persistedCurrentId) ~= "" then + local currentId = tostring(persistedCurrentId) + if type(store) == "table" and type(store.activeById) == "table" and type(store.activeById[currentId]) == "table" then + return currentId + end + end + local cp = pets and pets.currentPet + if type(cp) == "table" and cp.id ~= nil and tostring(cp.id) ~= "" then + local currentId = tostring(cp.id) + if type(store) == "table" and type(store.activeById) == "table" and type(store.activeById[currentId]) == "table" then + return currentId + end + end + end + + return nil +end + +function MTH_BOOK_GetStableDisplaySlot(petId, row, store) + if type(row) ~= "table" then return nil end + if type(store) ~= "table" then + store = MTH_BOOK_GetPetDatastore() + end + if MTH_BOOK_IsCurrentActivePetId(store, petId) then + return 0 + end + local slotNumber = tonumber(row.stableSlot) + if slotNumber and slotNumber > 0 then + return slotNumber + end + return nil +end + +local function MTH_BOOK_IsStableListPet(petId, row, store) + return MTH_BOOK_GetStableDisplaySlot(petId, row, store) ~= nil +end + +local function MTH_BOOK_StableSort(a, b) + if type(MTH_BOOKTAB_StableSort) == "function" then + return MTH_BOOKTAB_StableSort(a, b) + end + return tostring(a) < tostring(b) +end + +local function MTH_BOOK_GetSortKey(entry, col) + if MTH_BOOK_STATE.mode == "pets" then + local beast = MTH_DS_Beasts and MTH_DS_Beasts[entry] + if not beast then return nil end + local traits = MTH_BOOK_ParseBeastTraits(beast) + if col == 1 then return tonumber(entry) or 0 end + if col == 2 then return MTH_BOOK_ParseLevel(beast.lvl) or 0 end + if col == 3 then return MTH_BOOK_SafeLower(beast.family) end + if col == 4 then return MTH_BOOK_SafeLower(beast.name) end + if col == 5 then return MTH_BOOK_SafeLower(MTH_BOOK_GetBeastAbilitiesSummary(beast)) end + if col == 6 then return MTH_BOOK_SafeLower(MTH_BOOK_GetBeastZoneSummary(beast)) end + if col == 7 then return traits.rare and 1 or 0 end + if col == 8 then return traits.elite and 1 or 0 end + if col == 9 then return traits.unique and 1 or 0 end + end + + if MTH_BOOK_STATE.mode == "abilities" then + if col == 1 then return MTH_BOOK_SafeLower(entry.abilityToken) end + if col == 2 then return tonumber(entry.level) or 0 end + if col == 3 then return MTH_BOOK_SafeLower(entry.family) end + if col == 4 then return MTH_BOOK_SafeLower(entry.name) end + if col == 5 then return entry.rare and 1 or 0 end + end + + if MTH_BOOK_STATE.mode == "petabilities" then + if col == 1 then return MTH_BOOK_SafeLower(entry.ability) end + if col == 2 then return tonumber(entry.rankCount) or 0 end + if col == 3 then + local familyCount = tonumber(entry.familyCount) or 0 + return (familyCount * 10000) + string.len(MTH_BOOK_SafeLower(entry.familyList or "")) + end + end + + if MTH_BOOK_STATE.mode == "families" then + if col == 1 then return MTH_BOOK_SafeLower(entry.family) end + if col == 2 then return tonumber(entry.named) or 0 end + if col == 3 then return tonumber(entry.coords) or 0 end + if col == 4 then return tonumber(entry.abilities and table.getn(entry.abilities) or 0) end + if col == 5 then return MTH_BOOK_SafeLower(entry.dietText or "") end + end + + if MTH_BOOK_STATE.mode == "npcs" then + local vendors = MTH_DS_Vendors + local npc = vendors and vendors[entry] + if not npc then return nil end + local react = MTH_BOOK_GetNPCReactBucket(npc.fac) + local functionSummary = MTH_BOOK_GetNPCFunctionSummary(npc) + local zoneName = MTH_BOOK_GetNPCZoneSummary(npc) + if col == 1 then return tonumber(entry) or 0 end + if col == 2 then return MTH_BOOK_SafeLower(npc.name) end + if col == 3 then return react end + if col == 4 then return MTH_BOOK_SafeLower(functionSummary) end + if col == 5 then return MTH_BOOK_SafeLower(zoneName) end + end + + if MTH_BOOK_STATE.mode == "stable" then + local store = MTH_BOOK_GetPetDatastore() + local row = MTH_BOOK_GetPetStoreRow(entry) + if not row then return nil end + if col == 1 then return tostring(entry) end + if col == 2 then return MTH_BOOK_GetStableDisplaySlot(entry, row, store) or 9999 end + if col == 3 then return MTH_BOOK_SafeLower(row.name) end + if col == 4 then return MTH_BOOK_SafeLower(row.family) end + if col == 5 then return tonumber(row.level) or 0 end + if col == 6 then return tonumber(row.loyaltyLevel) or (row.stableInfo and tonumber(row.stableInfo.loyaltyLevel)) or 0 end + if col == 7 then return tonumber(row.lastSeen) or 0 end + end + + if MTH_BOOK_STATE.mode == "pethistory" then + local row = MTH_BOOK_GetPetStoreRow(entry) + if not row then return nil end + if col == 1 then return tostring(entry) end + if col == 2 then return MTH_BOOK_SafeLower(row.name) end + if col == 3 then return MTH_BOOK_SafeLower(row.family) end + if col == 4 then return tonumber(row.level) or 0 end + if col == 5 then return tonumber(row.abandonedAt) or 0 end + if col == 6 then return MTH_BOOK_SafeLower(tostring(row.abandonReason or "")) end + end + + local items = MTH_BOOK_GetItemsTable() + local item = items and items[entry] + if not item then return nil end + if col == 1 then return tonumber(entry) or 0 end + if col == 2 then return tonumber(item.reqlevel) or 0 end + if col == 3 then return tonumber(item.level) or 0 end + if col == 4 then return MTH_BOOK_SafeLower(item.subtype) end + if col == 5 then return MTH_BOOK_SafeLower(item.name) end + if col == 6 then return tonumber(item.dps) or 0 end + if col == 7 then return tonumber(item.speed) or 0 end + + return nil +end + +local function MTH_BOOK_DefaultCompare(a, b) + if MTH_BOOK_STATE.mode == "pets" then return MTH_BOOK_BeastSort(a, b) end + if MTH_BOOK_STATE.mode == "families" then + local fa = MTH_BOOK_SafeLower(a and a.family) + local fb = MTH_BOOK_SafeLower(b and b.family) + if fa ~= fb then return fa < fb end + return tostring(a and a.family or "") < tostring(b and b.family or "") + end + if MTH_BOOK_STATE.mode == "abilities" then return MTH_BOOK_AbilitySort(a, b) end + if MTH_BOOK_STATE.mode == "petabilities" then return MTH_BOOK_PetAbilitySort(a, b) end + if MTH_BOOK_STATE.mode == "npcs" then return MTH_BOOK_NPCSort(a, b) end + if MTH_BOOK_STATE.mode == "stable" then return MTH_BOOK_StableSort(a, b) end + if MTH_BOOK_STATE.mode == "pethistory" then return MTH_BOOK_PetHistorySort(a, b) end + return MTH_BOOK_ItemSort(a, b) +end + +local function MTH_BOOK_ApplyActiveSort(results) + local state = MTH_BOOK_GetSortState() + if not state.col then return end + + table.sort(results, function(a, b) + local ka = MTH_BOOK_GetSortKey(a, state.col) + local kb = MTH_BOOK_GetSortKey(b, state.col) + if ka ~= kb then + if ka == nil then return not state.asc end + if kb == nil then return state.asc end + if state.asc then + return ka < kb + end + return ka > kb + end + return MTH_BOOK_DefaultCompare(a, b) + end) +end + +local function MTH_BOOK_BuildResults() + local results = {} + + if MTH_BOOK_STATE.mode == "pets" then + if not MTH_DS_Beasts then return results end + for beastId, beast in pairs(MTH_DS_Beasts) do + if MTH_BOOK_BeastMatches(beastId, beast) then + table.insert(results, beastId) + end + end + table.sort(results, MTH_BOOK_BeastSort) + MTH_BOOK_ApplyActiveSort(results) + return results + end + + if MTH_BOOK_STATE.mode == "abilities" then + if not MTH_DS_Beasts then return results end + for beastId, beast in pairs(MTH_DS_Beasts) do + local beastLevel = MTH_BOOK_ParseLevel(beast.lvl) + if (not MTH_BOOK_STATE.minLevel or (beastLevel and beastLevel >= MTH_BOOK_STATE.minLevel)) + and (not MTH_BOOK_STATE.maxLevel or (beastLevel and beastLevel <= MTH_BOOK_STATE.maxLevel)) + and (not MTH_BOOK_STATE.flag2 or beast.rare) + and (not MTH_BOOK_STATE.flag3 or (beast.coords and table.getn(beast.coords) > 0)) + then + local abilities = MTH_BOOK_SplitAbilities(beast.abilities) + for _, abilityToken in ipairs(abilities) do + local abilityLower = MTH_BOOK_SafeLower(abilityToken) + local _, _, rank = string.find(abilityToken, "(%d+)$") + local abilityName = abilityToken + if rank then + abilityName = string.gsub(abilityToken, "%s*%d+$", "") + end + local abilityNameLower = MTH_BOOK_SafeLower(abilityName) + if (MTH_BOOK_STATE.quick == "all" or string.find(abilityNameLower, MTH_BOOK_STATE.quick, 1, true) ~= nil) + and (not MTH_BOOK_STATE.flag1 or rank ~= nil) + then + if MTH_BOOK_STATE.search == "" + or string.find(abilityLower, MTH_BOOK_STATE.search, 1, true) ~= nil + or string.find(MTH_BOOK_SafeLower(beast.name), MTH_BOOK_STATE.search, 1, true) ~= nil + or string.find(MTH_BOOK_SafeLower(beast.family), MTH_BOOK_STATE.search, 1, true) ~= nil + then + table.insert(results, { + beastId = beastId, + name = beast.name or "Unknown", + family = beast.family or "?", + level = beastLevel or 0, + rare = beast.rare and true or false, + ability = abilityName, + rank = rank, + abilityToken = abilityToken, + }) + end + end + end + end + end + table.sort(results, MTH_BOOK_AbilitySort) + MTH_BOOK_ApplyActiveSort(results) + return results + end + + if MTH_BOOK_STATE.mode == "petabilities" then + results = MTH_BOOK_BuildPetAbilitiesRows(false) + MTH_BOOK_ApplyActiveSort(results) + return results + end + + if MTH_BOOK_STATE.mode == "families" then + if type(MTH_BOOKTAB_BuildFamiliesRows) == "function" then + results = MTH_BOOKTAB_BuildFamiliesRows() + MTH_BOOK_FamiliesTrace("BuildResults families rows=" .. tostring(table.getn(results or {}))) + else + MTH_BOOK_FamiliesTrace("BuildResults families missing MTH_BOOKTAB_BuildFamiliesRows") + end + MTH_BOOK_ApplyActiveSort(results) + return results + end + + if MTH_BOOK_STATE.mode == "npcs" then + if not MTH_DS_Vendors then return results end + for npcId, vendor in pairs(MTH_DS_Vendors) do + if MTH_BOOK_NPCMatches(npcId, vendor) then + table.insert(results, npcId) + end + end + table.sort(results, MTH_BOOK_NPCSort) + MTH_BOOK_ApplyActiveSort(results) + return results + end + + if MTH_BOOK_STATE.mode == "stable" then + local store = MTH_BOOK_GetPetDatastore() + if type(store) ~= "table" or type(store.activeById) ~= "table" then return results end + MTH_BOOK_STABLE_CURRENT_ID_CACHE = MTH_BOOK_ResolveCurrentPetId(store) + local activeCount = MTH_BOOK_CountMap(store.activeById) + MTH_BOOK_DebugTrace("BuildResults stable: activeById=" .. tostring(activeCount) .. " activeCurrentId=" .. tostring(store.activeCurrentId) .. " resolvedCurrentId=" .. tostring(MTH_BOOK_STABLE_CURRENT_ID_CACHE)) + for petId, row in pairs(store.activeById) do + if type(row) == "table" and MTH_BOOK_IsStableListPet(petId, row, store) then + table.insert(results, petId) + end + end + MTH_BOOK_DebugTrace("BuildResults stable: included=" .. tostring(table.getn(results))) + table.sort(results, MTH_BOOK_StableSort) + MTH_BOOK_ApplyActiveSort(results) + return results + end + + if MTH_BOOK_STATE.mode == "pethistory" then + local store = MTH_BOOK_GetPetDatastore() + if type(store) ~= "table" or type(store.historyById) ~= "table" then return results end + for petId, row in pairs(store.historyById) do + if type(row) == "table" then + table.insert(results, petId) + end + end + table.sort(results, MTH_BOOK_PetHistorySort) + MTH_BOOK_ApplyActiveSort(results) + return results + end + + local items = MTH_BOOK_GetItemsTable() + if not items then return results end + for itemId, item in pairs(items) do + if MTH_BOOK_ItemMatches(itemId, item) then + table.insert(results, itemId) + end + end + table.sort(results, MTH_BOOK_ItemSort) + MTH_BOOK_ApplyActiveSort(results) + return results +end + +local function MTH_BOOK_GetTotalCount() + if MTH_BOOK_STATE.mode == "pets" or MTH_BOOK_STATE.mode == "abilities" then + return MTH_DS_Beasts and MTH_BOOK_CountMap(MTH_DS_Beasts) or 0 + end + if MTH_BOOK_STATE.mode == "petabilities" then + return table.getn(MTH_BOOK_BuildPetAbilitiesRows(true)) + end + if MTH_BOOK_STATE.mode == "families" then + if type(MTH_BOOKTAB_BuildFamiliesRows) == "function" then + return table.getn(MTH_BOOKTAB_BuildFamiliesRows()) + end + return 0 + end + if MTH_BOOK_STATE.mode == "npcs" then + return MTH_DS_Vendors and MTH_BOOK_CountMap(MTH_DS_Vendors) or 0 + end + if MTH_BOOK_STATE.mode == "stable" then + local store = MTH_BOOK_GetPetDatastore() + if type(store) ~= "table" or type(store.activeById) ~= "table" then return 0 end + local total = 0 + for petId, row in pairs(store.activeById) do + if type(row) == "table" and MTH_BOOK_IsStableListPet(petId, row, store) then + total = total + 1 + end + end + return total + end + if MTH_BOOK_STATE.mode == "pethistory" then + local store = MTH_BOOK_GetPetDatastore() + return (type(store) == "table" and type(store.historyById) == "table") and MTH_BOOK_CountMap(store.historyById) or 0 + end + local items = MTH_BOOK_GetItemsTable() + return items and MTH_BOOK_CountMap(items) or 0 +end + +local function MTH_BOOK_BuildPetFamilyOptions() + local familyMap = {} + + if MTH_DS_Families then + for familyName in pairs(MTH_DS_Families) do + if familyName and familyName ~= "" then + familyMap[MTH_BOOK_SafeLower(familyName)] = familyName + end + end + end + + if MTH_DS_Beasts then + for _, beast in pairs(MTH_DS_Beasts) do + if beast and beast.family and beast.family ~= "" then + local lower = MTH_BOOK_SafeLower(beast.family) + if not familyMap[lower] then + familyMap[lower] = beast.family + end + end + end + end + + local rows = {} + for value, label in pairs(familyMap) do + table.insert(rows, { value = value, label = label }) + end + + table.sort(rows, function(a, b) + return MTH_BOOK_SafeLower(a.label) < MTH_BOOK_SafeLower(b.label) + end) + + MTH_BOOK_PetFamilyOptions = { + { value = "all", label = "All Families" }, + } + + for i = 1, table.getn(rows) do + table.insert(MTH_BOOK_PetFamilyOptions, rows[i]) + end + + if MTH_BOOK_STATE.quick ~= "all" and not familyMap[MTH_BOOK_STATE.quick] then + MTH_BOOK_STATE.quick = "all" + end +end + +local function MTH_BOOK_GetPetFamilyLabel(value) + for i = 1, table.getn(MTH_BOOK_PetFamilyOptions) do + local option = MTH_BOOK_PetFamilyOptions[i] + if option and option.value == value then + return option.label, i + end + end + return "All Families", 1 +end + +local function MTH_BOOK_BuildPetAbilityOptions() + local abilityMap = {} + local selectedFamily = MTH_BOOK_SafeLower(MTH_BOOK_STATE.quick) + + if MTH_DS_Beasts then + for _, beast in pairs(MTH_DS_Beasts) do + local beastFamily = MTH_BOOK_SafeLower(beast and beast.family or "") + if selectedFamily == "all" or beastFamily == selectedFamily then + local abilities = MTH_BOOK_SplitAbilities(beast and beast.abilities) + for i = 1, table.getn(abilities) do + local abilityLower, abilityLabel, rank = MTH_BOOK_ParseAbilityToken(abilities[i]) + if abilityLower ~= "" and abilityLower ~= "none" and abilityLower ~= "unknown" then + local row = abilityMap[abilityLower] + if not row then + row = { value = abilityLower, label = abilityLabel, ranks = {} } + abilityMap[abilityLower] = row + end + if rank then + row.ranks[rank] = true + end + end + end + end + end + end + + if MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility then + for abilityName, bucket in pairs(MTH_DS_PetSpells.byAbility) do + local abilityLabel = tostring(abilityName or "") + local abilityLower = MTH_BOOK_SafeLower(abilityLabel) + if abilityLower ~= "" and abilityLower ~= "none" and abilityLower ~= "unknown" then + local row = abilityMap[abilityLower] + if not row then + row = { value = abilityLower, label = abilityLabel, ranks = {} } + abilityMap[abilityLower] = row + end + if bucket and bucket.spells then + for i = 1, table.getn(bucket.spells) do + local spell = bucket.spells[i] + if spell then + local rank = tonumber(spell.rankNumber) + if rank and rank > 0 then + row.ranks[rank] = true + end + end + end + end + end + end + end + + local rows = {} + for _, row in pairs(abilityMap) do + table.insert(rows, row) + end + + table.sort(rows, function(a, b) + return MTH_BOOK_SafeLower(a.label) < MTH_BOOK_SafeLower(b.label) + end) + + MTH_BOOK_PetAbilityOptions = { + { value = "all", label = "All abilities" }, + } + MTH_BOOK_PetAbilityRanks = {} + for i = 1, table.getn(rows) do + local row = rows[i] + table.insert(MTH_BOOK_PetAbilityOptions, { value = row.value, label = row.label }) + MTH_BOOK_PetAbilityRanks[row.value] = row.ranks + end + + if MTH_BOOK_STATE.petAbility ~= "all" and not abilityMap[MTH_BOOK_STATE.petAbility] then + MTH_BOOK_STATE.petAbility = "all" + MTH_BOOK_STATE.petRank = "all" + end +end + +local function MTH_BOOK_BuildPetRankOptions() + MTH_BOOK_PetRankOptions = { + { value = "all", label = "All ranks" }, + } + + if MTH_BOOK_STATE.petAbility == "all" then + MTH_BOOK_STATE.petRank = "all" + return false + end + + local ranksMap = MTH_BOOK_PetAbilityRanks and MTH_BOOK_PetAbilityRanks[MTH_BOOK_STATE.petAbility] + if not ranksMap then + MTH_BOOK_STATE.petRank = "all" + return false + end + + local ranks = {} + for rank in pairs(ranksMap) do + table.insert(ranks, tonumber(rank)) + end + table.sort(ranks) + + if table.getn(ranks) == 0 then + MTH_BOOK_STATE.petRank = "all" + return false + end + + for i = 1, table.getn(ranks) do + local rank = ranks[i] + table.insert(MTH_BOOK_PetRankOptions, { value = tostring(rank), label = "Rank " .. tostring(rank) }) + end + + if MTH_BOOK_STATE.petRank ~= "all" then + local keep = false + for i = 1, table.getn(ranks) do + if tostring(ranks[i]) == tostring(MTH_BOOK_STATE.petRank) then + keep = true + break + end + end + if not keep then + MTH_BOOK_STATE.petRank = "all" + end + end + + return true +end + +local function MTH_BOOK_GetPetAbilityLabel(value) + for i = 1, table.getn(MTH_BOOK_PetAbilityOptions) do + local option = MTH_BOOK_PetAbilityOptions[i] + if option and option.value == value then + return option.label, i + end + end + return "All abilities", 1 +end + +local function MTH_BOOK_GetPetRankLabel(value) + for i = 1, table.getn(MTH_BOOK_PetRankOptions) do + local option = MTH_BOOK_PetRankOptions[i] + if option and option.value == value then + return option.label, i + end + end + return "All ranks", 1 +end + +local function MTH_BOOK_GetPetLearnSourceLabel(value) + for i = 1, table.getn(MTH_BOOK_PetLearnSourceOptions) do + local option = MTH_BOOK_PetLearnSourceOptions[i] + if option and option.value == value then + return option.label + end + end + return "All Abilities" +end + +MTH_BOOK_GetPlayerLevelValue = function() + if type(UnitLevel) == "function" then + local lvl = tonumber(UnitLevel("player")) + if lvl and lvl > 0 then + return lvl + end + end + return nil +end +_G.MTH_BOOK_GetPlayerLevelValue = MTH_BOOK_GetPlayerLevelValue + +MTH_BOOK_IsSpellInLevelScope = function(trainLevel) + if not MTH_BOOK_STATE.petOnlyMyLevel then + return true + end + local playerLevel = MTH_BOOK_GetPlayerLevelValue() + if not playerLevel then + return true + end + local req = tonumber(trainLevel) + if not req then + return true + end + return req <= playerLevel +end +_G.MTH_BOOK_IsSpellInLevelScope = MTH_BOOK_IsSpellInLevelScope + +MTH_BOOK_GetScopedRankSummary = function(abilityName) + local rankMap = {} + local knownRankMap = {} + local minRank = nil + local maxRank = nil + local minTrainLevel = nil + local maxTrainLevel = nil + + if not (MTH_DS_PetSpells and MTH_DS_PetSpells.byAbility) then + return 0, 0, nil, nil, nil, nil + end + + local bucket = MTH_DS_PetSpells.byAbility[abilityName] + if not (bucket and bucket.spells) then + return 0, 0, nil, nil, nil, nil + end + + for i = 1, table.getn(bucket.spells) do + local spell = bucket.spells[i] + if spell and MTH_BOOK_IsSpellInLevelScope(tonumber(spell.trainLevel)) then + local rankNumber = tonumber(spell.rankNumber) + if not rankNumber and spell.rank then + local _, _, parsed = string.find(tostring(spell.rank), "(%d+)") + rankNumber = tonumber(parsed) + end + if rankNumber and rankNumber > 0 then + rankMap[rankNumber] = true + if MTH_BOOK_IsKnownRankForAbility(abilityName, rankNumber) then + knownRankMap[rankNumber] = true + end + if not minRank or rankNumber < minRank then minRank = rankNumber end + if not maxRank or rankNumber > maxRank then maxRank = rankNumber end + end + local trainLevel = tonumber(spell.trainLevel) + if trainLevel then + if not minTrainLevel or trainLevel < minTrainLevel then minTrainLevel = trainLevel end + if not maxTrainLevel or trainLevel > maxTrainLevel then maxTrainLevel = trainLevel end + end + end + end + + local rankCount = 0 + for _ in pairs(rankMap) do rankCount = rankCount + 1 end + local knownCount = 0 + for _ in pairs(knownRankMap) do knownCount = knownCount + 1 end + if rankCount == 0 then + local hasAnyScopedRows = false + for i = 1, table.getn(bucket.spells) do + local spell = bucket.spells[i] + if spell and MTH_BOOK_IsSpellInLevelScope(tonumber(spell.trainLevel)) then + hasAnyScopedRows = true + break + end + end + if hasAnyScopedRows then + rankCount = 1 + if MTH_BOOK_IsKnownRankForAbility(abilityName, 0) then + knownCount = 1 + end + end + end + if knownCount > rankCount then knownCount = rankCount end + + return knownCount, rankCount, minRank, maxRank, minTrainLevel, maxTrainLevel +end +_G.MTH_BOOK_GetScopedRankSummary = MTH_BOOK_GetScopedRankSummary + +local function MTH_BOOK_BuildNPCFunctionOptions() + MTH_BOOK_NPCFunctionOptions = { + { value = "all", label = "All Hunter Friends" }, + { value = "huntertrainer", label = "Hunter Trainer" }, + { value = "pettrainer", label = "Pet Trainer" }, + { value = "vendor", label = "Ammo Vendor" }, + } + + local allowed = { + all = true, + huntertrainer = true, + pettrainer = true, + vendor = true, + } + if not allowed[MTH_BOOK_STATE.npcFunction] then + MTH_BOOK_STATE.npcFunction = "all" + end +end + +local function MTH_BOOK_BuildNPCZoneOptions() + local seen = {} + local rows = {} + if MTH_DS_Vendors then + for _, vendor in pairs(MTH_DS_Vendors) do + local _, _, zones = MTH_BOOK_GetNPCZoneSummary(vendor) + for i = 1, table.getn(zones) do + local zone = zones[i] + if zone ~= "-" and not seen[zone] then + seen[zone] = true + table.insert(rows, { value = zone, label = zone }) + end + end + end + end + + table.sort(rows, function(a, b) + return MTH_BOOK_SafeLower(a.label) < MTH_BOOK_SafeLower(b.label) + end) + + MTH_BOOK_NPCZoneOptions = { + { value = "all", label = "All Zones" }, + } + for i = 1, table.getn(rows) do + table.insert(MTH_BOOK_NPCZoneOptions, rows[i]) + end + + if MTH_BOOK_STATE.npcZone ~= "all" and not seen[MTH_BOOK_STATE.npcZone] then + MTH_BOOK_STATE.npcZone = "all" + end +end + +local function MTH_BOOK_GetNPCFunctionLabelByValue(value) + for i = 1, table.getn(MTH_BOOK_NPCFunctionOptions) do + local option = MTH_BOOK_NPCFunctionOptions[i] + if option and option.value == value then + return option.label + end + end + return "All Hunter Friends" +end + +local function MTH_BOOK_GetNPCZoneLabelByValue(value) + for i = 1, table.getn(MTH_BOOK_NPCZoneOptions) do + local option = MTH_BOOK_NPCZoneOptions[i] + if option and option.value == value then + return option.label + end + end + return "All Zones" +end + +local function MTH_BOOK_UpdateFamilyDropdownText() + local familyDropdown = getglobal("MTH_BOOK_FamilyDropdown") + if not familyDropdown then return end + local label, index = MTH_BOOK_GetPetFamilyLabel(MTH_BOOK_STATE.quick) + MTH_BOOK_DropdownSetText(familyDropdown, label) +end + +local function MTH_BOOK_UpdateAbilityDropdownText() + local abilityDropdown = getglobal("MTH_BOOK_AbilityDropdown") + if not abilityDropdown then return end + local label, index = MTH_BOOK_GetPetAbilityLabel(MTH_BOOK_STATE.petAbility) + MTH_BOOK_DropdownSetText(abilityDropdown, label) +end + +local function MTH_BOOK_UpdateRankDropdownText() + local rankDropdown = getglobal("MTH_BOOK_RankDropdown") + if not rankDropdown then return end + local label, index = MTH_BOOK_GetPetRankLabel(MTH_BOOK_STATE.petRank) + MTH_BOOK_DropdownSetText(rankDropdown, label) +end + +local function MTH_BOOK_UpdatePetLearnSourceDropdownText() + local sourceDropdown = getglobal("MTH_BOOK_PetLearnSourceDropdown") + if not sourceDropdown then return end + MTH_BOOK_DropdownSetText(sourceDropdown, MTH_BOOK_GetPetLearnSourceLabel(MTH_BOOK_STATE.petLearnSource)) +end + +local function MTH_BOOK_GetItemSubtypeLabelByValue(value) + for i = 1, table.getn(MTH_BOOK_ItemSubtypeOptions) do + local option = MTH_BOOK_ItemSubtypeOptions[i] + if option and option.value == value then + return option.label + end + end + return "All Ranged" +end + +local function MTH_BOOK_UpdateItemSubtypeDropdownText() + local itemDropdown = getglobal("MTH_BOOK_ItemSubtypeDropdown") + if not itemDropdown then return end + MTH_BOOK_DropdownSetText(itemDropdown, MTH_BOOK_GetItemSubtypeLabelByValue(MTH_BOOK_STATE.itemSubtype)) +end + +local function MTH_BOOK_UpdateNPCDropdownTexts() + local functionDropdown = getglobal("MTH_BOOK_NPCFunctionDropdown") + local zoneDropdown = getglobal("MTH_BOOK_NPCZoneDropdown") + if functionDropdown then + MTH_BOOK_DropdownSetText(functionDropdown, MTH_BOOK_GetNPCFunctionLabelByValue(MTH_BOOK_STATE.npcFunction)) + end + if zoneDropdown then + MTH_BOOK_DropdownSetText(zoneDropdown, MTH_BOOK_GetNPCZoneLabelByValue(MTH_BOOK_STATE.npcZone)) + end +end + +local function MTH_BOOK_UpdateQuickFilterControls() + MTH_BOOK_UpdateCheckboxLayoutByMode() + if MTH_BOOK_STATE.mode == "items" then + MTH_BOOK_STATE.pageSize = 18 + elseif MTH_BOOK_STATE.mode == "families" then + MTH_BOOK_STATE.pageSize = 16 + elseif MTH_BOOK_STATE.mode == "stable" then + MTH_BOOK_STATE.pageSize = 5 + elseif MTH_BOOK_STATE.mode == "pethistory" then + MTH_BOOK_STATE.pageSize = 20 + else + MTH_BOOK_STATE.pageSize = 16 + end + + local familyDropdown = getglobal("MTH_BOOK_FamilyDropdown") + local abilityDropdown = getglobal("MTH_BOOK_AbilityDropdown") + local rankDropdown = getglobal("MTH_BOOK_RankDropdown") + local petLearnSourceDropdown = getglobal("MTH_BOOK_PetLearnSourceDropdown") + local itemSubtypeDropdown = getglobal("MTH_BOOK_ItemSubtypeDropdown") + local npcFunctionDropdown = getglobal("MTH_BOOK_NPCFunctionDropdown") + local npcZoneDropdown = getglobal("MTH_BOOK_NPCZoneDropdown") + local searchLabel = getglobal("MTH_BOOK_SearchLabel") + local minLabel = getglobal("MTH_BOOK_MinLabel") + local maxLabel = getglobal("MTH_BOOK_MaxLabel") + local search = getglobal("MTH_BOOK_Search") + local applyButton = getglobal("MTH_BOOK_ApplyButton") + local scanButton = getglobal("MTH_BOOK_PetBookScanButton") + local hideNoAbilities = getglobal("MTH_BOOK_HideNoAbilities") + local hideUnknown = getglobal("MTH_BOOK_HideUnknown") + local petOnlyMyLevel = getglobal("MTH_BOOK_PetOnlyMyLevel") + local petInZoneOnly = getglobal("MTH_BOOK_PetInZoneOnly") + local npcInZoneOnly = getglobal("MTH_BOOK_NPCInZoneOnly") + local showAllOnMapButton = getglobal("MTH_BOOK_ShowAllOnMapButton") + local minLevel = getglobal("MTH_BOOK_MinLevel") + local maxLevel = getglobal("MTH_BOOK_MaxLevel") + local stats = getglobal("MTH_BOOK_StatsText") + local prevButton = getglobal("MTH_BOOK_PrevButton") + local nextButton = getglobal("MTH_BOOK_NextButton") + local listSlider = getglobal("MTH_BOOK_ListSlider") + local sliderBackdrop = getglobal("MTH_BOOK_ListSliderBackdrop") + + if MTH_BOOK_STATE.mode == "petabilities" or MTH_BOOK_STATE.mode == "stable" then + if stats then stats:Hide() end + if prevButton then prevButton:Hide() end + if nextButton then nextButton:Hide() end + if listSlider then listSlider:Hide() end + if sliderBackdrop then sliderBackdrop:Hide() end + elseif MTH_BOOK_STATE.mode == "families" then + if stats then stats:Show() end + if prevButton then prevButton:Hide() end + if nextButton then nextButton:Hide() end + if listSlider then listSlider:Hide() end + if sliderBackdrop then sliderBackdrop:Hide() end + else + if stats then stats:Show() end + if prevButton then prevButton:Show() end + if nextButton then nextButton:Show() end + if listSlider then listSlider:Show() end + if sliderBackdrop then sliderBackdrop:Show() end + end + + if MTH_BOOK_STATE.mode == "pets" then + if searchLabel then searchLabel:Show() end + if minLabel then minLabel:Show() end + if maxLabel then maxLabel:Show() end + if searchLabel then + searchLabel:ClearAllPoints() + searchLabel:SetPoint("TOPLEFT", searchLabel:GetParent(), "TOPLEFT", 18, -82) + end + if minLabel then + minLabel:ClearAllPoints() + minLabel:SetPoint("TOPLEFT", minLabel:GetParent(), "TOPLEFT", 248, -82) + end + if maxLabel then + maxLabel:ClearAllPoints() + maxLabel:SetPoint("TOPLEFT", maxLabel:GetParent(), "TOPLEFT", 356, -82) + end + if search then search:Show() end + if search then + search:ClearAllPoints() + search:SetPoint("TOPLEFT", search:GetParent(), "TOPLEFT", 66, -80) + search:SetWidth(160) + end + if minLevel then + minLevel:ClearAllPoints() + minLevel:SetPoint("TOPLEFT", minLevel:GetParent(), "TOPLEFT", 302, -80) + minLevel:Show() + end + if maxLevel then + maxLevel:ClearAllPoints() + maxLevel:SetPoint("TOPLEFT", maxLevel:GetParent(), "TOPLEFT", 414, -80) + maxLevel:Show() + end + if applyButton then applyButton:Show() end + if applyButton then + applyButton:ClearAllPoints() + applyButton:SetPoint("TOPLEFT", applyButton:GetParent(), "TOPLEFT", 478, -80) + end + local resetButton = getglobal("MTH_BOOK_ResetButton") + if resetButton then + resetButton:ClearAllPoints() + resetButton:SetPoint("TOPLEFT", resetButton:GetParent(), "TOPLEFT", 546, -80) + end + if scanButton then scanButton:Hide() end + MTH_BOOK_BuildPetAbilityOptions() + for i = 1, 6 do + local btn = getglobal("MTH_BOOK_Filter" .. i) + if btn then btn:Hide() end + end + if familyDropdown then + familyDropdown:Show() + MTH_BOOK_UpdateFamilyDropdownText() + end + if abilityDropdown then + abilityDropdown:Show() + MTH_BOOK_UpdateAbilityDropdownText() + end + local rankVisible = MTH_BOOK_BuildPetRankOptions() + if rankDropdown then + if rankVisible then + rankDropdown:Show() + MTH_BOOK_UpdateRankDropdownText() + else + rankDropdown:Hide() + end + end + if petInZoneOnly then + petInZoneOnly:Show() + petInZoneOnly:SetChecked(MTH_BOOK_STATE.petInZoneOnly and 1 or nil) + end + if showAllOnMapButton then + if MTH_BOOK_STATE.petInZoneOnly then + showAllOnMapButton:Show() + else + showAllOnMapButton:Hide() + end + end + if petLearnSourceDropdown then petLearnSourceDropdown:Hide() end + if itemSubtypeDropdown then itemSubtypeDropdown:Hide() end + if npcFunctionDropdown then npcFunctionDropdown:Hide() end + if npcZoneDropdown then npcZoneDropdown:Hide() end + MTH_BOOK_UpdateFamilyDropdownText() + MTH_BOOK_UpdateAbilityDropdownText() + if hideNoAbilities then hideNoAbilities:Show() end + if hideUnknown then hideUnknown:Show() end + if petOnlyMyLevel then petOnlyMyLevel:Hide() end + if npcInZoneOnly then npcInZoneOnly:Hide() end + local requireVendor = getglobal("MTH_BOOK_RequireVendor") + local requireDrop = getglobal("MTH_BOOK_RequireDrop") + local requireObject = getglobal("MTH_BOOK_RequireObject") + if requireVendor then requireVendor:Show() end + if requireDrop then requireDrop:Show() end + if requireObject then requireObject:Show() end + elseif MTH_BOOK_STATE.mode == "petabilities" then + if searchLabel then searchLabel:Hide() end + if minLabel then minLabel:Hide() end + if maxLabel then maxLabel:Hide() end + if search then search:Hide() end + if applyButton then applyButton:Hide() end + if scanButton then scanButton:Show() end + if minLevel then minLevel:Hide() end + if maxLevel then maxLevel:Hide() end + for i = 1, 6 do + local btn = getglobal("MTH_BOOK_Filter" .. i) + if btn then btn:Hide() end + end + if familyDropdown then familyDropdown:Hide() end + if abilityDropdown then abilityDropdown:Hide() end + if rankDropdown then rankDropdown:Hide() end + if petLearnSourceDropdown then + petLearnSourceDropdown:Show() + MTH_BOOK_UpdatePetLearnSourceDropdownText() + end + if itemSubtypeDropdown then itemSubtypeDropdown:Hide() end + if petOnlyMyLevel then + petOnlyMyLevel:ClearAllPoints() + petOnlyMyLevel:SetPoint("TOPLEFT", petOnlyMyLevel:GetParent(), "TOPLEFT", 228, -80) + petOnlyMyLevel:Show() + petOnlyMyLevel:SetChecked(MTH_BOOK_STATE.petOnlyMyLevel and 1 or nil) + end + if petInZoneOnly then petInZoneOnly:Hide() end + if npcInZoneOnly then npcInZoneOnly:Hide() end + if showAllOnMapButton then showAllOnMapButton:Hide() end + if npcFunctionDropdown then npcFunctionDropdown:Hide() end + if npcZoneDropdown then npcZoneDropdown:Hide() end + if hideNoAbilities then hideNoAbilities:Hide() end + if hideUnknown then hideUnknown:Hide() end + local requireVendor = getglobal("MTH_BOOK_RequireVendor") + local requireDrop = getglobal("MTH_BOOK_RequireDrop") + local requireObject = getglobal("MTH_BOOK_RequireObject") + if requireVendor then requireVendor:Hide() end + if requireDrop then requireDrop:Hide() end + if requireObject then requireObject:Hide() end + local resetButton = getglobal("MTH_BOOK_ResetButton") + if resetButton then + resetButton:ClearAllPoints() + resetButton:SetPoint("TOPLEFT", resetButton:GetParent(), "TOPLEFT", 546, -80) + end + elseif MTH_BOOK_STATE.mode == "npcs" then + if searchLabel then searchLabel:Show() end + if minLabel then minLabel:Hide() end + if maxLabel then maxLabel:Hide() end + if search then search:Show() end + if search then + search:ClearAllPoints() + search:SetPoint("TOPLEFT", search:GetParent(), "TOPLEFT", 54, -80) + search:SetWidth(180) + end + if applyButton then applyButton:Show() end + if applyButton then + applyButton:ClearAllPoints() + applyButton:SetPoint("TOPLEFT", applyButton:GetParent(), "TOPLEFT", 612, -80) + end + local resetButton = getglobal("MTH_BOOK_ResetButton") + if resetButton then + resetButton:ClearAllPoints() + resetButton:SetPoint("TOPLEFT", resetButton:GetParent(), "TOPLEFT", 678, -80) + end + if scanButton then scanButton:Hide() end + if minLevel then minLevel:Hide() end + if maxLevel then maxLevel:Hide() end + for i = 1, 6 do + local btn = getglobal("MTH_BOOK_Filter" .. i) + if btn then btn:Hide() end + end + if familyDropdown then familyDropdown:Hide() end + if abilityDropdown then abilityDropdown:Hide() end + if rankDropdown then rankDropdown:Hide() end + if petLearnSourceDropdown then petLearnSourceDropdown:Hide() end + if itemSubtypeDropdown then itemSubtypeDropdown:Hide() end + if npcFunctionDropdown then npcFunctionDropdown:Show() end + if npcZoneDropdown then npcZoneDropdown:Hide() end + MTH_BOOK_BuildNPCFunctionOptions() + MTH_BOOK_BuildNPCZoneOptions() + MTH_BOOK_UpdateNPCDropdownTexts() + if hideNoAbilities then hideNoAbilities:Hide() end + if hideUnknown then hideUnknown:Show() end + if petOnlyMyLevel then petOnlyMyLevel:Hide() end + if petInZoneOnly then petInZoneOnly:Hide() end + if npcInZoneOnly then + npcInZoneOnly:ClearAllPoints() + npcInZoneOnly:SetPoint("TOPLEFT", npcInZoneOnly:GetParent(), "TOPLEFT", 438, -82) + npcInZoneOnly:Show() + npcInZoneOnly:SetChecked(MTH_BOOK_STATE.npcInZoneOnly and 1 or nil) + end + if showAllOnMapButton then showAllOnMapButton:Hide() end + local requireVendor = getglobal("MTH_BOOK_RequireVendor") + local requireDrop = getglobal("MTH_BOOK_RequireDrop") + local requireObject = getglobal("MTH_BOOK_RequireObject") + if requireVendor then requireVendor:Show() end + if requireDrop then requireDrop:Show() end + if requireObject then requireObject:Show() end + elseif MTH_BOOK_STATE.mode == "items" then + if searchLabel then searchLabel:Show() end + if minLabel then minLabel:Hide() end + if maxLabel then maxLabel:Hide() end + if search then search:Show() end + if search then + search:ClearAllPoints() + search:SetPoint("TOPLEFT", search:GetParent(), "TOPLEFT", 54, -80) + search:SetWidth(180) + end + if applyButton then applyButton:Show() end + if applyButton then + applyButton:ClearAllPoints() + applyButton:SetPoint("TOPLEFT", applyButton:GetParent(), "TOPLEFT", 612, -80) + end + local resetButton = getglobal("MTH_BOOK_ResetButton") + if resetButton then + resetButton:ClearAllPoints() + resetButton:SetPoint("TOPLEFT", resetButton:GetParent(), "TOPLEFT", 678, -80) + end + if scanButton then scanButton:Hide() end + if minLevel then minLevel:Hide() end + if maxLevel then maxLevel:Hide() end + for i = 1, 6 do + local btn = getglobal("MTH_BOOK_Filter" .. i) + if btn then btn:Hide() end + end + if familyDropdown then familyDropdown:Hide() end + if abilityDropdown then abilityDropdown:Hide() end + if rankDropdown then rankDropdown:Hide() end + if petLearnSourceDropdown then petLearnSourceDropdown:Hide() end + if itemSubtypeDropdown then + itemSubtypeDropdown:ClearAllPoints() + itemSubtypeDropdown:SetPoint("TOPLEFT", itemSubtypeDropdown:GetParent(), "TOPLEFT", 238, -80) + itemSubtypeDropdown:Show() + MTH_BOOK_UpdateItemSubtypeDropdownText() + end + if npcFunctionDropdown then npcFunctionDropdown:Hide() end + if npcZoneDropdown then npcZoneDropdown:Hide() end + if hideNoAbilities then hideNoAbilities:Hide() end + if hideUnknown then hideUnknown:Hide() end + if petOnlyMyLevel then + petOnlyMyLevel:ClearAllPoints() + petOnlyMyLevel:SetPoint("TOPLEFT", petOnlyMyLevel:GetParent(), "TOPLEFT", 430, -82) + petOnlyMyLevel:Show() + petOnlyMyLevel:SetChecked(MTH_BOOK_STATE.itemOnlyMyLevel and 1 or nil) + end + if petInZoneOnly then petInZoneOnly:Hide() end + if npcInZoneOnly then npcInZoneOnly:Hide() end + if showAllOnMapButton then showAllOnMapButton:Hide() end + local requireVendor = getglobal("MTH_BOOK_RequireVendor") + local requireDrop = getglobal("MTH_BOOK_RequireDrop") + local requireObject = getglobal("MTH_BOOK_RequireObject") + if requireVendor then requireVendor:Hide() end + if requireDrop then requireDrop:Hide() end + if requireObject then requireObject:Hide() end + elseif MTH_BOOK_STATE.mode == "stable" then + if searchLabel then searchLabel:Hide() end + if minLabel then minLabel:Hide() end + if maxLabel then maxLabel:Hide() end + if search then search:Hide() end + if applyButton then applyButton:Hide() end + local resetButton = getglobal("MTH_BOOK_ResetButton") + if resetButton then resetButton:Hide() end + if scanButton then scanButton:Hide() end + if minLevel then minLevel:Hide() end + if maxLevel then maxLevel:Hide() end + for i = 1, 6 do + local btn = getglobal("MTH_BOOK_Filter" .. i) + if btn then btn:Hide() end + end + if familyDropdown then familyDropdown:Hide() end + if abilityDropdown then abilityDropdown:Hide() end + if rankDropdown then rankDropdown:Hide() end + if petLearnSourceDropdown then petLearnSourceDropdown:Hide() end + if itemSubtypeDropdown then itemSubtypeDropdown:Hide() end + if npcFunctionDropdown then npcFunctionDropdown:Hide() end + if npcZoneDropdown then npcZoneDropdown:Hide() end + if hideNoAbilities then hideNoAbilities:Hide() end + if hideUnknown then hideUnknown:Hide() end + if petOnlyMyLevel then petOnlyMyLevel:Hide() end + if petInZoneOnly then petInZoneOnly:Hide() end + if npcInZoneOnly then npcInZoneOnly:Hide() end + if showAllOnMapButton then showAllOnMapButton:Hide() end + local requireVendor = getglobal("MTH_BOOK_RequireVendor") + local requireDrop = getglobal("MTH_BOOK_RequireDrop") + local requireObject = getglobal("MTH_BOOK_RequireObject") + if requireVendor then requireVendor:Hide() end + if requireDrop then requireDrop:Hide() end + if requireObject then requireObject:Hide() end + elseif MTH_BOOK_STATE.mode == "families" then + if searchLabel then searchLabel:Hide() end + if minLabel then minLabel:Hide() end + if maxLabel then maxLabel:Hide() end + if search then search:Hide() end + if applyButton then applyButton:Hide() end + local resetButton = getglobal("MTH_BOOK_ResetButton") + if resetButton then resetButton:Hide() end + if scanButton then scanButton:Hide() end + if minLevel then minLevel:Hide() end + if maxLevel then maxLevel:Hide() end + for i = 1, 6 do + local btn = getglobal("MTH_BOOK_Filter" .. i) + if btn then btn:Hide() end + end + if familyDropdown then familyDropdown:Hide() end + if abilityDropdown then abilityDropdown:Hide() end + if rankDropdown then rankDropdown:Hide() end + if petLearnSourceDropdown then petLearnSourceDropdown:Hide() end + if itemSubtypeDropdown then itemSubtypeDropdown:Hide() end + if npcFunctionDropdown then npcFunctionDropdown:Hide() end + if npcZoneDropdown then npcZoneDropdown:Hide() end + if hideNoAbilities then hideNoAbilities:Hide() end + if hideUnknown then hideUnknown:Hide() end + if petOnlyMyLevel then petOnlyMyLevel:Hide() end + if petInZoneOnly then petInZoneOnly:Hide() end + if npcInZoneOnly then npcInZoneOnly:Hide() end + if showAllOnMapButton then showAllOnMapButton:Hide() end + local requireVendor = getglobal("MTH_BOOK_RequireVendor") + local requireDrop = getglobal("MTH_BOOK_RequireDrop") + local requireObject = getglobal("MTH_BOOK_RequireObject") + if requireVendor then requireVendor:Hide() end + if requireDrop then requireDrop:Hide() end + if requireObject then requireObject:Hide() end + elseif MTH_BOOK_STATE.mode == "pethistory" then + if searchLabel then searchLabel:Hide() end + if minLabel then minLabel:Hide() end + if maxLabel then maxLabel:Hide() end + if search then search:Hide() end + if applyButton then applyButton:Hide() end + local resetButton = getglobal("MTH_BOOK_ResetButton") + if resetButton then resetButton:Hide() end + if scanButton then scanButton:Hide() end + if minLevel then minLevel:Hide() end + if maxLevel then maxLevel:Hide() end + for i = 1, 6 do + local btn = getglobal("MTH_BOOK_Filter" .. i) + if btn then btn:Hide() end + end + if familyDropdown then familyDropdown:Hide() end + if abilityDropdown then abilityDropdown:Hide() end + if rankDropdown then rankDropdown:Hide() end + if petLearnSourceDropdown then petLearnSourceDropdown:Hide() end + if itemSubtypeDropdown then itemSubtypeDropdown:Hide() end + if npcFunctionDropdown then npcFunctionDropdown:Hide() end + if npcZoneDropdown then npcZoneDropdown:Hide() end + if hideNoAbilities then hideNoAbilities:Hide() end + if hideUnknown then hideUnknown:Hide() end + if petOnlyMyLevel then petOnlyMyLevel:Hide() end + if petInZoneOnly then petInZoneOnly:Hide() end + if npcInZoneOnly then npcInZoneOnly:Hide() end + if showAllOnMapButton then showAllOnMapButton:Hide() end + local requireVendor = getglobal("MTH_BOOK_RequireVendor") + local requireDrop = getglobal("MTH_BOOK_RequireDrop") + local requireObject = getglobal("MTH_BOOK_RequireObject") + if requireVendor then requireVendor:Hide() end + if requireDrop then requireDrop:Hide() end + if requireObject then requireObject:Hide() end + else + if itemSubtypeDropdown then itemSubtypeDropdown:Hide() end + end + + if MTH_BOOK_STATE.mode ~= "petabilities" and MTH_BOOK_STATE.mode ~= "items" and petOnlyMyLevel then + petOnlyMyLevel:Hide() + end + if MTH_BOOK_STATE.mode ~= "pets" and petInZoneOnly then + petInZoneOnly:Hide() + end + if MTH_BOOK_STATE.mode ~= "npcs" and npcInZoneOnly then + npcInZoneOnly:Hide() + end + local resetButton = getglobal("MTH_BOOK_ResetButton") + if resetButton and MTH_BOOK_STATE.mode ~= "stable" and MTH_BOOK_STATE.mode ~= "families" and MTH_BOOK_STATE.mode ~= "pethistory" then + resetButton:Show() + end + MTH_BOOK_UpdateShowAllOnMapButton() + + MTH_BOOK_ApplyContentLayoutForMode() +end + +local function MTH_BOOK_InitFamilyDropdown() + local familyDropdown = getglobal("MTH_BOOK_FamilyDropdown") + if not familyDropdown then return end + MTH_BOOK_BuildPetFamilyOptions() + + UIDropDownMenu_Initialize(familyDropdown, function() + for i = 1, table.getn(MTH_BOOK_PetFamilyOptions) do + local option = MTH_BOOK_PetFamilyOptions[i] + local value = option.value + local label = option.label + local index = i + local info = MTH_BOOK_DropdownCreateInfo() + info.text = label + info.func = function() + MTH_BOOK_STATE.quick = value + MTH_BOOK_STATE.page = 1 + MTH_BOOK_STATE.selectedEntry = nil + MTH_BOOK_DropdownSetText(familyDropdown, label) + MTH_BOOK_RefreshFilter() + end + info.checked = (MTH_BOOK_STATE.quick == value) + UIDropDownMenu_AddButton(info) + end + end) + + UIDropDownMenu_SetWidth(150, familyDropdown) + UIDropDownMenu_JustifyText("LEFT", familyDropdown) + MTH_BOOK_UpdateFamilyDropdownText() +end + +local function MTH_BOOK_InitAbilityDropdown() + local abilityDropdown = getglobal("MTH_BOOK_AbilityDropdown") + if not abilityDropdown then return end + MTH_BOOK_BuildPetAbilityOptions() + + UIDropDownMenu_Initialize(abilityDropdown, function() + for i = 1, table.getn(MTH_BOOK_PetAbilityOptions) do + local option = MTH_BOOK_PetAbilityOptions[i] + local value = option.value + local label = option.label + local index = i + local info = MTH_BOOK_DropdownCreateInfo() + info.text = label + info.func = function() + MTH_BOOK_STATE.petAbility = value + MTH_BOOK_STATE.petRank = "all" + MTH_BOOK_BuildPetRankOptions() + MTH_BOOK_STATE.page = 1 + MTH_BOOK_STATE.selectedEntry = nil + MTH_BOOK_DropdownSetText(abilityDropdown, label) + MTH_BOOK_UpdateQuickFilterControls() + MTH_BOOK_RefreshFilter() + end + info.checked = (MTH_BOOK_STATE.petAbility == value) + UIDropDownMenu_AddButton(info) + end + end) + + UIDropDownMenu_SetWidth(150, abilityDropdown) + UIDropDownMenu_JustifyText("LEFT", abilityDropdown) + MTH_BOOK_UpdateAbilityDropdownText() +end + +local function MTH_BOOK_InitRankDropdown() + local rankDropdown = getglobal("MTH_BOOK_RankDropdown") + if not rankDropdown then return end + MTH_BOOK_BuildPetRankOptions() + + UIDropDownMenu_Initialize(rankDropdown, function() + for i = 1, table.getn(MTH_BOOK_PetRankOptions) do + local option = MTH_BOOK_PetRankOptions[i] + local value = option.value + local label = option.label + local index = i + local info = MTH_BOOK_DropdownCreateInfo() + info.text = label + info.func = function() + MTH_BOOK_STATE.petRank = value + MTH_BOOK_STATE.page = 1 + MTH_BOOK_STATE.selectedEntry = nil + MTH_BOOK_DropdownSetText(rankDropdown, label) + MTH_BOOK_RefreshFilter() + end + info.checked = (tostring(MTH_BOOK_STATE.petRank) == tostring(value)) + UIDropDownMenu_AddButton(info) + end + end) + + UIDropDownMenu_SetWidth(120, rankDropdown) + UIDropDownMenu_JustifyText("LEFT", rankDropdown) + MTH_BOOK_UpdateRankDropdownText() +end + +local function MTH_BOOK_InitPetLearnSourceDropdown() + local sourceDropdown = getglobal("MTH_BOOK_PetLearnSourceDropdown") + if not sourceDropdown then return end + + UIDropDownMenu_Initialize(sourceDropdown, function() + for i = 1, table.getn(MTH_BOOK_PetLearnSourceOptions) do + local option = MTH_BOOK_PetLearnSourceOptions[i] + local info = MTH_BOOK_DropdownCreateInfo() + info.text = option.label + info.func = function() + MTH_BOOK_STATE.petLearnSource = option.value + MTH_BOOK_STATE.page = 1 + MTH_BOOK_STATE.selectedEntry = nil + MTH_BOOK_STATE.selectedPetRankEntry = nil + MTH_BOOK_STATE.petRankRows = {} + MTH_BOOK_DropdownSetText(sourceDropdown, option.label) + MTH_BOOK_RefreshFilter() + end + info.checked = (MTH_BOOK_STATE.petLearnSource == option.value) + UIDropDownMenu_AddButton(info) + end + end) + + UIDropDownMenu_SetWidth(140, sourceDropdown) + UIDropDownMenu_JustifyText("LEFT", sourceDropdown) + MTH_BOOK_UpdatePetLearnSourceDropdownText() +end + +local function MTH_BOOK_InitItemSubtypeDropdown() + local itemDropdown = getglobal("MTH_BOOK_ItemSubtypeDropdown") + if not itemDropdown then return end + + UIDropDownMenu_Initialize(itemDropdown, function() + for i = 1, table.getn(MTH_BOOK_ItemSubtypeOptions) do + local option = MTH_BOOK_ItemSubtypeOptions[i] + local info = MTH_BOOK_DropdownCreateInfo() + info.text = option.label + info.func = function() + MTH_BOOK_STATE.itemSubtype = option.value + MTH_BOOK_STATE.page = 1 + MTH_BOOK_STATE.selectedEntry = nil + MTH_BOOK_DropdownSetText(itemDropdown, option.label) + MTH_BOOK_RefreshFilter() + end + info.checked = (MTH_BOOK_STATE.itemSubtype == option.value) + UIDropDownMenu_AddButton(info) + end + end) + + UIDropDownMenu_SetWidth(75, itemDropdown) + UIDropDownMenu_JustifyText("LEFT", itemDropdown) + MTH_BOOK_UpdateItemSubtypeDropdownText() +end + +local function MTH_BOOK_InitNPCFunctionDropdown() + local functionDropdown = getglobal("MTH_BOOK_NPCFunctionDropdown") + if not functionDropdown then return end + MTH_BOOK_BuildNPCFunctionOptions() + + UIDropDownMenu_Initialize(functionDropdown, function() + for i = 1, table.getn(MTH_BOOK_NPCFunctionOptions) do + local option = MTH_BOOK_NPCFunctionOptions[i] + local info = MTH_BOOK_DropdownCreateInfo() + info.text = option.label + info.func = function() + MTH_BOOK_STATE.npcFunction = option.value + MTH_BOOK_STATE.page = 1 + MTH_BOOK_STATE.selectedEntry = nil + MTH_BOOK_DropdownSetText(functionDropdown, option.label) + MTH_BOOK_RefreshFilter() + end + info.checked = (MTH_BOOK_STATE.npcFunction == option.value) + UIDropDownMenu_AddButton(info) + end + end) + + UIDropDownMenu_SetWidth(150, functionDropdown) + UIDropDownMenu_JustifyText("LEFT", functionDropdown) + MTH_BOOK_UpdateNPCDropdownTexts() +end +local function MTH_BOOK_AddDetailSection(lines, title) + table.insert(lines, "") + table.insert(lines, "|cFFFFD100" .. tostring(title) .. "|r") +end + +local function MTH_BOOK_AddDetailKV(lines, label, value) + table.insert(lines, tostring(label) .. ": " .. tostring(value)) +end + +local function MTH_BOOK_AddStableStyleKV(lines, label, value) + table.insert(lines, "|cff9a9a9a" .. tostring(label or "") .. ":|r |cffffffff" .. tostring(value or "-") .. "|r") +end + +local function MTH_BOOK_FormatDateTimeValue(value) + local ts = tonumber(value) + if not ts or ts <= 0 then + return "-" + end + if type(date) == "function" then + return date("%Y-%m-%d %H:%M:%S", ts) + end + return tostring(ts) +end + +local function MTH_BOOK_GetPetHistoryLostCause(row) + local reason = MTH_BOOK_SafeLower(type(row) == "table" and row.abandonReason or "") + if reason == "pet-runaway" then + return "Ran Away" + end + if string.find(reason, "runaway", 1, true) or string.find(reason, "run away", 1, true) then + return "Ran Away" + end + return "Abandonned" +end + +local function MTH_BOOK_FormatRespawnWindow(beast) + if not beast then return nil end + local minSec = tonumber(beast.respawnMinSeconds) + local maxSec = tonumber(beast.respawnMaxSeconds) + local samples = tonumber(beast.respawnSamples) + if not minSec and not maxSec then return nil end + if not minSec then minSec = maxSec end + if not maxSec then maxSec = minSec end + + local function fmt(sec) + sec = math.floor((tonumber(sec) or 0) + 0.5) + if sec < 0 then sec = 0 end + if SecondsToTime then + return SecondsToTime(sec) + end + if sec >= 3600 then + return string.format("%.1fh", sec / 3600) + elseif sec >= 60 then + return string.format("%.1fm", sec / 60) + end + return tostring(sec) .. "s" + end + + local value + if minSec == maxSec then + value = fmt(minSec) + else + value = fmt(minSec) .. " - " .. fmt(maxSec) + end + + if samples and samples > 0 then + value = value .. " (" .. tostring(samples) .. " samples)" + end + + return value +end + +function MTH_BOOK_ResolveIconPath(icon) + if not icon or icon == "" then return nil end + local iconText = tostring(icon) + if string.find(iconText, "\\", 1, true) then + return iconText + end + return "Interface\\Icons\\" .. iconText +end + +local MTH_BOOK_SpellTooltip = nil + +function MTH_BOOK_GetSpellTooltip() + if MTH_BOOK_SpellTooltip then return MTH_BOOK_SpellTooltip end + if not CreateFrame then return GameTooltip end + MTH_BOOK_SpellTooltip = CreateFrame("GameTooltip", "MTH_BOOK_SpellTooltip", UIParent, "GameTooltipTemplate") + MTH_BOOK_SpellTooltip.mthIcon = MTH_BOOK_SpellTooltip:CreateTexture(nil, "ARTWORK") + MTH_BOOK_SpellTooltip.mthIcon:SetPoint("TOPRIGHT", MTH_BOOK_SpellTooltip, "TOPRIGHT", -8, -8) + MTH_BOOK_SpellTooltip.mthIcon:SetWidth(36) + MTH_BOOK_SpellTooltip.mthIcon:SetHeight(36) + MTH_BOOK_SpellTooltip.mthIcon:Hide() + return MTH_BOOK_SpellTooltip +end + +function MTH_BOOK_SetSpellTooltipIcon(tooltip, icon) + if not tooltip then return end + if not tooltip.mthIcon then return end + local iconPath = MTH_BOOK_ResolveIconPath(icon) + if iconPath then + tooltip.mthIcon:SetTexture(iconPath) + tooltip.mthIcon:Show() + else + tooltip.mthIcon:Hide() + end +end + +function MTH_BOOK_HideSpellTooltip() + local tooltip = MTH_BOOK_SpellTooltip + if tooltip and tooltip.mthIcon then + tooltip.mthIcon:Hide() + end + if tooltip and tooltip.Hide then + tooltip:Hide() + end +end + +function MTH_BOOK_ShowBaselineTooltip(anchor, entry) + if not anchor or not entry then return end + local tooltip = MTH_BOOK_GetSpellTooltip() + if not tooltip then return end + + tooltip:SetOwner(anchor, "ANCHOR_RIGHT") + tooltip:ClearLines() + MTH_BOOK_SetSpellTooltipIcon(tooltip, entry.icon) + tooltip:AddLine(tostring(entry.ability or "Unknown"), 1.00, 0.82, 0.00) + tooltip:AddLine(" ") + tooltip:AddLine("Spell Baseline", 1.00, 0.82, 0.00) + tooltip:AddDoubleLine("Known Ranks", tostring(entry.knownRankCount or 0) .. "/" .. tostring(entry.rankCount or 0), 0.85, 0.85, 0.85, 1, 1, 1) + tooltip:AddDoubleLine("Families", tostring(entry.familyCount or 0), 0.85, 0.85, 0.85, 1, 1, 1) + tooltip:AddDoubleLine("Spell Rows", tostring(entry.spellRows or 0), 0.85, 0.85, 0.85, 1, 1, 1) + if entry.minRank and entry.maxRank then + if entry.minRank == entry.maxRank then + tooltip:AddDoubleLine("Rank Span", tostring(entry.minRank), 0.85, 0.85, 0.85, 1, 1, 1) + else + tooltip:AddDoubleLine("Rank Span", tostring(entry.minRank) .. "-" .. tostring(entry.maxRank), 0.85, 0.85, 0.85, 1, 1, 1) + end + end + + if entry.families and table.getn(entry.families) > 0 then + tooltip:AddLine(" ") + tooltip:AddLine("Families", 1.00, 0.82, 0.00) + for i = 1, table.getn(entry.families) do + tooltip:AddLine("- " .. tostring(entry.families[i]), 0.90, 0.90, 0.90) + if i >= 10 then + local remaining = table.getn(entry.families) - i + if remaining > 0 then + tooltip:AddLine("... +" .. tostring(remaining) .. " more", 0.70, 0.70, 0.70) + end + break + end + end + end + + tooltip:Show() +end + +function MTH_BOOK_ShowRankTooltip(anchor, rankEntry) + if not anchor or not rankEntry then return end + local tooltip = MTH_BOOK_GetSpellTooltip() + if not tooltip then return end + + tooltip:SetOwner(anchor, "ANCHOR_RIGHT") + tooltip:ClearLines() + MTH_BOOK_SetSpellTooltipIcon(tooltip, rankEntry.icon) + + local header = tostring(rankEntry.ability or "Spell") + header = header .. " (" .. MTH_BOOK_GetRankLabel(rankEntry.rankNumber, true) .. ")" + tooltip:AddLine(header, 1.00, 0.82, 0.00) + tooltip:AddLine(" ") + tooltip:AddLine("Current Rank", 1.00, 0.82, 0.00) + tooltip:AddDoubleLine("Rank", MTH_BOOK_GetRankLabel(rankEntry.rankNumber, true), 0.85, 0.85, 0.85, 1, 1, 1) + tooltip:AddDoubleLine("Known", rankEntry.isKnown and "v" or "-", 0.85, 0.85, 0.85, rankEntry.isKnown and 0.18 or 0.90, rankEntry.isKnown and 0.90 or 0.90, rankEntry.isKnown and 0.18 or 0.90) + tooltip:AddDoubleLine("Train Level", rankEntry.trainLevel and tostring(rankEntry.trainLevel) or "-", 0.85, 0.85, 0.85, 1, 1, 1) + tooltip:AddDoubleLine("Spell ID", rankEntry.id and tostring(rankEntry.id) or "-", 0.85, 0.85, 0.85, 1, 1, 1) + if rankEntry.cost and rankEntry.cost ~= "" then + tooltip:AddDoubleLine("Cost", tostring(rankEntry.cost), 0.85, 0.85, 0.85, 1, 1, 1) + end + if rankEntry.castTime and rankEntry.castTime ~= "" then + tooltip:AddDoubleLine("Cast", tostring(rankEntry.castTime), 0.85, 0.85, 0.85, 1, 1, 1) + end + if rankEntry.range and rankEntry.range ~= "" then + tooltip:AddDoubleLine("Range", tostring(rankEntry.range), 0.85, 0.85, 0.85, 1, 1, 1) + end + if rankEntry.school and rankEntry.school ~= "" then + tooltip:AddDoubleLine("School", tostring(rankEntry.school), 0.85, 0.85, 0.85, 1, 1, 1) + end + + if rankEntry.description and rankEntry.description ~= "" then + tooltip:AddLine(" ") + tooltip:AddLine("Description", 1.00, 0.82, 0.00) + tooltip:AddLine(tostring(rankEntry.description), 0.90, 0.90, 0.90, true) + end + + if rankEntry.effects then + local effectsType = type(rankEntry.effects) + if effectsType == "table" and table.getn(rankEntry.effects) > 0 then + tooltip:AddLine(" ") + tooltip:AddLine("Effects", 1.00, 0.82, 0.00) + for i = 1, table.getn(rankEntry.effects) do + tooltip:AddLine("- " .. tostring(rankEntry.effects[i]), 0.90, 0.90, 0.90, true) + end + elseif effectsType == "string" and rankEntry.effects ~= "" then + tooltip:AddLine(" ") + tooltip:AddLine("Effects", 1.00, 0.82, 0.00) + tooltip:AddLine(tostring(rankEntry.effects), 0.90, 0.90, 0.90, true) + end + end + + tooltip:Show() +end + +local function MTH_BOOK_WrapLine(text, maxChars) + local line = tostring(text or "") + if string.len(line) <= maxChars then return line end + local out = {} + local remaining = line + while string.len(remaining) > maxChars do + local chunk = string.sub(remaining, 1, maxChars) + local cut = nil + for i = maxChars, 1, -1 do + if string.sub(chunk, i, i) == " " then + cut = i + break + end + end + if not cut or cut < 8 then cut = maxChars end + table.insert(out, string.sub(remaining, 1, cut)) + remaining = string.gsub(string.sub(remaining, cut + 1), "^%s+", "") + end + if remaining ~= "" then table.insert(out, remaining) end + return table.concat(out, "\n") +end + +local function MTH_BOOK_WrapDetailText(text) + local src = tostring(text or "") + local lines = {} + local startPos = 1 + while true do + local nl = string.find(src, "\n", startPos, true) + if not nl then + table.insert(lines, MTH_BOOK_WrapLine(string.sub(src, startPos), 28)) + break + end + table.insert(lines, MTH_BOOK_WrapLine(string.sub(src, startPos, nl - 1), 28)) + startPos = nl + 1 + end + return table.concat(lines, "\n") +end + +function MTH_BOOK_SetDetailText(detail, text) + if not detail then return end + local wrapped = tostring(text or "") + if MTH_BOOK_STATE.mode ~= "pethistory" then + wrapped = MTH_BOOK_WrapDetailText(wrapped) + end + detail._mthLocking = true + detail:SetText(wrapped) + detail._mthLockedText = wrapped + detail._mthLocking = false +end + +function MTH_BOOK_ConfigureReadOnlyEditBox(detail) + if not detail then return end + if detail.SetFontObject then detail:SetFontObject(GameFontNormalSmall) end + if detail.SetJustifyH then detail:SetJustifyH("LEFT") end + if detail.SetJustifyV then detail:SetJustifyV("TOP") end + if detail.SetMultiLine then detail:SetMultiLine(true) end + if detail.SetAutoFocus then detail:SetAutoFocus(false) end + if detail.SetMaxLetters then detail:SetMaxLetters(0) end + if detail.SetTextInsets then detail:SetTextInsets(4, 4, 4, 4) end + if detail.EnableMouse then detail:EnableMouse(true) end + if detail.EnableKeyboard then detail:EnableKeyboard(true) end + if detail.SetScript then + detail:SetScript("OnChar", function() + if this._mthLockedText then + this._mthLocking = true + this:SetText(this._mthLockedText) + this._mthLocking = false + end + end) + detail:SetScript("OnKeyDown", function() + if this._mthLockedText then + this._mthLocking = true + this:SetText(this._mthLockedText) + this._mthLocking = false + end + end) + detail:SetScript("OnTextChanged", function() + if this._mthLocking then return end + local current = this:GetText() or "" + if this._mthLockedText and current ~= this._mthLockedText then + this._mthLocking = true + this:SetText(this._mthLockedText) + this._mthLocking = false + end + end) + detail:SetScript("OnEscapePressed", function() this:ClearFocus() end) + detail:SetScript("OnEnterPressed", function() this:ClearFocus() end) + end + detail._mthLockedText = detail:GetText() or "" +end + +function MTH_BOOK_UpdateDetail() + local detail = getglobal("MTH_BOOK_DetailBackdropDetailText") + if not detail then return end + local petTop = MTH_BOOK_STATE.petUI and MTH_BOOK_STATE.petUI.detailTop + local petBottom = MTH_BOOK_STATE.petUI and MTH_BOOK_STATE.petUI.detailBottom + if not MTH_BOOK_STATE.selectedEntry then + if MTH_BOOK_STATE.mode == "petabilities" then + if petTop and petBottom then + MTH_BOOK_SetDetailText(petTop, "|cFFFFD100Baseline|r\n\nSelect a spell baseline from the left list.") + MTH_BOOK_SetDetailText(petBottom, "|cFFFFD100Rank|r\n\nSelect a rank from the right list.") + else + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Pet Abilities|r\n\nSelect a spell baseline to inspect rank coverage and families.") + end + else + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Inspector|r\n\nSelect an entry from the list to view identity, stats, sources and locations.") + end + MTH_BOOK_UpdateOpenMapButton() + return + end + + if MTH_BOOK_STATE.mode == "npcs" then + local selected = MTH_BOOK_STATE.selectedEntry + if not selected or not selected.npcId then + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Inspector|r\n\nSelect an NPC to view details and map locations.") + MTH_BOOK_UpdateOpenMapButton() + return + end + local npcId = selected.npcId + local npc = MTH_DS_Vendors and MTH_DS_Vendors[npcId] + if not npc then + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Inspector|r\n\nSelect an NPC to view details and map locations.") + MTH_BOOK_UpdateOpenMapButton() + return + end + + local functionSummary = MTH_BOOK_GetNPCFunctionSummary(npc) + local zoneName, subzoneName = MTH_BOOK_GetNPCZoneSummary(npc) + local lines = {} + table.insert(lines, "|cFFFFFF00" .. tostring(npc.name or "Unknown") .. "|r") + MTH_BOOK_AddDetailSection(lines, "Identity") + MTH_BOOK_AddDetailKV(lines, "ID", npcId) + MTH_BOOK_AddDetailKV(lines, "React", MTH_BOOK_GetNPCReactBucket(npc.fac)) + MTH_BOOK_AddDetailKV(lines, "Function", functionSummary) + MTH_BOOK_AddDetailKV(lines, "Level", npc.lvl or "?") + + MTH_BOOK_AddDetailSection(lines, "Location") + MTH_BOOK_AddDetailKV(lines, "Zone", zoneName) + MTH_BOOK_AddDetailKV(lines, "Subzone", subzoneName) + if npc.coords and table.getn(npc.coords) > 0 then + for i = 1, table.getn(npc.coords) do + local c = npc.coords[i] + if c then + local x = tonumber(c[1] or 0) or 0 + local y = tonumber(c[2] or 0) or 0 + table.insert(lines, string.format(" - %s (%.1f, %.1f)", MTH_BOOK_GetZoneName(c[3]), x, y)) + end + if i >= 8 then break end + end + else + table.insert(lines, " - No coordinates") + end + + MTH_BOOK_SetDetailText(detail, table.concat(lines, "\n")) + MTH_BOOK_UpdateOpenMapButton() + return + end + + if MTH_BOOK_STATE.mode == "pets" then + local selected = MTH_BOOK_STATE.selectedEntry + if not selected or not selected.beastId then + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Inspector|r\n\nSelect an entry from the list to view identity, stats, sources and locations.") + MTH_BOOK_UpdateOpenMapButton() + return + end + local beastId = selected.beastId + local beast = MTH_DS_Beasts and MTH_DS_Beasts[beastId] + if not beast then + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Inspector|r\n\nSelect an entry from the list to view identity, stats, sources and locations.") + MTH_BOOK_UpdateOpenMapButton() + return + end + local lines = {} + table.insert(lines, "|cFFFFFF00" .. (beast.name or "Unknown") .. "|r") + MTH_BOOK_AddDetailSection(lines, "Identity") + MTH_BOOK_AddDetailKV(lines, "ID", beastId) + MTH_BOOK_AddDetailKV(lines, "Family", beast.family or "?") + + MTH_BOOK_AddDetailSection(lines, "Stats") + MTH_BOOK_AddDetailKV(lines, "Level", beast.lvl or "?") + MTH_BOOK_AddDetailKV(lines, "Attack Speed", beast.attackSpeed or "?") + local respawnWindow = MTH_BOOK_FormatRespawnWindow(beast) + if respawnWindow then + MTH_BOOK_AddDetailKV(lines, "Respawn", respawnWindow) + end + MTH_BOOK_AddDetailKV(lines, "Rare", beast.rare and "Yes" or "No") + MTH_BOOK_AddDetailKV(lines, "Abilities", beast.abilities or "None") + + local fam = MTH_DS_Families and beast.family and MTH_DS_Families[beast.family] + if fam and fam.abilities and table.getn(fam.abilities) > 0 then + MTH_BOOK_AddDetailSection(lines, "Family Pool") + table.insert(lines, " " .. table.concat(fam.abilities, ", ")) + end + + if beast.coords and table.getn(beast.coords) > 0 then + MTH_BOOK_AddDetailSection(lines, "Locations") + for i = 1, table.getn(beast.coords) do + local c = beast.coords[i] + if c then + local x = tonumber(c[1] or 0) or 0 + local y = tonumber(c[2] or 0) or 0 + local zoneId = c[3] + table.insert(lines, string.format(" - %s (%.1f, %.1f)", MTH_BOOK_GetZoneName(zoneId), x, y)) + end + if i >= 8 then break end + end + end + + MTH_BOOK_SetDetailText(detail, table.concat(lines, "\n")) + MTH_BOOK_UpdateOpenMapButton() + return + end + + if MTH_BOOK_STATE.mode == "abilities" then + local entry = MTH_BOOK_STATE.selectedEntry + if not entry or not entry.beastId then + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Inspector|r\n\nSelect an entry from the list to view identity, stats, sources and locations.") + MTH_BOOK_UpdateOpenMapButton() + return + end + local beast = MTH_DS_Beasts and MTH_DS_Beasts[entry.beastId] + if not beast then + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Inspector|r\n\nSelect an entry from the list to view identity, stats, sources and locations.") + MTH_BOOK_UpdateOpenMapButton() + return + end + local lines = {} + table.insert(lines, "|cFFFFFF00" .. tostring(entry.abilityToken) .. "|r") + + MTH_BOOK_AddDetailSection(lines, "Identity") + MTH_BOOK_AddDetailKV(lines, "Beast", tostring(beast.name or "Unknown") .. " (ID " .. tostring(entry.beastId) .. ")") + MTH_BOOK_AddDetailKV(lines, "Family", beast.family or "?") + + MTH_BOOK_AddDetailSection(lines, "Stats") + MTH_BOOK_AddDetailKV(lines, "Level", beast.lvl or "?") + MTH_BOOK_AddDetailKV(lines, "Attack Speed", beast.attackSpeed or "?") + local respawnWindow = MTH_BOOK_FormatRespawnWindow(beast) + if respawnWindow then + MTH_BOOK_AddDetailKV(lines, "Respawn", respawnWindow) + end + MTH_BOOK_AddDetailKV(lines, "Rare", beast.rare and "Yes" or "No") + + if beast.coords and table.getn(beast.coords) > 0 then + MTH_BOOK_AddDetailSection(lines, "Locations") + for i = 1, table.getn(beast.coords) do + local c = beast.coords[i] + if c then + local x = tonumber(c[1] or 0) or 0 + local y = tonumber(c[2] or 0) or 0 + local zoneId = c[3] + table.insert(lines, string.format(" - %s (%.1f, %.1f)", MTH_BOOK_GetZoneName(zoneId), x, y)) + end + if i >= 8 then break end + end + end + + MTH_BOOK_SetDetailText(detail, table.concat(lines, "\n")) + MTH_BOOK_UpdateOpenMapButton() + return + end + + if MTH_BOOK_STATE.mode == "petabilities" then + local entry = MTH_BOOK_STATE.selectedEntry + if not entry then + if petTop and petBottom then + MTH_BOOK_SetDetailText(petTop, "Select a spell baseline from the left list.") + MTH_BOOK_SetDetailText(petBottom, "Select a rank from the right list.") + if MTH_BOOK_STATE.petUI and MTH_BOOK_STATE.petUI.detailTopIcon then + MTH_BOOK_STATE.petUI.detailTopIcon:Hide() + end + else + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Pet Abilities|r\n\nSelect a spell baseline to inspect rank coverage and families.") + end + MTH_BOOK_UpdateOpenMapButton() + return + end + + local topLines = {} + table.insert(topLines, tostring(entry.ability or "Unknown")) + table.insert(topLines, "") + table.insert(topLines, "Baseline") + MTH_BOOK_AddDetailKV(topLines, "Ranks", entry.rankText ~= "" and entry.rankText or "") + MTH_BOOK_AddDetailKV(topLines, "Families", entry.familyCount or 0) + MTH_BOOK_AddDetailKV(topLines, "Spell Rows", entry.spellRows or 0) + if entry.minRank and entry.maxRank then + if entry.minRank == entry.maxRank then + MTH_BOOK_AddDetailKV(topLines, "Rank Span", tostring(entry.minRank)) + else + MTH_BOOK_AddDetailKV(topLines, "Rank Span", tostring(entry.minRank) .. "-" .. tostring(entry.maxRank)) + end + end + table.insert(topLines, "") + table.insert(topLines, "Families") + if entry.families and table.getn(entry.families) > 0 then + for i = 1, table.getn(entry.families) do + table.insert(topLines, " - " .. tostring(entry.families[i])) + end + else + table.insert(topLines, " - None") + end + + local rankEntry = MTH_BOOK_STATE.selectedPetRankEntry + local bottomLines = {} + if rankEntry then + table.insert(bottomLines, MTH_BOOK_GetRankLabel(rankEntry.rankNumber, true)) + table.insert(bottomLines, "") + table.insert(bottomLines, "Rank Detail") + MTH_BOOK_AddDetailKV(bottomLines, "Train Level", rankEntry.trainLevel or "-") + MTH_BOOK_AddDetailKV(bottomLines, "Spell ID", rankEntry.id or "-") + if rankEntry.cost and rankEntry.cost ~= "" then MTH_BOOK_AddDetailKV(bottomLines, "Cost", rankEntry.cost) end + if rankEntry.castTime and rankEntry.castTime ~= "" then MTH_BOOK_AddDetailKV(bottomLines, "Cast", rankEntry.castTime) end + if rankEntry.range and rankEntry.range ~= "" then MTH_BOOK_AddDetailKV(bottomLines, "Range", rankEntry.range) end + if rankEntry.school and rankEntry.school ~= "" then MTH_BOOK_AddDetailKV(bottomLines, "School", rankEntry.school) end + table.insert(bottomLines, "") + table.insert(bottomLines, "Description") + table.insert(bottomLines, " " .. tostring(rankEntry.description or "-")) + else + table.insert(bottomLines, "Select a rank from the right list.") + end + + if petTop and petBottom then + MTH_BOOK_SetDetailText(petTop, table.concat(topLines, "\n")) + MTH_BOOK_SetDetailText(petBottom, table.concat(bottomLines, "\n")) + if MTH_BOOK_STATE.petUI and MTH_BOOK_STATE.petUI.detailTopIcon then + if entry.icon and entry.icon ~= "" then + if string.find(entry.icon, "\\", 1, true) then + MTH_BOOK_STATE.petUI.detailTopIcon:SetTexture(entry.icon) + else + MTH_BOOK_STATE.petUI.detailTopIcon:SetTexture("Interface\\Icons\\" .. tostring(entry.icon)) + end + MTH_BOOK_STATE.petUI.detailTopIcon:Show() + else + MTH_BOOK_STATE.petUI.detailTopIcon:Hide() + end + end + else + local fallback = table.concat(topLines, "\n") .. "\n\n" .. table.concat(bottomLines, "\n") + MTH_BOOK_SetDetailText(detail, fallback) + end + MTH_BOOK_UpdateOpenMapButton() + return + end + + if MTH_BOOK_STATE.mode == "stable" then + local petId = MTH_BOOK_STATE.selectedEntry + local row = petId and MTH_BOOK_GetPetStoreRow(petId) + if not row then + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Stable|r\n\nSelect a stable entry to inspect stored pet identity and latest stable scan fields.") + MTH_BOOK_UpdateOpenMapButton() + return + end + + local lines = {} + table.insert(lines, "|cFFFFFF00" .. tostring(row.name or "Unknown") .. "|r") + MTH_BOOK_AddDetailSection(lines, "Identity") + MTH_BOOK_AddDetailKV(lines, "Pet ID", tostring(petId or "-")) + MTH_BOOK_AddDetailKV(lines, "Family", row.family or "-") + MTH_BOOK_AddDetailKV(lines, "Level", row.level or "-") + MTH_BOOK_AddDetailKV(lines, "Beast ID", row.beastId or "-") + + MTH_BOOK_AddDetailSection(lines, "Stable") + MTH_BOOK_AddDetailKV(lines, "Slot", MTH_BOOK_GetStableDisplaySlot(petId, row) or "-") + MTH_BOOK_AddDetailKV(lines, "Loyalty", MTH_BOOK_GetLoyaltyNameByLevel(tonumber(row.loyaltyLevel) or (row.stableInfo and tonumber(row.stableInfo.loyaltyLevel)) or nil)) + MTH_BOOK_AddDetailKV(lines, "Happiness", (row.stableInfo and row.stableInfo.happiness) or row.happiness or "-") + MTH_BOOK_AddDetailKV(lines, "State", row.state or "active") + MTH_BOOK_AddDetailKV(lines, "Last Seen", row.lastSeen or row.lastUpdated or "-") + + if type(row.tameContext) == "table" then + MTH_BOOK_AddDetailSection(lines, "Tamed") + MTH_BOOK_AddDetailKV(lines, "At", row.tameContext.timestamp or "-") + MTH_BOOK_AddDetailKV(lines, "Zone", row.tameContext.zone or "-") + MTH_BOOK_AddDetailKV(lines, "Subzone", row.tameContext.subZone or "-") + MTH_BOOK_AddDetailKV(lines, "Coord", row.tameContext.coordinate or "-") + MTH_BOOK_AddDetailKV(lines, "Hunter Lvl", row.tameContext.hunterLevel or "-") + end + + MTH_BOOK_SetDetailText(detail, table.concat(lines, "\n")) + MTH_BOOK_UpdateOpenMapButton() + return + end + + if MTH_BOOK_STATE.mode == "pethistory" then + local selected = MTH_BOOK_STATE.selectedEntry + local petId = selected + if type(selected) == "table" then + petId = selected.petId or selected.id + end + local row = petId and MTH_BOOK_GetPetStoreRow(petId) + if not row then + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Pet History|r\n\nSelect a history entry to inspect abandon context and archived pet data.") + MTH_BOOK_UpdateOpenMapButton() + return + end + + local stableInfo = (type(row.stableInfo) == "table") and row.stableInfo or nil + local stabledAt = tonumber(row.stabledAt) or tonumber(row.stableFirstSeenAt) or (stableInfo and tonumber(stableInfo.stabledAt) or nil) + local stabledSince = "-" + local stableElapsedFormatter = _G and _G["MTH_BOOK_FormatElapsedStableInfo"] or nil + if type(stableElapsedFormatter) == "function" then + stabledSince = stableElapsedFormatter(stabledAt) + end + local stableZone = stableInfo and tostring(stableInfo.stableZone or "") or "" + local stableSubZone = stableInfo and tostring(stableInfo.stableSubZone or "") or "" + local stableMasterName = stableInfo and tostring(stableInfo.stableMasterName or "") or "" + local stableLocation = "-" + if stableSubZone ~= "" and stableZone ~= "" then + stableLocation = stableSubZone .. " (" .. stableZone .. ")" + elseif stableSubZone ~= "" then + stableLocation = stableSubZone + elseif stableZone ~= "" then + stableLocation = stableZone + end + if stableMasterName == "" then + stableMasterName = "-" + end + + local legacyTameContext = type(row.tameContext) == "table" and row.tameContext or nil + local hasRecordedTame = row.tameRecorded == true + if not hasRecordedTame and legacyTameContext then + if legacyTameContext.name or legacyTameContext.zone or legacyTameContext.timestamp then + hasRecordedTame = true + end + end + local tamedAt = hasRecordedTame and (tonumber(row.tamedAt) or (legacyTameContext and tonumber(legacyTameContext.timestamp) or nil)) or nil + local tameZone = hasRecordedTame and tostring(row.tameZone or (legacyTameContext and legacyTameContext.zone) or "-") or "-" + local tameX = hasRecordedTame and (tonumber(row.tameX) or (legacyTameContext and tonumber(legacyTameContext.x) or nil)) or nil + local tameY = hasRecordedTame and (tonumber(row.tameY) or (legacyTameContext and tonumber(legacyTameContext.y) or nil)) or nil + local tameCoords = "-" + if tameX and tameY then + tameCoords = string.format("%.1f, %.1f", tameX, tameY) + end + local tameBeastId = hasRecordedTame and (tonumber(row.tameBeastId) or (legacyTameContext and tonumber(legacyTameContext.beastId) or nil)) or nil + local tameBeast = tameBeastId and MTH_DS_Beasts and MTH_DS_Beasts[tameBeastId] or nil + local tameBeastName = tostring((tameBeast and tameBeast.name) or (legacyTameContext and legacyTameContext.name) or "-") + + local lines = {} + table.insert(lines, "|cFFFFFF00" .. tostring(row.name or "Unknown") .. "|r") + + MTH_BOOK_AddDetailSection(lines, "Identity") + MTH_BOOK_AddStableStyleKV(lines, "Pet ID", tostring(petId or "-")) + MTH_BOOK_AddStableStyleKV(lines, "Name", row.name or "-") + MTH_BOOK_AddStableStyleKV(lines, "Family", row.family or "-") + MTH_BOOK_AddStableStyleKV(lines, "Level", row.level or "-") + MTH_BOOK_AddStableStyleKV(lines, "Loyalty", MTH_BOOK_GetLoyaltyNameByLevel(tonumber(row.loyaltyLevel) or (stableInfo and tonumber(stableInfo.loyaltyLevel)) or nil)) + + MTH_BOOK_AddDetailSection(lines, "Stable info") + MTH_BOOK_AddStableStyleKV(lines, "Stabled on", MTH_BOOK_FormatDateTimeValue(stabledAt)) + MTH_BOOK_AddStableStyleKV(lines, "Stabled since", stabledSince) + MTH_BOOK_AddStableStyleKV(lines, "Stable Master", stableMasterName) + MTH_BOOK_AddStableStyleKV(lines, "Location", stableLocation) + + MTH_BOOK_AddDetailSection(lines, "Taming info") + if hasRecordedTame then + MTH_BOOK_AddStableStyleKV(lines, "Tamed on", MTH_BOOK_FormatDateTimeValue(tamedAt)) + MTH_BOOK_AddStableStyleKV(lines, "Tamed Beast", tameBeastName) + MTH_BOOK_AddStableStyleKV(lines, "Taming Loc", tameZone) + MTH_BOOK_AddStableStyleKV(lines, "Coords", tameCoords) + else + table.insert(lines, "|cffb0b0b0No info|r") + end + + MTH_BOOK_AddDetailSection(lines, "Row info") + MTH_BOOK_AddStableStyleKV(lines, "ID", tostring(petId or "-")) + MTH_BOOK_AddStableStyleKV(lines, "Created", MTH_BOOK_FormatDateTimeValue(row.createdAt)) + MTH_BOOK_AddStableStyleKV(lines, "Origin", row.origin or row.lastSource or "-") + MTH_BOOK_AddStableStyleKV(lines, "Updated", MTH_BOOK_FormatDateTimeValue(row.lastUpdated or row.updatedAt or row.lastSeen)) + MTH_BOOK_AddStableStyleKV(lines, "Lost Date/Time", MTH_BOOK_FormatDateTimeValue(row.abandonedAt)) + MTH_BOOK_AddStableStyleKV(lines, "Lost Cause", MTH_BOOK_GetPetHistoryLostCause(row)) + + MTH_BOOK_SetDetailText(detail, table.concat(lines, "\n")) + MTH_BOOK_UpdateOpenMapButton() + return + end + + local selected = MTH_BOOK_STATE.selectedEntry + if not selected or not selected.itemId then + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Inspector|r\n\nSelect an entry from the list to view identity, stats, sources and locations.") + MTH_BOOK_UpdateOpenMapButton() + return + end + local itemId = selected.itemId + local items = MTH_BOOK_GetItemsTable() + local item = items and items[itemId] + if not item then + MTH_BOOK_SetDetailText(detail, "|cFFFFD100Inspector|r\n\nSelect an entry from the list to view identity, stats, sources and locations.") + MTH_BOOK_UpdateOpenMapButton() + return + end + + local v = MTH_BOOK_CountMap(item.vendors) + local d = MTH_BOOK_CountMap(item.drops) + local o = MTH_BOOK_CountMap(item.objects) + local atlas = item.atlas_sources or {} + local lines = {} + table.insert(lines, "|cFFFFFF00" .. (item.name or "Unknown") .. "|r") + + MTH_BOOK_AddDetailSection(lines, "Identity") + MTH_BOOK_AddDetailKV(lines, "ID", itemId) + MTH_BOOK_AddDetailKV(lines, "Subtype", item.subtype or "?") + + MTH_BOOK_AddDetailSection(lines, "Stats") + MTH_BOOK_AddDetailKV(lines, "Level", item.level or "?") + MTH_BOOK_AddDetailKV(lines, "Req Level", item.reqlevel or "-") + if item.quality then MTH_BOOK_AddDetailKV(lines, "Quality", item.quality) end + if item.dps then MTH_BOOK_AddDetailKV(lines, "DPS", item.dps) end + if item.speed then MTH_BOOK_AddDetailKV(lines, "Speed", item.speed) end + + MTH_BOOK_AddDetailSection(lines, "Sources") + MTH_BOOK_AddDetailKV(lines, "Vendors", v) + MTH_BOOK_AddDetailKV(lines, "Creature Drops", d) + MTH_BOOK_AddDetailKV(lines, "Object Sources", o) + + if atlas.boss or atlas.world or atlas.worlddrop or atlas.quest or atlas.vendor or atlas.pvp or atlas.crafted or atlas.event then + MTH_BOOK_AddDetailSection(lines, "Atlas Hints") + MTH_BOOK_AddDetailKV(lines, "Boss Loot", atlas.boss and "Yes" or "-") + MTH_BOOK_AddDetailKV(lines, "World Sources", atlas.world and "Yes" or "-") + MTH_BOOK_AddDetailKV(lines, "World Drop", atlas.worlddrop and "Yes" or "-") + MTH_BOOK_AddDetailKV(lines, "Quest Reward", atlas.quest and "Yes" or "-") + MTH_BOOK_AddDetailKV(lines, "Vendor / Faction", atlas.vendor and "Yes" or "-") + MTH_BOOK_AddDetailKV(lines, "PvP Reward", atlas.pvp and "Yes" or "-") + MTH_BOOK_AddDetailKV(lines, "Crafted", atlas.crafted and "Yes" or "-") + MTH_BOOK_AddDetailKV(lines, "Event", atlas.event and "Yes" or "-") + end + MTH_BOOK_SetDetailText(detail, table.concat(lines, "\n")) + MTH_BOOK_UpdateOpenMapButton() +end + +local function MTH_BOOK_UpdateHeader() + local stats = getglobal("MTH_BOOK_StatsText") + if not stats then return end + local mode = MTH_BOOK_STATE.mode or "pets" + local tabDef = MTH_BOOK_GetTabDefinition(mode) + local modeLabel = (tabDef and tabDef.headerLabel) or "Beasts" + if not tabDef then + if mode == "families" then modeLabel = "Families" end + if mode == "petabilities" then modeLabel = "Pet Abilities" end + if mode == "items" then modeLabel = "Ranged Weapons" end + if mode == "npcs" then modeLabel = "NPC Finder" end + if mode == "stable" then modeLabel = "Stable" end + if mode == "pethistory" then modeLabel = "Pet History" end + end + local total = MTH_BOOK_GetTotalCount() + local filtered = table.getn(MTH_BOOK_STATE.results) + local pages = math.max(1, math.ceil(filtered / MTH_BOOK_STATE.pageSize)) + if mode == "families" then + local namedTotal = 0 + local coordsTotal = 0 + for i = 1, table.getn(MTH_BOOK_STATE.results or {}) do + local row = MTH_BOOK_STATE.results[i] + if type(row) == "table" then + namedTotal = namedTotal + (tonumber(row.named) or 0) + coordsTotal = coordsTotal + (tonumber(row.coords) or 0) + end + end + stats:SetText("Named: " .. tostring(namedTotal) .. " | Coords: " .. tostring(coordsTotal)) + return + end + stats:SetText(modeLabel .. ": " .. total .. " | Filtered: " .. filtered .. " | Page " .. MTH_BOOK_STATE.page .. "/" .. pages) +end + +local function MTH_BOOK_GetColumnLabels() + local tabDef = MTH_BOOK_GetTabDefinition(MTH_BOOK_STATE.mode) + if tabDef and type(tabDef.columnLabels) == "table" then + return tabDef.columnLabels + end + if MTH_BOOK_STATE.mode == "pets" then + return { "ID", "Lvl", "Family", "Name", "Abilities", "Zone", "R", "E", "U" } + elseif MTH_BOOK_STATE.mode == "petabilities" then + return { "Ability Name", "Ranks", "Families" } + elseif MTH_BOOK_STATE.mode == "stable" then + return { "Pet ID", "Slot", "Name", "Family", "Lvl", "Loyalty", "Seen" } + elseif MTH_BOOK_STATE.mode == "pethistory" then + return { "Pet ID", "Name", "Family", "Lvl", "Lost Date/Time", "Lost Cause" } + elseif MTH_BOOK_STATE.mode == "abilities" then + return { "Ability", "Lvl", "Family", "Beast", "Rare" } + elseif MTH_BOOK_STATE.mode == "npcs" then + return { "ID", "Name", "React", "Function", "Zone" } + end + return { "ID", "Req", "Lvl", "Type", "Name", "DPS", "Speed", "Source" } +end + +local function MTH_BOOK_GetColumnLayout() + local tabDef = MTH_BOOK_GetTabDefinition(MTH_BOOK_STATE.mode) + if tabDef and type(tabDef.columnLayout) == "table" then + return tabDef.columnLayout + end + if MTH_BOOK_STATE.mode == "pets" then + return { + { x = 8, width = 28, align = "LEFT" }, + { x = 38, width = 30, align = "LEFT" }, + { x = 68, width = 74, align = "LEFT" }, + { x = 142, width = 130, align = "LEFT" }, + { x = 272, width = 180, align = "LEFT" }, + { x = 452, width = 68, align = "LEFT" }, + { x = 520, width = 10, align = "CENTER" }, + { x = 532, width = 10, align = "CENTER" }, + { x = 544, width = 10, align = "CENTER" }, + } + elseif MTH_BOOK_STATE.mode == "petabilities" then + return { + { x = 8, width = 160, align = "LEFT" }, + { x = 170, width = 52, align = "LEFT" }, + { x = 224, width = 328, align = "LEFT" }, + } + elseif MTH_BOOK_STATE.mode == "stable" then + return { + { x = 8, width = 78, align = "LEFT" }, + { x = 88, width = 34, align = "LEFT" }, + { x = 124, width = 118, align = "LEFT" }, + { x = 244, width = 92, align = "LEFT" }, + { x = 338, width = 34, align = "LEFT" }, + { x = 374, width = 114, align = "LEFT" }, + { x = 490, width = 52, align = "LEFT" }, + } + elseif MTH_BOOK_STATE.mode == "pethistory" then + return { + { x = 8, width = 66, align = "LEFT" }, + { x = 76, width = 104, align = "LEFT" }, + { x = 182, width = 84, align = "LEFT" }, + { x = 268, width = 30, align = "LEFT" }, + { x = 300, width = 128, align = "LEFT" }, + { x = 430, width = 78, align = "LEFT" }, + } + elseif MTH_BOOK_STATE.mode == "abilities" then + return { + { x = 8, width = 112, align = "LEFT" }, + { x = 120, width = 40, align = "LEFT" }, + { x = 160, width = 78, align = "LEFT" }, + { x = 238, width = 142, align = "LEFT" }, + { x = 380, width = 38, align = "LEFT" }, + } + elseif MTH_BOOK_STATE.mode == "npcs" then + return { + { x = 8, width = 52, align = "LEFT" }, + { x = 60, width = 140, align = "LEFT" }, + { x = 200, width = 40, align = "LEFT" }, + { x = 242, width = 160, align = "LEFT" }, + { x = 404, width = 148, align = "LEFT" }, + } + end + + return { + { x = 8, width = 36, align = "LEFT" }, + { x = 44, width = 30, align = "LEFT" }, + { x = 74, width = 30, align = "LEFT" }, + { x = 104, width = 62, align = "LEFT" }, + { x = 166, width = 188, align = "LEFT" }, + { x = 356, width = 44, align = "LEFT" }, + { x = 402, width = 40, align = "LEFT" }, + { x = 444, width = 108, align = "LEFT" }, + } +end + +local function MTH_BOOK_ApplyColumnLayout(parent) + if not parent then return end + local layout = MTH_BOOK_GetColumnLayout() + + for i = 1, MTH_BOOK_MAX_COLS do + local header = MTH_BOOK_STATE.headerCols[i] + local hit = MTH_BOOK_STATE.headerButtons[i] + local info = layout[i] + if header then + if info then + header:Show() + header:ClearAllPoints() + header:SetPoint("TOPLEFT", parent, "TOPLEFT", info.x, -8) + header:SetWidth(info.width) + header:SetJustifyH(info.align or "LEFT") + if hit then + hit:Show() + hit:ClearAllPoints() + hit:SetPoint("TOPLEFT", parent, "TOPLEFT", info.x, -8) + hit:SetWidth(info.width) + hit:SetHeight(14) + end + else + header:Hide() + header:SetText("") + if hit then hit:Hide() end + end + end + end + + for row = 1, MTH_BOOK_STATE.pageSize do + local btn = MTH_BOOK_STATE.buttons[row] + if btn and btn.cols then + local parentWidth = tonumber(parent:GetWidth()) or 0 + local rowWidth = parentWidth - 8 + if rowWidth < 220 then rowWidth = 220 end + btn:SetWidth(rowWidth) + + for i = 1, MTH_BOOK_MAX_COLS do + local col = btn.cols[i] + local info = layout[i] + if col then + if info then + col:Show() + col:ClearAllPoints() + col:SetPoint("LEFT", btn, "LEFT", info.x - 4, 0) + col:SetWidth(info.width) + col:SetJustifyH(info.align or "LEFT") + else + col:Hide() + col:SetText("") + end + end + end + + if btn.bookLinkButton then + local info = layout[1] + if (MTH_BOOK_STATE.mode == "pets" or MTH_BOOK_STATE.mode == "npcs" or MTH_BOOK_STATE.mode == "items") and info then + btn.bookLinkButton:ClearAllPoints() + btn.bookLinkButton:SetPoint("LEFT", btn, "LEFT", info.x - 4, 0) + btn.bookLinkButton:SetWidth(info.width) + btn.bookLinkButton:SetHeight(btn:GetHeight()) + btn.bookLinkButton:Show() + else + btn.bookLinkButton:Hide() + end + end + + if btn.itemLinkButton then btn.itemLinkButton:Hide() end + + if btn.nameHoverButton then + if MTH_BOOK_STATE.mode == "items" and layout[5] then + btn.nameHoverButton:ClearAllPoints() + btn.nameHoverButton:SetPoint("LEFT", btn, "LEFT", layout[5].x + 12, 0) + btn.nameHoverButton:SetWidth(layout[5].width - 16) + btn.nameHoverButton:SetHeight(btn:GetHeight()) + btn.nameHoverButton:Show() + else + btn.nameHoverButton:Hide() + end + end + + if btn.itemIcon then + if MTH_BOOK_STATE.mode == "items" and layout[5] then + btn.itemIcon:ClearAllPoints() + btn.itemIcon:SetPoint("LEFT", btn, "LEFT", layout[5].x - 4, 0) + btn.itemIcon:Show() + if btn.cols and btn.cols[5] then + btn.cols[5]:ClearAllPoints() + btn.cols[5]:SetPoint("LEFT", btn, "LEFT", layout[5].x + 12, 0) + btn.cols[5]:SetWidth(layout[5].width - 16) + end + else + btn.itemIcon:Hide() + end + end + end + end +end + +local function MTH_BOOK_GetRowValues(entry) + if MTH_BOOK_STATE.mode == "pets" then + local beast = MTH_DS_Beasts and MTH_DS_Beasts[entry] + if not beast then return { "", "", "", "", "", "", "", "", "" } end + local traits = MTH_BOOK_ParseBeastTraits(beast) + local abilities = MTH_BOOK_GetBeastAbilitiesSummary(beast) + local zone = MTH_BOOK_GetBeastZoneSummary(beast) + return { + "|cFF33CCFF" .. tostring(entry) .. "|r", + tostring(beast.lvl or "?"), + tostring(beast.family or "?"), + tostring(beast.name or "Unknown"), + abilities, + zone, + (traits.rare and "R" or ""), + (traits.elite and "E" or ""), + (traits.unique and "U" or ""), + } + end + + if MTH_BOOK_STATE.mode == "abilities" then + return { + tostring(entry.abilityToken or "?"), + tostring(entry.level or 0), + tostring(entry.family or "?"), + tostring(entry.name or "Unknown"), + (entry.rare and "R" or ""), + } + end + + if MTH_BOOK_STATE.mode == "petabilities" then + local families = entry.familyList or "" + if families == "" then families = "-" end + return { + tostring(entry.ability or "Unknown"), + tostring(entry.rankText or ""), + families, + } + end + + if MTH_BOOK_STATE.mode == "families" then + local names = {} + if type(entry.abilities) == "table" then + for i = 1, table.getn(entry.abilities) do + local ability = entry.abilities[i] + if type(ability) == "table" and ability.name and ability.name ~= "" then + table.insert(names, tostring(ability.name)) + end + end + end + local abilitiesText = table.concat(names, ", ") + if abilitiesText == "" then abilitiesText = "-" end + local dietText = tostring(entry.dietText or "") + if dietText == "" then dietText = "-" end + return { + tostring(entry.family or "-"), + tostring(entry.named or 0), + tostring(entry.coords or 0), + abilitiesText, + dietText, + } + end + + if MTH_BOOK_STATE.mode == "stable" then + local row = MTH_BOOK_GetPetStoreRow(entry) + if not row then return { "", "", "", "", "", "", "" } end + local slotDisplay = MTH_BOOK_GetStableDisplaySlot(entry, row) or "-" + local loyalty = MTH_BOOK_GetLoyaltyNameByLevel(tonumber(row.loyaltyLevel) or (row.stableInfo and tonumber(row.stableInfo.loyaltyLevel)) or nil) + return { + tostring(entry or ""), + tostring(slotDisplay), + tostring(row.name or "-"), + tostring(row.family or "-"), + tostring(row.level or "-"), + tostring(loyalty), + tostring(row.lastSeen or row.lastUpdated or 0), + } + end + + if MTH_BOOK_STATE.mode == "pethistory" then + local row = MTH_BOOK_GetPetStoreRow(entry) + if not row then return { "", "", "", "", "", "" } end + local lostCause = MTH_BOOK_GetPetHistoryLostCause(row) + return { + tostring(entry or ""), + tostring(row.name or "-"), + tostring(row.family or "-"), + tostring(row.level or "-"), + MTH_BOOK_FormatDateTimeValue(row.abandonedAt), + tostring(lostCause), + } + end + + if MTH_BOOK_STATE.mode == "npcs" then + local vendor = MTH_DS_Vendors and MTH_DS_Vendors[entry] + if not vendor then return { "", "", "", "", "" } end + local react = MTH_BOOK_GetNPCReactBucket(vendor.fac) + local functionSummary = MTH_BOOK_GetNPCFunctionSummary(vendor) + local zoneName = MTH_BOOK_GetNPCZoneSummary(vendor) + return { + "|cFF33CCFF" .. tostring(entry) .. "|r", + tostring(vendor.name or "Unknown"), + react, + functionSummary, + zoneName, + } + end + + local items = MTH_BOOK_GetItemsTable() + local item = items and items[entry] + if not item then return { "", "", "", "", "", "", "", "" } end + local itemName = tostring(item.name or "Unknown") + local colorCode = MTH_BOOK_GetItemQualityColorCode(item.quality) + local sourceText = "-" + local hasVendors = MTH_BOOK_CountMap(item.vendors) > 0 + local hasDrops = MTH_BOOK_CountMap(item.drops) > 0 + local hasObjects = MTH_BOOK_CountMap(item.objects) > 0 + local concreteCount = 0 + if hasVendors then concreteCount = concreteCount + 1 end + if hasDrops then concreteCount = concreteCount + 1 end + if hasObjects then concreteCount = concreteCount + 1 end + if concreteCount > 1 then + sourceText = "Mixed" + elseif hasVendors then + sourceText = "Vendor" + elseif hasDrops then + sourceText = "Drop" + elseif hasObjects then + sourceText = "Object" + else + local atlas = item.atlas_sources + if atlas then + if atlas.boss then + sourceText = "Boss" + elseif atlas.quest then + sourceText = "Quest" + elseif atlas.vendor then + sourceText = "Vendor" + elseif atlas.pvp then + sourceText = "PvP" + elseif atlas.crafted then + sourceText = "Crafted" + elseif atlas.worlddrop then + sourceText = "World Drop" + elseif atlas.world then + sourceText = "World" + elseif atlas.event then + sourceText = "Event" + end + end + end + return { + "|cFF33CCFF" .. tostring(entry) .. "|r", + tostring(item.reqlevel or 0), + tostring(item.level or 0), + tostring(item.subtype or "?"), + "|c" .. colorCode .. itemName .. "|r", + (item.dps and tostring(item.dps) or "-"), + (item.speed and tostring(item.speed) or "-"), + sourceText, + } +end + +local function MTH_BOOK_EnsurePetAbilitiesUI(listParent, detailParent) + if type(MTH_BOOKTAB_EnsurePetAbilitiesUI) == "function" then + return MTH_BOOKTAB_EnsurePetAbilitiesUI(listParent, detailParent) + end +end + +local function MTH_BOOK_SetPetAbilitiesUIVisible(visible) + if type(MTH_BOOKTAB_SetPetAbilitiesUIVisible) == "function" then + return MTH_BOOKTAB_SetPetAbilitiesUIVisible(visible) + end +end + +local function MTH_BOOK_SetFamiliesUIVisible(visible) + if type(MTH_BOOKTAB_SetFamiliesUIVisible) == "function" then + return MTH_BOOKTAB_SetFamiliesUIVisible(visible) + end + MTH_BOOK_FamiliesTrace("SetFamiliesUIVisible missing MTH_BOOKTAB_SetFamiliesUIVisible") +end + +function MTH_BOOK_EnsureStableUI() + if type(MTH_BOOKTAB_EnsureStableUI) == "function" then + return MTH_BOOKTAB_EnsureStableUI() + end +end + +function MTH_BOOK_SetStableUIVisible(visible) + if type(MTH_BOOKTAB_SetStableUIVisible) == "function" then + return MTH_BOOKTAB_SetStableUIVisible(visible) + end +end + +function MTH_BOOK_RenderStableCards() + if type(MTH_BOOKTAB_RenderStableCards) == "function" then + return MTH_BOOKTAB_RenderStableCards() + end +end + +MTH_BOOK_UpdatePetAbilitiesLists = function() + if type(MTH_BOOKTAB_UpdatePetAbilitiesLists) == "function" then + return MTH_BOOKTAB_UpdatePetAbilitiesLists() + end +end +_G.MTH_BOOK_UpdatePetAbilitiesLists = MTH_BOOK_UpdatePetAbilitiesLists + +local function MTH_BOOK_UpdateListHeaders() + if MTH_BOOK_STATE.mode == "petabilities" or MTH_BOOK_STATE.mode == "stable" then + for i = 1, MTH_BOOK_MAX_COLS do + local header = MTH_BOOK_STATE.headerCols[i] + local hit = MTH_BOOK_STATE.headerButtons[i] + if header then header:SetText(""); header:Hide() end + if hit then hit:Hide() end + end + return + end + + local labels = MTH_BOOK_GetColumnLabels() + local state = MTH_BOOK_GetSortState() + for i = 1, MTH_BOOK_MAX_COLS do + local header = MTH_BOOK_STATE.headerCols[i] + local hit = MTH_BOOK_STATE.headerButtons[i] + if header then + local label = labels[i] or "" + if label ~= "" and state and state.col == i then + label = label .. (state.asc and " ^" or " v") + end + header:SetText(label) + if hit then + hit:EnableMouse(labels[i] and labels[i] ~= "") + end + end + end + local listParent = getglobal("MTH_BOOK_ListBackdrop") + if listParent then + MTH_BOOK_ApplyColumnLayout(listParent) + end +end + +local function MTH_BOOK_OnHeaderClick(col) + local labels = MTH_BOOK_GetColumnLabels() + if not labels[col] or labels[col] == "" then return end + local state = MTH_BOOK_GetSortState() + if state.col == col then + state.asc = not state.asc + else + state.col = col + state.asc = true + end + MTH_BOOK_STATE.page = 1 + MTH_BOOK_STATE.selectedEntry = nil + MTH_BOOK_STATE.selectedPetRankEntry = nil + MTH_BOOK_STATE.petRankRows = {} + MTH_BOOK_STATE.forcedBeastId = nil + MTH_BOOK_RefreshFilter() +end + +local function MTH_BOOK_AssignEntry(btn, entry) + btn.entry = nil + if MTH_BOOK_STATE.mode == "pets" then + btn.entry = { beastId = entry } + elseif MTH_BOOK_STATE.mode == "families" then + btn.entry = entry + elseif MTH_BOOK_STATE.mode == "npcs" then + btn.entry = { npcId = entry } + elseif MTH_BOOK_STATE.mode == "abilities" then + btn.entry = entry + elseif MTH_BOOK_STATE.mode == "petabilities" then + btn.entry = entry + elseif MTH_BOOK_STATE.mode == "pethistory" then + btn.entry = { petId = entry } + else + btn.entry = { itemId = entry } + end +end + +MTH_BOOK_UpdateResults = function() + MTH_BOOK_SetFamiliesListVisualScale(MTH_BOOK_STATE.mode == "families") + + local resultCount = table.getn(MTH_BOOK_STATE.results) + local totalPages = math.max(1, math.ceil(resultCount / MTH_BOOK_STATE.pageSize)) + local buttonCount = table.getn(MTH_BOOK_STATE.buttons) + if MTH_BOOK_STATE.page < 1 then MTH_BOOK_STATE.page = 1 end + if MTH_BOOK_STATE.page > totalPages then MTH_BOOK_STATE.page = totalPages end + + local slider = getglobal("MTH_BOOK_ListSlider") + local sliderBackdrop = getglobal("MTH_BOOK_ListSliderBackdrop") + + if MTH_BOOK_STATE.mode == "stable" then + MTH_BOOK_DebugTrace("UpdateResults stable: resultCount=" .. tostring(resultCount)) + if slider then slider:Hide() end + MTH_BOOK_SetPetAbilitiesUIVisible(false) + MTH_BOOK_SetFamiliesUIVisible(false) + MTH_BOOK_EnsureStableUI() + if not MTH_BOOK_STATE.stableUI then + MTH_BOOK_DebugTrace("UpdateResults stable: custom UI unavailable, fallback to list") + MTH_BOOK_SetStableUIVisible(false) + else + MTH_BOOK_DebugTrace("UpdateResults stable: custom UI active") + MTH_BOOK_SetStableUIVisible(true) + end + + for i = 1, buttonCount do + local btn = MTH_BOOK_STATE.buttons[i] + if btn then + if MTH_BOOK_STATE.stableUI then + btn:Hide() + else + btn:Show() + end + end + end + + if MTH_BOOK_STATE.stableUI then + MTH_BOOK_RenderStableCards() + local stableFrame = MTH_BOOK_STATE.stableUI.frame + if stableFrame and stableFrame.SetScript then + stableFrame:SetScript("OnUpdate", function() + stableFrame:SetScript("OnUpdate", nil) + if MTH_BOOK_STATE.mode == "stable" and MTH_BOOK_STATE.stableUI then + MTH_BOOK_RenderStableCards() + end + end) + end + end + MTH_BOOK_UpdateHeader() + if not MTH_BOOK_STATE.stableUI then + MTH_BOOK_SetStableUIVisible(false) + MTH_BOOK_SetPetAbilitiesUIVisible(false) + -- continue with generic list rendering + else + return + end + end + + if MTH_BOOK_STATE.mode == "petabilities" then + if slider then slider:Hide() end + local listParent = getglobal("MTH_BOOK_ListBackdrop") + local detailParent = getglobal("MTH_BOOK_DetailBackdrop") + MTH_BOOK_SetStableUIVisible(false) + MTH_BOOK_EnsurePetAbilitiesUI(listParent, detailParent) + MTH_BOOK_SetPetAbilitiesUIVisible(true) + MTH_BOOK_SetFamiliesUIVisible(false) + + for i = 1, buttonCount do + local btn = MTH_BOOK_STATE.buttons[i] + if btn then btn:Hide() end + end + + MTH_BOOK_UpdatePetAbilitiesLists() + MTH_BOOK_UpdateHeader() + MTH_BOOK_UpdateDetail() + return + end + + if MTH_BOOK_STATE.mode == "families" then + MTH_BOOK_FamiliesTrace("UpdateResults families using safe generic renderer") + MTH_BOOK_STATE.page = 1 + if sliderBackdrop then sliderBackdrop:Hide() end + end + + if slider then + if MTH_BOOK_STATE.mode == "families" then + slider:Hide() + else + if sliderBackdrop then sliderBackdrop:Show() end + slider:Show() + slider:SetMinMaxValues(1, totalPages) + slider:SetValueStep(1) + slider:SetValue(MTH_BOOK_STATE.page) + MTH_BOOK_HideSliderTemplateTexts() + MTH_BOOK_UpdateSliderMarker(slider) + end + end + + MTH_BOOK_SetPetAbilitiesUIVisible(false) + MTH_BOOK_SetStableUIVisible(false) + MTH_BOOK_SetFamiliesUIVisible(false) + + local startIndex = ((MTH_BOOK_STATE.page - 1) * MTH_BOOK_STATE.pageSize) + 1 + for i = 1, buttonCount do + local btn = MTH_BOOK_STATE.buttons[i] + if btn then + local entry = nil + if i <= MTH_BOOK_STATE.pageSize then + local rowIndex = startIndex + i - 1 + entry = MTH_BOOK_STATE.results[rowIndex] + end + if entry then + MTH_BOOK_AssignEntry(btn, entry) + local values = MTH_BOOK_GetRowValues(entry) + for c = 1, MTH_BOOK_MAX_COLS do + if btn.cols and btn.cols[c] then + btn.cols[c]:SetText(values[c] or "") + end + end + if btn.itemIcon then + if MTH_BOOK_STATE.mode == "items" then + local items = MTH_BOOK_GetItemsTable() + local item = items and items[entry] + if item and item.icon and item.icon ~= "" then + if string.find(tostring(item.icon), "\\", 1, true) then + btn.itemIcon:SetTexture(tostring(item.icon)) + else + btn.itemIcon:SetTexture("Interface\\Icons\\" .. tostring(item.icon)) + end + btn.itemIcon:Show() + else + btn.itemIcon:SetTexture(nil) + btn.itemIcon:Hide() + end + else + btn.itemIcon:SetTexture(nil) + btn.itemIcon:Hide() + end + end + if btn.familyAbilityButtons then + local abilityEntries = nil + local abilityColIndex = nil + local tooltipFunc = nil + if MTH_BOOK_STATE.mode == "families" and type(entry) == "table" and type(entry.abilities) == "table" then + abilityEntries = entry.abilities + abilityColIndex = 4 + tooltipFunc = MTH_BOOKTAB_ShowFamilyAbilityTooltip + elseif MTH_BOOK_STATE.mode == "pets" then + local beast = MTH_DS_Beasts and MTH_DS_Beasts[entry] + if beast and type(MTH_BOOKTAB_BuildBeastAbilityEntries) == "function" then + abilityEntries = MTH_BOOKTAB_BuildBeastAbilityEntries(beast) + abilityColIndex = 5 + tooltipFunc = MTH_BOOKTAB_ShowBeastAbilityTooltip + end + end + + if type(abilityEntries) == "table" and table.getn(abilityEntries) > 0 then + if btn.cols and btn.cols[abilityColIndex] then btn.cols[abilityColIndex]:SetText("") end + local layout = MTH_BOOK_GetColumnLayout() + local col = layout and layout[abilityColIndex] + local startX = ((col and col.x) or 232) - 4 + local maxWidth = (col and col.width) or 292 + local usedWidth = 0 + local abilitiesCount = table.getn(abilityEntries) + for chipIndex = 1, table.getn(btn.familyAbilityButtons) do + local chip = btn.familyAbilityButtons[chipIndex] + local ability = abilityEntries[chipIndex] + if chip and ability and type(ability) == "table" and ability.name and ability.name ~= "" then + local label = tostring(ability.name) + if chipIndex < abilitiesCount then + label = label .. "," + end + chip.text:SetText(label) + local textWidth = chip.text:GetStringWidth() or 24 + if textWidth < 16 then textWidth = 16 end + local chipWidth = ((MTH_BOOK_STATE.mode == "families") and 15 or 14) + textWidth + if usedWidth + chipWidth > maxWidth then + chip:Hide() + else + chip:ClearAllPoints() + chip:SetPoint("LEFT", btn, "LEFT", startX + usedWidth, 0) + chip:SetWidth(chipWidth) + chip.abilityEntry = ability + chip.abilityTooltipFunc = tooltipFunc + local iconPath = MTH_BOOK_ResolveIconPath(ability.icon) + if iconPath then + chip.icon:SetTexture(iconPath) + else + chip.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") + end + chip:Show() + usedWidth = usedWidth + chipWidth + 4 + end + else + if chip then + chip.abilityEntry = nil + chip.abilityTooltipFunc = nil + chip:Hide() + end + end + end + else + for chipIndex = 1, table.getn(btn.familyAbilityButtons) do + local chip = btn.familyAbilityButtons[chipIndex] + if chip then + chip.abilityEntry = nil + chip.abilityTooltipFunc = nil + chip:Hide() + end + end + end + end + btn:Show() + else + btn.entry = nil + for c = 1, MTH_BOOK_MAX_COLS do + if btn.cols and btn.cols[c] then + btn.cols[c]:SetText("") + end + end + if btn.itemIcon then + btn.itemIcon:SetTexture(nil) + btn.itemIcon:Hide() + end + if btn.familyAbilityButtons then + for chipIndex = 1, table.getn(btn.familyAbilityButtons) do + local chip = btn.familyAbilityButtons[chipIndex] + if chip then + chip.abilityEntry = nil + chip.abilityTooltipFunc = nil + chip:Hide() + end + end + end + btn:Hide() + end + end + end + + MTH_BOOK_UpdateHeader() + MTH_BOOK_UpdateDetail() +end + +MTH_BOOK_RefreshFilter = function() + MTH_BOOK_UpdateQuickFilterControls() + MTH_BOOK_UpdateListHeaders() + if MTH_BOOK_STATE.mode == "stable" and type(MTH_PETS_RefreshCurrentPet) == "function" then + MTH_PETS_RefreshCurrentPet() + end + MTH_BOOK_STATE.results = MTH_BOOK_BuildResults() + MTH_BOOK_UpdateResults() +end + +local function MTH_BOOK_UpdateModeLabels() + local header = getglobal("MTH_BOOK_HeaderText") + if header then header:SetText("The Great Book of Huntards") end + + local filterNames = {} + local check1 = "" + local check2 = "" + local check3 = "" + local searchLabel = getglobal("MTH_BOOK_SearchLabel") + local minLabel = getglobal("MTH_BOOK_MinLabel") + local maxLabel = getglobal("MTH_BOOK_MaxLabel") + + if MTH_BOOK_STATE.mode == "pets" then + filterNames = { "All", "Cats", "Wolves", "Boars", "Spiders", "" } + check1 = "Rare" + check2 = "Elite" + check3 = "Unique" + elseif MTH_BOOK_STATE.mode == "families" then + filterNames = { "", "", "", "", "", "" } + check1 = "" + check2 = "" + check3 = "" + elseif MTH_BOOK_STATE.mode == "petabilities" then + filterNames = { "", "", "", "", "", "" } + check1 = "" + check2 = "" + check3 = "" + elseif MTH_BOOK_STATE.mode == "npcs" then + filterNames = { "", "", "", "", "", "" } + check1 = "Alliance" + check2 = "Horde" + check3 = "Neutral" + elseif MTH_BOOK_STATE.mode == "abilities" then + filterNames = { "All", "Bite", "Claw", "Dash", "Screech", "Charge" } + check1 = "Ranked only" + check2 = "Rare beasts" + check3 = "Has coords" + else + filterNames = { "", "", "", "", "", "" } + check1 = "" + check2 = "" + check3 = "" + end + + for i = 1, 6 do + local btn = getglobal("MTH_BOOK_Filter" .. i) + if btn then btn:SetText(filterNames[i] or "") end + end + + if getglobal("MTH_BOOK_RequireVendorText") then getglobal("MTH_BOOK_RequireVendorText"):SetText(check1) end + if getglobal("MTH_BOOK_RequireDropText") then getglobal("MTH_BOOK_RequireDropText"):SetText(check2) end + if getglobal("MTH_BOOK_RequireObjectText") then getglobal("MTH_BOOK_RequireObjectText"):SetText(check3) end + + if searchLabel then + if MTH_BOOK_STATE.mode == "npcs" then + searchLabel:SetText("Name") + else + searchLabel:SetText("Search") + end + end + if minLabel then + if MTH_BOOK_STATE.mode == "npcs" or MTH_BOOK_STATE.mode == "items" or MTH_BOOK_STATE.mode == "stable" or MTH_BOOK_STATE.mode == "families" or MTH_BOOK_STATE.mode == "pethistory" then + minLabel:Hide() + else + minLabel:Show() + minLabel:SetText("Min Lvl") + end + end + if maxLabel then + if MTH_BOOK_STATE.mode == "npcs" or MTH_BOOK_STATE.mode == "items" or MTH_BOOK_STATE.mode == "stable" or MTH_BOOK_STATE.mode == "families" or MTH_BOOK_STATE.mode == "pethistory" then + maxLabel:Hide() + else + maxLabel:Show() + maxLabel:SetText("Max Lvl") + end + end + + if getglobal("MTH_BOOK_HideNoAbilitiesText") then getglobal("MTH_BOOK_HideNoAbilitiesText"):SetText("Hide No Abilities") end + if getglobal("MTH_BOOK_PetOnlyMyLevelText") then + if MTH_BOOK_STATE.mode == "items" then + getglobal("MTH_BOOK_PetOnlyMyLevelText"):SetText("Show only my level") + else + getglobal("MTH_BOOK_PetOnlyMyLevelText"):SetText("Only my level") + end + local onlyMyLevel = getglobal("MTH_BOOK_PetOnlyMyLevel") + if onlyMyLevel then + getglobal("MTH_BOOK_PetOnlyMyLevelText"):ClearAllPoints() + getglobal("MTH_BOOK_PetOnlyMyLevelText"):SetPoint("LEFT", onlyMyLevel, "RIGHT", 1, 1) + end + end + if getglobal("MTH_BOOK_PetInZoneOnlyText") then + getglobal("MTH_BOOK_PetInZoneOnlyText"):SetText("In this zone only") + local zoneOnly = getglobal("MTH_BOOK_PetInZoneOnly") + if zoneOnly then + getglobal("MTH_BOOK_PetInZoneOnlyText"):ClearAllPoints() + getglobal("MTH_BOOK_PetInZoneOnlyText"):SetPoint("LEFT", zoneOnly, "RIGHT", 1, 1) + end + end + if getglobal("MTH_BOOK_NPCInZoneOnlyText") then + getglobal("MTH_BOOK_NPCInZoneOnlyText"):SetText("In this zone only") + local zoneOnlyNpc = getglobal("MTH_BOOK_NPCInZoneOnly") + if zoneOnlyNpc then + getglobal("MTH_BOOK_NPCInZoneOnlyText"):ClearAllPoints() + getglobal("MTH_BOOK_NPCInZoneOnlyText"):SetPoint("LEFT", zoneOnlyNpc, "RIGHT", 1, 1) + end + end + if getglobal("MTH_BOOK_HideUnknownText") then + if MTH_BOOK_STATE.mode == "npcs" then + getglobal("MTH_BOOK_HideUnknownText"):SetText("Hide no zone") + else + getglobal("MTH_BOOK_HideUnknownText"):SetText("Hide Unknown") + end + end + MTH_BOOK_UpdateQuickFilterControls() +end + +local function MTH_BOOK_ApplyInputs() + local search = getglobal("MTH_BOOK_Search") + MTH_BOOK_STATE.search = MTH_BOOK_SafeLower(search and search:GetText() or "") + if MTH_BOOK_STATE.mode == "npcs" or MTH_BOOK_STATE.mode == "items" or MTH_BOOK_STATE.mode == "stable" or MTH_BOOK_STATE.mode == "families" or MTH_BOOK_STATE.mode == "pethistory" then + MTH_BOOK_STATE.minLevel = nil + MTH_BOOK_STATE.maxLevel = nil + else + MTH_BOOK_STATE.minLevel = MTH_BOOK_ParseBoxNumber("MTH_BOOK_MinLevel") + MTH_BOOK_STATE.maxLevel = MTH_BOOK_ParseBoxNumber("MTH_BOOK_MaxLevel") + end + + local requireVendor = getglobal("MTH_BOOK_RequireVendor") + local requireDrop = getglobal("MTH_BOOK_RequireDrop") + local requireObject = getglobal("MTH_BOOK_RequireObject") + local hideNoAbilities = getglobal("MTH_BOOK_HideNoAbilities") + local hideUnknown = getglobal("MTH_BOOK_HideUnknown") + local petOnlyMyLevel = getglobal("MTH_BOOK_PetOnlyMyLevel") + local petInZoneOnly = getglobal("MTH_BOOK_PetInZoneOnly") + local npcInZoneOnly = getglobal("MTH_BOOK_NPCInZoneOnly") + local showAllOnMapButton = getglobal("MTH_BOOK_ShowAllOnMapButton") + MTH_BOOK_STATE.flag1 = requireVendor and requireVendor:GetChecked() == 1 or false + MTH_BOOK_STATE.flag2 = requireDrop and requireDrop:GetChecked() == 1 or false + MTH_BOOK_STATE.flag3 = requireObject and requireObject:GetChecked() == 1 or false + if MTH_BOOK_STATE.mode == "pets" then + MTH_BOOK_STATE.petHideNoAbilities = hideNoAbilities and hideNoAbilities:GetChecked() == 1 or false + MTH_BOOK_STATE.petHideUnknown = hideUnknown and hideUnknown:GetChecked() == 1 or false + MTH_BOOK_STATE.petInZoneOnly = petInZoneOnly and petInZoneOnly:GetChecked() == 1 or false + elseif MTH_BOOK_STATE.mode == "petabilities" then + MTH_BOOK_STATE.petOnlyMyLevel = petOnlyMyLevel and petOnlyMyLevel:GetChecked() == 1 or false + elseif MTH_BOOK_STATE.mode == "items" then + MTH_BOOK_STATE.itemOnlyMyLevel = petOnlyMyLevel and petOnlyMyLevel:GetChecked() == 1 or false + elseif MTH_BOOK_STATE.mode == "npcs" then + MTH_BOOK_STATE.npcHideNoZone = hideUnknown and hideUnknown:GetChecked() == 1 or false + MTH_BOOK_STATE.npcInZoneOnly = npcInZoneOnly and npcInZoneOnly:GetChecked() == 1 or false + end + + MTH_BOOK_STATE.page = 1 + MTH_BOOK_STATE.selectedEntry = nil + MTH_BOOK_STATE.selectedPetRankEntry = nil + MTH_BOOK_STATE.petRankRows = {} + MTH_BOOK_RefreshFilter() +end + +local function MTH_BOOK_SetQuickFilter(index) + if MTH_BOOK_STATE.mode == "pets" then + local map = { [1] = "all", [2] = "cats", [3] = "wolves", [4] = "boars", [5] = "spiders", [6] = "all" } + MTH_BOOK_STATE.quick = map[index] or "all" + elseif MTH_BOOK_STATE.mode == "families" then + MTH_BOOK_STATE.quick = "all" + elseif MTH_BOOK_STATE.mode == "petabilities" then + MTH_BOOK_STATE.quick = "all" + elseif MTH_BOOK_STATE.mode == "npcs" then + MTH_BOOK_STATE.quick = "all" + elseif MTH_BOOK_STATE.mode == "abilities" then + local map = { [1] = "all", [2] = "bite", [3] = "claw", [4] = "dash", [5] = "screech", [6] = "charge" } + MTH_BOOK_STATE.quick = map[index] or "all" + else + local map = { [1] = "all", [2] = "arrow", [3] = "bullet", [4] = "bow", [5] = "gun", [6] = "crossbow" } + MTH_BOOK_STATE.quick = map[index] or "all" + end + MTH_BOOK_STATE.page = 1 + MTH_BOOK_STATE.selectedEntry = nil + MTH_BOOK_STATE.forcedBeastId = nil + MTH_BOOK_RefreshFilter() +end + +local function MTH_BOOK_StyleSectionTab(button) + if not button then return end + button:SetHeight(24) + button:SetScript("OnShow", nil) +end + +local function MTH_BOOK_UpdateSectionTabs() + local sectionPets = getglobal("MTH_BOOK_SectionPets") + local sectionFamilies = getglobal("MTH_BOOK_SectionFamilies") + local sectionPetAbilities = getglobal("MTH_BOOK_SectionPetAbilities") + local sectionItems = getglobal("MTH_BOOK_SectionItems") + local sectionNPCs = getglobal("MTH_BOOK_SectionNPCs") + local sectionStable = getglobal("MTH_BOOK_SectionStable") + local sectionPetHistory = getglobal("MTH_BOOK_SectionPetHistory") + + local function setState(button, selected) + if not button then return end + if selected then + PanelTemplates_SelectTab(button) + else + PanelTemplates_DeselectTab(button) + end + end + + setState(sectionPets, MTH_BOOK_STATE.mode == "pets") + setState(sectionFamilies, MTH_BOOK_STATE.mode == "families") + setState(sectionPetAbilities, MTH_BOOK_STATE.mode == "petabilities") + setState(sectionItems, MTH_BOOK_STATE.mode == "items") + setState(sectionNPCs, MTH_BOOK_STATE.mode == "npcs") + setState(sectionStable, MTH_BOOK_STATE.mode == "stable") + setState(sectionPetHistory, MTH_BOOK_STATE.mode == "pethistory") +end + +local function MTH_BOOK_LayoutSectionTabs() + local tabBar = getglobal("MTH_BOOK_TabBar") + local sectionPets = getglobal("MTH_BOOK_SectionPets") + local sectionFamilies = getglobal("MTH_BOOK_SectionFamilies") + local sectionPetAbilities = getglobal("MTH_BOOK_SectionPetAbilities") + local sectionItems = getglobal("MTH_BOOK_SectionItems") + local sectionNPCs = getglobal("MTH_BOOK_SectionNPCs") + local sectionStable = getglobal("MTH_BOOK_SectionStable") + local sectionPetHistory = getglobal("MTH_BOOK_SectionPetHistory") + if not (tabBar and sectionPets and sectionFamilies and sectionPetAbilities and sectionItems and sectionNPCs) then return end + + if sectionStable then + sectionStable:ClearAllPoints() + sectionStable:SetPoint("TOPLEFT", tabBar, "TOPLEFT", 0, 0) + end + + sectionPetAbilities:ClearAllPoints() + if sectionStable then + sectionPetAbilities:SetPoint("LEFT", sectionStable, "RIGHT", 2, 0) + else + sectionPetAbilities:SetPoint("TOPLEFT", tabBar, "TOPLEFT", 0, 0) + end + + sectionPets:ClearAllPoints() + sectionPets:SetPoint("LEFT", sectionPetAbilities, "RIGHT", 2, 0) + + sectionFamilies:ClearAllPoints() + sectionFamilies:SetPoint("LEFT", sectionPets, "RIGHT", 2, 0) + + sectionNPCs:ClearAllPoints() + sectionNPCs:SetPoint("LEFT", sectionFamilies, "RIGHT", 2, 0) + + sectionItems:ClearAllPoints() + sectionItems:SetPoint("LEFT", sectionNPCs, "RIGHT", 2, 0) + + if sectionPetHistory then + sectionPetHistory:ClearAllPoints() + sectionPetHistory:SetPoint("LEFT", sectionItems, "RIGHT", 2, 0) + end +end + +local function MTH_BOOK_GetFilterWidgets() + return { + search = getglobal("MTH_BOOK_Search"), + min = getglobal("MTH_BOOK_MinLevel"), + max = getglobal("MTH_BOOK_MaxLevel"), + requireVendor = getglobal("MTH_BOOK_RequireVendor"), + requireDrop = getglobal("MTH_BOOK_RequireDrop"), + requireObject = getglobal("MTH_BOOK_RequireObject"), + hideNoAbilities = getglobal("MTH_BOOK_HideNoAbilities"), + hideUnknown = getglobal("MTH_BOOK_HideUnknown"), + abilityDropdown = getglobal("MTH_BOOK_AbilityDropdown"), + rankDropdown = getglobal("MTH_BOOK_RankDropdown"), + npcFunctionDropdown = getglobal("MTH_BOOK_NPCFunctionDropdown"), + npcZoneDropdown = getglobal("MTH_BOOK_NPCZoneDropdown"), + petLearnSourceDropdown = getglobal("MTH_BOOK_PetLearnSourceDropdown"), + itemSubtypeDropdown = getglobal("MTH_BOOK_ItemSubtypeDropdown"), + petOnlyMyLevel = getglobal("MTH_BOOK_PetOnlyMyLevel"), + petInZoneOnly = getglobal("MTH_BOOK_PetInZoneOnly"), + npcInZoneOnly = getglobal("MTH_BOOK_NPCInZoneOnly"), + } +end + +local function MTH_BOOK_ResetStateForMode(mode) + MTH_BOOK_STATE.search = "" + MTH_BOOK_STATE.quick = "all" + MTH_BOOK_STATE.minLevel = nil + MTH_BOOK_STATE.maxLevel = nil + MTH_BOOK_STATE.flag1 = false + MTH_BOOK_STATE.flag2 = false + MTH_BOOK_STATE.flag3 = false + MTH_BOOK_STATE.petAbility = "all" + MTH_BOOK_STATE.petRank = "all" + MTH_BOOK_STATE.petLearnSource = "all" + MTH_BOOK_STATE.petOnlyMyLevel = false + MTH_BOOK_STATE.itemOnlyMyLevel = false + MTH_BOOK_STATE.petInZoneOnly = false + MTH_BOOK_STATE.itemSubtype = "all" + MTH_BOOK_STATE.npcFunction = "all" + MTH_BOOK_STATE.npcZone = "all" + MTH_BOOK_STATE.npcInZoneOnly = false + MTH_BOOK_STATE.npcHideNoZone = true + MTH_BOOK_STATE.petHideNoAbilities = false + MTH_BOOK_STATE.petHideUnknown = false + MTH_BOOK_STATE.forcedBeastId = nil + MTH_BOOK_STATE.petLeftOffset = 0 + + if mode == "pets" then + MTH_BOOK_STATE.petHideNoAbilities = true + MTH_BOOK_STATE.petHideUnknown = true + elseif mode == "families" then + MTH_BOOK_STATE.quick = "all" + elseif mode == "petabilities" then + MTH_BOOK_STATE.petLearnSource = "beast" + MTH_BOOK_STATE.petOnlyMyLevel = true + elseif mode == "items" then + MTH_BOOK_STATE.itemSubtype = "all" + MTH_BOOK_STATE.itemOnlyMyLevel = false + elseif mode == "npcs" then + MTH_BOOK_STATE.flag1, MTH_BOOK_STATE.flag2, MTH_BOOK_STATE.flag3 = MTH_BOOK_GetPlayerReactDefaults() + MTH_BOOK_STATE.npcHideNoZone = true + end + + MTH_BOOK_STATE.page = 1 + MTH_BOOK_STATE.selectedEntry = nil + MTH_BOOK_STATE.selectedPetRankEntry = nil + MTH_BOOK_STATE.petRankRows = {} +end + +local function MTH_BOOK_ApplyStateToWidgets(widgets) + if widgets.search then widgets.search:SetText("") end + if widgets.min then widgets.min:SetText("") end + if widgets.max then widgets.max:SetText("") end + if widgets.requireVendor then widgets.requireVendor:SetChecked(MTH_BOOK_STATE.flag1 and 1 or nil) end + if widgets.requireDrop then widgets.requireDrop:SetChecked(MTH_BOOK_STATE.flag2 and 1 or nil) end + if widgets.requireObject then widgets.requireObject:SetChecked(MTH_BOOK_STATE.flag3 and 1 or nil) end + if widgets.hideNoAbilities then + widgets.hideNoAbilities:SetChecked(MTH_BOOK_STATE.mode == "pets" and MTH_BOOK_STATE.petHideNoAbilities and 1 or nil) + end + if widgets.hideUnknown then + if MTH_BOOK_STATE.mode == "pets" then + widgets.hideUnknown:SetChecked(MTH_BOOK_STATE.petHideUnknown and 1 or nil) + elseif MTH_BOOK_STATE.mode == "npcs" then + widgets.hideUnknown:SetChecked(MTH_BOOK_STATE.npcHideNoZone and 1 or nil) + else + widgets.hideUnknown:SetChecked(nil) + end + end + if widgets.petOnlyMyLevel then + if MTH_BOOK_STATE.mode == "petabilities" then + widgets.petOnlyMyLevel:SetChecked(MTH_BOOK_STATE.petOnlyMyLevel and 1 or nil) + elseif MTH_BOOK_STATE.mode == "items" then + widgets.petOnlyMyLevel:SetChecked(MTH_BOOK_STATE.itemOnlyMyLevel and 1 or nil) + else + widgets.petOnlyMyLevel:SetChecked(nil) + end + end + if widgets.petInZoneOnly then + widgets.petInZoneOnly:SetChecked(MTH_BOOK_STATE.mode == "pets" and MTH_BOOK_STATE.petInZoneOnly and 1 or nil) + end + if widgets.npcInZoneOnly then + widgets.npcInZoneOnly:SetChecked(MTH_BOOK_STATE.mode == "npcs" and MTH_BOOK_STATE.npcInZoneOnly and 1 or nil) + end +end + +local function MTH_BOOK_SetMode(mode) + if mode == "abilities" then mode = "pets" end + if MTH_BOOK_STATE.mode == mode then return end + if mode == "families" then + MTH_BOOK_FamiliesTrace("SetMode -> families") + end + MTH_BOOK_STATE.mode = mode + + local widgets = MTH_BOOK_GetFilterWidgets() + MTH_BOOK_ResetStateForMode(mode) + MTH_BOOK_ApplyStateToWidgets(widgets) + MTH_BOOK_BuildPetAbilityOptions() + MTH_BOOK_BuildPetRankOptions() + MTH_BOOK_UpdatePetLearnSourceDropdownText() + + MTH_BOOK_UpdateModeLabels() + MTH_BOOK_UpdateSectionTabs() + MTH_BOOK_RefreshFilter() +end + +function MTH_BOOK_JumpToBeastById(beastId) + local id = tonumber(beastId) + if not id then + return false + end + local beast = MTH_DS_Beasts and MTH_DS_Beasts[id] + if type(beast) ~= "table" then + return false + end + + MTH_BOOK_STATE.mode = "pets" + local widgets = MTH_BOOK_GetFilterWidgets() + MTH_BOOK_ResetStateForMode("pets") + MTH_BOOK_STATE.search = tostring(id) + MTH_BOOK_STATE.forcedBeastId = id + MTH_BOOK_ApplyStateToWidgets(widgets) + if widgets.search then widgets.search:SetText(MTH_BOOK_STATE.search) end + MTH_BOOK_BuildPetAbilityOptions() + MTH_BOOK_BuildPetRankOptions() + MTH_BOOK_UpdatePetLearnSourceDropdownText() + MTH_BOOK_UpdateModeLabels() + MTH_BOOK_UpdateSectionTabs() + MTH_BOOK_RefreshFilter() + + MTH_BOOK_STATE.selectedEntry = { + beastId = id, + name = beast.name, + } + MTH_BOOK_UpdateResults() + MTH_BOOK_UpdateDetail() + return true +end + +local function MTH_BOOK_CreateResultButtons(parent) + for i = 1, MTH_BOOK_MAX_COLS do + local colIndex = i + local headerButton = CreateFrame("Button", nil, parent) + headerButton:RegisterForClicks("LeftButtonUp") + headerButton:SetFrameLevel(parent:GetFrameLevel() + 10) + headerButton:SetScript("OnClick", function() + MTH_BOOK_OnHeaderClick(colIndex) + end) + headerButton:EnableMouse(true) + MTH_BOOK_STATE.headerButtons[i] = headerButton + + local header = parent:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + header:SetHeight(14) + header:SetTextColor(1.00, 0.82, 0.00) + MTH_BOOK_STATE.headerCols[i] = header + end + + MTH_BOOK_UpdateListHeaders() + + local y = -24 + for i = 1, MTH_BOOK_MAX_ROWS do + local btn = CreateFrame("Button", "MTH_BOOK_ResultButton" .. i, parent) + btn:RegisterForClicks("LeftButtonUp", "RightButtonUp") + btn:SetPoint("TOPLEFT", parent, "TOPLEFT", 4, y) + btn:SetWidth(556) + btn:SetHeight(19) + btn.entry = nil + btn.cols = {} + + local highlight = btn:CreateTexture(nil, "HIGHLIGHT") + highlight:SetAllPoints(btn) + highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + highlight:SetBlendMode("ADD") + highlight:SetAlpha(0.35) + + local line = btn:CreateTexture(nil, "BORDER") + line:SetTexture("Interface\\Buttons\\WHITE8X8") + line:SetVertexColor(0.30, 0.30, 0.30, 0.55) + line:SetPoint("BOTTOMLEFT", btn, "BOTTOMLEFT", 2, 0) + line:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", -2, 0) + line:SetHeight(1) + + for c = 1, MTH_BOOK_MAX_COLS do + local col = btn:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + col:SetHeight(14) + col:SetTextColor(0.86, 0.86, 0.86) + col:SetJustifyH("LEFT") + btn.cols[c] = col + end + + btn.bookLinkButton = CreateFrame("Button", nil, btn) + btn.bookLinkButton:Hide() + btn.bookLinkButton:SetScript("OnClick", function() + if this:GetParent() and this:GetParent().entry then + if this:GetParent().entry.beastId then + MTH_BOOK_OpenNPCLink(this:GetParent().entry.beastId) + elseif this:GetParent().entry.npcId then + MTH_BOOK_OpenNPCLink(this:GetParent().entry.npcId) + elseif this:GetParent().entry.itemId then + MTH_BOOK_OpenItemDatabaseLink(this:GetParent().entry.itemId) + end + end + end) + btn.bookLinkButton:SetScript("OnEnter", function() + return + end) + btn.bookLinkButton:SetScript("OnLeave", function() + return + end) + + btn.itemLinkButton = CreateFrame("Button", nil, btn) + btn.itemLinkButton:Hide() + btn.itemLinkButton:SetScript("OnClick", nil) + btn.itemLinkButton:SetScript("OnEnter", nil) + btn.itemLinkButton:SetScript("OnLeave", nil) + + btn.itemIcon = btn:CreateTexture(nil, "ARTWORK") + btn.itemIcon:SetWidth(14) + btn.itemIcon:SetHeight(14) + btn.itemIcon:Hide() + + btn.nameHoverButton = CreateFrame("Button", nil, btn) + btn.nameHoverButton:Hide() + btn.nameHoverButton:SetScript("OnEnter", function() + if this:GetParent() and this:GetParent().entry and this:GetParent().entry.itemId then + MTH_BOOK_ShowItemTooltip(this, this:GetParent().entry.itemId) + end + end) + btn.nameHoverButton:SetScript("OnLeave", function() + MTH_BOOK_HideItemTooltip() + end) + btn.nameHoverButton:SetScript("OnClick", function() + if not this:GetParent() or not this:GetParent().entry then return end + if MTH_BOOK_STATE.mode == "items" and this:GetParent().entry.itemId and type(IsShiftKeyDown) == "function" and IsShiftKeyDown() then + if MTH_BOOK_InsertItemLinkToChat(this:GetParent().entry.itemId) then + return + end + end + MTH_BOOK_STATE.selectedEntry = this:GetParent().entry + MTH_BOOK_UpdateDetail() + if arg1 == "RightButton" then + MTH_BOOK_OpenSelectedBeastOnMap() + MTH_BOOK_TryTargetSelectedEntry() + end + end) + + btn.familyAbilityButtons = {} + for chipIndex = 1, 12 do + local chip = CreateFrame("Button", nil, btn) + chip:SetHeight(14) + chip.abilityEntry = nil + chip.abilityTooltipFunc = nil + chip.icon = chip:CreateTexture(nil, "ARTWORK") + chip.icon:SetPoint("LEFT", chip, "LEFT", 0, 0) + chip.icon:SetWidth(12) + chip.icon:SetHeight(12) + chip.text = chip:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + chip.text:SetPoint("LEFT", chip.icon, "RIGHT", 2, 0) + chip.text:SetJustifyH("LEFT") + chip:SetScript("OnEnter", function() + if not this or not this.abilityEntry then return end + if type(this.abilityTooltipFunc) == "function" then + this.abilityTooltipFunc(this, this.abilityEntry) + elseif type(MTH_BOOKTAB_ShowFamilyAbilityTooltip) == "function" then + MTH_BOOKTAB_ShowFamilyAbilityTooltip(this, this.abilityEntry) + end + end) + chip:SetScript("OnLeave", function() + if type(MTH_BOOK_HideSpellTooltip) == "function" then + MTH_BOOK_HideSpellTooltip() + end + end) + chip:Hide() + btn.familyAbilityButtons[chipIndex] = chip + end + + btn:SetScript("OnClick", function() + if this.entry then + if MTH_BOOK_STATE.mode == "items" and this.entry.itemId and type(IsShiftKeyDown) == "function" and IsShiftKeyDown() then + if MTH_BOOK_InsertItemLinkToChat(this.entry.itemId) then + return + end + end + + MTH_BOOK_STATE.selectedEntry = this.entry + MTH_BOOK_UpdateDetail() + if arg1 == "RightButton" then + MTH_BOOK_OpenSelectedBeastOnMap() + MTH_BOOK_TryTargetSelectedEntry() + end + end + end) + btn:SetScript("OnEnter", function() + return + end) + btn:SetScript("OnLeave", function() + return + end) + MTH_BOOK_STATE.buttons[i] = btn + y = y - 20 + end + + MTH_BOOK_ApplyColumnLayout(parent) +end + +local function MTH_BOOK_ScrollPage(delta) + if MTH_BOOK_STATE.mode == "petabilities" or MTH_BOOK_STATE.mode == "stable" or MTH_BOOK_STATE.mode == "families" then return end + if not delta or delta == 0 then return end + if delta > 0 then + MTH_BOOK_STATE.page = MTH_BOOK_STATE.page - 1 + else + MTH_BOOK_STATE.page = MTH_BOOK_STATE.page + 1 + end + MTH_BOOK_UpdateResults() +end + +local function MTH_BOOK_ResetAllFiltersAndRefresh() + local widgets = MTH_BOOK_GetFilterWidgets() + MTH_BOOK_ResetStateForMode(MTH_BOOK_STATE.mode) + MTH_BOOK_ApplyStateToWidgets(widgets) + + MTH_BOOK_BuildPetAbilityOptions() + MTH_BOOK_BuildPetRankOptions() + MTH_BOOK_BuildNPCFunctionOptions() + MTH_BOOK_BuildNPCZoneOptions() + if widgets.abilityDropdown then MTH_BOOK_UpdateAbilityDropdownText() end + if widgets.rankDropdown then MTH_BOOK_UpdateRankDropdownText() end + if widgets.petLearnSourceDropdown then MTH_BOOK_UpdatePetLearnSourceDropdownText() end + if widgets.itemSubtypeDropdown then MTH_BOOK_UpdateItemSubtypeDropdownText() end + if widgets.npcFunctionDropdown or widgets.npcZoneDropdown then MTH_BOOK_UpdateNPCDropdownTexts() end + + MTH_BOOK_UpdateModeLabels() + MTH_BOOK_RefreshFilter() +end + +local function MTH_BOOK_WireUI(frame) + if frame and frame.SetBackdropColor then + frame:SetBackdropColor(0.05, 0.05, 0.05, 1.00) + frame:SetBackdropBorderColor(0.35, 0.35, 0.35, 1.00) + end + + local closeButton = getglobal("MTH_BOOK_CloseButton") + local applyButton = getglobal("MTH_BOOK_ApplyButton") + local resetButton = getglobal("MTH_BOOK_ResetButton") + local sectionPets = getglobal("MTH_BOOK_SectionPets") + local sectionFamilies = getglobal("MTH_BOOK_SectionFamilies") + local sectionPetAbilities = getglobal("MTH_BOOK_SectionPetAbilities") + local sectionItems = getglobal("MTH_BOOK_SectionItems") + local sectionNPCs = getglobal("MTH_BOOK_SectionNPCs") + local sectionStable = getglobal("MTH_BOOK_SectionStable") + local sectionPetHistory = getglobal("MTH_BOOK_SectionPetHistory") + local requireVendor = getglobal("MTH_BOOK_RequireVendor") + local requireDrop = getglobal("MTH_BOOK_RequireDrop") + local requireObject = getglobal("MTH_BOOK_RequireObject") + local abilityDropdown = getglobal("MTH_BOOK_AbilityDropdown") + local rankDropdown = getglobal("MTH_BOOK_RankDropdown") + local petLearnSourceDropdown = getglobal("MTH_BOOK_PetLearnSourceDropdown") + local itemSubtypeDropdown = getglobal("MTH_BOOK_ItemSubtypeDropdown") + local npcFunctionDropdown = getglobal("MTH_BOOK_NPCFunctionDropdown") + local npcZoneDropdown = getglobal("MTH_BOOK_NPCZoneDropdown") + local petBookScanButton = getglobal("MTH_BOOK_PetBookScanButton") + local hideNoAbilities = getglobal("MTH_BOOK_HideNoAbilities") + local hideUnknown = getglobal("MTH_BOOK_HideUnknown") + local petOnlyMyLevel = getglobal("MTH_BOOK_PetOnlyMyLevel") + local petInZoneOnly = getglobal("MTH_BOOK_PetInZoneOnly") + local npcInZoneOnly = getglobal("MTH_BOOK_NPCInZoneOnly") + local showAllOnMapButton = getglobal("MTH_BOOK_ShowAllOnMapButton") + local prevButton = getglobal("MTH_BOOK_PrevButton") + local nextButton = getglobal("MTH_BOOK_NextButton") + local openMapButton = getglobal("MTH_BOOK_OpenMapButton") + local listSlider = getglobal("MTH_BOOK_ListSlider") + local search = getglobal("MTH_BOOK_Search") + local min = getglobal("MTH_BOOK_MinLevel") + local max = getglobal("MTH_BOOK_MaxLevel") + + if closeButton then + closeButton:ClearAllPoints() + closeButton:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -8, -8) + closeButton:SetWidth(22) + closeButton:SetHeight(22) + closeButton:SetText("X") + closeButton:SetScript("OnClick", function() this:GetParent():Hide() end) + end + if applyButton then applyButton:SetScript("OnClick", MTH_BOOK_ApplyInputs) end + + if resetButton then + resetButton:SetScript("OnClick", MTH_BOOK_ResetAllFiltersAndRefresh) + end + + if sectionPets then sectionPets:SetScript("OnClick", function() MTH_BOOK_SetMode("pets") end) end + if sectionFamilies then sectionFamilies:SetScript("OnClick", function() MTH_BOOK_SetMode("families") end) end + if sectionPetAbilities then sectionPetAbilities:SetScript("OnClick", function() MTH_BOOK_SetMode("petabilities") end) end + if sectionItems then sectionItems:SetScript("OnClick", function() MTH_BOOK_SetMode("items") end) end + if sectionNPCs then sectionNPCs:SetScript("OnClick", function() MTH_BOOK_SetMode("npcs") end) end + if sectionStable then sectionStable:SetScript("OnClick", function() MTH_BOOK_SetMode("stable") end) end + if sectionPetHistory then sectionPetHistory:SetScript("OnClick", function() MTH_BOOK_SetMode("pethistory") end) end + + MTH_BOOK_StyleSectionTab(sectionPets) + MTH_BOOK_StyleSectionTab(sectionFamilies) + MTH_BOOK_StyleSectionTab(sectionPetAbilities) + MTH_BOOK_StyleSectionTab(sectionItems) + MTH_BOOK_StyleSectionTab(sectionNPCs) + MTH_BOOK_StyleSectionTab(sectionStable) + MTH_BOOK_StyleSectionTab(sectionPetHistory) + MTH_BOOK_LayoutSectionTabs() + + for i = 1, 6 do + local filterBtn = getglobal("MTH_BOOK_Filter" .. i) + if filterBtn then + filterBtn:SetScript("OnClick", function() MTH_BOOK_SetQuickFilter(i) end) + end + end + + MTH_BOOK_InitFamilyDropdown() + MTH_BOOK_InitAbilityDropdown() + MTH_BOOK_InitRankDropdown() + MTH_BOOK_InitPetLearnSourceDropdown() + MTH_BOOK_InitItemSubtypeDropdown() + MTH_BOOK_InitNPCFunctionDropdown() + + if petBookScanButton then + petBookScanButton:SetScript("OnClick", function() + local function getRowCount() + local getNumCrafts = (type(getglobal) == "function" and getglobal("GetNumCrafts")) or (_G and _G["GetNumCrafts"]) + if type(getNumCrafts) == "function" then + return tonumber(getNumCrafts()) or 0 + end + return 0 + end + + local function isTrainingVisible() + local beastTraining = (type(getglobal) == "function" and getglobal("BeastTrainingFrame")) or (_G and _G["BeastTrainingFrame"]) + local petTraining = (type(getglobal) == "function" and getglobal("PetTrainingFrame")) or (_G and _G["PetTrainingFrame"]) + if beastTraining and ((beastTraining.IsVisible and beastTraining:IsVisible()) or (beastTraining.IsShown and beastTraining:IsShown())) then + return true + end + if petTraining and ((petTraining.IsVisible and petTraining:IsVisible()) or (petTraining.IsShown and petTraining:IsShown())) then + return true + end + return false + end + + local function runScanNow() + if MTH and MTH.Print then + MTH:Print("Beast Training opened. Running scan...") + end + if SlashCmdList and SlashCmdList.MTH then + SlashCmdList.MTH("trainscan") + end + end + + if not isTrainingVisible() then + local castSpellByName = (type(getglobal) == "function" and getglobal("CastSpellByName")) or (_G and _G["CastSpellByName"]) + if type(castSpellByName) == "function" then + castSpellByName("Beast Training") + end + end + + if isTrainingVisible() then + runScanNow() + return + end + + if getRowCount() > 0 then + runScanNow() + return + end + + local waitFrame = CreateFrame("Frame") + local elapsed = 0 + local done = false + + waitFrame:RegisterEvent("PET_TRAINING_SHOW") + waitFrame:SetScript("OnEvent", function() + if done then return end + done = true + this:UnregisterEvent("PET_TRAINING_SHOW") + this:SetScript("OnEvent", nil) + this:SetScript("OnUpdate", nil) + runScanNow() + end) + + waitFrame:SetScript("OnUpdate", function() + if done then + this:SetScript("OnUpdate", nil) + return + end + + elapsed = elapsed + (arg1 or 0) + local rows = getRowCount() + if rows > 0 then + done = true + this:UnregisterEvent("PET_TRAINING_SHOW") + this:SetScript("OnEvent", nil) + this:SetScript("OnUpdate", nil) + runScanNow() + return + end + + if isTrainingVisible() then + done = true + this:UnregisterEvent("PET_TRAINING_SHOW") + this:SetScript("OnEvent", nil) + this:SetScript("OnUpdate", nil) + runScanNow() + return + end + + if elapsed >= 6 then + done = true + this:UnregisterEvent("PET_TRAINING_SHOW") + this:SetScript("OnEvent", nil) + this:SetScript("OnUpdate", nil) + if MTH and MTH.Print then + MTH:Print("Could not open Beast Training automatically. Open Beast Training manually.") + end + end + end) + end) + end + + if requireVendor then requireVendor:SetScript("OnClick", MTH_BOOK_ApplyInputs) end + if requireDrop then requireDrop:SetScript("OnClick", MTH_BOOK_ApplyInputs) end + if requireObject then requireObject:SetScript("OnClick", MTH_BOOK_ApplyInputs) end + if hideNoAbilities then hideNoAbilities:SetScript("OnClick", MTH_BOOK_ApplyInputs) end + if hideUnknown then hideUnknown:SetScript("OnClick", MTH_BOOK_ApplyInputs) end + if petOnlyMyLevel then petOnlyMyLevel:SetScript("OnClick", MTH_BOOK_ApplyInputs) end + if petInZoneOnly then petInZoneOnly:SetScript("OnClick", MTH_BOOK_ApplyInputs) end + if npcInZoneOnly then npcInZoneOnly:SetScript("OnClick", MTH_BOOK_ApplyInputs) end + if showAllOnMapButton then showAllOnMapButton:SetScript("OnClick", MTH_BOOK_ShowAllFilteredBeastsOnMap) end + + if prevButton then + prevButton:SetScript("OnClick", function() + MTH_BOOK_STATE.page = MTH_BOOK_STATE.page - 1 + MTH_BOOK_UpdateResults() + end) + end + if nextButton then + nextButton:SetScript("OnClick", function() + MTH_BOOK_STATE.page = MTH_BOOK_STATE.page + 1 + MTH_BOOK_UpdateResults() + end) + end + if openMapButton then + openMapButton:SetScript("OnClick", MTH_BOOK_OpenSelectedBeastOnMap) + end + + if listSlider then + listSlider:SetMinMaxValues(1, 1) + listSlider:SetValueStep(1) + listSlider:SetValue(1) + listSlider:Show() + local sliderThumb = getglobal("MTH_BOOK_ListSliderThumbTexture") + if sliderThumb then + sliderThumb:SetTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical") + sliderThumb:SetWidth(20) + sliderThumb:SetHeight(24) + end + MTH_BOOK_HideSliderTemplateTexts() + listSlider:SetScript("OnShow", function() + MTH_BOOK_HideSliderTemplateTexts() + end) + listSlider:SetScript("OnMouseDown", function() + MTH_BOOK_STATE.sliderDragging = true + MTH_BOOK_SetSliderFromCursor(this) + end) + listSlider:SetScript("OnMouseUp", function() + MTH_BOOK_SetSliderFromCursor(this) + MTH_BOOK_STATE.sliderDragging = false + end) + listSlider:SetScript("OnUpdate", function() + if MTH_BOOK_STATE.sliderDragging then + if IsMouseButtonDown and not IsMouseButtonDown("LeftButton") then + MTH_BOOK_STATE.sliderDragging = false + else + MTH_BOOK_SetSliderFromCursor(this) + end + end + end) + listSlider:SetScript("OnValueChanged", function() + local value = this:GetValue() + if not value then return end + value = math.floor(value + 0.5) + MTH_BOOK_HideSliderTemplateTexts() + MTH_BOOK_UpdateSliderMarker(this) + if value ~= MTH_BOOK_STATE.page then + MTH_BOOK_STATE.page = value + MTH_BOOK_UpdateResults() + end + end) + MTH_BOOK_UpdateSliderMarker(listSlider) + end + + if search then + search:SetAutoFocus(false) + search:SetScript("OnEnterPressed", function() this:ClearFocus(); MTH_BOOK_ApplyInputs() end) + search:SetScript("OnEscapePressed", function() this:ClearFocus() end) + end + if min then + min:SetNumeric(true) + min:SetAutoFocus(false) + min:SetScript("OnEnterPressed", function() this:ClearFocus(); MTH_BOOK_ApplyInputs() end) + min:SetScript("OnEscapePressed", function() this:ClearFocus() end) + end + if max then + max:SetNumeric(true) + max:SetAutoFocus(false) + max:SetScript("OnEnterPressed", function() this:ClearFocus(); MTH_BOOK_ApplyInputs() end) + max:SetScript("OnEscapePressed", function() this:ClearFocus() end) + end + + local listParent = getglobal("MTH_BOOK_ListBackdrop") + local topAreaBackdrop = getglobal("MTH_BOOK_TopAreaBackdrop") + local sliderBackdrop = getglobal("MTH_BOOK_ListSliderBackdrop") + local detailParent = getglobal("MTH_BOOK_DetailBackdrop") + + if topAreaBackdrop then + topAreaBackdrop:SetBackdropColor(0.10, 0.10, 0.10, 1.00) + topAreaBackdrop:SetBackdropBorderColor(0.28, 0.28, 0.28, 1.00) + topAreaBackdrop:EnableMouse(false) + topAreaBackdrop:SetFrameLevel(1) + end + + local topWidgets = { + getglobal("MTH_BOOK_StatsText"), + getglobal("MTH_BOOK_SearchLabel"), + getglobal("MTH_BOOK_MinLabel"), + getglobal("MTH_BOOK_MaxLabel"), + getglobal("MTH_BOOK_Search"), + getglobal("MTH_BOOK_MinLevel"), + getglobal("MTH_BOOK_MaxLevel"), + getglobal("MTH_BOOK_ApplyButton"), + getglobal("MTH_BOOK_ResetButton"), + getglobal("MTH_BOOK_RequireVendor"), + getglobal("MTH_BOOK_RequireDrop"), + getglobal("MTH_BOOK_RequireObject"), + getglobal("MTH_BOOK_AbilityDropdown"), + getglobal("MTH_BOOK_RankDropdown"), + getglobal("MTH_BOOK_PetLearnSourceDropdown"), + getglobal("MTH_BOOK_ItemSubtypeDropdown"), + getglobal("MTH_BOOK_PetBookScanButton"), + getglobal("MTH_BOOK_NPCFunctionDropdown"), + getglobal("MTH_BOOK_NPCZoneDropdown"), + getglobal("MTH_BOOK_HideNoAbilities"), + getglobal("MTH_BOOK_HideUnknown"), + getglobal("MTH_BOOK_PetOnlyMyLevel"), + } + for i = 1, table.getn(topWidgets) do + local widget = topWidgets[i] + if widget and widget.Show then widget:Show() end + end + + if listParent then + listParent:SetBackdropColor(0.09, 0.09, 0.09, 1.00) + listParent:SetBackdropBorderColor(0.24, 0.24, 0.24, 1.00) + listParent:SetFrameLevel(2) + if listParent.SetClipsChildren then listParent:SetClipsChildren(true) end + if listSlider then + listSlider:SetFrameStrata("HIGH") + listSlider:SetFrameLevel(listParent:GetFrameLevel() + 8) + end + if sliderBackdrop then + sliderBackdrop:SetFrameStrata("DIALOG") + sliderBackdrop:SetFrameLevel(listParent:GetFrameLevel() + 6) + end + listParent:EnableMouseWheel(true) + listParent:SetScript("OnMouseWheel", function() + MTH_BOOK_ScrollPage(arg1) + end) + MTH_BOOK_CreateResultButtons(listParent) + end + + if sliderBackdrop then + sliderBackdrop:SetBackdropColor(0.01, 0.01, 0.01, 1.00) + sliderBackdrop:SetBackdropBorderColor(0.30, 0.30, 0.30, 1.00) + sliderBackdrop:EnableMouse(true) + if listSlider then + sliderBackdrop:SetScript("OnMouseDown", function() + MTH_BOOK_STATE.sliderDragging = true + MTH_BOOK_SetSliderFromCursor(listSlider) + end) + sliderBackdrop:SetScript("OnMouseUp", function() + MTH_BOOK_SetSliderFromCursor(listSlider) + MTH_BOOK_STATE.sliderDragging = false + end) + sliderBackdrop:SetScript("OnUpdate", function() + if MTH_BOOK_STATE.sliderDragging then + if IsMouseButtonDown and not IsMouseButtonDown("LeftButton") then + MTH_BOOK_STATE.sliderDragging = false + else + MTH_BOOK_SetSliderFromCursor(listSlider) + end + end + end) + end + end + + local detail = getglobal("MTH_BOOK_DetailBackdropDetailText") + if detail then MTH_BOOK_ConfigureReadOnlyEditBox(detail) end + + if detailParent then + detailParent:SetBackdropColor(0.09, 0.09, 0.09, 1.00) + detailParent:SetBackdropBorderColor(0.24, 0.24, 0.24, 1.00) + detailParent:SetFrameLevel(2) + if detailParent.SetClipsChildren then detailParent:SetClipsChildren(true) end + end + + if listParent and detailParent then + MTH_BOOK_EnsurePetAbilitiesUI(listParent, detailParent) + MTH_BOOK_SetPetAbilitiesUIVisible(false) + end + + MTH_BOOK_UpdateModeLabels() + MTH_BOOK_UpdateListHeaders() + MTH_BOOK_SyncTopAreaColorWithSelectedTab() + MTH_BOOK_UpdateSectionTabs() + MTH_BOOK_UpdateOpenMapButton() + if requireVendor then requireVendor:SetChecked(MTH_BOOK_STATE.flag1 and 1 or nil) end + if requireDrop then requireDrop:SetChecked(MTH_BOOK_STATE.flag2 and 1 or nil) end + if requireObject then requireObject:SetChecked(MTH_BOOK_STATE.flag3 and 1 or nil) end + if hideNoAbilities then hideNoAbilities:SetChecked(MTH_BOOK_STATE.mode == "pets" and MTH_BOOK_STATE.petHideNoAbilities and 1 or nil) end + if hideUnknown then hideUnknown:SetChecked(MTH_BOOK_STATE.mode == "pets" and MTH_BOOK_STATE.petHideUnknown and 1 or nil) end + if petOnlyMyLevel then + if MTH_BOOK_STATE.mode == "petabilities" then + petOnlyMyLevel:SetChecked(MTH_BOOK_STATE.petOnlyMyLevel and 1 or nil) + elseif MTH_BOOK_STATE.mode == "items" then + petOnlyMyLevel:SetChecked(MTH_BOOK_STATE.itemOnlyMyLevel and 1 or nil) + else + petOnlyMyLevel:SetChecked(nil) + end + end + if petInZoneOnly then petInZoneOnly:SetChecked(MTH_BOOK_STATE.mode == "pets" and MTH_BOOK_STATE.petInZoneOnly and 1 or nil) end + if npcInZoneOnly then npcInZoneOnly:SetChecked(MTH_BOOK_STATE.mode == "npcs" and MTH_BOOK_STATE.npcInZoneOnly and 1 or nil) end +end + +local function MTH_BOOK_EnsureWindow() + if MTH_BOOK_Browser then return MTH_BOOK_Browser end + local frame = CreateFrame("Frame", "MTH_BOOK_", UIParent, "MTH_BOOK_Template") + if not frame then + MTH_HB_ReportGuard("Failed to create MTH_BOOK_ frame") + return nil + end + if UISpecialFrames then + local already = false + for i = 1, table.getn(UISpecialFrames) do + if UISpecialFrames[i] == "MTH_BOOK_" then + already = true + break + end + end + if not already then + table.insert(UISpecialFrames, "MTH_BOOK_") + end + end + MTH_BOOK_Browser = frame + MTH_BOOK_WireUI(frame) + return frame +end + +_G.MTH_BOOK_GetZoneName = MTH_BOOK_GetZoneName +_G.MTH_BOOK_GetPlayerLevelValue = MTH_BOOK_GetPlayerLevelValue +_G.MTH_BOOK_IsSpellInLevelScope = MTH_BOOK_IsSpellInLevelScope +_G.MTH_BOOK_GetScopedRankSummary = MTH_BOOK_GetScopedRankSummary +_G.MTH_BOOK_UpdateResults = MTH_BOOK_UpdateResults +_G.MTH_BOOK_RefreshFilter = MTH_BOOK_RefreshFilter + +function MTH_OpenHunterBook() + if not MTH_HB_RequireOpenDeps() then return end + local frame = MTH_BOOK_EnsureWindow() + if not frame then return end + frame:Show() + MTH_BOOK_RefreshFilter() +end + +function MTH_ToggleHunterBook() + if not MTH_HB_RequireOpenDeps() then return end + local frame = MTH_BOOK_EnsureWindow() + if not frame then return end + if frame:IsShown() then + frame:Hide() + else + frame:Show() + MTH_BOOK_RefreshFilter() + end +end + diff --git a/api/hunterbook.xml b/api/hunterbook.xml new file mode 100644 index 0000000..2ce5b76 --- /dev/null +++ b/api/hunterbook.xml @@ -0,0 +1,333 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + this:RegisterForDrag("LeftButton") + + + this:StartMoving(); + + + this:StopMovingOrSizing(); + + + + diff --git a/api/localization.lua b/api/localization.lua new file mode 100644 index 0000000..e5a0f77 --- /dev/null +++ b/api/localization.lua @@ -0,0 +1,180 @@ +------------------------------------------------------ +-- MetaHunt Localization System +------------------------------------------------------ + +MTH.localization = {} +MTH.currentLocale = GetLocale() + +-- Binding Headers (must be defined early for keybindings) +BINDING_HEADER_FEEDOMATIC = "[MetaHunt] Feed-O-Matic" +BINDING_HEADER_ZAspectHeader = "[MetaHunt] ZAspect Buttons" +BINDING_HEADER_ZTrackHeader = "[MetaHunt] ZTrack Buttons" +BINDING_HEADER_ZTrapHeader = "[MetaHunt] ZTrap Buttons" +BINDING_HEADER_ZPetHeader = "[MetaHunt] ZPet Buttons" +BINDING_HEADER_ZAmmoHeader = "[MetaHunt] ZAmmo Buttons" + +-- Localization table structure +local L = {} + +-- English (default) +if MTH.currentLocale == "enUS" or MTH.currentLocale == "enGB" then + + -- FeedOMatic + L.FEEDOMATIC_NAME = "Feed-O-Matic" + L.FEEDOMATIC_DESC = "Helps a Hunter keep his pets well fed" + L.TOOLTIPS_OPTION_FOOD = "Add tooltips on food" + L.TOOLTIPS_OPTION_FOOD_HELP = "Show your current pet's food preference directly in item tooltips." + L.TOOLTIPS_OPTION_OWNPET = "Activate on my pet" + L.TOOLTIPS_OPTION_OWNPET_HELP = "Show your own pet status details in tooltip when hovering your pet context." + L.TOOLTIPS_FOOD_QUALITY_UNKNOWN = "%s can eat this, but hasn't tried it yet." + L.TOOLTIPS_FOOD_QUALITY_UNDER = "%s doesn't like this anymore." + L.TOOLTIPS_FOOD_QUALITY_MIGHT = "%s might eat this." + L.TOOLTIPS_FOOD_QUALITY_WILL = "%s will eat this." + L.TOOLTIPS_FOOD_QUALITY_LIKE = "%s likes to eat this." + L.TOOLTIPS_FOOD_QUALITY_LOVE = "%s loves to eat this." + L.TOOLTIPS_FOOD_MARKER_UNKNOWN = "can eat this, but hasn't tried it yet." + L.TOOLTIPS_FOOD_MARKER_UNDER = "doesn't like this anymore." + L.TOOLTIPS_FOOD_MARKER_MIGHT = "might eat this." + L.TOOLTIPS_FOOD_MARKER_WILL = "will eat this." + L.TOOLTIPS_FOOD_MARKER_LIKE = "likes to eat this." + L.TOOLTIPS_FOOD_MARKER_LOVE = "loves to eat this." + + -- zhunter + L.ZHUNTER_NAME = "zhunter" + L.ZHUNTER_DESC = "Various features to make a hunter's life easier" + +-- German / Deutsch +elseif MTH.currentLocale == "deDE" then + + L.FEEDOMATIC_NAME = "Futter-O-Mat" + L.ZHUNTER_NAME = "zhunter" + L.TOOLTIPS_OPTION_FOOD = "Add tooltips on food" + L.TOOLTIPS_OPTION_FOOD_HELP = "Show your current pet's food preference directly in item tooltips." + L.TOOLTIPS_OPTION_OWNPET = "Activate on my pet" + L.TOOLTIPS_OPTION_OWNPET_HELP = "Show your own pet status details in tooltip when hovering your pet context." + L.TOOLTIPS_FOOD_QUALITY_UNKNOWN = "%s can eat this, but hasn't tried it yet." + L.TOOLTIPS_FOOD_QUALITY_UNDER = "%s doesn't like this anymore." + L.TOOLTIPS_FOOD_QUALITY_MIGHT = "%s might eat this." + L.TOOLTIPS_FOOD_QUALITY_WILL = "%s will eat this." + L.TOOLTIPS_FOOD_QUALITY_LIKE = "%s likes to eat this." + L.TOOLTIPS_FOOD_QUALITY_LOVE = "%s loves to eat this." + L.TOOLTIPS_FOOD_MARKER_UNKNOWN = "can eat this, but hasn't tried it yet." + L.TOOLTIPS_FOOD_MARKER_UNDER = "doesn't like this anymore." + L.TOOLTIPS_FOOD_MARKER_MIGHT = "might eat this." + L.TOOLTIPS_FOOD_MARKER_WILL = "will eat this." + L.TOOLTIPS_FOOD_MARKER_LIKE = "likes to eat this." + L.TOOLTIPS_FOOD_MARKER_LOVE = "loves to eat this." + +-- French / Français +elseif MTH.currentLocale == "frFR" then + + L.FEEDOMATIC_NAME = "Nourricier" + L.ZHUNTER_NAME = "zhunter" + L.TOOLTIPS_OPTION_FOOD = "Add tooltips on food" + L.TOOLTIPS_OPTION_FOOD_HELP = "Show your current pet's food preference directly in item tooltips." + L.TOOLTIPS_OPTION_OWNPET = "Activate on my pet" + L.TOOLTIPS_OPTION_OWNPET_HELP = "Show your own pet status details in tooltip when hovering your pet context." + L.TOOLTIPS_FOOD_QUALITY_UNKNOWN = "%s can eat this, but hasn't tried it yet." + L.TOOLTIPS_FOOD_QUALITY_UNDER = "%s doesn't like this anymore." + L.TOOLTIPS_FOOD_QUALITY_MIGHT = "%s might eat this." + L.TOOLTIPS_FOOD_QUALITY_WILL = "%s will eat this." + L.TOOLTIPS_FOOD_QUALITY_LIKE = "%s likes to eat this." + L.TOOLTIPS_FOOD_QUALITY_LOVE = "%s loves to eat this." + L.TOOLTIPS_FOOD_MARKER_UNKNOWN = "can eat this, but hasn't tried it yet." + L.TOOLTIPS_FOOD_MARKER_UNDER = "doesn't like this anymore." + L.TOOLTIPS_FOOD_MARKER_MIGHT = "might eat this." + L.TOOLTIPS_FOOD_MARKER_WILL = "will eat this." + L.TOOLTIPS_FOOD_MARKER_LIKE = "likes to eat this." + L.TOOLTIPS_FOOD_MARKER_LOVE = "loves to eat this." + +end + +MTH.localization[MTH.currentLocale] = L + +-- Get localized string +function MTH:GetLocalization(key, default) + if L[key] then + return L[key] + end + return default or key +end + +-- Module localization helper +function MTH:RegisterModuleLocalization(module_name, localizationTable) + if not self.localization.modules then + self.localization.modules = {} + end + self.localization.modules[module_name] = localizationTable +end + +function MTH:GetModuleLocalization(module_name, key, default) + if self.localization.modules and self.localization.modules[module_name] then + local L = self.localization.modules[module_name] + if L[key] then + return L[key] + end + end + return default or key +end + +local MTH_SPELL_LOCALE = { + enUS = { + ["Banish"] = "Banish", + ["Bestial Wrath"] = "Bestial Wrath", + ["Concussive Shot"] = "Concussive Shot", + ["Counterattack"] = "Counterattack", + ["Critical Mass"] = "Critical Mass", + ["Clever Traps"] = "Clever Traps", + ["Deterrence"] = "Deterrence", + ["Explosive Trap"] = "Explosive Trap", + ["Explosive Trap Effect"] = "Explosive Trap Effect", + ["Feed Pet"] = "Feed Pet", + ["Flare"] = "Flare", + ["Freezing Trap"] = "Freezing Trap", + ["Freezing Trap Effect"] = "Freezing Trap Effect", + ["Frost Trap"] = "Frost Trap", + ["Frost Trap Aura"] = "Frost Trap Aura", + ["Hunter's Mark"] = "Hunter's Mark", + ["Holy Strength"] = "Holy Strength", + ["Immolation Trap"] = "Immolation Trap", + ["Immolation Trap Effect"] = "Immolation Trap Effect", + ["Improved Concussive Shot"] = "Improved Concussive Shot", + ["Improved Wing Clip"] = "Improved Wing Clip", + ["Perception"] = "Perception", + ["Piercing Shot"] = "Piercing Shot", + ["Piercing Shots"] = "Piercing Shots", + ["Quick Shots"] = "Quick Shots", + ["Rapid Fire"] = "Rapid Fire", + ["Scare Beast"] = "Scare Beast", + ["Scatter Shot"] = "Scatter Shot", + ["Scorpid Poison"] = "Scorpid Poison", + ["Scorpid Sting"] = "Scorpid Sting", + ["Serpent Sting"] = "Serpent Sting", + ["Stoneform"] = "Stoneform", + ["Unstable Power"] = "Unstable Power", + ["Ephemeral Power"] = "Ephemeral Power", + ["Viper Sting"] = "Viper Sting", + ["War Stomp"] = "War Stomp", + ["Will of the Forsaken"] = "Will of the Forsaken", + ["Wing Clip"] = "Wing Clip", + ["Wyvern Sting"] = "Wyvern Sting", + ["Blood Fury"] = "Blood Fury", + ["Berserking"] = "Berserking", + }, +} + +MTH.Const = MTH.Const or {} +MTH.Const.SpellLocaleMap = MTH_SPELL_LOCALE + +function MTH:LocalizeSpell(spellToken) + local key = tostring(spellToken or "") + if key == "" then + return key + end + local locale = tostring(self.currentLocale or GetLocale() or "enUS") + local localeMap = MTH_SPELL_LOCALE[locale] or MTH_SPELL_LOCALE.enUS + if localeMap and localeMap[key] then + return localeMap[key] + end + return key +end diff --git a/api/map-markers.lua b/api/map-markers.lua new file mode 100644 index 0000000..47cfb34 --- /dev/null +++ b/api/map-markers.lua @@ -0,0 +1,1262 @@ +MTH_Map = { + enabled = true, + showWorld = true, + showMinimap = true, + activeSource = "beasts", + providers = {}, + nodesByZone = {}, + worldPins = {}, + minimapPins = {}, + zoneNameToId = {}, + zoneNameNormToId = {}, + mapZoneCache = {}, + focusNodes = {}, + pendingZoneOpenId = nil, + pendingZoneOpenSourceId = nil, + pendingZoneOpenAttempts = 0, + pendingZoneOpenNextAttemptAt = 0, + controller = nil, + minimapTick = 0, + lastMapContext = "n/a", + lastWorldReason = "n/a", + lastMiniReason = "n/a", + nodeRevision = 0, +} + +local MTH_MAP_MINIMAP_ZOOM = { + [0] = { [0] = 300, [1] = 240, [2] = 180, [3] = 120, [4] = 80, [5] = 50 }, + [1] = { [0] = 466 + 2/3, [1] = 400, [2] = 333 + 1/3, [3] = 266 + 2/6, [4] = 200, [5] = 133 + 1/3 }, +} + +local MTH_MAP_FAMILY_PALETTE = { + { 0.95, 0.35, 0.35 }, + { 0.95, 0.55, 0.20 }, + { 0.95, 0.78, 0.20 }, + { 0.82, 0.92, 0.20 }, + { 0.45, 0.90, 0.35 }, + { 0.20, 0.88, 0.62 }, + { 0.20, 0.82, 0.95 }, + { 0.35, 0.65, 0.95 }, + { 0.62, 0.55, 0.95 }, + { 0.88, 0.45, 0.88 }, + { 0.95, 0.45, 0.68 }, + { 0.78, 0.78, 0.82 }, +} + +local MTH_MAP_FAMILY_COLOR_CACHE = {} + +local function MTH_Map_Mod(a, b) + if b == 0 then return 0 end + if math.mod then + return math.mod(a, b) + end + if math.fmod then + return math.fmod(a, b) + end + return 0 +end + +local function MTH_Map_Log(msg) + if MTH and MTH.debug and MTH.Print then + MTH:Print("[MAP] " .. tostring(msg), "debug") + end +end + +local function MTH_Map_NormalizeName(name) + if not name then return "" end + local lowered = string.lower(tostring(name)) + return string.gsub(lowered, "[^%w]", "") +end + +local function MTH_Map_GetFamilyColor(family) + local key = MTH_Map_NormalizeName(family) + if key == "" then + return 0.95, 0.82, 0.10 + end + + if MTH_MAP_FAMILY_COLOR_CACHE[key] then + local c = MTH_MAP_FAMILY_COLOR_CACHE[key] + return c[1], c[2], c[3] + end + + local names = {} + local seen = {} + if MTH_DS_Beasts then + for _, beast in pairs(MTH_DS_Beasts) do + if beast and beast.family then + local n = MTH_Map_NormalizeName(beast.family) + if n ~= "" and not seen[n] then + seen[n] = beast.family + table.insert(names, beast.family) + end + end + end + end + + table.sort(names, function(a, b) + return MTH_Map_NormalizeName(a) < MTH_Map_NormalizeName(b) + end) + + for i = 1, table.getn(names) do + local normalized = MTH_Map_NormalizeName(names[i]) + if not MTH_MAP_FAMILY_COLOR_CACHE[normalized] then + local paletteIndex = MTH_Map_Mod((i - 1), table.getn(MTH_MAP_FAMILY_PALETTE)) + 1 + MTH_MAP_FAMILY_COLOR_CACHE[normalized] = MTH_MAP_FAMILY_PALETTE[paletteIndex] + end + end + + local resolved = MTH_MAP_FAMILY_COLOR_CACHE[key] + if resolved then + return resolved[1], resolved[2], resolved[3] + end + + return 0.95, 0.82, 0.10 +end + +local function MTH_Map_StripThePrefix(name) + if not name then return nil end + local lowered = string.lower(name) + if string.sub(lowered, 1, 4) == "the " and string.len(name) > 4 then + return string.sub(name, 5) + end + return nil +end + +local function MTH_Map_GetZoneNameById(zoneId) + local normalizedId = tonumber(zoneId) or zoneId + if not normalizedId then return nil end + + if MTH_DS_ZoneNamesFallback and MTH_DS_ZoneNamesFallback[normalizedId] then + return MTH_DS_ZoneNamesFallback[normalizedId] + end + + if not MTH_DS_Zones then return nil end + local row = MTH_DS_Zones[normalizedId] or MTH_DS_Zones[tostring(normalizedId)] + if not row or not row.names then return nil end + return row.names.enUS or row.names[GetLocale()] or row.names.deDE or row.names.frFR +end + +local function MTH_Map_MinimapIndoorState() + local tempzoom = 0 + local state = 1 + + if GetCVar("minimapZoom") == GetCVar("minimapInsideZoom") then + if (GetCVar("minimapInsideZoom") + 0) >= 3 then + Minimap:SetZoom(Minimap:GetZoom() - 1) + tempzoom = 1 + else + Minimap:SetZoom(Minimap:GetZoom() + 1) + tempzoom = -1 + end + end + + if (GetCVar("minimapInsideZoom") + 0) == Minimap:GetZoom() then + state = 0 + end + + Minimap:SetZoom(Minimap:GetZoom() + tempzoom) + return state +end + +local function MTH_Map_AddNode(list, zoneId, x, y, title, detail, r, g, b, meta) + local nx = tonumber(x) + local ny = tonumber(y) + local zid = tonumber(zoneId) + if not nx or not ny or not zid then return end + if nx <= 0 or ny <= 0 or nx > 100 or ny > 100 then return end + + local node = { + zoneId = zid, + x = nx, + y = ny, + title = title or "Unknown", + detail = detail, + color = { r or 1, g or 1, b or 1 }, + } + if type(meta) == "table" then + for key, value in pairs(meta) do + node[key] = value + end + end + table.insert(list, node) +end + +local function MTH_Map_TT_Trim(value) + if value == nil then return "" end + local text = tostring(value) + text = string.gsub(text, "^%s+", "") + text = string.gsub(text, "%s+$", "") + return text +end + +local function MTH_Map_TT_Lower(value) + if value == nil then return "" end + return string.lower(tostring(value)) +end + +local function MTH_Map_TT_GetKnownSpellMap() + if not MTH_CharSavedVariables or not MTH_CharSavedVariables.petTraining then + return nil + end + return MTH_CharSavedVariables.petTraining.spellMap +end + +local function MTH_Map_TT_ParseAbilityToken(token) + local text = MTH_Map_TT_Trim(token) + if text == "" then + return "", nil + end + local _, _, abilityName, rankText = string.find(text, "^(.-)%s+(%d+)$") + if abilityName and abilityName ~= "" then + return MTH_Map_TT_Trim(abilityName), tonumber(rankText) + end + return text, nil +end + +local function MTH_Map_TT_HunterKnowsAbility(abilityName, rankNumber) + local abilityLower = MTH_Map_TT_Lower(abilityName) + if abilityLower == "" then return false end + + local spellMap = MTH_Map_TT_GetKnownSpellMap() + if type(spellMap) ~= "table" then + return false + end + + for _, row in pairs(spellMap) do + if row and row.name and row.isKnown ~= false then + if MTH_Map_TT_Lower(row.name) == abilityLower then + if rankNumber then + if tonumber(row.rankNumber) == tonumber(rankNumber) then + return true + end + else + return true + end + end + end + end + + return false +end + +local function MTH_Map_TT_ParseAbilities(abilitiesText) + local text = MTH_Map_TT_Trim(abilitiesText) + local parsed = {} + if text == "" or text == "None" then + return parsed + end + + local startPos = 1 + local len = string.len(text) + while startPos <= len do + local commaPos = string.find(text, ",", startPos, true) + local token + if commaPos then + token = string.sub(text, startPos, commaPos - 1) + startPos = commaPos + 1 + else + token = string.sub(text, startPos) + startPos = len + 1 + end + + token = MTH_Map_TT_Trim(token) + if token ~= "" then + local abilityName, rankNumber = MTH_Map_TT_ParseAbilityToken(token) + table.insert(parsed, { + text = token, + known = MTH_Map_TT_HunterKnowsAbility(abilityName, rankNumber), + }) + end + end + + return parsed +end + +local function MTH_Map_GetCoordsForUnit(unitId, fallbackCoords) + if not unitId then return fallbackCoords end + + if MTH_DS_UnitCoords and MTH_DS_UnitCoords[unitId] and table.getn(MTH_DS_UnitCoords[unitId]) > 0 then + return MTH_DS_UnitCoords[unitId] + end + + if MTH_DS_CreatureCoords and MTH_DS_CreatureCoords[unitId] and table.getn(MTH_DS_CreatureCoords[unitId]) > 0 then + return MTH_DS_CreatureCoords[unitId] + end + + return fallbackCoords +end + +function MTH_Map:BuildZoneLookup() + self.zoneNameToId = {} + self.zoneNameNormToId = {} + + if MTH_DS_Zones then + for zoneId, row in pairs(MTH_DS_Zones) do + local zid = tonumber(zoneId) + if zid and row and row.names then + for _, value in pairs(row.names) do + if value and value ~= "" then + self.zoneNameToId[value] = zid + self.zoneNameNormToId[MTH_Map_NormalizeName(value)] = zid + end + end + end + end + end + + if MTH_DS_ZoneNamesFallback then + for zoneId, value in pairs(MTH_DS_ZoneNamesFallback) do + local zid = tonumber(zoneId) + if zid and value and value ~= "" then + if not self.zoneNameToId[value] then + self.zoneNameToId[value] = zid + end + local norm = MTH_Map_NormalizeName(value) + if not self.zoneNameNormToId[norm] then + self.zoneNameNormToId[norm] = zid + end + end + end + end + + local count = 0 + for _ in pairs(self.zoneNameNormToId) do + count = count + 1 + end + MTH_Map_Log("Zone lookup rebuilt: " .. tostring(count) .. " entries") +end + +function MTH_Map:GetMapIDByName(name) + if not name or name == "" then return nil end + + if not next(self.zoneNameNormToId or {}) then + self:BuildZoneLookup() + end + + local id = self.zoneNameToId[name] + if id then return id end + + local normalized = MTH_Map_NormalizeName(name) + id = self.zoneNameNormToId[normalized] + if id then return id end + + local stripped = MTH_Map_StripThePrefix(name) + if stripped then + id = self.zoneNameToId[stripped] + if id then return id end + id = self.zoneNameNormToId[MTH_Map_NormalizeName(stripped)] + if id then return id end + else + local withThe = "The " .. name + id = self.zoneNameToId[withThe] + if id then return id end + id = self.zoneNameNormToId[MTH_Map_NormalizeName(withThe)] + if id then return id end + end + + return nil +end + +function MTH_Map:ResolveZoneHierarchy(zoneId) + local inputId = tonumber(zoneId) + local resolved = { + inputId = inputId, + zoneId = inputId, + parentId = nil, + subzoneId = nil, + continentId = nil, + hasMinimapSize = false, + } + + if not inputId then + return resolved + end + + local sizes = MTH_DS_MinimapSizes or (pfDB and pfDB["minimap"]) or {} + local visited = {} + local cursor = inputId + local depth = 0 + + while cursor and not visited[cursor] and depth < 8 do + visited[cursor] = true + local row = MTH_DS_Zones and (MTH_DS_Zones[cursor] or MTH_DS_Zones[tostring(cursor)]) + if not row then + break + end + + local parent = tonumber(row.parent or row.continent) + if parent and not resolved.continentId then + resolved.continentId = parent + end + + if parent and parent > 0 and parent ~= cursor then + resolved.subzoneId = resolved.subzoneId or cursor + resolved.parentId = parent + cursor = parent + resolved.zoneId = cursor + depth = depth + 1 + else + break + end + end + + local renderZoneId = resolved.zoneId + if not sizes[renderZoneId] then + if sizes[inputId] then + renderZoneId = inputId + elseif resolved.parentId and sizes[resolved.parentId] then + renderZoneId = resolved.parentId + end + end + + resolved.zoneId = renderZoneId + resolved.hasMinimapSize = sizes[renderZoneId] and true or false + return resolved +end + +function MTH_Map:GetCurrentMapID() + local continent = GetCurrentMapContinent and GetCurrentMapContinent() or nil + local zone = GetCurrentMapZone and GetCurrentMapZone() or nil + local zoneName = nil + local mapId = nil + local worldMapShown = WorldMapFrame and WorldMapFrame.IsShown and WorldMapFrame:IsShown() or false + + if continent and zone and continent > 0 and zone > 0 and GetMapZones then + if not self.mapZoneCache[continent] then + self.mapZoneCache[continent] = { GetMapZones(continent) } + end + zoneName = self.mapZoneCache[continent][zone] + mapId = self:GetMapIDByName(zoneName) + if mapId then + local resolved = self:ResolveZoneHierarchy(mapId) + local normalized = resolved.zoneId or mapId + self.lastMapContext = string.format("continent=%s zone=%s mapZone='%s' real=nil mapId=%s normalized=%s", tostring(continent), tostring(zone), tostring(zoneName), tostring(mapId), tostring(normalized)) + return normalized + end + end + + if worldMapShown then + self.lastMapContext = string.format("continent=%s zone=%s mapZone='%s' real=skipped mapId=nil (zoomed-out)", tostring(continent), tostring(zone), tostring(zoneName)) + return nil + end + + local real = GetRealZoneText and GetRealZoneText() or nil + if real then + mapId = self:GetMapIDByName(real) + local resolved = self:ResolveZoneHierarchy(mapId) + local normalized = resolved.zoneId or mapId + self.lastMapContext = string.format("continent=%s zone=%s mapZone='%s' real='%s' mapId=%s normalized=%s", tostring(continent), tostring(zone), tostring(zoneName), tostring(real), tostring(mapId), tostring(normalized)) + return normalized + end + + self.lastMapContext = string.format("continent=%s zone=%s mapZone='%s' real=nil mapId=nil", tostring(continent), tostring(zone), tostring(zoneName)) + + return nil +end + +function MTH_Map:RegisterProvider(key, provider) + if not key or type(key) ~= "string" then return false end + if type(provider) ~= "table" or type(provider.buildNodes) ~= "function" then return false end + self.providers[key] = provider + return true +end + +local function MTH_Map_ClearPendingZoneOpen() + MTH_Map.pendingZoneOpenId = nil + MTH_Map.pendingZoneOpenSourceId = nil + MTH_Map.pendingZoneOpenAttempts = 0 + MTH_Map.pendingZoneOpenNextAttemptAt = 0 +end + +function MTH_Map:SetSource(key) + if not self.providers[key] then + return false + end + if key ~= "focus" and self.activeSource == key and self.nodesByZone and next(self.nodesByZone) then + self:UpdateWorldMap() + self:UpdateMinimap() + return true + end + self.activeSource = key + self:RebuildNodes() + self:UpdateWorldMap() + self:UpdateMinimap() + return true +end + +local function MTH_Map_ApplyWorldMapZone(targetZoneId, sourceZoneId) + if not (GetMapContinents and GetMapZones and SetMapZoom) then + return false + end + + local targetNames = {} + local resolvedName = MTH_Map_GetZoneNameById(targetZoneId) + if resolvedName and resolvedName ~= "" then + targetNames[MTH_Map_NormalizeName(resolvedName)] = true + end + local sourceName = MTH_Map_GetZoneNameById(sourceZoneId) + if sourceName and sourceName ~= "" then + targetNames[MTH_Map_NormalizeName(sourceName)] = true + end + + local continents = { GetMapContinents() } + for continentId = 1, table.getn(continents) do + local zones = { GetMapZones(continentId) } + for mapIndex = 1, table.getn(zones) do + local zoneName = zones[mapIndex] + if zoneName and targetNames[MTH_Map_NormalizeName(zoneName)] then + SetMapZoom(continentId, mapIndex) + return true + end + end + end + + return false +end + +function MTH_Map:OpenWorldMapForZone(zoneId) + local zid = tonumber(zoneId) + if not zid then return false end + local resolved = self:ResolveZoneHierarchy(zid) + local targetZoneId = tonumber(resolved.zoneId or zid) or zid + + if ToggleWorldMap then + if not WorldMapFrame or not WorldMapFrame:IsShown() then + ToggleWorldMap() + end + elseif WorldMapFrame and not WorldMapFrame:IsShown() then + WorldMapFrame:Show() + end + + if pfMap and pfMap.SetMapByID then + pfMap:SetMapByID(targetZoneId) + local currentAfterPF = self:GetCurrentMapID() + if tonumber(currentAfterPF) == tonumber(targetZoneId) then + MTH_Map_ClearPendingZoneOpen() + return true + end + end + + local zoomApplied = MTH_Map_ApplyWorldMapZone(targetZoneId, zid) + if zoomApplied then + local currentAfterZoom = self:GetCurrentMapID() + if tonumber(currentAfterZoom) == tonumber(targetZoneId) then + MTH_Map_ClearPendingZoneOpen() + return true + end + end + + local nowMapId = self:GetCurrentMapID() + if tonumber(nowMapId) == tonumber(targetZoneId) then + MTH_Map_ClearPendingZoneOpen() + return true + end + + self.pendingZoneOpenId = targetZoneId + self.pendingZoneOpenSourceId = zid + self.pendingZoneOpenAttempts = 8 + self.pendingZoneOpenNextAttemptAt = (GetTime and GetTime() or 0) + 0.25 + return true +end + +function MTH_Map:FocusBeast(beastId, beast) + local id = tonumber(beastId) + if not id then return false end + + local row = beast or (MTH_DS_Beasts and MTH_DS_Beasts[id]) + if not row then return false end + + local coords = MTH_Map_GetCoordsForUnit(id, row.coords) + if not coords or table.getn(coords) == 0 then return false end + + local cr, cg, cb = MTH_Map_GetFamilyColor(row.family) + local nodes = {} + local firstZoneId = nil + + for i = 1, table.getn(coords) do + local c = coords[i] + if c and c[1] and c[2] and c[3] then + local zoneId = tonumber(c[3]) + if zoneId and not firstZoneId then + firstZoneId = zoneId + end + local details = string.format("Family: %s\nLevel: %s\nAbilities: %s", row.family or "?", row.lvl or "?", row.abilities or "None") + MTH_Map_AddNode(nodes, c[3], c[1], c[2], string.format("%s (%d)", row.name or "Unknown", id), details, cr, cg, cb, { kind = "beast", beastId = id }) + end + end + + if table.getn(nodes) == 0 then return false end + + self.focusNodes = nodes + if not self:SetSource("focus") then + return false + end + + if firstZoneId then + self:OpenWorldMapForZone(firstZoneId) + end + + self:UpdateWorldMap() + self:UpdateMinimap() + return true +end + +function MTH_Map:FocusVendor(vendorId, vendor) + local id = tonumber(vendorId) + if not id then return false end + + local row = vendor or (MTH_DS_Vendors and MTH_DS_Vendors[id]) + if not row then return false end + + local coords = MTH_Map_GetCoordsForUnit(id, row.coords) + if not coords or table.getn(coords) == 0 then return false end + + local react = tostring(row.fac or "AH") + local cr, cg, cb = 0.20, 0.75, 0.95 + if react == "A" then + cr, cg, cb = 0.30, 0.55, 1.00 + elseif react == "H" then + cr, cg, cb = 0.95, 0.35, 0.35 + elseif react == "AH" then + cr, cg, cb = 0.95, 0.90, 0.35 + end + + local metaTags = "-" + if row.meta then + local labels = {} + for key, value in pairs(row.meta) do + if type(key) == "string" and value and tostring(value) ~= "" then + table.insert(labels, key) + end + end + if table.getn(labels) > 0 then + table.sort(labels) + metaTags = table.concat(labels, ", ") + end + end + + local nodes = {} + local firstZoneId = nil + for i = 1, table.getn(coords) do + local c = coords[i] + if c and c[1] and c[2] and c[3] then + local zoneId = tonumber(c[3]) + if zoneId and not firstZoneId then + firstZoneId = zoneId + end + local details = string.format("React: %s\nLevel: %s\nFunctions: %s", react ~= "" and react or "?", row.lvl or "?", metaTags) + MTH_Map_AddNode(nodes, c[3], c[1], c[2], string.format("%s (%d)", row.name or "Unknown", id), details, cr, cg, cb, { kind = "vendor", vendorId = id }) + end + end + + if table.getn(nodes) == 0 then return false end + + self.focusNodes = nodes + if not self:SetSource("focus") then + return false + end + + if firstZoneId then + self:OpenWorldMapForZone(firstZoneId) + end + + self:UpdateWorldMap() + self:UpdateMinimap() + return true +end + +function MTH_Map:SetEnabled(enabled) + self.enabled = enabled and true or false + if not self.enabled then + self:HideAllPins() + else + self:UpdateWorldMap() + self:UpdateMinimap() + end +end + +function MTH_Map:SetWorldEnabled(enabled) + self.showWorld = enabled and true or false + if not self.showWorld then + self:HideWorldPins() + else + self:UpdateWorldMap() + end +end + +function MTH_Map:SetMinimapEnabled(enabled) + self.showMinimap = enabled and true or false + if not self.showMinimap then + self:HideMinimapPins() + else + self:UpdateMinimap() + end +end + +function MTH_Map:GetStatusText() + return string.format("source=%s, enabled=%s, world=%s, minimap=%s", self.activeSource, self.enabled and "on" or "off", self.showWorld and "on" or "off", self.showMinimap and "on" or "off") +end + +local function MTH_Map_CountList(list) + local count = 0 + if not list then return 0 end + for _ in pairs(list) do + count = count + 1 + end + return count +end + +function MTH_Map:GetStatsText() + local zones = MTH_Map_CountList(self.nodesByZone) + local activeNodes = 0 + for _, rows in pairs(self.nodesByZone) do + activeNodes = activeNodes + MTH_Map_CountList(rows) + end + + local worldTotal = MTH_Map_CountList(self.worldPins) + local worldVisible = 0 + for _, pin in pairs(self.worldPins) do + if pin and pin.IsShown and pin:IsShown() then worldVisible = worldVisible + 1 end + end + + local miniTotal = MTH_Map_CountList(self.minimapPins) + local miniVisible = 0 + for _, pin in pairs(self.minimapPins) do + if pin and pin.IsShown and pin:IsShown() then miniVisible = miniVisible + 1 end + end + + return string.format("nodes=%d zones=%d world=%d/%d mini=%d/%d worldReason=%s miniReason=%s map=%s", activeNodes, zones, worldVisible, worldTotal, miniVisible, miniTotal, tostring(self.lastWorldReason), tostring(self.lastMiniReason), tostring(self.lastMapContext)) +end + +function MTH_Map:GetContextText() + local continent = GetCurrentMapContinent and GetCurrentMapContinent() or nil + local zone = GetCurrentMapZone and GetCurrentMapZone() or nil + local zoneName = nil + if continent and zone and continent > 0 and zone > 0 and GetMapZones then + if not self.mapZoneCache[continent] then + self.mapZoneCache[continent] = { GetMapZones(continent) } + end + zoneName = self.mapZoneCache[continent][zone] + end + + local mapIdFromZone = zoneName and self:GetMapIDByName(zoneName) or nil + local realZoneName = GetRealZoneText and GetRealZoneText() or nil + local mapIdFromReal = realZoneName and self:GetMapIDByName(realZoneName) or nil + local baseMapId = mapIdFromZone or mapIdFromReal + local resolved = self:ResolveZoneHierarchy(baseMapId) + + return string.format( + "continent=%s zoneIndex=%s mapZone='%s' realZone='%s' mapId(zone)=%s mapId(real)=%s sourceZone=%s subzone=%s parent=%s renderZone=%s minimap=%s", + tostring(continent), + tostring(zone), + tostring(zoneName), + tostring(realZoneName), + tostring(mapIdFromZone), + tostring(mapIdFromReal), + tostring(resolved.inputId), + tostring(resolved.subzoneId), + tostring(resolved.parentId), + tostring(resolved.zoneId), + resolved.hasMinimapSize and "yes" or "no" + ) +end + +function MTH_Map:BuildPin(parent, name, isMinimap) + local pin = CreateFrame("Button", name, parent) + pin:SetWidth(isMinimap and 14 or 17) + pin:SetHeight(isMinimap and 14 or 17) + pin:SetFrameStrata(isMinimap and "MEDIUM" or "FULLSCREEN") + pin:SetFrameLevel(isMinimap and 8 or 120) + + pin.tex = pin:CreateTexture(nil, "ARTWORK") + pin.tex:SetAllPoints(pin) + pin.tex:SetTexture("Interface\\AddOns\\MetaHunt\\img\\node") + pin.tex:SetVertexColor(1, 1, 1, 1) + + pin:SetScript("OnEnter", function() + if not this.meta then return end + GameTooltip:SetOwner(this, "ANCHOR_RIGHT") + GameTooltip:ClearLines() + GameTooltip:AddLine(this.meta.title or "Unknown", 1, 0.85, 0.2) + if this.meta.detail and this.meta.detail ~= "" then + GameTooltip:AddLine(this.meta.detail, 0.9, 0.9, 0.9, true) + end + local beastId = this.meta.beastId and tonumber(this.meta.beastId) or nil + local beast = beastId and MTH_DS_Beasts and MTH_DS_Beasts[beastId] or nil + if beast and beast.abilities then + local abilities = MTH_Map_TT_ParseAbilities(beast.abilities) + if table.getn(abilities) > 0 then + GameTooltip:AddLine("Pet Abilities:", 1.0, 0.95, 0.45) + for i = 1, table.getn(abilities) do + local entry = abilities[i] + if entry and entry.known then + GameTooltip:AddLine(" " .. tostring(entry.text or ""), 0.2, 1.0, 0.2) + else + GameTooltip:AddLine(" " .. tostring(entry and entry.text or ""), 1.0, 0.25, 0.25) + end + end + end + end + if this.meta.subzoneId and this.meta.zoneId and tonumber(this.meta.subzoneId) ~= tonumber(this.meta.zoneId) then + local subzoneName = MTH_Map_GetZoneNameById(this.meta.subzoneId) + if subzoneName then + GameTooltip:AddLine("Subzone: " .. subzoneName, 0.7, 0.9, 1) + end + end + local zoneName = MTH_Map_GetZoneNameById(this.meta.zoneId) + if zoneName then + GameTooltip:AddLine(zoneName .. string.format(" (%.1f, %.1f)", this.meta.x or 0, this.meta.y or 0), 0.6, 0.8, 1) + end + GameTooltip:Show() + end) + + pin:SetScript("OnLeave", function() + GameTooltip:Hide() + end) + + pin:Hide() + return pin +end + +function MTH_Map:HideWorldPins() + for i = 1, table.getn(self.worldPins) do + if self.worldPins[i] then self.worldPins[i]:Hide() end + end +end + +function MTH_Map:HideMinimapPins() + for i = 1, table.getn(self.minimapPins) do + if self.minimapPins[i] then self.minimapPins[i]:Hide() end + end +end + +function MTH_Map:HideAllPins() + self:HideWorldPins() + self:HideMinimapPins() +end + +function MTH_Map:RebuildNodes() + self:BuildZoneLookup() + self.nodesByZone = {} + self.nodeRevision = (tonumber(self.nodeRevision) or 0) + 1 + self._lastWorldRenderKey = nil + self._lastMiniState = nil + local provider = self.providers[self.activeSource] + if not provider then return end + + local allNodes = provider.buildNodes() + if type(allNodes) ~= "table" then return end + + for i = 1, table.getn(allNodes) do + local node = allNodes[i] + if node and node.zoneId then + local sourceZoneId = tonumber(node.zoneId) + local resolved = self:ResolveZoneHierarchy(sourceZoneId) + local zoneId = tonumber(resolved.zoneId or sourceZoneId) + if zoneId then + node.sourceZoneId = sourceZoneId + node.zoneId = zoneId + node.parentZoneId = resolved.parentId + node.subzoneId = resolved.subzoneId + if not self.nodesByZone[zoneId] then self.nodesByZone[zoneId] = {} end + table.insert(self.nodesByZone[zoneId], node) + end + end + end + + MTH_Map_Log("Rebuilt nodes for source '" .. tostring(self.activeSource) .. "'") +end + +function MTH_Map:UpdateWorldMap() + if not self.enabled or not self.showWorld then + self.lastWorldReason = "disabled" + self._lastWorldRenderKey = nil + self:HideWorldPins() + return + end + + local worldButton = WorldMapButton + if not WorldMapFrame or not WorldMapFrame:IsShown() or not worldButton then + self.lastWorldReason = "worldmap-hidden-or-missing" + self._lastWorldRenderKey = nil + self:HideWorldPins() + return + end + + local mapId = self:GetCurrentMapID() + if not mapId or not self.nodesByZone[mapId] then + self.lastWorldReason = "no-mapid-or-nodes" + self._lastWorldRenderKey = nil + self:HideWorldPins() + return + end + + local nodes = self.nodesByZone[mapId] + local worldWidth = worldButton:GetWidth() or 0 + local worldHeight = worldButton:GetHeight() or 0 + local worldKey = tostring(mapId) + .. "|" .. tostring(worldWidth) + .. "x" .. tostring(worldHeight) + .. "|" .. tostring(table.getn(nodes)) + .. "|" .. tostring(self.nodeRevision or 0) + .. "|" .. tostring(self.activeSource or "") + if self._lastWorldRenderKey == worldKey then + self.lastWorldReason = "cached(map=" .. tostring(mapId) .. ")" + return + end + local shown = 0 + + for i = 1, table.getn(nodes) do + local node = nodes[i] + if node then + shown = shown + 1 + if not self.worldPins[shown] then + self.worldPins[shown] = self:BuildPin(worldButton, "MTHMapWorldPin" .. shown, nil) + end + + local pin = self.worldPins[shown] + pin.meta = node + pin.tex:SetVertexColor(node.color[1], node.color[2], node.color[3], 0.95) + + local x = node.x / 100 * worldButton:GetWidth() + local y = node.y / 100 * worldButton:GetHeight() + pin:ClearAllPoints() + pin:SetPoint("CENTER", worldButton, "TOPLEFT", x, -y) + pin:Show() + end + end + + for i = shown + 1, table.getn(self.worldPins) do + if self.worldPins[i] then self.worldPins[i]:Hide() end + end + + self._lastWorldRenderKey = worldKey + self.lastWorldReason = "ok(map=" .. tostring(mapId) .. ",shown=" .. tostring(shown) .. ")" +end + +function MTH_Map:UpdateMinimap() + if not self.enabled or not self.showMinimap then + self.lastMiniReason = "disabled" + self._lastMiniState = nil + self:HideMinimapPins() + return + end + + if not Minimap or not GetPlayerMapPosition then + self.lastMiniReason = "minimap-or-position-api-missing" + self._lastMiniState = nil + self:HideMinimapPins() + return + end + + local mapId = self:GetCurrentMapID() + if not mapId or not self.nodesByZone[mapId] then + self.lastMiniReason = "no-mapid-or-nodes" + self._lastMiniState = nil + self:HideMinimapPins() + return + end + + local minimapSizes = MTH_DS_MinimapSizes or (pfDB and pfDB["minimap"]) + local mapSize = minimapSizes and minimapSizes[mapId] + if not mapSize then + self.lastMiniReason = "missing-minimap-size" + self._lastMiniState = nil + self:HideMinimapPins() + return + end + + local xPlayer, yPlayer = GetPlayerMapPosition("player") + if not xPlayer or not yPlayer or (xPlayer == 0 and yPlayer == 0) then + self.lastMiniReason = "player-position-unavailable" + self._lastMiniState = nil + self:HideMinimapPins() + return + end + + xPlayer = xPlayer * 100 + yPlayer = yPlayer * 100 + + local zoom = Minimap:GetZoom() + local indoor = MTH_Map_MinimapIndoorState() + local zoomSet = MTH_MAP_MINIMAP_ZOOM[indoor] + if not zoomSet then + self.lastMiniReason = "missing-zoomset" + self._lastMiniState = nil + self:HideMinimapPins() + return + end + + local mapZoom = zoomSet[zoom] + if not mapZoom then + self.lastMiniReason = "missing-mapzoom" + self._lastMiniState = nil + self:HideMinimapPins() + return + end + + local mapWidth = mapSize[1] + local mapHeight = mapSize[2] + if not mapWidth or not mapHeight or mapWidth == 0 or mapHeight == 0 then + self.lastMiniReason = "invalid-map-size" + self._lastMiniState = nil + self:HideMinimapPins() + return + end + + local miniState = self._lastMiniState + if miniState + and miniState.mapId == mapId + and miniState.zoom == zoom + and miniState.indoor == indoor + and miniState.nodeRevision == (self.nodeRevision or 0) + then + local dx = math.abs((xPlayer or 0) - (miniState.xPlayer or 0)) + local dy = math.abs((yPlayer or 0) - (miniState.yPlayer or 0)) + if dx < 0.05 and dy < 0.05 then + self.lastMiniReason = "throttled-small-move" + return + end + end + + local xScale = mapZoom / mapWidth + local yScale = mapZoom / mapHeight + local xDraw = Minimap:GetWidth() / xScale / 100 + local yDraw = Minimap:GetHeight() / yScale / 100 + + local nodes = self.nodesByZone[mapId] + local shown = 0 + + for i = 1, table.getn(nodes) do + local node = nodes[i] + if node then + local xPos = (node.x - xPlayer) * xDraw + local yPos = (node.y - yPlayer) * yDraw + local distance = math.sqrt(xPos * xPos + yPos * yPos) + local shouldShow = (distance + 6) < (Minimap:GetWidth() / 2) + + if shouldShow then + shown = shown + 1 + if not self.minimapPins[shown] then + self.minimapPins[shown] = self:BuildPin(Minimap, "MTHMapMiniPin" .. shown, true) + end + + local pin = self.minimapPins[shown] + pin.meta = node + pin.tex:SetVertexColor(node.color[1], node.color[2], node.color[3], 0.95) + pin:ClearAllPoints() + pin:SetPoint("CENTER", Minimap, "CENTER", xPos, -yPos) + pin:Show() + end + end + end + + for i = shown + 1, table.getn(self.minimapPins) do + if self.minimapPins[i] then self.minimapPins[i]:Hide() end + end + + self._lastMiniState = { + mapId = mapId, + zoom = zoom, + indoor = indoor, + xPlayer = xPlayer, + yPlayer = yPlayer, + nodeRevision = self.nodeRevision or 0, + } + + self.lastMiniReason = "ok(map=" .. tostring(mapId) .. ",shown=" .. tostring(shown) .. ")" +end + +function MTH_Map:RegisterDefaultProviders() + self:RegisterProvider("focus", { + name = "Focused Beast", + buildNodes = function() + if MTH_Map.focusNodes and table.getn(MTH_Map.focusNodes) > 0 then + return MTH_Map.focusNodes + end + return {} + end, + }) + + self:RegisterProvider("beasts", { + name = "Beasts", + buildNodes = function() + local nodes = {} + if not MTH_DS_Beasts then return nodes end + + for beastId, beast in pairs(MTH_DS_Beasts) do + local coords = beast and MTH_Map_GetCoordsForUnit(beastId, beast.coords) + if beast and coords and table.getn(coords) > 0 then + local cr, cg, cb = MTH_Map_GetFamilyColor(beast.family) + for i = 1, table.getn(coords) do + local c = coords[i] + if c and c[1] and c[2] and c[3] then + local details = string.format("Family: %s\nLevel: %s\nAbilities: %s", beast.family or "?", beast.lvl or "?", beast.abilities or "None") + MTH_Map_AddNode(nodes, c[3], c[1], c[2], string.format("%s (%d)", beast.name or "Unknown", beastId), details, cr, cg, cb, { kind = "beast", beastId = beastId }) + end + end + end + end + + return nodes + end, + }) + + self:RegisterProvider("vendors", { + name = "Vendors", + buildNodes = function() + local nodes = {} + if not MTH_DS_Vendors then return nodes end + + for vendorId, vendor in pairs(MTH_DS_Vendors) do + local coords = vendor and MTH_Map_GetCoordsForUnit(vendorId, vendor.coords) + if vendor and coords and table.getn(coords) > 0 then + for i = 1, table.getn(coords) do + local c = coords[i] + if c and c[1] and c[2] and c[3] then + local details = string.format("Faction: %s\nLevel: %s", vendor.fac or "?", vendor.lvl or "?") + MTH_Map_AddNode(nodes, c[3], c[1], c[2], string.format("%s (%d)", vendor.name or "Unknown", vendorId), details, 0.20, 0.90, 1.00, { kind = "vendor", vendorId = vendorId }) + end + end + end + end + + return nodes + end, + }) + + self:RegisterProvider("drops", { + name = "Drops", + buildNodes = function() + local nodes = {} + local items = MTH_DS_Items or MTH_DS_AmmoItems + if not items then return nodes end + local unresolvedCount = 0 + local unresolvedSamples = {} + + for itemId, item in pairs(items) do + if item and item.drops then + for creatureId, chance in pairs(item.drops) do + local creature = MTH_DS_Beasts and MTH_DS_Beasts[creatureId] + local coords = MTH_Map_GetCoordsForUnit(creatureId, creature and creature.coords) + local creatureName = nil + + if MTH_DS_CreatureCoords and MTH_DS_CreatureCoords[creatureId] and table.getn(MTH_DS_CreatureCoords[creatureId]) > 0 then + creatureName = (MTH_DS_CreatureNames and MTH_DS_CreatureNames[creatureId]) or (creature and creature.name) + elseif creature and creature.coords and table.getn(creature.coords) > 0 then + creatureName = creature.name + end + + if coords and table.getn(coords) > 0 then + for i = 1, table.getn(coords) do + local c = coords[i] + if c and c[1] and c[2] and c[3] then + local pct = tonumber(chance) + local chanceText = pct and string.format("%.2f%%", pct) or tostring(chance or "?") + local details = string.format( + "Item: %s (%d)\nDrop Creature: %s (%d)\nDrop Chance: %s", + item.name or "Unknown", + tonumber(itemId) or 0, + creatureName or "Unknown", + tonumber(creatureId) or 0, + chanceText + ) + MTH_Map_AddNode( + nodes, + c[3], + c[1], + c[2], + string.format("%s ↠%s", item.name or "Unknown Item", creatureName or "Unknown Creature"), + details, + 1.00, + 0.35, + 0.35 + ) + end + end + else + unresolvedCount = unresolvedCount + 1 + if table.getn(unresolvedSamples) < 5 then + table.insert(unresolvedSamples, tostring(creatureId)) + end + end + end + end + end + + if unresolvedCount > 0 then + MTH_Map_Log("drops source: " .. tostring(unresolvedCount) .. " drop-creature links have no loaded coordinates (sample IDs: " .. table.concat(unresolvedSamples, ", ") .. ")") + end + + return nodes + end, + }) +end + +function MTH_Map:Init() + self:BuildZoneLookup() + self:RegisterDefaultProviders() + self:RebuildNodes() + + if not self.controller then + self.controller = CreateFrame("Frame", "MTHMapController", UIParent) + self.controller:RegisterEvent("PLAYER_ENTERING_WORLD") + self.controller:RegisterEvent("ZONE_CHANGED") + self.controller:RegisterEvent("ZONE_CHANGED_NEW_AREA") + self.controller:RegisterEvent("MINIMAP_ZONE_CHANGED") + self.controller:RegisterEvent("WORLD_MAP_UPDATE") + + self.controller:SetScript("OnEvent", function(frame, eventName) + eventName = eventName or event + if eventName == "PLAYER_ENTERING_WORLD" or eventName == "ZONE_CHANGED" or eventName == "ZONE_CHANGED_NEW_AREA" or eventName == "MINIMAP_ZONE_CHANGED" then + if SetMapToCurrentZone and (not WorldMapFrame or not WorldMapFrame:IsShown()) then + SetMapToCurrentZone() + end + end + MTH_Map:UpdateWorldMap() + MTH_Map:UpdateMinimap() + end) + + self.controller:SetScript("OnUpdate", function(frame) + frame = frame or this + if not frame then return end + local now = GetTime() or 0 + if (frame._mthTick or 0) > now then return end + frame._mthTick = now + 0.12 + + if MTH_Map.pendingZoneOpenId and MTH_Map.pendingZoneOpenAttempts and MTH_Map.pendingZoneOpenAttempts > 0 then + if (MTH_Map.pendingZoneOpenNextAttemptAt or 0) <= now then + MTH_Map.pendingZoneOpenNextAttemptAt = now + 0.25 + + if WorldMapFrame and WorldMapFrame:IsShown() then + if pfMap and pfMap.SetMapByID then + pfMap:SetMapByID(MTH_Map.pendingZoneOpenId) + end + MTH_Map_ApplyWorldMapZone(MTH_Map.pendingZoneOpenId, MTH_Map.pendingZoneOpenSourceId) + local nowMapId = MTH_Map:GetCurrentMapID() + if tonumber(nowMapId) == tonumber(MTH_Map.pendingZoneOpenId) then + MTH_Map_ClearPendingZoneOpen() + MTH_Map:UpdateWorldMap() + else + MTH_Map.pendingZoneOpenAttempts = MTH_Map.pendingZoneOpenAttempts - 1 + end + else + MTH_Map.pendingZoneOpenAttempts = MTH_Map.pendingZoneOpenAttempts - 1 + end + end + + if MTH_Map.pendingZoneOpenAttempts <= 0 then + MTH_Map_ClearPendingZoneOpen() + end + end + + if MTH_Map.enabled and MTH_Map.showMinimap then + MTH_Map:UpdateMinimap() + end + end) + end + + MTH_Map_Log("Map system initialized") +end + +MTH_Map:Init() diff --git a/api/minimap-button.lua b/api/minimap-button.lua new file mode 100644 index 0000000..fda07dd --- /dev/null +++ b/api/minimap-button.lua @@ -0,0 +1,171 @@ +------------------------------------------------------ +-- MetaHunt Minimap Button +------------------------------------------------------ + +if not MTH_MinimapButton then + MTH_MinimapButton = {} +end + +local function MTH_MB_GetStore() + if not MTH_CharSavedVariables then + MTH_CharSavedVariables = {} + end + if type(MTH_CharSavedVariables.minimapButton) ~= "table" then + MTH_CharSavedVariables.minimapButton = {} + end + local store = MTH_CharSavedVariables.minimapButton + if store.angle == nil then + store.angle = 220 + end + return store +end + +local function MTH_MB_GetRadius() + return 80 +end + +local function MTH_MB_UpdatePosition() + if not MTH_MinimapButton.frame then + return + end + local store = MTH_MB_GetStore() + local angle = tonumber(store.angle) or 220 + local rad = math.rad(angle) + local radius = MTH_MB_GetRadius() + local x = math.cos(rad) * radius + local y = math.sin(rad) * radius + MTH_MinimapButton.frame:ClearAllPoints() + MTH_MinimapButton.frame:SetPoint("CENTER", Minimap, "CENTER", x, y) +end + +local function MTH_MB_UpdateDragPosition() + if not MTH_MinimapButton.frame then + return + end + local mx, my = Minimap:GetCenter() + local px, py = GetCursorPosition() + local scale = Minimap:GetEffectiveScale() + if scale and scale ~= 0 then + px = px / scale + py = py / scale + end + local dx = px - mx + local dy = py - my + local angle = math.deg(math.atan2(dy, dx)) + local store = MTH_MB_GetStore() + store.angle = angle + MTH_MB_UpdatePosition() +end + +local function MTH_MB_SetHunterIcon(texture) + if not texture then + return + end + texture:SetTexture("Interface\\GLUES\\CHARACTERCREATE\\UI-CHARACTERCREATE-CLASSES") + if CLASS_ICON_TCOORDS and CLASS_ICON_TCOORDS["HUNTER"] then + local coords = CLASS_ICON_TCOORDS["HUNTER"] + texture:SetTexCoord(coords[1], coords[2], coords[3], coords[4]) + else + texture:SetTexCoord(0, 0.25, 0.25, 0.5) + end +end + +function MTH_MinimapButton:Initialize() + if self.frame then + MTH_MB_UpdatePosition() + self.frame:Show() + return + end + + local button = CreateFrame("Button", "MTH_MinimapButtonFrame", Minimap) + if not button then + return + end + + button:SetWidth(31) + button:SetHeight(31) + button:SetFrameStrata("MEDIUM") + button:RegisterForClicks("LeftButtonUp", "RightButtonUp") + button:RegisterForDrag("LeftButton") + + local border = button:CreateTexture(nil, "OVERLAY") + border:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder") + border:SetWidth(54) + border:SetHeight(54) + border:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0) + + local icon = button:CreateTexture(nil, "BACKGROUND") + icon:SetWidth(20) + icon:SetHeight(20) + icon:SetPoint("CENTER", button, "CENTER", 0, 0) + MTH_MB_SetHunterIcon(icon) + + local highlight = button:CreateTexture(nil, "HIGHLIGHT") + highlight:SetTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight") + highlight:SetBlendMode("ADD") + highlight:SetWidth(31) + highlight:SetHeight(31) + highlight:SetPoint("CENTER", button, "CENTER", 0, 0) + + button:SetScript("OnEnter", function(self) + self = self or this + if not self then return end + GameTooltip:SetOwner(self, "ANCHOR_LEFT") + GameTooltip:SetText("MetaHunt", 1, 0.82, 0) + GameTooltip:AddLine("Left-Click: Open Hunter Book", 1, 1, 1) + GameTooltip:AddLine("Right-Click: Open MetaHunt Options", 1, 1, 1) + GameTooltip:AddLine("Drag: Move button", 0.8, 0.8, 0.8) + GameTooltip:Show() + end) + + button:SetScript("OnLeave", function() + GameTooltip:Hide() + end) + + button:SetScript("OnDragStart", function(self) + self = self or this + if not self then return end + self.dragging = 1 + self:SetScript("OnUpdate", function() + MTH_MB_UpdateDragPosition() + end) + end) + + button:SetScript("OnDragStop", function(self) + self = self or this + if not self then return end + self.dragging = nil + self:SetScript("OnUpdate", nil) + MTH_MB_UpdateDragPosition() + end) + + button:SetScript("OnClick", function(_, mouseButton) + mouseButton = mouseButton or arg1 + if mouseButton == "RightButton" then + if type(MTH_OpenOptions) == "function" then + MTH_OpenOptions("General") + end + return + end + + if type(MTH_OpenHunterBook) == "function" then + MTH_OpenHunterBook() + elseif type(MTH_ToggleHunterBook) == "function" then + MTH_ToggleHunterBook() + end + end) + + self.frame = button + self.icon = icon + MTH_MB_UpdatePosition() +end + +local loader = CreateFrame("Frame", "MTH_MinimapButtonLoader") +loader:RegisterEvent("ADDON_LOADED") +loader:SetScript("OnEvent", function(_, eventName, addonName) + eventName = eventName or event + addonName = addonName or arg1 + if eventName == "ADDON_LOADED" and addonName == "MetaHunt" then + MTH_MinimapButton:Initialize() + end +end) diff --git a/api/options-autobuy.lua b/api/options-autobuy.lua new file mode 100644 index 0000000..bc37638 --- /dev/null +++ b/api/options-autobuy.lua @@ -0,0 +1,845 @@ +local MTH_AB_READY = MTH_OptionsRequire and MTH_OptionsRequire("options-autobuy", { + "MTH_GetFrame", + "MTH_ClearContainer", + "MTH_CreateCheckbox", +}) + +local MTH_AB_LAYOUT = { + LEFT_SECTION_X = 0, + RIGHT_SECTION_X = 290, + ROW_CHECK_X = 0, + ROW_CHECK_LABEL_X = 27, + ROW_DROPDOWN_X = 33, + ROW_DROPDOWN_WIDTH = 100, + ROW_QTY_GAP = 6, + ROW_SUFFIX_GAP = 6, + ROW_STEP = 40, + SECTION_TITLE_Y = -110, + SECTION_ROWS_Y0 = -138, +} + +local MTH_AB_STATE = { + container = nil, + built = false, + syncing = false, + controls = { + moduleEnabled = nil, + projectilesEnabled = nil, + petFoodEnabled = nil, + petFoodQty = nil, + petFoodQtyLabel = nil, + petFoodQtySuffix = nil, + petFoodScopeCurrent = nil, + petFoodScopeAll = nil, + petFoodTitle = nil, + petFoodPetLines = {}, + title = nil, + body = nil, + sectionArrowTitle = nil, + sectionBulletTitle = nil, + rows = { + arrow = {}, + bullet = {}, + }, + }, +} + +local function MTH_AB_SetCheckedNoClick(check, checked) + if not check then return end + MTH_AB_STATE.syncing = true + check:SetChecked(checked and true or false) + MTH_AB_STATE.syncing = false +end + +local function MTH_AB_GetEngine() + return MTH_AutoBuyEngine +end + +local function MTH_AB_GetStore() + local engine = MTH_AB_GetEngine() + if engine and engine.EnsureDefaults then + return engine:EnsureDefaults() + end + MTH_AutoBuy_Saved = MTH_AutoBuy_Saved or {} + return MTH_AutoBuy_Saved +end + +local function MTH_AB_NormalizeRule(rule) + if type(rule) ~= "table" then + rule = {} + end + if rule.enabled == nil then + rule.enabled = false + end + rule.enabled = rule.enabled and true or false + rule.itemId = rule.itemId and (tonumber(rule.itemId) or rule.itemId) or nil + + local stacks = tonumber(rule.stacks) + if not stacks then + stacks = rule.enabled and 1 or 0 + end + stacks = math.floor(stacks) + if stacks < 0 then + stacks = 0 + end + if rule.enabled and stacks < 1 then + stacks = 1 + end + rule.stacks = stacks + return rule +end + +local function MTH_AB_EnsureConfig() + local store = MTH_AB_GetStore() + if store.enabled == nil then store.enabled = true end + if type(store.projectiles) ~= "table" then store.projectiles = {} end + if type(store.petFood) ~= "table" then store.petFood = {} end + if store.projectiles.enabled == nil then store.projectiles.enabled = true end + if store.petFood.enabled == nil then store.petFood.enabled = false end + if store.petFood.stacks == nil then store.petFood.stacks = 1 end + if store.petFood.scope == nil then store.petFood.scope = "current" end + store.petFood.stacks = math.floor(tonumber(store.petFood.stacks) or 1) + if store.petFood.stacks < 1 then store.petFood.stacks = 1 end + if store.petFood.scope ~= "current" and store.petFood.scope ~= "all" then + store.petFood.scope = "current" + end + if type(store.projectiles.arrows) ~= "table" then store.projectiles.arrows = {} end + if type(store.projectiles.bullets) ~= "table" then store.projectiles.bullets = {} end + if type(store.projectiles.arrows.rules) ~= "table" then store.projectiles.arrows.rules = {} end + if type(store.projectiles.bullets.rules) ~= "table" then store.projectiles.bullets.rules = {} end + + for i = 1, 3 do + store.projectiles.arrows.rules[i] = MTH_AB_NormalizeRule(store.projectiles.arrows.rules[i]) + store.projectiles.bullets.rules[i] = MTH_AB_NormalizeRule(store.projectiles.bullets.rules[i]) + end + + return store +end + +local function MTH_AB_SetPetFoodStacks(store, value) + if type(store) ~= "table" or type(store.petFood) ~= "table" then + return + end + local stacks = math.floor(tonumber(value) or 1) + if stacks < 1 then stacks = 1 end + store.petFood.stacks = stacks +end + +local function MTH_AB_TitleCase(text) + local source = tostring(text or "") + if source == "" then return "" end + return string.upper(string.sub(source, 1, 1)) .. string.lower(string.sub(source, 2)) +end + +local function MTH_AB_NormalizeFamilyToken(text) + local value = string.lower(tostring(text or "")) + value = string.gsub(value, "%b()", " ") + value = string.gsub(value, "[^%a]", "") + return value +end + +local function MTH_AB_SingularizeFamilyToken(token) + local value = tostring(token or "") + if string.len(value) <= 3 then + return value + end + if string.sub(value, -3) == "ves" and string.len(value) > 4 then + return string.sub(value, 1, -4) .. "f" + end + if string.sub(value, -3) == "ies" and string.len(value) > 4 then + return string.sub(value, 1, -4) .. "y" + end + if string.sub(value, -3) == "xes" and string.len(value) > 4 then + return string.sub(value, 1, -3) + end + if string.sub(value, -4) == "ches" and string.len(value) > 5 then + return string.sub(value, 1, -3) + end + if string.sub(value, -4) == "shes" and string.len(value) > 5 then + return string.sub(value, 1, -3) + end + if string.sub(value, -3) == "zes" and string.len(value) > 4 then + return string.sub(value, 1, -3) + end + if string.sub(value, -3) == "ses" then + return string.sub(value, 1, -3) + end + if string.sub(value, -1) == "s" then + return string.sub(value, 1, -2) + end + return value +end + +local function MTH_AB_FindFamilyDietRow(familyName) + if type(MTH_DS_Families) ~= "table" then + return nil + end + + local needle = MTH_AB_NormalizeFamilyToken(familyName) + if needle == "" then + return nil + end + + local needleSingular = MTH_AB_SingularizeFamilyToken(needle) + for key, row in pairs(MTH_DS_Families) do + local candidate = MTH_AB_NormalizeFamilyToken(key) + if candidate ~= "" then + if candidate == needle or candidate == needleSingular then + return row + end + local candidateSingular = MTH_AB_SingularizeFamilyToken(candidate) + if candidateSingular == needle or candidateSingular == needleSingular then + return row + end + end + end + + return nil +end + +local function MTH_AB_GetPetDietText(familyName) + local family = tostring(familyName or "") + if family == "" then + return "unknown diet" + end + local row = MTH_AB_FindFamilyDietRow(family) + if type(row) ~= "table" or type(row.food) ~= "table" then + return "unknown diet" + end + local diets = {} + for i = 1, table.getn(row.food) do + local diet = tostring(row.food[i] or "") + if diet ~= "" then + table.insert(diets, MTH_AB_TitleCase(diet)) + end + end + if table.getn(diets) == 0 then + return "unknown diet" + end + return table.concat(diets, ", ") +end + +local function MTH_AB_GetPetFoodEntries() + if type(MTH_PETS_RefreshCurrentPet) == "function" then + pcall(MTH_PETS_RefreshCurrentPet) + end + + local entries = {} + local defaultIcon = "Interface\\Icons\\INV_Misc_QuestionMark" + local function buildEntryFromRow(row) + if type(row) ~= "table" then + return nil + end + local name = tostring(row.name or "") + if name == "" then + return nil + end + local family = tostring(row.family or "") + local icon = tostring((type(row.stableInfo) == "table" and row.stableInfo.icon) or row.icon or "") + if icon == "" and type(row.stableRaw) == "table" then + icon = tostring(row.stableRaw[1] or "") + end + if icon == "" then icon = defaultIcon end + return { + icon = icon, + name = name, + family = family, + diets = MTH_AB_GetPetDietText(family), + } + end + + local function setSlot(slot, data) + entries[slot + 1] = data + end + + for slot = 0, 4 do + setSlot(slot, { + icon = defaultIcon, + name = "", + diets = "", + placeholder = (slot == 0) and "NO CURRENT PET !" or "Free slot", + }) + end + + local petsRoot = type(MTH_PETS_GetRootStore) == "function" and MTH_PETS_GetRootStore() or nil + local petStore = petsRoot and petsRoot.petStore or nil + local activeById = petStore and petStore.activeById or nil + local stableSlotIndex = petStore and petStore.stableSlotIndex or nil + local currentInfo = type(MTH_GetCurrentPetInfo) == "function" and MTH_GetCurrentPetInfo() or nil + if currentInfo and currentInfo.liveExists then + local currentName = tostring(currentInfo.name or "") + if currentName ~= "" then + local currentFamily = tostring(currentInfo.family or "") + local currentIcon = defaultIcon + local currentId = currentInfo.id + if currentId and type(activeById) == "table" and type(activeById[currentId]) == "table" then + local currentRow = activeById[currentId] + local iconFromRow = tostring((type(currentRow.stableInfo) == "table" and currentRow.stableInfo.icon) or currentRow.icon or "") + if iconFromRow == "" and type(currentRow.stableRaw) == "table" then + iconFromRow = tostring(currentRow.stableRaw[1] or "") + end + if iconFromRow ~= "" then + currentIcon = iconFromRow + end + end + setSlot(0, { + icon = currentIcon, + name = currentName, + family = currentFamily, + diets = MTH_AB_GetPetDietText(currentFamily), + }) + end + end + + for slot = 1, 4 do + local petId = nil + if type(stableSlotIndex) == "table" then + petId = stableSlotIndex[slot] + end + local row = (petId and type(activeById) == "table") and activeById[petId] or nil + if type(row) ~= "table" and type(activeById) == "table" then + for _, candidate in pairs(activeById) do + if type(candidate) == "table" and tonumber(candidate.stableSlot) == slot then + row = candidate + break + end + end + end + local entry = buildEntryFromRow(row) + if entry then + setSlot(slot, entry) + end + end + + return entries +end + +local function MTH_AB_RefreshPetFoodListUI() + local lines = MTH_AB_STATE.controls.petFoodPetLines or {} + if table.getn(lines) == 0 then + return + end + + local entries = MTH_AB_GetPetFoodEntries() + for i = 1, table.getn(lines) do + local line = lines[i] + local entry = entries[i] + if line and line.text and line.icon then + if entry then + line.icon:Show() + line.icon:SetTexture(tostring(entry.icon or "Interface\\Icons\\INV_Misc_QuestionMark")) + line.text:Show() + if tostring(entry.name or "") ~= "" then + line.text:SetText(tostring(entry.name) .. " likes " .. tostring(entry.diets or "unknown diet")) + else + line.text:SetText(tostring(entry.placeholder or "")) + end + else + line.icon:Hide() + line.text:SetText("") + end + end + end +end + +local function MTH_AB_GetBuyableItems(subtype) + if type(MTH_AutoBuy_GetProjectileItems) == "function" then + local result = MTH_AutoBuy_GetProjectileItems(subtype) + if type(result) == "table" then + return result + end + end + + local wanted = string.lower(tostring(subtype or "")) + local result = {} + local source = MTH_DS_AmmoItems or {} + for itemId, row in pairs(source) do + if type(row) == "table" and string.lower(tostring(row.subtype or "")) == wanted and type(row.vendors) == "table" then + for _ in pairs(row.vendors) do + table.insert(result, { + itemId = tonumber(itemId) or itemId, + name = tostring(row.name or ("Item " .. tostring(itemId))), + level = tonumber(row.level) or 0, + reqlevel = tonumber(row.reqlevel) or 0, + }) + break + end + end + end + + table.sort(result, function(a, b) + if (a.level or 0) ~= (b.level or 0) then + return (a.level or 0) < (b.level or 0) + end + if tostring(a.name or "") ~= tostring(b.name or "") then + return tostring(a.name or "") < tostring(b.name or "") + end + return tonumber(a.itemId or 0) < tonumber(b.itemId or 0) + end) + + return result +end + +local function MTH_AB_GetItemLabel(item) + if type(item) ~= "table" then return "Unknown" end + return tostring(item.name or ("Item " .. tostring(item.itemId or ""))) +end + +local function MTH_AB_DropdownSetText(frame, text) + if UIDropDownMenu_SetText then + UIDropDownMenu_SetText(tostring(text or ""), frame) + return + end + if not frame then return end + local textRegion = getglobal(frame:GetName() .. "Text") + if textRegion and textRegion.SetText then + textRegion:SetText(tostring(text or "")) + end +end + +local function MTH_AB_FindItemById(list, itemId) + itemId = tonumber(itemId) + if not itemId or type(list) ~= "table" then return nil end + for i = 1, table.getn(list) do + if tonumber(list[i].itemId) == itemId then + return list[i] + end + end + return nil +end + +local function MTH_AB_GetRule(store, subtype, index) + local bucket = subtype == "arrow" and store.projectiles.arrows or store.projectiles.bullets + if type(bucket.rules[index]) ~= "table" then + bucket.rules[index] = MTH_AB_NormalizeRule(nil) + end + bucket.rules[index] = MTH_AB_NormalizeRule(bucket.rules[index]) + return bucket.rules[index] +end + +local function MTH_AB_SetRuleEnabled(store, subtype, index, enabled) + local rule = MTH_AB_GetRule(store, subtype, index) + rule.enabled = enabled and true or false + if not rule.enabled then + rule.itemId = nil + rule.stacks = 0 + elseif rule.stacks < 1 then + rule.stacks = 1 + end +end + +local function MTH_AB_SetRuleItem(store, subtype, index, itemId) + local rule = MTH_AB_GetRule(store, subtype, index) + rule.itemId = itemId and (tonumber(itemId) or itemId) or nil +end + +local function MTH_AB_SetRuleStacks(store, subtype, index, value) + local rule = MTH_AB_GetRule(store, subtype, index) + local stacks = math.floor(tonumber(value) or 0) + if stacks < 0 then stacks = 0 end + if rule.enabled and stacks < 1 then stacks = 1 end + rule.stacks = stacks +end + +local function MTH_AB_RefreshRowUI(row, store, moduleEnabled, projectilesEnabled) + local rule = MTH_AB_GetRule(store, row.subtype, row.index) + local rowEnabled = moduleEnabled and projectilesEnabled and rule.enabled + local rowInteractive = moduleEnabled and projectilesEnabled + local muted = rowInteractive and 1 or 0.5 + + MTH_AB_SetCheckedNoClick(row.check, rule.enabled) + if rowInteractive then + if row.check.Enable then row.check:Enable() end + else + if row.check.Disable then row.check:Disable() end + end + + if UIDropDownMenu_EnableDropDown and UIDropDownMenu_DisableDropDown then + if rowEnabled then + UIDropDownMenu_EnableDropDown(row.dropdown) + else + UIDropDownMenu_DisableDropDown(row.dropdown) + end + end + + local selectedItem = MTH_AB_FindItemById(row.items, rule.itemId) + if selectedItem then + MTH_AB_DropdownSetText(row.dropdown, MTH_AB_GetItemLabel(selectedItem)) + else + MTH_AB_DropdownSetText(row.dropdown, "Select item...") + end + + if row.qty then + if rowEnabled then + if row.qty.Enable then row.qty:Enable() end + if row.qty.EnableMouse then row.qty:EnableMouse(true) end + if row.qty.SetTextColor then row.qty:SetTextColor(1, 1, 1) end + else + if row.qty.Disable then row.qty:Disable() end + if row.qty.EnableMouse then row.qty:EnableMouse(false) end + if row.qty.ClearFocus then row.qty:ClearFocus() end + if row.qty.SetTextColor then row.qty:SetTextColor(0.6, 0.6, 0.6) end + end + row.qty:SetText(tostring(rule.stacks or 0)) + end + + if row.label and row.label.SetTextColor then + row.label:SetTextColor(muted, muted, muted) + end + if row.suffix and row.suffix.SetTextColor then + row.suffix:SetTextColor(muted, muted, muted) + end +end + +local function MTH_AB_RefreshOptionsUI() + if not MTH_AB_STATE.built then return end + local store = MTH_AB_EnsureConfig() + local moduleEnabled = true + if MTH and MTH.IsModuleEnabled then + moduleEnabled = MTH:IsModuleEnabled("autobuy", true) and true or false + end + + MTH_AB_SetCheckedNoClick(MTH_AB_STATE.controls.moduleEnabled, moduleEnabled) + MTH_AB_SetCheckedNoClick(MTH_AB_STATE.controls.projectilesEnabled, store.projectiles.enabled and true or false) + + if MTH_AB_STATE.controls.projectilesEnabled then + local proj = MTH_AB_STATE.controls.projectilesEnabled + if moduleEnabled then + if proj.Enable then + proj:Enable() + end + else + if proj.Disable then + proj:Disable() + end + end + end + + if MTH_AB_STATE.controls.petFoodEnabled then + local pf = MTH_AB_STATE.controls.petFoodEnabled + MTH_AB_SetCheckedNoClick(pf, store.petFood.enabled and true or false) + if moduleEnabled then + if pf.Enable then pf:Enable() end + else + if pf.Disable then pf:Disable() end + end + end + + local petFoodInteractive = moduleEnabled and store.petFood.enabled + if MTH_AB_STATE.controls.petFoodQty then + local qty = MTH_AB_STATE.controls.petFoodQty + qty:SetText(tostring(store.petFood.stacks or 1)) + if petFoodInteractive then + if qty.Enable then qty:Enable() end + if qty.EnableMouse then qty:EnableMouse(true) end + if qty.SetTextColor then qty:SetTextColor(1, 1, 1) end + else + if qty.Disable then qty:Disable() end + if qty.EnableMouse then qty:EnableMouse(false) end + if qty.ClearFocus then qty:ClearFocus() end + if qty.SetTextColor then qty:SetTextColor(0.6, 0.6, 0.6) end + end + end + + if MTH_AB_STATE.controls.petFoodScopeCurrent then + MTH_AB_SetCheckedNoClick(MTH_AB_STATE.controls.petFoodScopeCurrent, store.petFood.scope == "current") + if petFoodInteractive then + if MTH_AB_STATE.controls.petFoodScopeCurrent.Enable then MTH_AB_STATE.controls.petFoodScopeCurrent:Enable() end + else + if MTH_AB_STATE.controls.petFoodScopeCurrent.Disable then MTH_AB_STATE.controls.petFoodScopeCurrent:Disable() end + end + end + + if MTH_AB_STATE.controls.petFoodScopeAll then + MTH_AB_SetCheckedNoClick(MTH_AB_STATE.controls.petFoodScopeAll, store.petFood.scope == "all") + if petFoodInteractive then + if MTH_AB_STATE.controls.petFoodScopeAll.Enable then MTH_AB_STATE.controls.petFoodScopeAll:Enable() end + else + if MTH_AB_STATE.controls.petFoodScopeAll.Disable then MTH_AB_STATE.controls.petFoodScopeAll:Disable() end + end + end + + MTH_AB_RefreshPetFoodListUI() + + for i = 1, 3 do + MTH_AB_RefreshRowUI(MTH_AB_STATE.controls.rows.arrow[i], store, moduleEnabled, store.projectiles.enabled) + MTH_AB_RefreshRowUI(MTH_AB_STATE.controls.rows.bullet[i], store, moduleEnabled, store.projectiles.enabled) + end +end + +local function MTH_AB_CreateRow(container, subtype, index, xBase, yOffset) + local row = { + subtype = subtype, + index = index, + items = MTH_AB_GetBuyableItems(subtype), + } + local nameBase = "MetaHuntOptionsAutoBuy_" .. subtype .. "_" .. tostring(index) + + row.check = MTH_CreateCheckbox(container, nameBase .. "Check", "", yOffset, xBase + MTH_AB_LAYOUT.ROW_CHECK_X) + row.label = container:CreateFontString(nameBase .. "Label", "ARTWORK", "GameFontNormalSmall") + row.label:SetPoint("TOPLEFT", container, "TOPLEFT", xBase + MTH_AB_LAYOUT.ROW_CHECK_LABEL_X, yOffset - 5) + row.label:SetText("On?") + + row.dropdown = CreateFrame("Frame", nameBase .. "Drop", container, "UIDropDownMenuTemplate") + row.dropdown:ClearAllPoints() + row.dropdown:SetPoint("TOPLEFT", container, "TOPLEFT", xBase + MTH_AB_LAYOUT.ROW_DROPDOWN_X, yOffset + 3) + if UIDropDownMenu_SetWidth then + UIDropDownMenu_SetWidth(MTH_AB_LAYOUT.ROW_DROPDOWN_WIDTH, row.dropdown) + end + if UIDropDownMenu_JustifyText then + UIDropDownMenu_JustifyText("LEFT", row.dropdown) + end + + if UIDropDownMenu_Initialize then + UIDropDownMenu_Initialize(row.dropdown, function() + local infoFactory = UIDropDownMenu_CreateInfo + local info = type(infoFactory) == "function" and infoFactory() or {} + info.text = "Select item..." + info.func = function() + local store = MTH_AB_EnsureConfig() + MTH_AB_SetRuleItem(store, subtype, index, nil) + MTH_AB_RefreshOptionsUI() + end + if UIDropDownMenu_AddButton then UIDropDownMenu_AddButton(info) end + + for j = 1, table.getn(row.items) do + local item = row.items[j] + local label = MTH_AB_GetItemLabel(item) + local itemId = item.itemId + local entry = type(infoFactory) == "function" and infoFactory() or {} + entry.text = label + entry.func = function() + local store = MTH_AB_EnsureConfig() + MTH_AB_SetRuleItem(store, subtype, index, itemId) + MTH_AB_RefreshOptionsUI() + end + if UIDropDownMenu_AddButton then UIDropDownMenu_AddButton(entry) end + end + end) + end + + row.qty = CreateFrame("EditBox", nameBase .. "Qty", container, "InputBoxTemplate") + local dropButton = getglobal(nameBase .. "DropButton") + if dropButton then + row.qty:SetPoint("LEFT", dropButton, "RIGHT", MTH_AB_LAYOUT.ROW_QTY_GAP, -1) + else + row.qty:SetPoint("LEFT", row.dropdown, "RIGHT", MTH_AB_LAYOUT.ROW_QTY_GAP + 6, -3) + end + row.qty:SetWidth(40) + row.qty:SetHeight(20) + row.qty:SetNumeric(true) + row.qty:SetAutoFocus(false) + + row.suffix = container:CreateFontString(nameBase .. "Suffix", "ARTWORK", "GameFontNormalSmall") + row.suffix:SetPoint("LEFT", row.qty, "RIGHT", MTH_AB_LAYOUT.ROW_SUFFIX_GAP, 0) + row.suffix:SetText("Stacks") + + if row.check then + row.check:SetScript("OnClick", function(self) + self = self or this + if not self or MTH_AB_STATE.syncing then return end + local store = MTH_AB_EnsureConfig() + MTH_AB_SetRuleEnabled(store, subtype, index, self:GetChecked() and true or false) + MTH_AB_RefreshOptionsUI() + end) + end + + row.qty:SetScript("OnEnterPressed", function(self) + self = self or this + if not self then return end + local store = MTH_AB_EnsureConfig() + MTH_AB_SetRuleStacks(store, subtype, index, self:GetText()) + MTH_AB_RefreshOptionsUI() + self:ClearFocus() + end) + row.qty:SetScript("OnEditFocusLost", function(self) + self = self or this + if not self then return end + local store = MTH_AB_EnsureConfig() + MTH_AB_SetRuleStacks(store, subtype, index, self:GetText()) + MTH_AB_RefreshOptionsUI() + end) + + return row +end + +local function MTH_AB_BuildUI(container) + if MTH_AB_STATE.built and MTH_AB_STATE.container == container then + return + end + + MTH_ClearContainer(container) + MTH_AB_STATE.container = container + MTH_AB_STATE.built = true + + local controls = MTH_AB_STATE.controls + + controls.title = container:CreateFontString("MetaHuntOptionsAutoBuyTitle", "ARTWORK", "GameFontHighlight") + if controls.title then + if controls.title.SetPoint then controls.title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -10) end + if controls.title.SetText then controls.title:SetText("Auto Buy") end + end + + controls.body = container:CreateFontString("MetaHuntOptionsAutoBuyBody", "ARTWORK", "GameFontNormalSmall") + if controls.body then + if controls.body.SetPoint then controls.body:SetPoint("TOPLEFT", container, "TOPLEFT", 176, -30) end + if controls.body.SetWidth then controls.body:SetWidth(400) end + if controls.body.SetJustifyH then controls.body:SetJustifyH("LEFT") end + if controls.body.SetTextColor then controls.body:SetTextColor(1, 1, 1) end + if controls.body.SetText then + controls.body:SetText("Configure automatic buying rules that triggers when you open a vendor.\nThe quantity refers to how many STACKS of the item you want to have in your bags after your purchase") + end + end + + controls.moduleEnabled = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyModuleEnabled", "Enable Auto Buy module", -30) + if controls.moduleEnabled then + controls.moduleEnabled:SetScript("OnClick", function(self) + self = self or this + if not self or MTH_AB_STATE.syncing then return end + if MTH and MTH.SetModuleEnabled then + local ok, err = MTH:SetModuleEnabled("autobuy", self:GetChecked() and true or false) + if not ok and MTH and MTH.Print then + MTH:Print("Failed to change Auto Buy state: " .. tostring(err), "error") + end + end + MTH_AB_RefreshOptionsUI() + end) + end + + controls.projectilesEnabled = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyProjectilesEnabled", "Enable for Projectiles", -74) + if controls.projectilesEnabled then + controls.projectilesEnabled:SetScript("OnClick", function(self) + self = self or this + if not self or MTH_AB_STATE.syncing then return end + local store = MTH_AB_EnsureConfig() + store.projectiles.enabled = self:GetChecked() and true or false + MTH_AB_RefreshOptionsUI() + end) + end + + controls.sectionArrowTitle = container:CreateFontString("MetaHuntOptionsAutoBuyArrowsTitle", "ARTWORK", "GameFontHighlight") + if controls.sectionArrowTitle then + if controls.sectionArrowTitle.SetPoint then + controls.sectionArrowTitle:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X, MTH_AB_LAYOUT.SECTION_TITLE_Y) + end + if controls.sectionArrowTitle.SetText then controls.sectionArrowTitle:SetText("Arrows") end + end + + controls.sectionBulletTitle = container:CreateFontString("MetaHuntOptionsAutoBuyBulletsTitle", "ARTWORK", "GameFontHighlight") + if controls.sectionBulletTitle then + if controls.sectionBulletTitle.SetPoint then + controls.sectionBulletTitle:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.RIGHT_SECTION_X, MTH_AB_LAYOUT.SECTION_TITLE_Y) + end + if controls.sectionBulletTitle.SetText then controls.sectionBulletTitle:SetText("Bullets") end + end + + for i = 1, 3 do + local rowY = MTH_AB_LAYOUT.SECTION_ROWS_Y0 - ((i - 1) * MTH_AB_LAYOUT.ROW_STEP) + controls.rows.arrow[i] = MTH_AB_CreateRow(container, "arrow", i, MTH_AB_LAYOUT.LEFT_SECTION_X, rowY) + controls.rows.bullet[i] = MTH_AB_CreateRow(container, "bullet", i, MTH_AB_LAYOUT.RIGHT_SECTION_X, rowY) + end + + controls.petFoodTitle = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodTitle", "ARTWORK", "GameFontHighlight") + if controls.petFoodTitle then + controls.petFoodTitle:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X, -300) + controls.petFoodTitle:SetText("Pet Food") + end + + controls.petFoodEnabled = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyPetFoodEnabled", "Enable for Pet Food", -322, MTH_AB_LAYOUT.LEFT_SECTION_X) + if controls.petFoodEnabled then + controls.petFoodEnabled:SetScript("OnClick", function(self) + self = self or this + if not self or MTH_AB_STATE.syncing then return end + local store = MTH_AB_EnsureConfig() + store.petFood.enabled = self:GetChecked() and true or false + MTH_AB_RefreshOptionsUI() + end) + end + + controls.petFoodQtyLabel = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodQtyLabel", "ARTWORK", "GameFontNormalSmall") + if controls.petFoodQtyLabel then + controls.petFoodQtyLabel:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X + 28, -350) + controls.petFoodQtyLabel:SetText("Quantity :") + end + + controls.petFoodQty = CreateFrame("EditBox", "MetaHuntOptionsAutoBuyPetFoodQty", container, "InputBoxTemplate") + if controls.petFoodQty then + controls.petFoodQty:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X + 95, -347) + controls.petFoodQty:SetWidth(40) + controls.petFoodQty:SetHeight(20) + controls.petFoodQty:SetNumeric(true) + controls.petFoodQty:SetAutoFocus(false) + controls.petFoodQty:SetScript("OnEnterPressed", function(self) + self = self or this + if not self then return end + local store = MTH_AB_EnsureConfig() + MTH_AB_SetPetFoodStacks(store, self:GetText()) + MTH_AB_RefreshOptionsUI() + self:ClearFocus() + end) + controls.petFoodQty:SetScript("OnEditFocusLost", function(self) + self = self or this + if not self then return end + local store = MTH_AB_EnsureConfig() + MTH_AB_SetPetFoodStacks(store, self:GetText()) + MTH_AB_RefreshOptionsUI() + end) + end + + controls.petFoodQtySuffix = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodQtySuffix", "ARTWORK", "GameFontNormalSmall") + if controls.petFoodQtySuffix then + controls.petFoodQtySuffix:SetPoint("LEFT", controls.petFoodQty, "RIGHT", 8, 0) + controls.petFoodQtySuffix:SetText("stacks") + end + + controls.petFoodScopeCurrent = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyPetFoodScopeCurrent", "Buy only for my current pet", -376, MTH_AB_LAYOUT.LEFT_SECTION_X + 28) + if controls.petFoodScopeCurrent then + controls.petFoodScopeCurrent:SetScript("OnClick", function(self) + self = self or this + if not self or MTH_AB_STATE.syncing then return end + local store = MTH_AB_EnsureConfig() + store.petFood.scope = "current" + MTH_AB_RefreshOptionsUI() + end) + end + + controls.petFoodScopeAll = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyPetFoodScopeAll", "Buy for all my pets", -402, MTH_AB_LAYOUT.LEFT_SECTION_X + 28) + if controls.petFoodScopeAll then + controls.petFoodScopeAll:SetScript("OnClick", function(self) + self = self or this + if not self or MTH_AB_STATE.syncing then return end + local store = MTH_AB_EnsureConfig() + store.petFood.scope = "all" + MTH_AB_RefreshOptionsUI() + end) + end + + controls.petFoodPetLines = {} + for i = 1, 5 do + local rowY = -434 - ((i - 1) * 22) + local icon = container:CreateTexture("MetaHuntOptionsAutoBuyPetFoodPetIcon" .. tostring(i), "ARTWORK") + icon:SetWidth(16) + icon:SetHeight(16) + icon:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X + 28, rowY) + icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") + local text = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodPetText" .. tostring(i), "ARTWORK", "GameFontNormalSmall") + text:SetPoint("LEFT", icon, "RIGHT", 6, 0) + text:SetJustifyH("LEFT") + text:SetTextColor(1, 1, 1) + text:SetText("") + controls.petFoodPetLines[i] = { icon = icon, text = text } + end +end + +function MTH_SetupAutoBuyOptions() + if not MTH_AB_READY then return end + local container = MTH_GetFrame("MetaHuntOptionsAutoBuy") + if not container then return end + + if CloseDropDownMenus then + CloseDropDownMenus() + end + + MTH_AB_BuildUI(container) + MTH_AB_RefreshOptionsUI() +end diff --git a/api/options-chronometer.lua b/api/options-chronometer.lua new file mode 100644 index 0000000..37913c8 --- /dev/null +++ b/api/options-chronometer.lua @@ -0,0 +1,597 @@ +local function MTH_CHRON_GetGlobal(name) + if type(getglobal) == "function" then + return getglobal(name) + end + if _G then + return _G[name] + end + return nil +end + +local function MTH_CHRON_IsReady() + local required = { + "MTH_GetFrame", + "MTH_ClearContainer", + "MTH_CreateCheckbox", + "MTH_CreateSlider", + "MTH_CreateActionButton", + } + + for i = 1, table.getn(required) do + if MTH_CHRON_GetGlobal(required[i]) == nil then + return false + end + end + return true +end + +local MTH_CHRON_STATE = { + controls = {}, +} + +local MTH_CHRON_DEFAULTS = { + growup = false, + reverse = false, + fadeonkill = true, + fadeonfade = true, + barposition = {}, + ghost = 0, + selfbars = true, + barwidth = 220, + barheight = 16, + spacing = 0, + barscale = 1, + iconposition = "LEFT", + textsize = 10, + textcolor = "white", + bgcolor = "teal", + barcolor = "gray", + bordercolor = "black", + bordertex = "None", + bgalpha = 0.5, + text = "$t", + onlyself = false, + disabledSpells = { + COMMON = { + ["Ephemeral Power"] = {}, + ["Mind Quickening"] = {}, + ["Unstable Power"] = {}, + }, + RACIAL = {}, + HUNTER = {}, + }, +} + +local function MTH_CHRON_SeedDefaultCommonDisabled(commonBucket) + if type(commonBucket) ~= "table" then + return + end + local names = { + "Ephemeral Power", + "Mind Quickening", + "Unstable Power", + } + for i = 1, table.getn(names) do + local baseName = names[i] + if commonBucket[baseName] == nil then + commonBucket[baseName] = {} + end + if MTH and MTH.LocalizeSpell then + local localized = MTH:LocalizeSpell(baseName) + if localized and localized ~= "" and commonBucket[localized] == nil then + commonBucket[localized] = {} + end + end + end +end + +local function MTH_CHRON_CopyTable(source) + local target = {} + for key, value in pairs(source) do + if type(value) == "table" then + target[key] = MTH_CHRON_CopyTable(value) + else + target[key] = value + end + end + return target +end + +local function MTH_CHRON_GetProfile() + if not (MTH and MTH.GetModuleSavedVariables) then + return MTH_CHRON_CopyTable(MTH_CHRON_DEFAULTS) + end + + local store = MTH:GetModuleSavedVariables("chronometer") + if type(store.profile) ~= "table" then + store.profile = {} + end + local profile = store.profile + + for key, value in pairs(MTH_CHRON_DEFAULTS) do + if profile[key] == nil then + if type(value) == "table" then + profile[key] = MTH_CHRON_CopyTable(value) + else + profile[key] = value + end + end + end + + if type(profile.disabledSpells) ~= "table" then + profile.disabledSpells = {} + end + if type(profile.disabledSpells.COMMON) ~= "table" then + profile.disabledSpells.COMMON = {} + end + MTH_CHRON_SeedDefaultCommonDisabled(profile.disabledSpells.COMMON) + if type(profile.disabledSpells.RACIAL) ~= "table" then + profile.disabledSpells.RACIAL = {} + end + + local _, class = UnitClass("player") + class = class or "HUNTER" + if type(profile.disabledSpells[class]) ~= "table" then + profile.disabledSpells[class] = {} + end + + return profile +end + +local function MTH_CHRON_GetTimerDefinitions() + local engine = MTH_ChronometerHunter + if engine and engine.timers and (engine.timers[engine.SPELL] or engine.timers[engine.EVENT]) then + return engine.timers, engine.SPELL, engine.EVENT + end + + if not engine then + return nil, 1, 2 + end + + local fake = { + SPELL = 1, + EVENT = 2, + groups = {}, + timers = {}, + } + function fake:AddGroup(id, forall, color) + if color then + self.groups[id] = { fa = forall, cr = color } + else + self.groups[id] = { fa = forall } + end + end + function fake:AddTimer(kind, name, duration, targeted, isgain, selforselect, extra) + if type(self.timers[kind]) ~= "table" then + self.timers[kind] = {} + end + if type(extra) ~= "table" then + extra = {} + end + self.timers[kind][name] = { + d = duration, + k = { t = targeted, g = isgain, s = selforselect }, + x = extra, + } + end + + if type(engine.dataSetup) == "table" then + for _, setupFunc in pairs(engine.dataSetup) do + if type(setupFunc) == "function" then + setupFunc(fake) + end + end + end + + return fake.timers, fake.SPELL, fake.EVENT +end + +local function MTH_CHRON_SortKeys(list) + table.sort(list, function(a, b) + return tostring(a) < tostring(b) + end) +end + +local function MTH_CHRON_BuildTimerLists() + local timers, spellKind, eventKind = MTH_CHRON_GetTimerDefinitions() + local classSpells, classEvents, racial = {}, {}, {} + if type(timers) ~= "table" then + return classSpells, classEvents, racial + end + + local function classify(timerRecord) + if timerRecord and timerRecord.x and timerRecord.x.cl == "COMMON" then + return "COMMON" + elseif timerRecord and timerRecord.x and timerRecord.x.cl == "RACIAL" then + return "RACIAL" + end + local _, class = UnitClass("player") + return class or "HUNTER" + end + + for timerName, timerRecord in pairs(timers[spellKind] or {}) do + local bucket = classify(timerRecord) + if bucket == "RACIAL" then + table.insert(racial, timerName) + elseif bucket ~= "COMMON" then + table.insert(classSpells, timerName) + end + end + + for timerName, timerRecord in pairs(timers[eventKind] or {}) do + local bucket = classify(timerRecord) + if bucket == "RACIAL" then + table.insert(racial, timerName) + elseif bucket ~= "COMMON" then + table.insert(classEvents, timerName) + end + end + + MTH_CHRON_SortKeys(classSpells) + MTH_CHRON_SortKeys(classEvents) + MTH_CHRON_SortKeys(racial) + return classSpells, classEvents, racial +end + +local function MTH_CHRON_ApplyLiveProfile(profile) + if not profile then + return + end + local engine = MTH_ChronometerHunter + if not (engine and engine._mth_enabled) then + return + end + + engine.profile = profile + if engine.SetCandyBarGroupGrowth then + engine:SetCandyBarGroupGrowth("MTHChronometer", profile.growup and true or false) + end + if engine.SetCandyBarGroupVerticalSpacing then + engine:SetCandyBarGroupVerticalSpacing("MTHChronometer", profile.spacing or 0) + end + + if type(engine.bars) == "table" then + for i = 1, 20 do + local bar = engine.bars[i] + if bar and bar.id then + if profile.barscale and engine.SetCandyBarScale then + engine:SetCandyBarScale(bar.id, profile.barscale) + end + if profile.barwidth and engine.SetCandyBarWidth then + engine:SetCandyBarWidth(bar.id, profile.barwidth) + end + if profile.barheight and engine.SetCandyBarHeight then + engine:SetCandyBarHeight(bar.id, profile.barheight) + end + if profile.iconposition and engine.SetCandyBarIconPosition then + engine:SetCandyBarIconPosition(bar.id, profile.iconposition) + end + if profile.textsize and engine.SetCandyBarFontSize then + engine:SetCandyBarFontSize(bar.id, profile.textsize) + end + if profile.text and engine.SetCandyBarText then + local target = bar.target or "none" + local text = target == "none" and (bar.name or "") or profile.text + text = string.gsub(tostring(text or ""), "$t", tostring(target)) + text = string.gsub(tostring(text or ""), "$s", tostring(bar.name or "")) + engine:SetCandyBarText(bar.id, text) + end + if engine.SetCandyBarReversed then + engine:SetCandyBarReversed(bar.id, profile.reverse and true or false) + end + end + end + end +end + +local function MTH_CHRON_MakeTitle(parent, name, text, x, y) + local label = parent:CreateFontString(name, "ARTWORK", "GameFontHighlight") + label:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y) + label:SetText(text) + return label +end + +local function MTH_CHRON_MakeSmall(parent, name, text, x, y) + local label = parent:CreateFontString(name, "ARTWORK", "GameFontNormalSmall") + label:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y) + label:SetText(text) + return label +end + + +local function MTH_CHRON_MakeInput(parent, name, x, y, width, height, value, onAccept) + local edit = CreateFrame("EditBox", name, parent, "InputBoxTemplate") + if not edit then + return nil + end + edit:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y) + edit:SetWidth(width or 200) + edit:SetHeight(height or 20) + edit:SetAutoFocus(false) + edit:SetText(tostring(value or "")) + edit:SetScript("OnEnterPressed", function(self) + self = self or this + if not self then return end + if type(onAccept) == "function" then + onAccept(self:GetText() or "") + end + self:ClearFocus() + end) + return edit +end + +local function MTH_CHRON_NormalizeSection(tabKey) + if not tabKey and _G then + tabKey = _G.MTH_CHRON_OPTIONS_SECTION + end + if tabKey == "ChronometerBar" then + return "bar" + elseif tabKey == "ChronometerClassSpells" then + return "classspells" + elseif tabKey == "ChronometerClassEvents" then + return "classevents" + elseif tabKey == "ChronometerRacial" then + return "racial" + end + return "general" +end + +local function MTH_CHRON_RenderTimerSection(container, profile, title, items, bucket) + local timerX = 20 + local timerY = -84 + MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerTimersHeader", title, timerX, timerY) + timerY = timerY - 24 + + if table.getn(items) == 0 then + MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerSectionEmpty", "(none)", timerX + 8, timerY) + return + end + + for i = 1, table.getn(items) do + if timerY < -560 then + MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerOverflow", "...more", timerX + 8, timerY) + break + end + local timerName = items[i] + local controlName = "MetaHuntOptionsChronometerTimer" .. tostring(bucket) .. tostring(i) + local check = MTH_CreateCheckbox(container, controlName, tostring(timerName), timerY, timerX) + if check then + check:SetChecked(profile.disabledSpells[bucket][timerName] == nil and true or false) + check:SetScript("OnClick", function(self) + self = self or this + if not self then return end + if self:GetChecked() then + profile.disabledSpells[bucket][timerName] = nil + else + profile.disabledSpells[bucket][timerName] = {} + end + end) + end + timerY = timerY - 20 + end +end + +function MTH_RefreshChronometerOptions(tabKey) + if not MTH_CHRON_IsReady() then return end + MTH_SetupChronometerOptions(tabKey) +end + +function MTH_SetupChronometerOptions(tabKey) + if not MTH_CHRON_IsReady() then + if MTH and MTH.Print then + MTH:Print("Chronometer options dependencies missing", "error") + end + return + end + local section = MTH_CHRON_NormalizeSection(tabKey) + local container = MTH_GetFrame("MetaHuntOptionsChronometer") + if not container then return end + + MTH_ClearContainer(container) + MTH_CHRON_STATE.controls = {} + + local profile = MTH_CHRON_GetProfile() + local _, class = UnitClass("player") + class = class or "HUNTER" + + MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerTitle", "Chronometer", 20, -10) + + local moduleCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerEnabled", "Enable Chronometer module", -54) + if moduleCheck then + local module = MTH and MTH:GetModule("chronometer") + moduleCheck:SetChecked(module and module.enabled and true or false) + moduleCheck:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local enabled = self:GetChecked() and true or false + if MTH and MTH.SetModuleEnabled then + MTH:SetModuleEnabled("chronometer", enabled) + end + end) + end + + if section == "general" then + MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerGeneralHeader", "General", 20, -84) + + local ghostSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerGhost", "Ghost Duration", 0, 30, 1, -112) + if ghostSlider then + ghostSlider:SetValue(tonumber(profile.ghost) or 0) + ghostSlider.onChange = function(value) + profile.ghost = tonumber(value) or 0 + MTH_CHRON_ApplyLiveProfile(profile) + end + end + + local killCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerFadeOnKill", "Fade on kill", -154) + if killCheck then + killCheck:SetChecked(profile.fadeonkill and true or false) + killCheck:SetScript("OnClick", function(self) + self = self or this + if not self then return end + profile.fadeonkill = self:GetChecked() and true or false + end) + end + + local fadeCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerFadeOnFade", "Fade on aura fade", -179) + if fadeCheck then + fadeCheck:SetChecked(profile.fadeonfade and true or false) + fadeCheck:SetScript("OnClick", function(self) + self = self or this + if not self then return end + profile.fadeonfade = self:GetChecked() and true or false + end) + end + + local selfCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerSelfBars", "Show self bars", -204) + if selfCheck then + selfCheck:SetChecked(profile.selfbars and true or false) + selfCheck:SetScript("OnClick", function(self) + self = self or this + if not self then return end + profile.selfbars = self:GetChecked() and true or false + end) + end + + local onlySelfCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerOnlySelf", "Only self target", -229) + if onlySelfCheck then + onlySelfCheck:SetChecked(profile.onlyself and true or false) + onlySelfCheck:SetScript("OnClick", function(self) + self = self or this + if not self then return end + profile.onlyself = self:GetChecked() and true or false + end) + end + elseif section == "bar" then + MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerBarHeader", "Bars", 20, -84) + + local runTestButton = MTH_CreateActionButton(container, "MetaHuntOptionsChronometerRunTest", "Run Test", 20, -112, 130, 22, function() + local engine = MTH_ChronometerHunter + if engine and engine.RunTest then + engine:RunTest() + end + end) + local anchorButton = MTH_CreateActionButton(container, "MetaHuntOptionsChronometerAnchor", "Toggle Anchor", 165, -112, 130, 22, function() + local engine = MTH_ChronometerHunter + if engine and engine.ToggleAnchor then + engine:ToggleAnchor() + end + end) + if runTestButton then runTestButton:Show() end + if anchorButton then anchorButton:Show() end + + local anchorX = tonumber(profile.barposition and profile.barposition.x) or 0 + local anchorY = tonumber(profile.barposition and profile.barposition.y) or 0 + MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerAnchorPos", "Anchor: X " .. tostring(anchorX) .. " Y " .. tostring(anchorY), 20, -142) + + local widthSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarWidth", "Bar Width", 80, 320, 1, -180) + if widthSlider then + widthSlider:SetValue(tonumber(profile.barwidth) or 220) + widthSlider.onChange = function(value) + profile.barwidth = tonumber(value) or 220 + MTH_CHRON_ApplyLiveProfile(profile) + end + end + + local scaleSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarScale", "Bar Scale", 0.5, 1.5, 0.1, -222) + if scaleSlider then + scaleSlider:SetValue(tonumber(profile.barscale) or 1) + scaleSlider.onChange = function(value) + profile.barscale = tonumber(value) or 1 + MTH_CHRON_ApplyLiveProfile(profile) + end + end + + local heightSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarHeight", "Bar Height", 8, 30, 1, -264) + if heightSlider then + heightSlider:SetValue(tonumber(profile.barheight) or 16) + heightSlider.onChange = function(value) + profile.barheight = tonumber(value) or 16 + MTH_CHRON_ApplyLiveProfile(profile) + end + end + + local spacingSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarSpacing", "Bar Spacing", 0, 15, 1, -306) + if spacingSlider then + spacingSlider:SetValue(tonumber(profile.spacing) or 0) + spacingSlider.onChange = function(value) + profile.spacing = tonumber(value) or 0 + MTH_CHRON_ApplyLiveProfile(profile) + end + end + + local textSizeSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerTextSize", "Text Size", 8, 20, 1, -348) + if textSizeSlider then + textSizeSlider:SetValue(tonumber(profile.textsize) or 10) + textSizeSlider.onChange = function(value) + profile.textsize = tonumber(value) or 10 + MTH_CHRON_ApplyLiveProfile(profile) + end + end + + MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerTextPatternLabel", "Bar Text ($s spell, $t target)", 20, -390) + MTH_CHRON_MakeInput(container, "MetaHuntOptionsChronometerTextPattern", 20, -408, 220, 20, profile.text or "$t", function(newText) + if not newText or newText == "" then + newText = "$t" + end + profile.text = tostring(newText) + MTH_CHRON_ApplyLiveProfile(profile) + end) + + local growCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerGrowUp", "Grow bars upward", -438) + if growCheck then + growCheck:SetChecked(profile.growup and true or false) + growCheck:SetScript("OnClick", function(self) + self = self or this + if not self then return end + profile.growup = self:GetChecked() and true or false + MTH_CHRON_ApplyLiveProfile(profile) + end) + end + + local reverseCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerReverse", "Reverse bars", -463) + if reverseCheck then + reverseCheck:SetChecked(profile.reverse and true or false) + reverseCheck:SetScript("OnClick", function(self) + self = self or this + if not self then return end + profile.reverse = self:GetChecked() and true or false + MTH_CHRON_ApplyLiveProfile(profile) + end) + end + + MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerIconPosLabel", "Icon Position", 20, -494) + local iconLeft = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerIconLeft", "Left", -514) + local iconRight = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerIconRight", "Right", -538) + if iconLeft and iconRight then + iconLeft:SetChecked(profile.iconposition ~= "RIGHT") + iconRight:SetChecked(profile.iconposition == "RIGHT") + iconLeft:SetScript("OnClick", function() + profile.iconposition = "LEFT" + iconLeft:SetChecked(true) + iconRight:SetChecked(nil) + MTH_CHRON_ApplyLiveProfile(profile) + end) + iconRight:SetScript("OnClick", function() + profile.iconposition = "RIGHT" + iconRight:SetChecked(true) + iconLeft:SetChecked(nil) + MTH_CHRON_ApplyLiveProfile(profile) + end) + end + + else + local classSpells, classEvents, racial = MTH_CHRON_BuildTimerLists() + if section == "classspells" then + MTH_CHRON_RenderTimerSection(container, profile, "Hunter Spells", classSpells, class) + elseif section == "classevents" then + MTH_CHRON_RenderTimerSection(container, profile, "Hunter Events", classEvents, class) + elseif section == "racial" then + MTH_CHRON_RenderTimerSection(container, profile, "Racial", racial, "RACIAL") + end + end +end + + if _G then + _G.MTH_SetupChronometerOptions = MTH_SetupChronometerOptions + _G.MTH_RefreshChronometerOptions = MTH_RefreshChronometerOptions + end diff --git a/api/options-credits.lua b/api/options-credits.lua new file mode 100644 index 0000000..f8c080d --- /dev/null +++ b/api/options-credits.lua @@ -0,0 +1,193 @@ +local MTH_CREDITS_READY = MTH_OptionsRequire and MTH_OptionsRequire("options-credits", { + "MTH_GetFrame", + "MTH_ClearContainer", +}) + +local MTH_CREDITS_FONT_DELTA = 2 + +local function MTH_CreditsBumpFont(fs) + if not fs or not fs.GetFont or not fs.SetFont then return end + local path, size, flags = fs:GetFont() + if path and size then + fs:SetFont(path, size + MTH_CREDITS_FONT_DELTA, flags) + end +end + +local function MTH_CreditsInsertLink(url) + local link = tostring(url or "") + if link == "" then return end + if type(MTH_InsertLinkToChat) == "function" and MTH_InsertLinkToChat(link) then + if MTH and MTH.Print then + MTH:Print("Link inserted in chat: " .. link) + end + return + end + if MTH and MTH.Print then + MTH:Print("Link: " .. link) + end +end + +local function MTH_CreditsClearFrame(frame) + if not frame then return end + local children = { frame:GetChildren() } + for i = 1, table.getn(children) do + if children[i] and children[i].Hide then + children[i]:Hide() + end + end + local regions = { frame:GetRegions() } + for i = 1, table.getn(regions) do + if regions[i] and regions[i].Hide then + regions[i]:Hide() + end + end +end + +local function MTH_CreditsCreateLink(parent, label, url, anchorTo, y) + local button = CreateFrame("Button", nil, parent) + if not button then return nil end + button:SetPoint("TOPLEFT", anchorTo, "BOTTOMLEFT", 0, y or -4) + button:SetHeight(18) + button:SetWidth(500) + local text = button:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + text:SetPoint("LEFT", button, "LEFT", 0, 0) + text:SetJustifyH("LEFT") + text:SetText(label) + text:SetTextColor(0.40, 0.75, 1.00) + MTH_CreditsBumpFont(text) + button:SetScript("OnClick", function() + MTH_CreditsInsertLink(url) + end) + button:SetScript("OnEnter", function() + text:SetTextColor(0.60, 0.90, 1.00) + end) + button:SetScript("OnLeave", function() + text:SetTextColor(0.40, 0.75, 1.00) + end) + return button +end + +local function MTH_CreditsCreateText(parent, anchorTo, text, font, y, r, g, b) + local fs = parent:CreateFontString(nil, "ARTWORK", font or "GameFontNormalSmall") + if anchorTo == parent then + fs:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, y or 0) + else + fs:SetPoint("TOPLEFT", anchorTo, "BOTTOMLEFT", 0, y or -8) + end + fs:SetWidth(500) + fs:SetJustifyH("LEFT") + fs:SetJustifyV("TOP") + fs:SetTextColor(r or 0.93, g or 0.93, b or 0.93) + fs:SetText(text) + MTH_CreditsBumpFont(fs) + return fs +end + +function MTH_SetupCreditsOptions() + if not MTH_CREDITS_READY then return end + + local container = MTH_GetFrame("MetaHuntOptionsCredits") + if not container then return end + + MTH_ClearContainer(container) + + local title = container:CreateFontString("MetaHuntOptionsCreditsTitle", "ARTWORK", "GameFontNormal") + title:SetPoint("TOPLEFT", container, "TOPLEFT", 10, -10) + title:SetText("Credits") + title:SetTextColor(1.00, 0.82, 0.00) + + local scrollFrame = MTH_GetFrame("MetaHuntOptionsCreditsScroll") + if not scrollFrame then + scrollFrame = CreateFrame("ScrollFrame", "MetaHuntOptionsCreditsScroll", container, "UIPanelScrollFrameTemplate") + end + scrollFrame:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -10) + scrollFrame:SetPoint("BOTTOMRIGHT", container, "BOTTOMRIGHT", -30, 10) + scrollFrame:Show() + + local content = MTH_GetFrame("MetaHuntOptionsCreditsContent") + if not content then + content = CreateFrame("Frame", "MetaHuntOptionsCreditsContent", scrollFrame) + end + content:SetWidth(520) + content:SetHeight(2600) + MTH_CreditsClearFrame(content) + scrollFrame:SetScrollChild(content) + + local cursor = MTH_CreditsCreateText(content, content, "About the code", "GameFontNormal", -2, 1.00, 0.82, 0.00) + cursor = MTH_CreditsCreateText(content, cursor, + "This addon is, in others, a compilation of old vanilla addons that I loved and played with during years. But most of them had become very buggy with TwoW as new contain was added to the server. \n I fixed, reworked, enhanced, and melted them within a modern modular framework that I created.\n" + .. "I want to make it crystal clear about what work is mine, and what is not and respectfully credit the original authors for the great stuff I've taken from them:", + "GameFontNormalSmall", -10) + + cursor = MTH_CreditsCreateText(content, cursor, + "- zBars, Antidaze and Autostrip were core functionalities of Vanilla zHunterMod addon. And I kept the \"z\" naming of bars/buttons to always remember it. On top of this I have created myself zAmmo, zCompanions, zMount, zToys.\nAlso the SmartAmmo feature idea is coming from Zhuntermod: there was a file in it with that functionality, but was like a work-in-progress, totally unfunctional and even \"dangerous\" in its current state, and was not active in the addon. Since it was a fucking great idea, I recoded it mostly from scratch and made it work reliably and safely.", + "GameFontNormalSmall", -10) + + cursor = MTH_CreditsCreateText(content, cursor, + "- Feed-o-Matic was entirely the work of Fizzwidget. I made it work for Twow, by melting it as a module calling Metahunt core to get pet's state and up-to-date data for new pet families and their diet.", + "GameFontNormalSmall", -10) + cursor = MTH_CreditsCreateLink(content, + "https://www.wowinterface.com/downloads/info4160-FizzwidgetFeed-O-Matic.html", + "https://www.wowinterface.com/downloads/info4160-FizzwidgetFeed-O-Matic.html", + cursor, + -4) + + cursor = MTH_CreditsCreateText(content, cursor, + "- Tooltips module is based on Hunter Helper, another vanilla addon of Fizzwidget. I kept the general idea, but there's not much original code left from it in Metahunt as I refactored everything so it plugs on MetaHunt controlled event handlers, can work with Turtle wow data, and is now usable on other things than Beasts.", + "GameFontNormalSmall", -10) + cursor = MTH_CreditsCreateLink(content, + "https://legacy-wow.com/vanilla-addons/fizzwidget-hunter-helper/", + "https://legacy-wow.com/vanilla-addons/fizzwidget-hunter-helper/", + cursor, + -4) + + cursor = MTH_CreditsCreateText(content, cursor, + "- Chronometer was an old and very popular vanilla addon working with ACE2 libraries. It has known uncountable iterations by various people over the years, and I can't credit them all of them here. The current version used by Metahunt is based on this Twow conversion made by \"wigan91\". I have reworked it to have it Hunter-focused-only: removed the non-hunter/race stuff, improved the definition for spells/effects config that were not correct, added missing hunter spells (not many), and implemented my own bar color scheme fitting better the hunter spells.", + "GameFontNormalSmall", -10) + cursor = MTH_CreditsCreateLink(content, + "https://github.com/wigan91/Chronometer-TWoW", + "https://github.com/wigan91/Chronometer-TWoW", + cursor, + -4) + + cursor = MTH_CreditsCreateText(content, cursor, + "- The Map/Marker system is the one of pfQuest from Master Shagu. Unfortunately he is no more reachable for some months and I could not have some talk with him about this. I mostly let it untouched, I just made slight modifications so it can integrate better within Metahunt, and doesn't conflict with pfQuest.", + "GameFontNormalSmall", -10) + + cursor = MTH_CreditsCreateText(content, cursor, + "- The system allowing to fetch itemlinks in game (In the Weapon tab of the Hunter Book notably) is the one of Atlas Twow.", + "GameFontNormalSmall", -10) + + cursor = MTH_CreditsCreateText(content, cursor, "About data sources", "GameFontNormal", -14, 1.00, 0.82, 0.00) + cursor = MTH_CreditsCreateText(content, cursor, + "Metahunt ships with its own Turtle-WoW datastores, limited to hunter stuff only, and most of this data is up-to-date (Feb 2026).\n\n" + .. "Sources used to build the data include:", + "GameFontNormalSmall", -10) + + cursor = MTH_CreditsCreateText(content, cursor, + "- pfQuest for Beasts and Ammo vendors NPCs and their spawn points (and beasts respawn times if any). Thank you again Shagu.", + "GameFontNormalSmall", -10) + + cursor = MTH_CreditsCreateText(content, cursor, + "- This invaluable and very accurate spreadsheet for beasts having pet abilities. A big thanks to all twow players that crafted this! Now you dont need that sheet anymore, you have Metahunt! And the Great Book of Huntards is awesome and accurate also because of YOU.", + "GameFontNormalSmall", -10) + cursor = MTH_CreditsCreateLink(content, + "https://docs.google.com/spreadsheets/d/1u33knqlYXvY-jR6pvTd58gyDg8OYEuwYsSoEnIjeA8k/edit?gid=4136205#gid=4136205", + "https://docs.google.com/spreadsheets/d/1u33knqlYXvY-jR6pvTd58gyDg8OYEuwYsSoEnIjeA8k/edit?gid=4136205#gid=4136205", + cursor, + -4) + + cursor = MTH_CreditsCreateText(content, cursor, + "- Atlas twow for ranged weapon sources\n\n" + .. "- Twow wiki for pet diet (which was incomplete, missing Serpents and Foxes)", + "GameFontNormalSmall", -10) + + cursor = MTH_CreditsCreateText(content, cursor, + "With that being said, go back hunting fellas!", + "GameFontNormalSmall", -10, 1.00, 0.82, 0.00) + + cursor = MTH_CreditsCreateText(content, cursor, + "\n" + .. "Metasploit , of Nordaanar.", + "GameFontNormalSmall", -10) +end diff --git a/api/options-feedomatic.lua b/api/options-feedomatic.lua new file mode 100644 index 0000000..b2606e2 --- /dev/null +++ b/api/options-feedomatic.lua @@ -0,0 +1,373 @@ +local MTH_FOM_READY = MTH_OptionsRequire and MTH_OptionsRequire("options-feedomatic", { + "MTH_GetFrame", + "MTH_ClearContainer", + "MTH_CreateCheckbox", +}) + +local MTH_FOM_STATE = { + enableCheck = nil, + keepOpenEdit = nil, + bindStatus = nil, + bindButton = nil, + bindClearButton = nil, + bindingCapture = false, + ctrl = {}, +} + +local function MTH_EnsureFOMConfig() + if FOM_Config then + return + end + + if FOM_Config_Default then + FOM_Config = {} + for key, value in pairs(FOM_Config_Default) do + FOM_Config[key] = value + end + return + end + + FOM_Config = { + Enabled = false, + Alert = "emote", + Level = "content", + KeepOpenSlots = 8, + AvoidUsefulFood = true, + AvoidQuestFood = true, + AvoidBonusFood = true, + Fallback = false, + SaveForCookingLevel = 1, + PreferHigherQuality = true, + AudioWarning = true, + TextWarning = true, + IconWarning = true, + } +end + +local function MTH_FOM_SetAlert(mode) + MTH_EnsureFOMConfig() + if mode == "none" then + FOM_Config.Alert = nil + else + FOM_Config.Alert = mode + end +end + +local function MTH_FOM_SetLevel(level) + MTH_EnsureFOMConfig() + if level == "off" then + FOM_Config.Level = nil + else + FOM_Config.Level = level + end +end + +local function MTH_FOM_SetCookingLevel(level) + MTH_EnsureFOMConfig() + FOM_Config.SaveForCookingLevel = level +end + +local function MTH_FOM_GetBindingDisplayText() + if not GetBindingKey then + return "Unavailable" + end + local key1, key2 = GetBindingKey("FEEDOMATIC") + if not key1 and not key2 then + return "Unbound" + end + if key1 and key2 then + return key1 .. " / " .. key2 + end + return key1 or key2 or "Unbound" +end + +local function MTH_FOM_UpdateBindingStatus() + local bindStatus = MTH_FOM_STATE.bindStatus + if not bindStatus or not bindStatus.SetText then + return + end + if MTH_FOM_STATE.bindingCapture then + bindStatus:SetText("Feed key: press a key... (ESC to clear)") + else + bindStatus:SetText("Feed key: " .. MTH_FOM_GetBindingDisplayText()) + end +end + +local function MTH_FOM_StopBindingCapture() + MTH_FOM_STATE.bindingCapture = false + if MTH_FOM_STATE.bindButton then + if MTH_FOM_STATE.bindButton.SetPropagateKeyboardInput then + MTH_FOM_STATE.bindButton:SetPropagateKeyboardInput(true) + end + MTH_FOM_STATE.bindButton:EnableKeyboard(false) + MTH_FOM_STATE.bindButton:SetText("Set Key") + end + MTH_FOM_UpdateBindingStatus() +end + +local function MTH_FOM_StartBindingCapture() + MTH_FOM_STATE.bindingCapture = true + if MTH_FOM_STATE.bindButton then + MTH_FOM_STATE.bindButton:SetText("Press key...") + MTH_FOM_STATE.bindButton:EnableKeyboard(true) + if MTH_FOM_STATE.bindButton.SetPropagateKeyboardInput then + MTH_FOM_STATE.bindButton:SetPropagateKeyboardInput(false) + end + end + MTH_FOM_UpdateBindingStatus() +end + +local function MTH_FOM_BuildBindingChord(key) + local chord = "" + if IsControlKeyDown and IsControlKeyDown() and key ~= "LCTRL" and key ~= "RCTRL" then chord = chord .. "CTRL-" end + if IsAltKeyDown and IsAltKeyDown() and key ~= "LALT" and key ~= "RALT" then chord = chord .. "ALT-" end + if IsShiftKeyDown and IsShiftKeyDown() and key ~= "LSHIFT" and key ~= "RSHIFT" then chord = chord .. "SHIFT-" end + return chord .. key +end + +local function MTH_FOM_SaveBinding(key) + if not SetBinding or not SaveBindings or not GetBindingKey then + if MTH and MTH.Print then + MTH:Print("Keybinding APIs unavailable.") + end + return + end + if InCombatLockdown and InCombatLockdown() then + if MTH and MTH.Print then + MTH:Print("Cannot change keybindings during combat.") + end + MTH_FOM_StopBindingCapture() + return + end + + local old1, old2 = GetBindingKey("FEEDOMATIC") + if old1 then SetBinding(old1) end + if old2 then SetBinding(old2) end + + if key and key ~= "" and key ~= "ESCAPE" then + SetBinding(key, "FEEDOMATIC") + end + + local bindingSet = 1 + if GetCurrentBindingSet then + bindingSet = GetCurrentBindingSet() or 1 + end + SaveBindings(bindingSet) + MTH_FOM_StopBindingCapture() +end + +function MTH_RefreshFeedOMaticOptions() + if not MTH_FOM_READY then return end + MTH_EnsureFOMConfig() + + if MTH_FOM_STATE.enableCheck then + local module = MTH and MTH:GetModule("feedomatic") + MTH_FOM_STATE.enableCheck:SetChecked(module and module.enabled and true or false) + end + + if MTH_FOM_STATE.keepOpenEdit then + MTH_FOM_STATE.keepOpenEdit:SetText(tostring(FOM_Config.KeepOpenSlots or 0)) + end + + MTH_FOM_UpdateBindingStatus() + + if MTH_FOM_STATE.ctrl.AvoidQuestFood then MTH_FOM_STATE.ctrl.AvoidQuestFood:SetChecked(FOM_Config.AvoidQuestFood and 1 or nil) end + if MTH_FOM_STATE.ctrl.AvoidBonusFood then MTH_FOM_STATE.ctrl.AvoidBonusFood:SetChecked(FOM_Config.AvoidBonusFood and 1 or nil) end + if MTH_FOM_STATE.ctrl.PreferHigherQuality then MTH_FOM_STATE.ctrl.PreferHigherQuality:SetChecked(FOM_Config.PreferHigherQuality and 1 or nil) end + if MTH_FOM_STATE.ctrl.Fallback then MTH_FOM_STATE.ctrl.Fallback:SetChecked(FOM_Config.Fallback and 1 or nil) end + if MTH_FOM_STATE.ctrl.AudioWarning then MTH_FOM_STATE.ctrl.AudioWarning:SetChecked(FOM_Config.AudioWarning and true or false) end + if MTH_FOM_STATE.ctrl.AudioWarningBell then MTH_FOM_STATE.ctrl.AudioWarningBell:SetChecked(FOM_Config.AudioWarning == "bell" and 1 or nil) end + if MTH_FOM_STATE.ctrl.TextWarning then MTH_FOM_STATE.ctrl.TextWarning:SetChecked(FOM_Config.TextWarning and 1 or nil) end + if MTH_FOM_STATE.ctrl.IconWarning then MTH_FOM_STATE.ctrl.IconWarning:SetChecked(FOM_Config.IconWarning and 1 or nil) end + + if MTH_FOM_STATE.ctrl.AlertEmote then MTH_FOM_STATE.ctrl.AlertEmote:SetChecked(FOM_Config.Alert == "emote" and 1 or nil) end + if MTH_FOM_STATE.ctrl.AlertChat then MTH_FOM_STATE.ctrl.AlertChat:SetChecked(FOM_Config.Alert == "chat" and 1 or nil) end + if MTH_FOM_STATE.ctrl.AlertNone then MTH_FOM_STATE.ctrl.AlertNone:SetChecked((not FOM_Config.Alert) and 1 or nil) end + + if MTH_FOM_STATE.ctrl.LevelContent then MTH_FOM_STATE.ctrl.LevelContent:SetChecked(FOM_Config.Level == "content" and 1 or nil) end + if MTH_FOM_STATE.ctrl.LevelUnhappy then MTH_FOM_STATE.ctrl.LevelUnhappy:SetChecked(FOM_Config.Level == "unhappy" and 1 or nil) end + if MTH_FOM_STATE.ctrl.LevelOff then MTH_FOM_STATE.ctrl.LevelOff:SetChecked((not FOM_Config.Level) and 1 or nil) end + + if MTH_FOM_STATE.ctrl.SaveCookOrange then MTH_FOM_STATE.ctrl.SaveCookOrange:SetChecked(FOM_Config.SaveForCookingLevel == 3 and 1 or nil) end + if MTH_FOM_STATE.ctrl.SaveCookYellow then MTH_FOM_STATE.ctrl.SaveCookYellow:SetChecked(FOM_Config.SaveForCookingLevel == 2 and 1 or nil) end + if MTH_FOM_STATE.ctrl.SaveCookGreen then MTH_FOM_STATE.ctrl.SaveCookGreen:SetChecked(FOM_Config.SaveForCookingLevel == 1 and 1 or nil) end + if MTH_FOM_STATE.ctrl.SaveCookAll then MTH_FOM_STATE.ctrl.SaveCookAll:SetChecked(FOM_Config.SaveForCookingLevel == 0 and 1 or nil) end + if MTH_FOM_STATE.ctrl.SaveCookNone then MTH_FOM_STATE.ctrl.SaveCookNone:SetChecked(FOM_Config.SaveForCookingLevel and FOM_Config.SaveForCookingLevel >= 4 and 1 or nil) end +end + +function MTH_SetupFeedOMaticOptions() + if not MTH_FOM_READY then return end + local container = MTH_GetFrame("MetaHuntOptionsFeedOMatic") + if not container then return end + local rightColumnX = 300 + local yAdjust = -64 + + MTH_ClearContainer(container) + MTH_FOM_STATE.ctrl = {} + + local title = container:CreateFontString("MetaHuntOptionsFeedOMaticTitle", "ARTWORK", "GameFontHighlight") + title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -10) + title:SetText("Feed-O-Matic") + + local statusNotice = container:CreateFontString("MetaHuntOptionsFeedOMaticStatusNotice", "ARTWORK", "GameFontNormalSmall") + statusNotice:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -34) + statusNotice:SetWidth(600) + statusNotice:SetJustifyH("LEFT") + statusNotice:SetJustifyV("TOP") + statusNotice:SetTextColor(0.35, 0.65, 1) + statusNotice:SetText("Feed-O-Matic, created by the great Fizzwidget, is not yet entirely ready for TurtleWoW because I am missing \na reliable list of all foods, mostly impossible to fetch from the DB.\n It still works much better in this version, but all stuff related to Food buff and Cooking isnt fully functional.") + + local moduleCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsFeedOMaticEnabled", "Enable FeedOMatic module", -32 + yAdjust) + if moduleCheck then + moduleCheck:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local enabled = self:GetChecked() == 1 + if MTH and MTH.SetModuleEnabled then + local ok, err = MTH:SetModuleEnabled("feedomatic", enabled) + if not ok and MTH and MTH.Print then + MTH:Print("Failed to change FeedOMatic state: " .. tostring(err)) + end + end + MTH_RefreshFeedOMaticOptions() + end) + MTH_FOM_STATE.enableCheck = moduleCheck + end + + local leftHeader = container:CreateFontString("MetaHuntOptionsFeedOMaticGeneralHeader", "ARTWORK", "GameFontHighlight") + leftHeader:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -62 + yAdjust) + leftHeader:SetText("General") + + local bindStatus = container:CreateFontString("MetaHuntOptionsFOMBindStatus", "ARTWORK", "GameFontNormalSmall") + bindStatus:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -80 + yAdjust) + bindStatus:SetText("Feed key: " .. MTH_FOM_GetBindingDisplayText()) + MTH_FOM_STATE.bindStatus = bindStatus + + local bindButton = CreateFrame("Button", "MetaHuntOptionsFOMBindButton", container, "UIPanelButtonTemplate") + bindButton:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -100 + yAdjust) + bindButton:SetWidth(90) + bindButton:SetHeight(22) + bindButton:SetText("Set Key") + bindButton:EnableKeyboard(false) + bindButton:SetScript("OnClick", function() + if MTH_FOM_STATE.bindingCapture then + MTH_FOM_StopBindingCapture() + else + MTH_FOM_StartBindingCapture() + end + end) + bindButton:SetScript("OnKeyDown", function(_, key) + if not MTH_FOM_STATE.bindingCapture then + return + end + key = key or arg1 + if not key or key == "" then + return + end + if key == "ESCAPE" then + MTH_FOM_SaveBinding(nil) + return + end + if key == "UNKNOWN" or key == "PRINTSCREEN" then + return + end + MTH_FOM_SaveBinding(MTH_FOM_BuildBindingChord(key)) + end) + MTH_FOM_STATE.bindButton = bindButton + + local bindClearButton = CreateFrame("Button", "MetaHuntOptionsFOMBindClearButton", container, "UIPanelButtonTemplate") + bindClearButton:SetPoint("LEFT", bindButton, "RIGHT", 8, 0) + bindClearButton:SetWidth(70) + bindClearButton:SetHeight(22) + bindClearButton:SetText("Clear") + bindClearButton:SetScript("OnClick", function() + MTH_FOM_SaveBinding(nil) + end) + MTH_FOM_STATE.bindClearButton = bindClearButton + + local bindHint = container:CreateFontString("MetaHuntOptionsFOMBindHint", "ARTWORK", "GameFontNormalSmall") + bindHint:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -128 + yAdjust) + bindHint:SetWidth(360) + bindHint:SetJustifyH("LEFT") + bindHint:SetJustifyV("TOP") + bindHint:SetTextColor(0.35, 0.65, 1) + bindHint:SetText("You should assign a key bind for feed-o-matic,\nin order to feed your pet automatically.\nThe key \"P\" is an excellent candidate !") + + MTH_FOM_STATE.ctrl.AvoidQuestFood = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAvoidQuest", FOM_OptionsButtonText and FOM_OptionsButtonText["AvoidQuestFood"] or "Avoid quest foods", -170 + yAdjust) + MTH_FOM_STATE.ctrl.AvoidBonusFood = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAvoidBonus", FOM_OptionsButtonText and FOM_OptionsButtonText["AvoidBonusFood"] or "Avoid bonus foods", -195 + yAdjust) + MTH_FOM_STATE.ctrl.PreferHigherQuality = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMPreferQuality", FOM_OptionsButtonText and FOM_OptionsButtonText["PreferHigherQuality"] or "Prefer higher quality food", -220 + yAdjust) + MTH_FOM_STATE.ctrl.Fallback = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMFallback", FOM_OptionsButtonText and FOM_OptionsButtonText["Fallback"] or "Fallback to avoided foods", -245 + yAdjust) + + if MTH_FOM_STATE.ctrl.AvoidQuestFood then MTH_FOM_STATE.ctrl.AvoidQuestFood:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.AvoidQuestFood = self:GetChecked() == 1 end) end + if MTH_FOM_STATE.ctrl.AvoidBonusFood then MTH_FOM_STATE.ctrl.AvoidBonusFood:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.AvoidBonusFood = self:GetChecked() == 1 end) end + if MTH_FOM_STATE.ctrl.PreferHigherQuality then MTH_FOM_STATE.ctrl.PreferHigherQuality:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.PreferHigherQuality = self:GetChecked() == 1 end) end + if MTH_FOM_STATE.ctrl.Fallback then MTH_FOM_STATE.ctrl.Fallback:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.Fallback = self:GetChecked() == 1 end) end + + local keepOpenLabel = container:CreateFontString("MetaHuntOptionsFOMKeepOpenLabel", "ARTWORK", "GameFontNormalSmall") + keepOpenLabel:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -302 + yAdjust) + keepOpenLabel:SetText("Keep open bag slots:") + + local keepOpen = CreateFrame("EditBox", "MetaHuntOptionsFOMKeepOpen", container, "InputBoxTemplate") + keepOpen:SetPoint("LEFT", keepOpenLabel, "RIGHT", 8, 0) + keepOpen:SetWidth(40) + keepOpen:SetHeight(20) + keepOpen:SetNumeric(true) + keepOpen:SetAutoFocus(false) + keepOpen:SetScript("OnTextChanged", function(self) + self = self or this + if not self then return end + local value = tonumber(self:GetText()) or 0 + FOM_Config.KeepOpenSlots = value + end) + keepOpen:SetScript("OnEnterPressed", function(self) self = self or this if self then self:ClearFocus() end end) + keepOpen:SetScript("OnEscapePressed", function(self) self = self or this if self then self:ClearFocus() end end) + MTH_FOM_STATE.keepOpenEdit = keepOpen + + local notifyHeader = container:CreateFontString("MetaHuntOptionsFOMNotifyHeader", "ARTWORK", "GameFontHighlight") + notifyHeader:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -336 + yAdjust) + notifyHeader:SetText("Notify when feeding") + + MTH_FOM_STATE.ctrl.AlertEmote = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAlertEmote", FOM_OptionsButtonText and FOM_OptionsButtonText["AlertEmote"] or "Via emote", -360 + yAdjust) + MTH_FOM_STATE.ctrl.AlertChat = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAlertChat", FOM_OptionsButtonText and FOM_OptionsButtonText["AlertChat"] or "In chat", -385 + yAdjust) + MTH_FOM_STATE.ctrl.AlertNone = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAlertNone", FOM_OptionsButtonText and FOM_OptionsButtonText["AlertNone"] or "Don't notify", -410 + yAdjust) + if MTH_FOM_STATE.ctrl.AlertEmote then MTH_FOM_STATE.ctrl.AlertEmote:SetScript("OnClick", function() MTH_FOM_SetAlert("emote"); MTH_RefreshFeedOMaticOptions() end) end + if MTH_FOM_STATE.ctrl.AlertChat then MTH_FOM_STATE.ctrl.AlertChat:SetScript("OnClick", function() MTH_FOM_SetAlert("chat"); MTH_RefreshFeedOMaticOptions() end) end + if MTH_FOM_STATE.ctrl.AlertNone then MTH_FOM_STATE.ctrl.AlertNone:SetScript("OnClick", function() MTH_FOM_SetAlert("none"); MTH_RefreshFeedOMaticOptions() end) end + + local warnHeader = container:CreateFontString("MetaHuntOptionsFOMWarnHeader", "ARTWORK", "GameFontHighlight") + warnHeader:SetPoint("TOPLEFT", container, "TOPLEFT", rightColumnX, -276 + yAdjust) + warnHeader:SetText("Warn when pet needs feeding") + + MTH_FOM_STATE.ctrl.LevelContent = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMLevelContent", FOM_OptionsButtonText and FOM_OptionsButtonText["LevelContent"] or "When content", -300 + yAdjust, rightColumnX) + MTH_FOM_STATE.ctrl.LevelUnhappy = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMLevelUnhappy", FOM_OptionsButtonText and FOM_OptionsButtonText["LevelUnhappy"] or "When unhappy", -325 + yAdjust, rightColumnX) + MTH_FOM_STATE.ctrl.LevelOff = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMLevelOff", FOM_OptionsButtonText and FOM_OptionsButtonText["LevelOff"] or "Don't warn", -350 + yAdjust, rightColumnX) + if MTH_FOM_STATE.ctrl.LevelContent then MTH_FOM_STATE.ctrl.LevelContent:SetScript("OnClick", function() MTH_FOM_SetLevel("content"); MTH_RefreshFeedOMaticOptions() end) end + if MTH_FOM_STATE.ctrl.LevelUnhappy then MTH_FOM_STATE.ctrl.LevelUnhappy:SetScript("OnClick", function() MTH_FOM_SetLevel("unhappy"); MTH_RefreshFeedOMaticOptions() end) end + if MTH_FOM_STATE.ctrl.LevelOff then MTH_FOM_STATE.ctrl.LevelOff:SetScript("OnClick", function() MTH_FOM_SetLevel("off"); MTH_RefreshFeedOMaticOptions() end) end + + MTH_FOM_STATE.ctrl.AudioWarning = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAudioWarning", FOM_OptionsButtonText and FOM_OptionsButtonText["AudioWarning"] or "Play sound", -378 + yAdjust, rightColumnX) + MTH_FOM_STATE.ctrl.AudioWarningBell = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAudioWarningBell", FOM_OptionsButtonText and FOM_OptionsButtonText["AudioWarningBell"] or "Use bell sound", -403 + yAdjust, rightColumnX) + MTH_FOM_STATE.ctrl.TextWarning = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMTextWarning", FOM_OptionsButtonText and FOM_OptionsButtonText["TextWarning"] or "Show text", -428 + yAdjust, rightColumnX) + MTH_FOM_STATE.ctrl.IconWarning = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMIconWarning", FOM_OptionsButtonText and FOM_OptionsButtonText["IconWarning"] or "Flash icon", -453 + yAdjust, rightColumnX) + if MTH_FOM_STATE.ctrl.AudioWarning then MTH_FOM_STATE.ctrl.AudioWarning:SetScript("OnClick", function(self) self = self or this if not self then return end if self:GetChecked() == 1 then if FOM_Config.AudioWarning ~= "bell" then FOM_Config.AudioWarning = true end else FOM_Config.AudioWarning = nil end MTH_RefreshFeedOMaticOptions() end) end + if MTH_FOM_STATE.ctrl.AudioWarningBell then MTH_FOM_STATE.ctrl.AudioWarningBell:SetScript("OnClick", function(self) self = self or this if not self then return end if self:GetChecked() == 1 then FOM_Config.AudioWarning = "bell" else FOM_Config.AudioWarning = true end MTH_RefreshFeedOMaticOptions() end) end + if MTH_FOM_STATE.ctrl.TextWarning then MTH_FOM_STATE.ctrl.TextWarning:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.TextWarning = self:GetChecked() == 1 end) end + if MTH_FOM_STATE.ctrl.IconWarning then MTH_FOM_STATE.ctrl.IconWarning:SetScript("OnClick", function(self) self = self or this if not self then return end FOM_Config.IconWarning = self:GetChecked() == 1 end) end + + local cookHeader = container:CreateFontString("MetaHuntOptionsFOMCookingHeader", "ARTWORK", "GameFontHighlight") + cookHeader:SetPoint("TOPLEFT", container, "TOPLEFT", rightColumnX, -62 + yAdjust) + cookHeader:SetText("Avoid foods used in cooking") + + MTH_FOM_STATE.ctrl.SaveCookOrange = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookOrange", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_Orange"] or "Only difficult recipes", -86 + yAdjust, rightColumnX) + MTH_FOM_STATE.ctrl.SaveCookYellow = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookYellow", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_Yellow"] or "Medium or better", -111 + yAdjust, rightColumnX) + MTH_FOM_STATE.ctrl.SaveCookGreen = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookGreen", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_Green"] or "Easy or better", -136 + yAdjust, rightColumnX) + MTH_FOM_STATE.ctrl.SaveCookAll = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookAll", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_All"] or "All foods", -161 + yAdjust, rightColumnX) + MTH_FOM_STATE.ctrl.SaveCookNone = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookNone", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_None"] or "Do not save cooking foods", -186 + yAdjust, rightColumnX) + if MTH_FOM_STATE.ctrl.SaveCookOrange then MTH_FOM_STATE.ctrl.SaveCookOrange:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(3); MTH_RefreshFeedOMaticOptions() end) end + if MTH_FOM_STATE.ctrl.SaveCookYellow then MTH_FOM_STATE.ctrl.SaveCookYellow:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(2); MTH_RefreshFeedOMaticOptions() end) end + if MTH_FOM_STATE.ctrl.SaveCookGreen then MTH_FOM_STATE.ctrl.SaveCookGreen:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(1); MTH_RefreshFeedOMaticOptions() end) end + if MTH_FOM_STATE.ctrl.SaveCookAll then MTH_FOM_STATE.ctrl.SaveCookAll:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(0); MTH_RefreshFeedOMaticOptions() end) end + if MTH_FOM_STATE.ctrl.SaveCookNone then MTH_FOM_STATE.ctrl.SaveCookNone:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(4); MTH_RefreshFeedOMaticOptions() end) end + + MTH_RefreshFeedOMaticOptions() +end diff --git a/api/options-shell.lua b/api/options-shell.lua new file mode 100644 index 0000000..3e7a929 --- /dev/null +++ b/api/options-shell.lua @@ -0,0 +1,220 @@ +MetaHuntOptions = nil +local MTH_OPTIONS_SETUP = {} -- Track which frames have been set up +MTH_OPTIONS_CONST = MTH_OPTIONS_CONST or {} +MTH_OPTIONS_CONST.NAV_DEFAULT_WIDTH = MTH_OPTIONS_CONST.NAV_DEFAULT_WIDTH or 120 +MTH_OPTIONS_CONST.WINDOW_PADDING = MTH_OPTIONS_CONST.WINDOW_PADDING or 12 +MTH_OPTIONS_CONST.CONTENT_TOP_OFFSET = MTH_OPTIONS_CONST.CONTENT_TOP_OFFSET or -72 +MTH_OPTIONS_CONST.CONTENT_HEIGHT = MTH_OPTIONS_CONST.CONTENT_HEIGHT or 540 +MTH_OPTIONS_CONST.CLOSE_BTN_SIZE = MTH_OPTIONS_CONST.CLOSE_BTN_SIZE or 22 +MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_X = MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_X or -8 +MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_Y = MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_Y or -8 +local MTH_OPTIONS_SHELL_READY = MTH_OptionsRequire and MTH_OptionsRequire("options-shell", { + "MTH_GetFrame", + "MTH_BuildOptionsTree", +}) + +local function MTH_RegisterEscClose(frameName) + if not (frameName and UISpecialFrames) then + return + end + for i = 1, table.getn(UISpecialFrames) do + if UISpecialFrames[i] == frameName then + return + end + end + table.insert(UISpecialFrames, frameName) +end + +local function MTH_EnsureOptionsFrame() + if MetaHuntOptions then + return MetaHuntOptions + end + + MetaHuntOptions = CreateFrame("Frame", "MetaHuntOptions", UIParent, "MetaHuntOptionsTemplate") + if not MetaHuntOptions then + if MTH and MTH.Print then + MTH:Print("[MTH OPTIONS] Failed to create MetaHuntOptions", "error") + end + return nil + end + MTH_RegisterEscClose("MetaHuntOptions") + + local nav = MTH_GetFrame("MetaHuntOptionsNav") + if nav and nav.SetBackdropColor then + nav:SetBackdropColor(0.08, 0.08, 0.08, 0.95) + nav:SetBackdropBorderColor(0.25, 0.25, 0.25, 1) + end + + local nav = MTH_GetFrame("MetaHuntOptionsNav") + local navWidth = nav and nav:GetWidth() or MTH_OPTIONS_CONST.NAV_DEFAULT_WIDTH + local contentLeft = MTH_OPTIONS_CONST.WINDOW_PADDING + navWidth + MTH_OPTIONS_CONST.WINDOW_PADDING + local contentWidth = (MetaHuntOptions:GetWidth() or 720) - contentLeft - MTH_OPTIONS_CONST.WINDOW_PADDING + for i = 1, table.getn(MTH_OPTIONS_TABS) do + local frame = MTH_GetFrame(MTH_OPTIONS_TABS[i].frame) + if frame then + frame:ClearAllPoints() + frame:SetPoint("TOPLEFT", MetaHuntOptions, "TOPLEFT", contentLeft, MTH_OPTIONS_CONST.CONTENT_TOP_OFFSET) + frame:SetWidth(contentWidth) + frame:SetHeight(MTH_OPTIONS_CONST.CONTENT_HEIGHT) + frame:SetFrameLevel(MetaHuntOptions:GetFrameLevel() + 1) + frame:EnableMouse(true) + frame:Hide() + end + end + + local closeBtn = MTH_GetFrame("MetaHuntOptionsCloseButton") + if closeBtn then + closeBtn:ClearAllPoints() + closeBtn:SetPoint("TOPRIGHT", MetaHuntOptions, "TOPRIGHT", MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_X, MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_Y) + closeBtn:SetWidth(MTH_OPTIONS_CONST.CLOSE_BTN_SIZE) + closeBtn:SetHeight(MTH_OPTIONS_CONST.CLOSE_BTN_SIZE) + closeBtn:SetText("X") + closeBtn:SetScript("OnClick", function() MetaHuntOptions:Hide() end) + end + + MTH_BuildOptionsTree() + MTH_SelectOptionsTab("General") + + return MetaHuntOptions +end + +function MTH_SelectOptionsTab(tabKey) + if not MTH_OPTIONS_SHELL_READY then return end + local optionsFrame = MTH_EnsureOptionsFrame() + if not optionsFrame then + return + end + MTH_OPTIONS_STATE = MTH_OPTIONS_STATE or {} + MTH_OPTIONS_STATE.activeTab = tabKey + MTH_OPTIONS_ACTIVE_TAB = tabKey + + for i = 1, table.getn(MTH_OPTIONS_TABS) do + local frame = MTH_GetFrame(MTH_OPTIONS_TABS[i].frame) + if frame then + frame:Hide() + end + end + + for i = 1, table.getn(MTH_OPTIONS_TABS) do + if MTH_OPTIONS_TABS[i].key == tabKey then + local frame = MTH_GetFrame(MTH_OPTIONS_TABS[i].frame) + if frame then + frame:Show() + end + end + end + MTH_BuildOptionsTree() + + if tabKey == "General" then + if MTH_SetupGeneralOptions then + MTH_SetupGeneralOptions() + MTH_OPTIONS_SETUP["General"] = true + end + elseif tabKey == "Pet" then + if not MTH_OPTIONS_SETUP["Pet"] then + MTH_SetupPetOptions() + MTH_OPTIONS_SETUP["Pet"] = true + end + elseif tabKey == "Track" then + if not MTH_OPTIONS_SETUP["Track"] then + MTH_SetupTrackOptions() + MTH_OPTIONS_SETUP["Track"] = true + end + elseif tabKey == "Aspect" then + if not MTH_OPTIONS_SETUP["Aspect"] then + MTH_SetupAspectOptions() + MTH_OPTIONS_SETUP["Aspect"] = true + end + elseif tabKey == "Trap" then + if not MTH_OPTIONS_SETUP["Trap"] then + MTH_SetupTrapOptions() + MTH_OPTIONS_SETUP["Trap"] = true + end + elseif tabKey == "Ammo" then + if not MTH_OPTIONS_SETUP["Ammo"] then + MTH_SetupAmmoOptions() + MTH_OPTIONS_SETUP["Ammo"] = true + else + MTH_SetupAmmoOptions() + end + elseif tabKey == "Mounts" then + if not MTH_OPTIONS_SETUP["Mounts"] then + MTH_SetupMountsOptions() + MTH_OPTIONS_SETUP["Mounts"] = true + end + elseif tabKey == "Companions" then + if not MTH_OPTIONS_SETUP["Companions"] then + MTH_SetupCompanionsOptions() + MTH_OPTIONS_SETUP["Companions"] = true + end + elseif tabKey == "Toys" then + if not MTH_OPTIONS_SETUP["Toys"] then + MTH_SetupToysOptions() + MTH_OPTIONS_SETUP["Toys"] = true + end + elseif tabKey == "SmartAmmo" then + if not MTH_OPTIONS_SETUP["SmartAmmo"] then + MTH_SetupSmartAmmoOptions() + MTH_OPTIONS_SETUP["SmartAmmo"] = true + end + elseif tabKey == "FeedOMatic" then + if not MTH_OPTIONS_SETUP["FeedOMatic"] then + MTH_SetupFeedOMaticOptions() + MTH_OPTIONS_SETUP["FeedOMatic"] = true + end + MTH_RefreshFeedOMaticOptions() + elseif tabKey == "AutoBuy" then + if not MTH_OPTIONS_SETUP["AutoBuy"] then + MTH_SetupAutoBuyOptions() + MTH_OPTIONS_SETUP["AutoBuy"] = true + else + MTH_SetupAutoBuyOptions() + end + elseif tabKey == "Credits" then + local setupCredits = _G and _G["MTH_SetupCreditsOptions"] + if type(setupCredits) == "function" then + setupCredits() + end + MTH_OPTIONS_SETUP["Credits"] = true + elseif string.sub(tabKey or "", 1, 11) == "Chronometer" then + if not MTH_OPTIONS_SETUP[tabKey] then + if _G then + _G.MTH_CHRON_OPTIONS_SECTION = tabKey + end + if type(MTH_SetupChronometerOptions) == "function" then + MTH_SetupChronometerOptions() + MTH_OPTIONS_SETUP[tabKey] = true + elseif MTH and MTH.Print then + MTH:Print("Chronometer options module not loaded", "error") + end + else + if _G then + _G.MTH_CHRON_OPTIONS_SECTION = tabKey + end + if type(MTH_RefreshChronometerOptions) == "function" then + MTH_RefreshChronometerOptions() + elseif type(MTH_SetupChronometerOptions) == "function" then + MTH_SetupChronometerOptions() + end + end + end +end + +function MTH_ResetAndSelectOptionsTab(tabKey) + if not MTH_OPTIONS_SHELL_READY then return end + if not tabKey then + return + end + MTH_OPTIONS_SETUP[tabKey] = nil + MTH_SelectOptionsTab(tabKey) +end + +function MTH_OpenOptions(tabKey) + if not MTH_OPTIONS_SHELL_READY then return end + local optionsFrame = MTH_EnsureOptionsFrame() + if not optionsFrame then + return + end + optionsFrame:Show() + MTH_SelectOptionsTab(tabKey or "General") +end diff --git a/api/options-tree.lua b/api/options-tree.lua new file mode 100644 index 0000000..8e2cdb6 --- /dev/null +++ b/api/options-tree.lua @@ -0,0 +1,184 @@ +MTH_OPTIONS_TREE_STATE = MTH_OPTIONS_TREE_STATE or { zButtons = true, chronometer = true } +MTH_OPTIONS_TREE_BUTTONS = MTH_OPTIONS_TREE_BUTTONS or {} +MTH_OPTIONS_STATE = MTH_OPTIONS_STATE or {} +if MTH_OPTIONS_STATE.activeTab == nil and MTH_OPTIONS_ACTIVE_TAB ~= nil then + MTH_OPTIONS_STATE.activeTab = MTH_OPTIONS_ACTIVE_TAB +end +MTH_OPTIONS_ACTIVE_TAB = MTH_OPTIONS_STATE.activeTab +MTH_OPTIONS_CONST = MTH_OPTIONS_CONST or { + NAV_DEFAULT_WIDTH = 120, + NAV_INNER_PADDING = 16, + TREE_START_Y = -10, + TREE_BUTTON_HEIGHT = 20, + TREE_ROW_STEP = 22, + TREE_INDENT_BASE = 8, + TREE_CHILD_INDENT = 14, + HIGHLIGHT_ALPHA = 0.25, + SELECTED_BG_R = 0.35, + SELECTED_BG_G = 0.25, + SELECTED_BG_B = 0.10, + SELECTED_BG_A = 0.55, + SELECTED_TEXT_R = 1.00, + SELECTED_TEXT_G = 0.82, + SELECTED_TEXT_B = 0.00, + GROUP_TEXT_R = 1.00, + GROUP_TEXT_G = 0.82, + GROUP_TEXT_B = 0.00, +} + +MTH_OPTIONS_TABS = MTH_OPTIONS_TABS or { + { key = "General", label = "General", frame = "MetaHuntOptionsGeneral" }, + { key = "Pet", label = "ZPet", frame = "MetaHuntOptionsPet" }, + { key = "Track", label = "ZTrack", frame = "MetaHuntOptionsTrack" }, + { key = "Aspect", label = "ZAspect", frame = "MetaHuntOptionsAspect" }, + { key = "Trap", label = "ZTrap", frame = "MetaHuntOptionsTrap" }, + { key = "Ammo", label = "ZAmmo", frame = "MetaHuntOptionsAmmo" }, + { key = "Mounts", label = "ZMounts", frame = "MetaHuntOptionsMounts" }, + { key = "Companions", label = "ZCompanions", frame = "MetaHuntOptionsCompanions" }, + { key = "Toys", label = "ZToys", frame = "MetaHuntOptionsToys" }, + { key = "SmartAmmo", label = "Smart Ammo", frame = "MetaHuntOptionsSmartAmmo" }, + { key = "FeedOMatic", label = "FeedOMatic", frame = "MetaHuntOptionsFeedOMatic" }, + { key = "AutoBuy", label = "Auto Buy", frame = "MetaHuntOptionsAutoBuy" }, + { key = "ChronometerGeneral", label = "General", frame = "MetaHuntOptionsChronometer" }, + { key = "ChronometerBar", label = "Bar", frame = "MetaHuntOptionsChronometer" }, + { key = "ChronometerClassSpells", label = "Hunter Spells", frame = "MetaHuntOptionsChronometer" }, + { key = "ChronometerClassEvents", label = "Hunter Events", frame = "MetaHuntOptionsChronometer" }, + { key = "ChronometerRacial", label = "Racial", frame = "MetaHuntOptionsChronometer" }, + { key = "Credits", label = "Credits", frame = "MetaHuntOptionsCredits" }, +} + +MTH_OPTIONS_TREE = MTH_OPTIONS_TREE or { + { label = "General", key = "General" }, + { label = "Auto Buy", key = "AutoBuy" }, + { label = "Feed-O-Matic", key = "FeedOMatic" }, + { + label = "zButtons", + node = "zButtons", + children = { + { label = "zAmmo", key = "Ammo" }, + { label = "zPet", key = "Pet" }, + { label = "zAspect", key = "Aspect" }, + { label = "zTrack", key = "Track" }, + { label = "zTrap", key = "Trap" }, + { label = "zMounts", key = "Mounts" }, + { label = "zCompanions", key = "Companions" }, + { label = "zToys", key = "Toys" }, + }, + }, + { + label = "Chronometer", + node = "chronometer", + children = { + { label = "General", key = "ChronometerGeneral" }, + { label = "Bar", key = "ChronometerBar" }, + { label = "Hunter Spells", key = "ChronometerClassSpells" }, + { label = "Hunter Events", key = "ChronometerClassEvents" }, + { label = "Racial", key = "ChronometerRacial" }, + }, + }, + { label = "Credits", key = "Credits" }, +} + +function MTH_BuildOptionsTree() + local nav = MTH_GetFrame and MTH_GetFrame("MetaHuntOptionsNav") or nil + if not nav then + return + end + local navInnerWidth = (nav:GetWidth() or MTH_OPTIONS_CONST.NAV_DEFAULT_WIDTH) - MTH_OPTIONS_CONST.NAV_INNER_PADDING + + local yOffset = MTH_OPTIONS_CONST.TREE_START_Y + local buttonIndex = 1 + + local function acquireButton() + local button = MTH_OPTIONS_TREE_BUTTONS[buttonIndex] + if not button then + button = CreateFrame("Button", "MetaHuntOptionsNavItem"..buttonIndex, nav) + button:SetHeight(MTH_OPTIONS_CONST.TREE_BUTTON_HEIGHT) + button:SetNormalTexture("") + button:SetPushedTexture("") + button:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + button.highlight = button:GetHighlightTexture() + if button.highlight then + button.highlight:SetBlendMode("ADD") + button.highlight:SetVertexColor(1, 1, 1, MTH_OPTIONS_CONST.HIGHLIGHT_ALPHA) + end + + button.selectedTexture = button:CreateTexture(nil, "BACKGROUND") + button.selectedTexture:SetTexture("Interface\\Buttons\\WHITE8X8") + button.selectedTexture:SetVertexColor(MTH_OPTIONS_CONST.SELECTED_BG_R, MTH_OPTIONS_CONST.SELECTED_BG_G, MTH_OPTIONS_CONST.SELECTED_BG_B, MTH_OPTIONS_CONST.SELECTED_BG_A) + button.selectedTexture:SetPoint("TOPLEFT", button, "TOPLEFT", 0, -1) + button.selectedTexture:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 0, 1) + button.selectedTexture:Hide() + + button.expander = button:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + button.expander:SetPoint("LEFT", button, "LEFT", 4, 0) + + button.label = button:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + button.label:SetJustifyH("LEFT") + button.label:SetPoint("LEFT", button, "LEFT", 18, 0) + button.label:SetPoint("RIGHT", button, "RIGHT", -4, 0) + + MTH_OPTIONS_TREE_BUTTONS[buttonIndex] = button + end + button:Show() + buttonIndex = buttonIndex + 1 + return button + end + + local function placeLeaf(node, indent) + local button = acquireButton() + local xOffset = MTH_OPTIONS_CONST.TREE_INDENT_BASE + (indent or 0) + local width = navInnerWidth - (indent or 0) + button:ClearAllPoints() + button:SetPoint("TOPLEFT", nav, "TOPLEFT", xOffset, yOffset) + button:SetWidth(width) + button.expander:SetText("") + button.label:SetPoint("LEFT", button, "LEFT", 4, 0) + button.label:SetText(node.label) + button:SetScript("OnClick", function() MTH_SelectOptionsTab(node.key) end) + if MTH_OPTIONS_STATE.activeTab == node.key then + button.selectedTexture:Show() + button.label:SetTextColor(MTH_OPTIONS_CONST.SELECTED_TEXT_R, MTH_OPTIONS_CONST.SELECTED_TEXT_G, MTH_OPTIONS_CONST.SELECTED_TEXT_B) + else + button.selectedTexture:Hide() + button.label:SetTextColor(1, 1, 1) + end + button.expander:SetTextColor(1, 1, 1) + yOffset = yOffset - MTH_OPTIONS_CONST.TREE_ROW_STEP + end + + for i = 1, table.getn(MTH_OPTIONS_TREE) do + local node = MTH_OPTIONS_TREE[i] + if node.children then + local expanded = MTH_OPTIONS_TREE_STATE[node.node] and true or false + local button = acquireButton() + button:ClearAllPoints() + button:SetPoint("TOPLEFT", nav, "TOPLEFT", MTH_OPTIONS_CONST.TREE_INDENT_BASE, yOffset) + button:SetWidth(navInnerWidth) + button.expander:SetText(expanded and "-" or "+") + button.expander:SetTextColor(MTH_OPTIONS_CONST.GROUP_TEXT_R, MTH_OPTIONS_CONST.GROUP_TEXT_G, MTH_OPTIONS_CONST.GROUP_TEXT_B) + button.label:SetPoint("LEFT", button, "LEFT", 18, 0) + button.label:SetText(node.label) + button.label:SetTextColor(MTH_OPTIONS_CONST.GROUP_TEXT_R, MTH_OPTIONS_CONST.GROUP_TEXT_G, MTH_OPTIONS_CONST.GROUP_TEXT_B) + button.selectedTexture:Hide() + button:SetScript("OnClick", function() + MTH_OPTIONS_TREE_STATE[node.node] = not MTH_OPTIONS_TREE_STATE[node.node] + MTH_BuildOptionsTree() + end) + yOffset = yOffset - MTH_OPTIONS_CONST.TREE_ROW_STEP + if expanded then + for j = 1, table.getn(node.children) do + placeLeaf(node.children[j], MTH_OPTIONS_CONST.TREE_CHILD_INDENT) + end + end + else + placeLeaf(node, 0) + end + end + + for i = buttonIndex, table.getn(MTH_OPTIONS_TREE_BUTTONS) do + if MTH_OPTIONS_TREE_BUTTONS[i] then + MTH_OPTIONS_TREE_BUTTONS[i]:Hide() + end + end +end diff --git a/api/options-zbuttons.lua b/api/options-zbuttons.lua new file mode 100644 index 0000000..a90ffe8 --- /dev/null +++ b/api/options-zbuttons.lua @@ -0,0 +1,1008 @@ +local MTH_ZBUTTONS_READY = MTH_OptionsRequire and MTH_OptionsRequire("options-zbuttons", { + "MTH_GetFrame", + "MTH_CreateSlider", + "MTH_CreateCheckbox", + "MTH_ClearContainer", + "MTH_CreateActionButton", +}) + +local function MTH_ZB_RefreshTab(tabName) + if type(MTH_ResetAndSelectOptionsTab) == "function" then + MTH_ResetAndSelectOptionsTab(tabName) + elseif type(MTH_SelectOptionsTab) == "function" then + MTH_SelectOptionsTab(tabName) + end +end + +local function MTH_RebuildSpellOrder(buttonName, buttonObj, itemList, maxButtons) + if not (buttonName and buttonObj and itemList and maxButtons) then return end + local info = {} + local visible = ZHunterMod_Saved[buttonName]["visible"] or {} + local infoIndex = 1 + for i = 1, maxButtons do + local spellIndex = ZHunterMod_Saved[buttonName]["spells"][i] + if spellIndex and visible[spellIndex] ~= false then + info[infoIndex] = itemList[spellIndex] + infoIndex = infoIndex + 1 + end + end + if ZSpellButton_SetButtons then + buttonObj.found = ZSpellButton_SetButtons(buttonObj, info) + end +end + +local function MTH_RefreshButtonLayout(buttonName, buttonObj, itemList, maxButtons) + local resolvedButtonObj = buttonObj or getglobal(buttonName) + if not resolvedButtonObj then return end + MTH_RebuildSpellOrder(buttonName, resolvedButtonObj, itemList, maxButtons) + local setupFunc = getglobal(buttonName.."_SetupSizeAndPosition") + if type(setupFunc) == "function" then + setupFunc() + end + if ZSpellButton_UpdateButton then + ZSpellButton_UpdateButton(resolvedButtonObj) + end + if ZSpellButton_UpdateCooldown then + ZSpellButton_UpdateCooldown(resolvedButtonObj) + end + if resolvedButtonObj.count and resolvedButtonObj.name then + for i = 1, resolvedButtonObj.count do + local child = getglobal(resolvedButtonObj.name .. i) + if child and child.id then + if ZSpellButton_UpdateButton then + ZSpellButton_UpdateButton(child) + end + if ZSpellButton_UpdateCooldown then + ZSpellButton_UpdateCooldown(child) + end + end + end + end +end + +local function MTH_RefreshButtonGeometry(buttonName, buttonObj) + local resolvedButtonObj = buttonObj or getglobal(buttonName) + if not resolvedButtonObj then return end + local setupFunc = getglobal(buttonName.."_SetupSizeAndPosition") + if type(setupFunc) == "function" then + setupFunc() + end + if ZSpellButton_UpdateButton then + ZSpellButton_UpdateButton(resolvedButtonObj) + end + if ZSpellButton_UpdateCooldown then + ZSpellButton_UpdateCooldown(resolvedButtonObj) + end + if resolvedButtonObj.count and resolvedButtonObj.name then + for i = 1, resolvedButtonObj.count do + local child = getglobal(resolvedButtonObj.name .. i) + if child and child.id then + if ZSpellButton_UpdateButton then + ZSpellButton_UpdateButton(child) + end + if ZSpellButton_UpdateCooldown then + ZSpellButton_UpdateCooldown(child) + end + end + end + end +end + +local function MTH_SetButtonEnabledState(buttonName, buttonObj, enabled) + if not (buttonName and ZHunterMod_Saved and ZHunterMod_Saved[buttonName]) then return end + ZHunterMod_Saved[buttonName]["enabled"] = enabled and true or false + + local resolvedButtonObj = buttonObj or getglobal(buttonName) + if not resolvedButtonObj then return end + + if not enabled then + if resolvedButtonObj.Hide then + resolvedButtonObj:Hide() + end + if resolvedButtonObj.count and resolvedButtonObj.name then + for i = 1, resolvedButtonObj.count do + local child = getglobal(resolvedButtonObj.name .. i) + if child and child.Hide then + child:Hide() + end + end + end + return + end + + if ZHunterMod_Saved[buttonName]["parent"] and ZHunterMod_Saved[buttonName]["parent"]["hide"] then + if resolvedButtonObj.Hide then + resolvedButtonObj:Hide() + end + return + end + + if resolvedButtonObj.Show then + resolvedButtonObj:Show() + end + MTH_RefreshButtonGeometry(buttonName, resolvedButtonObj) +end + +local function MTH_GetButtonItemList(buttonName) + if buttonName == "zButtonPet" then + return getglobal("ZHunterMod_Pet_Spells") + elseif buttonName == "zButtonTrack" then + return getglobal("ZHunterMod_Track_Spells") + elseif buttonName == "zButtonAspect" then + return getglobal("ZHunterMod_Aspect_Spells") + elseif buttonName == "zButtonTrap" then + return getglobal("ZHunterMod_Trap_Spells") + elseif buttonName == "zButtonAmmo" then + local sharedBullets = getglobal("MTH_AMMO_BULLETS") + local sharedArrows = getglobal("MTH_AMMO_ARROWS") + if GetInventoryItemLink and GetItemInfo then + local link = GetInventoryItemLink("player", 18) + if link then + local _, _, itemId = string.find(link, "item:(%d+)") + if itemId then + local _, _, _, _, _, weaponType = GetItemInfo(itemId) + if weaponType == MTH_WEAPON_GUNS then + return sharedBullets or getglobal("ZHunterMod_Ammo_Buttons") + elseif weaponType == MTH_WEAPON_BOWS or weaponType == MTH_WEAPON_CROSSBOWS then + return sharedArrows or getglobal("ZHunterMod_Ammo_Buttons") + end + end + end + end + return getglobal("ZHunterMod_Ammo_Buttons") or sharedArrows + elseif buttonName == "zButtonMounts" or buttonName == "zButtonCompanions" or buttonName == "zButtonToys" then + if ZHunterMod_Saved and ZHunterMod_Saved[buttonName] and ZHunterMod_Saved[buttonName]["spells"] then + return ZHunterMod_Saved[buttonName]["spells"] + end + end + return nil +end + +local MTH_AmmoOptionsWatcher = nil + +local function MTH_RefreshAmmoOptionsIfVisible() + if not MTH_GetFrame then return end + local container = MTH_GetFrame("MetaHuntOptionsAmmo") + if not (container and container:IsVisible()) then + return + end + if type(MTH_SetupAmmoOptions) == "function" then + MTH_SetupAmmoOptions() + end +end + +local function MTH_EnsureAmmoOptionsWatcher() + if MTH_AmmoOptionsWatcher then + return + end + MTH_AmmoOptionsWatcher = CreateFrame("Frame", "MTHAmmoOptionsWatcherFrame") + if not MTH_AmmoOptionsWatcher then + return + end + MTH_AmmoOptionsWatcher:RegisterEvent("UNIT_INVENTORY_CHANGED") + MTH_AmmoOptionsWatcher:RegisterEvent("BAG_UPDATE") + MTH_AmmoOptionsWatcher:SetScript("OnEvent", function(_, eventName, unit) + eventName = eventName or event + unit = unit or arg1 + if eventName == "UNIT_INVENTORY_CHANGED" and unit and unit ~= "player" then + return + end + MTH_RefreshAmmoOptionsIfVisible() + end) +end + +local function MTH_SetupButtonOptions(containerName, buttonName, displayName, maxButtons) + local container = MTH_GetFrame(containerName) + if not container then + if MTH and MTH.Print then + MTH:Print("[MTH] Container not found: "..containerName, "error") + end + return + end + + MTH_ClearContainer(container) + + if not ZHunterMod_Saved or not ZHunterMod_Saved[buttonName] then + local errText = container:CreateFontString(containerName.."Error", "ARTWORK", "GameFontNormal") + errText:SetPoint("CENTER", container, "CENTER", 0, 0) + errText:SetText("Configuration not loaded for "..displayName) + errText:SetTextColor(1, 0.5, 0.5) + if MTH and MTH.Print then + MTH:Print("[MTH] zhunter saved data missing for "..buttonName, "error") + end + return + end + + local buttonObj = getglobal(buttonName) + local containerWidth = container:GetWidth() or 500 + local leftWidth = 260 + local rightX = leftWidth + 44 + local rightWidth = containerWidth - rightX - 12 + + local function MTH_ZB_EnsureSection(sectionName, titleText, yOffset, sectionHeight) + local section = CreateFrame("Frame", sectionName, container, "OptionFrameBoxTemplate") + if not section then return nil end + section:ClearAllPoints() + section:SetPoint("TOPLEFT", container, "TOPLEFT", 16, yOffset) + section:SetWidth(leftWidth) + section:SetHeight(sectionHeight) + section:Show() + + local title = getglobal(sectionName .. "Title") + if title then + title:SetText(titleText) + end + + return section + end + + if ZHunterMod_Saved[buttonName]["enabled"] == nil then + ZHunterMod_Saved[buttonName]["enabled"] = true + end + local enableLabel = tostring(displayName or "") + if string.len(enableLabel) > 0 then + enableLabel = string.lower(string.sub(enableLabel, 1, 1)) .. string.sub(enableLabel, 2) + end + + local enableButton = MTH_CreateCheckbox(container, containerName.."EnableButton", "Enable "..enableLabel, -8) + if enableButton then + enableButton:SetChecked(ZHunterMod_Saved[buttonName]["enabled"] and true or false) + enableButton.buttonName = buttonName + enableButton.buttonObj = buttonObj + enableButton:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local checked = self:GetChecked() and true or false + MTH_SetButtonEnabledState(self.buttonName, self.buttonObj, checked) + end) + end + + local parentSection = MTH_ZB_EnsureSection(containerName.."ParentSection", "Parent Button", -52, 116) + local childrenSection = MTH_ZB_EnsureSection(containerName.."ChildrenSection", "Children Buttons", -184, 235) + + local parentYOffset = -18 + local childrenYOffset = -18 + + local btnSize = MTH_CreateSlider(childrenSection or container, containerName.."ButtonSize", "Button Size", 10, 100, 1, childrenYOffset) + if btnSize then + btnSize:SetWidth(leftWidth - 40) + btnSize:SetValue(ZHunterMod_Saved[buttonName]["children"]["size"] or 36) + btnSize.buttonName = buttonName + btnSize.buttonObj = buttonObj + btnSize.itemList = MTH_GetButtonItemList(buttonName) + btnSize.maxButtons = maxButtons + btnSize.onChange = function(val, slider) + local frame = slider or btnSize + local btnName = frame and frame.buttonName + if not btnName or not (ZHunterMod_Saved and ZHunterMod_Saved[btnName]) then return end + if not ZHunterMod_Saved[btnName]["children"] then + ZHunterMod_Saved[btnName]["children"] = {} + end + local size = math.floor((val or 0) + 0.5) + ZHunterMod_Saved[btnName]["children"]["size"] = size + MTH_RefreshButtonGeometry(btnName, frame.buttonObj) + end + end + childrenYOffset = childrenYOffset - 50 + + local rowCount = MTH_CreateSlider(childrenSection or container, containerName.."RowCount", "Number of Rows", 1, maxButtons, 1, childrenYOffset) + if rowCount then + rowCount:SetWidth(leftWidth - 40) + rowCount:SetValue(ZHunterMod_Saved[buttonName]["rows"] or 1) + rowCount.buttonName = buttonName + rowCount.buttonObj = buttonObj + rowCount.itemList = MTH_GetButtonItemList(buttonName) + rowCount.maxButtons = maxButtons + rowCount.onChange = function(val, slider) + local frame = slider or rowCount + local btnName = frame and frame.buttonName + if not btnName or not (ZHunterMod_Saved and ZHunterMod_Saved[btnName]) then return end + ZHunterMod_Saved[btnName]["rows"] = math.floor((val or 1) + 0.5) + MTH_RefreshButtonGeometry(btnName, frame.buttonObj) + end + end + childrenYOffset = childrenYOffset - 40 + + if ZHunterMod_Saved[buttonName]["firstbutton"] == nil then + ZHunterMod_Saved[buttonName]["firstbutton"] = "RIGHT" + end + local expandLeft = MTH_CreateCheckbox(childrenSection or container, containerName.."ExpandLeft", "Expand Left (opposite side)", childrenYOffset) + if expandLeft then + expandLeft:SetChecked(ZHunterMod_Saved[buttonName]["firstbutton"] == "LEFT") + expandLeft.buttonName = buttonName + expandLeft.buttonObj = buttonObj + expandLeft:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local checked = self:GetChecked() and true or false + local btnName = self.buttonName + if not btnName or not (ZHunterMod_Saved and ZHunterMod_Saved[btnName]) then return end + ZHunterMod_Saved[btnName]["firstbutton"] = checked and "LEFT" or "RIGHT" + ZHunterMod_Saved[btnName]["horizontal"] = checked and 1 or nil + ZHunterMod_Saved[btnName]["vertical"] = checked and 1 or nil + MTH_RefreshButtonGeometry(btnName, self.buttonObj) + end) + end + childrenYOffset = childrenYOffset - 30 + + local hideClick = MTH_CreateCheckbox(childrenSection or container, containerName.."HideOnClick", "Hide Buttons On Click", childrenYOffset) + if hideClick then + hideClick:SetChecked(ZHunterMod_Saved[buttonName]["children"]["hideonclick"]) + hideClick.buttonName = buttonName + hideClick.buttonObj = buttonObj + if hideClick.buttonObj then + hideClick.buttonObj.hideonclick = ZHunterMod_Saved[buttonName]["children"]["hideonclick"] and true or false + end + hideClick:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local checked = self:GetChecked() and true or false + ZHunterMod_Saved[self.buttonName]["children"]["hideonclick"] = checked + local btnObj = self.buttonObj or getglobal(self.buttonName) + if btnObj then btnObj.hideonclick = checked end + end) + end + childrenYOffset = childrenYOffset - 25 + + local showTooltip = MTH_CreateCheckbox(childrenSection or container, containerName.."ShowTooltip", "Show Tooltip", childrenYOffset) + if showTooltip then + showTooltip:SetChecked(ZHunterMod_Saved[buttonName]["tooltip"]) + showTooltip.buttonName = buttonName + showTooltip.buttonObj = buttonObj + if showTooltip.buttonObj then + showTooltip.buttonObj.tooltip = ZHunterMod_Saved[buttonName]["tooltip"] and true or false + end + showTooltip:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local checked = self:GetChecked() and true or false + ZHunterMod_Saved[self.buttonName]["tooltip"] = checked + local btnObj = self.buttonObj or getglobal(self.buttonName) + if btnObj then btnObj.tooltip = checked end + end) + end + childrenYOffset = childrenYOffset - 25 + + if buttonName == "zButtonAmmo" then + if ZHunterMod_Saved[buttonName]["showammoname"] == nil then + ZHunterMod_Saved[buttonName]["showammoname"] = 1 + end + local showAmmoName = MTH_CreateCheckbox(childrenSection or container, containerName.."ShowAmmoName", "Show ammo name", childrenYOffset) + if showAmmoName then + showAmmoName:SetChecked(ZHunterMod_Saved[buttonName]["showammoname"]) + showAmmoName.buttonName = buttonName + showAmmoName.buttonObj = buttonObj + showAmmoName.maxButtons = maxButtons + showAmmoName:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local checked = self:GetChecked() and true or false + local btnName = self.buttonName + ZHunterMod_Saved[btnName]["showammoname"] = checked + local btnObj = self.buttonObj or getglobal(btnName) + if btnObj then + for i = 1, (btnObj.count or 0) do + local child = getglobal(btnName..i) + if child and child.ammoname and not child.isspell then + zButtonAmmo_UpdateButton(child) + end + end + if btnObj.ammoname and not btnObj.isspell then + zButtonAmmo_UpdateButton(btnObj) + end + end + end) + end + childrenYOffset = childrenYOffset - 35 + else + childrenYOffset = childrenYOffset - 35 + end + + local mainSize = MTH_CreateSlider(parentSection or container, containerName.."MainButtonSize", "Button Size", 10, 100, 1, parentYOffset) + if mainSize then + mainSize:SetWidth(leftWidth - 40) + mainSize:SetValue(ZHunterMod_Saved[buttonName]["parent"]["size"] or 36) + mainSize.buttonName = buttonName + mainSize.buttonObj = buttonObj + mainSize.onChange = function(val, slider) + local frame = slider or mainSize + local btnName = frame and frame.buttonName + local btnObj = (frame and frame.buttonObj) or (btnName and getglobal(btnName)) + if not btnName or not (ZHunterMod_Saved and ZHunterMod_Saved[btnName]) then return end + if not ZHunterMod_Saved[btnName]["parent"] then + ZHunterMod_Saved[btnName]["parent"] = {} + end + local size = math.floor((val or 0) + 0.5) + ZHunterMod_Saved[btnName]["parent"]["size"] = size + if btnObj then + btnObj:SetWidth(size) + btnObj:SetHeight(size) + end + MTH_RefreshButtonGeometry(btnName, btnObj) + end + end + parentYOffset = parentYOffset - 35 + + local hideButton = MTH_CreateCheckbox(parentSection or container, containerName.."HideButton", "Hide Button", parentYOffset) + if hideButton then + hideButton:SetChecked(ZHunterMod_Saved[buttonName]["parent"]["hide"]) + hideButton.buttonName = buttonName + hideButton.buttonObj = buttonObj + hideButton:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local checked = self:GetChecked() == 1 + ZHunterMod_Saved[self.buttonName]["parent"]["hide"] = checked + if self.buttonObj then + if checked then self.buttonObj:Hide() else self.buttonObj:Show() end + end + end) + end + parentYOffset = parentYOffset - 25 + + local useCircle = MTH_CreateCheckbox(parentSection or container, containerName.."UseCircle", "Use Circle Button", parentYOffset) + if useCircle then + useCircle:SetChecked(ZHunterMod_Saved[buttonName]["parent"]["circle"]) + useCircle.buttonName = buttonName + useCircle.buttonObj = buttonObj + useCircle:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local checked = self:GetChecked() == 1 + ZHunterMod_Saved[self.buttonName]["parent"]["circle"] = checked + if self.buttonObj and self.buttonObj.circle then + if checked then self.buttonObj.circle:Show() else self.buttonObj.circle:Hide() end + end + end) + end + + MTH_SetButtonEnabledState(buttonName, buttonObj, ZHunterMod_Saved[buttonName]["enabled"] and true or false) + + local advHeader = container:CreateFontString(containerName.."AdvHeader", "ARTWORK", "GameFontHighlight") + advHeader:SetPoint("TOPLEFT", container, "TOPLEFT", rightX, -10) + advHeader:SetText("Spell Order") + + local itemList = MTH_GetButtonItemList(buttonName) + + if not itemList or not ZHunterMod_Saved[buttonName]["spells"] then + local noData = container:CreateFontString(containerName.."NoData", "ARTWORK", "GameFontNormalSmall") + noData:SetPoint("TOPRIGHT", container, "TOPRIGHT", -80, -50) + noData:SetText("Spell data not loaded") + noData:SetTextColor(1, 0.5, 0.5) + return + end + + if not ZHunterMod_Saved[buttonName]["visible"] then + ZHunterMod_Saved[buttonName]["visible"] = {} + end + for i = 1, maxButtons do + if ZHunterMod_Saved[buttonName]["visible"][i] == nil then + ZHunterMod_Saved[buttonName]["visible"][i] = 1 + end + end + + local listY = -35 + + for i = 1, maxButtons do + local spellIndex = ZHunterMod_Saved[buttonName]["spells"][i] + local spellName = itemList[spellIndex] or itemList[i] or "" + local controlsOffset = 86 + local labelWidth = rightWidth - controlsOffset + if labelWidth < 130 then labelWidth = 130 end + local rowX = rightX + local downX = rowX + 30 + local upX = downX + 22 + local labelX = upX + 26 + + local showToggle = MTH_CreateCheckbox(container, containerName.."ShowToggle"..i, "", 0) + if showToggle then + showToggle:ClearAllPoints() + showToggle:SetPoint("TOPLEFT", container, "TOPLEFT", rowX, listY + 2) + showToggle.spellIndex = spellIndex or i + showToggle.buttonName = buttonName + showToggle.containerName = containerName + showToggle.buttonObj = buttonObj + showToggle.itemList = itemList + showToggle.maxButtons = maxButtons + local showText = getglobal(containerName.."ShowToggle"..i.."Text") + if showText then + showText:SetText("") + end + showToggle:SetChecked(ZHunterMod_Saved[buttonName]["visible"][showToggle.spellIndex] ~= false) + showToggle:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local checked = self:GetChecked() == 1 + local btnName = self.buttonName + local visibleIndex = self.spellIndex + if not ZHunterMod_Saved[btnName]["visible"] then + ZHunterMod_Saved[btnName]["visible"] = {} + end + ZHunterMod_Saved[btnName]["visible"][visibleIndex] = checked + MTH_RefreshButtonLayout(btnName, self.buttonObj, self.itemList, self.maxButtons) + end) + end + + local downBtn = CreateFrame("Button", nil, container, "UIPanelButtonTemplate") + downBtn:SetPoint("TOPLEFT", container, "TOPLEFT", downX, listY) + downBtn:SetWidth(20) + downBtn:SetHeight(20) + downBtn:SetText("-") + downBtn.spellIndex = i + downBtn.buttonName = buttonName + downBtn.containerName = containerName + downBtn.buttonObj = buttonObj + downBtn.itemList = itemList + downBtn.maxButtons = maxButtons + downBtn:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local idx = self.spellIndex + local btnName = self.buttonName + local contName = self.containerName + local maxBtns = self.maxButtons + if idx < maxBtns then + local temp = ZHunterMod_Saved[btnName]["spells"][idx+1] + ZHunterMod_Saved[btnName]["spells"][idx+1] = ZHunterMod_Saved[btnName]["spells"][idx] + ZHunterMod_Saved[btnName]["spells"][idx] = temp + MTH_RebuildSpellOrder(btnName, self.buttonObj, self.itemList, self.maxButtons) + local tabName = gsub(contName, "MetaHuntOptions", "") + MTH_ZB_RefreshTab(tabName) + end + end) + + local upBtn = CreateFrame("Button", nil, container, "UIPanelButtonTemplate") + upBtn:SetPoint("TOPLEFT", container, "TOPLEFT", upX, listY) + upBtn:SetWidth(20) + upBtn:SetHeight(20) + upBtn:SetText("+") + upBtn.spellIndex = i + upBtn.buttonName = buttonName + upBtn.containerName = containerName + upBtn.buttonObj = buttonObj + upBtn.itemList = itemList + upBtn.maxButtons = maxButtons + upBtn:SetScript("OnClick", function(self) + self = self or this + if not self then return end + local idx = self.spellIndex + local btnName = self.buttonName + local contName = self.containerName + if idx > 1 then + local temp = ZHunterMod_Saved[btnName]["spells"][idx-1] + ZHunterMod_Saved[btnName]["spells"][idx-1] = ZHunterMod_Saved[btnName]["spells"][idx] + ZHunterMod_Saved[btnName]["spells"][idx] = temp + MTH_RebuildSpellOrder(btnName, self.buttonObj, self.itemList, self.maxButtons) + local tabName = gsub(contName, "MetaHuntOptions", "") + MTH_ZB_RefreshTab(tabName) + end + end) + + local label = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + label:SetPoint("TOPLEFT", container, "TOPLEFT", labelX, listY) + label:SetText(spellName) + label:SetWidth(labelWidth) + label:SetJustifyH("LEFT") + + listY = listY - 25 + end +end + +function MTH_SetupPetOptions() + if not MTH_ZBUTTONS_READY then return end + MTH_SetupButtonOptions("MetaHuntOptionsPet", "zButtonPet", "ZPet", 10) +end + +function MTH_SetupTrackOptions() + if not MTH_ZBUTTONS_READY then return end + MTH_SetupButtonOptions("MetaHuntOptionsTrack", "zButtonTrack", "ZTrack", 11) +end + +function MTH_SetupAspectOptions() + if not MTH_ZBUTTONS_READY then return end + MTH_SetupButtonOptions("MetaHuntOptionsAspect", "zButtonAspect", "ZAspect", 7) +end + +function MTH_SetupTrapOptions() + if not MTH_ZBUTTONS_READY then return end + MTH_SetupButtonOptions("MetaHuntOptionsTrap", "zButtonTrap", "ZTrap", 4) +end + +function MTH_SetupAmmoOptions() + if not MTH_ZBUTTONS_READY then return end + MTH_EnsureAmmoOptionsWatcher() + local itemList = MTH_GetButtonItemList("zButtonAmmo") + local maxButtons = itemList and table.getn(itemList) or 0 + if maxButtons < 1 then + maxButtons = ZHunterMod_Ammo_Buttons and table.getn(ZHunterMod_Ammo_Buttons) or 11 + end + MTH_SetupButtonOptions("MetaHuntOptionsAmmo", "zButtonAmmo", "ZAmmo", maxButtons) +end + +function MTH_SetupMountsOptions() + if not MTH_ZBUTTONS_READY then return end + local itemList = MTH_GetButtonItemList("zButtonMounts") + local maxButtons = itemList and table.getn(itemList) or 1 + MTH_SetupButtonOptions("MetaHuntOptionsMounts", "zButtonMounts", "ZMounts", maxButtons) +end + +function MTH_SetupCompanionsOptions() + if not MTH_ZBUTTONS_READY then return end + local itemList = MTH_GetButtonItemList("zButtonCompanions") + local maxButtons = itemList and table.getn(itemList) or 1 + MTH_SetupButtonOptions("MetaHuntOptionsCompanions", "zButtonCompanions", "ZCompanions", maxButtons) +end + +function MTH_SetupToysOptions() + if not MTH_ZBUTTONS_READY then return end + local itemList = MTH_GetButtonItemList("zButtonToys") + local maxButtons = itemList and table.getn(itemList) or 1 + MTH_SetupButtonOptions("MetaHuntOptionsToys", "zButtonToys", "ZToys", maxButtons) +end + +function MTH_SetupSmartAmmoOptions() + if not MTH_ZBUTTONS_READY then return end + local container = MTH_GetFrame("MetaHuntOptionsSmartAmmo") + if not container then return end + + MTH_ClearContainer(container) + + local title = container:CreateFontString("MetaHuntOptionsSmartAmmoTitle", "ARTWORK", "GameFontHighlight") + title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -10) + title:SetText("Smart Ammo") + + local body = container:CreateFontString("MetaHuntOptionsSmartAmmoBody", "ARTWORK", "GameFontNormalSmall") + body:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -38) + body:SetWidth(390) + body:SetJustifyH("LEFT") + body:SetText("Configure Smart Ammo directly from MetaHunt.") + + local moduleToggle = MTH_CreateCheckbox(container, "MetaHuntOptionsSmartAmmoModule", "Enable SmartAmmo Module", -68) + local moduleEnabled = true + if MTH and MTH.IsModuleEnabled then + moduleEnabled = MTH:IsModuleEnabled("smartammo", true) and true or false + end + if moduleToggle then + moduleToggle:SetChecked(moduleEnabled) + moduleToggle:SetScript("OnClick", function(self) + self = self or this + if not self then return end + if MTH and MTH.SetModuleEnabled then + MTH:SetModuleEnabled("smartammo", self:GetChecked() and true or false) + end + MTH_SetupSmartAmmoOptions() + end) + end + + local smartToggle = MTH_CreateCheckbox(container, "MetaHuntOptionsSmartAmmoEnabled", "Enable Smart Ammo Swaps", -94) + if smartToggle then + local enabled = type(MTHSmartAmmo_GetSmartEnabled) == "function" and MTHSmartAmmo_GetSmartEnabled() and true or false + smartToggle:SetChecked(enabled) + if not moduleEnabled then + smartToggle:Disable() + end + smartToggle:SetScript("OnClick", function(self) + self = self or this + if not self then return end + if type(MTHSmartAmmo_SetSmartEnabled) == "function" then + MTHSmartAmmo_SetSmartEnabled(self:GetChecked() and 1 or nil) + end + end) + end + + local reloadToggle = MTH_CreateCheckbox(container, "MetaHuntOptionsSmartAmmoReload", "Enable Auto-Fallback", -120) + if reloadToggle then + local reloadEnabled = true + if type(MTHSmartAmmo_GetReloadEnabled) == "function" then + reloadEnabled = MTHSmartAmmo_GetReloadEnabled() and true or false + end + reloadToggle:SetChecked(reloadEnabled) + if not moduleEnabled then + reloadToggle:Disable() + end + reloadToggle:SetScript("OnClick", function(self) + self = self or this + if not self then return end + if type(MTHSmartAmmo_SetReloadEnabled) == "function" then + MTHSmartAmmo_SetReloadEnabled(self:GetChecked() and 1 or nil) + end + end) + end +end + +function MTH_SetupGeneralOptions() + if not MTH_ZBUTTONS_READY then return end + local parentFrame = MTH_GetFrame("MetaHuntOptionsGeneral") + if not parentFrame then + return + end + + MTH_ClearContainer(parentFrame) + + local stripSaved = AutoStrip_GetSaved and AutoStrip_GetSaved() or {} + + local parentWidth = parentFrame:GetWidth() or 0 + if parentWidth < 460 then + parentWidth = 520 + end + + local gutter = 16 + local colGap = 16 + local colWidth = math.floor((parentWidth - (gutter * 2) - colGap) / 2) + if colWidth < 220 then + colWidth = 220 + end + + local leftX = gutter + local rightX = gutter + colWidth + colGap + local topY = -8 + + local function ensureSection(name, title, yOffset, height, column) + local section = getglobal(name) + if not section then + section = CreateFrame("Frame", name, parentFrame, "OptionFrameBoxTemplate") + end + if not section then + return nil + end + + section:SetParent(parentFrame) + section:ClearAllPoints() + if column == "right" then + section:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", rightX, yOffset) + else + section:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", leftX, yOffset) + end + section:SetWidth(colWidth) + section:SetHeight(height) + section:Show() + + local titleFrame = getglobal(name .. "Title") + if titleFrame then + titleFrame:SetText(title) + end + + return section + end + + local function ensureCheckbox(section, name, label, yOffset, checked) + local check = getglobal(name) + if not check then + check = CreateFrame("CheckButton", name, section, "OptionsCheckButtonTemplate") + end + if not check then + return nil + end + + check:SetParent(section) + check:ClearAllPoints() + check:SetPoint("TOPLEFT", section, "TOPLEFT", 12, yOffset) + check:SetChecked(checked and true or false) + check:Show() + + local text = getglobal(name .. "Text") + if text then + text:SetText(label) + else + local lbl = section:CreateFontString(name .. "Text", "ARTWORK", "GameFontNormal") + lbl:SetPoint("LEFT", check, "RIGHT", 5, 0) + lbl:SetText(label) + end + + return check + end + + local function ensureHelpText(section, name, text, yOffset) + local help = getglobal(name) + if not help then + help = section:CreateFontString(name, "ARTWORK") + help:SetFont("Fonts\\FRIZQT__.TTF", 10) + help:SetJustifyH("LEFT") + end + help:ClearAllPoints() + help:SetPoint("TOPLEFT", section, "TOPLEFT", 14, yOffset) + help:SetWidth(colWidth - 24) + help:SetText(text) + help:Show() + end + + local smartAmmoSection = ensureSection("MetaHuntGeneralSmartAmmoBox", "Smart Ammo", topY, 186, "left") + if smartAmmoSection then + local smartModuleEnabled = MTH and MTH.IsModuleEnabled and MTH:IsModuleEnabled("smartammo", true) + local smartModuleToggle = ensureCheckbox(smartAmmoSection, "MetaHuntGeneralSmartAmmoModuleToggle", "Enable module", -10, smartModuleEnabled) + if smartModuleToggle then + smartModuleToggle:SetScript("OnClick", function() + local enabled = this:GetChecked() and true or false + if MTH and MTH.SetModuleEnabled then + local ok = MTH:SetModuleEnabled("smartammo", enabled) + if not ok then + this:SetChecked(smartModuleEnabled and true or false) + return + end + smartModuleEnabled = enabled + end + MTH_SetupGeneralOptions() + end) + end + + ensureHelpText(smartAmmoSection, "MetaHuntGeneralSmartAmmoModuleHelp", "Automatically swaps arrows/bullets based on equipped ranged weapon.", -34) + + local smartEnabled = type(MTHSmartAmmo_GetSmartEnabled) == "function" and MTHSmartAmmo_GetSmartEnabled() and true or false + local smartToggle = ensureCheckbox(smartAmmoSection, "MetaHuntGeneralSmartAmmoEnabledToggle", "Enable Smart Ammo Swaps", -68, smartEnabled) + if smartToggle then + if not smartModuleEnabled then + smartToggle:Disable() + end + smartToggle:SetScript("OnClick", function() + if type(MTHSmartAmmo_SetSmartEnabled) == "function" then + MTHSmartAmmo_SetSmartEnabled(this:GetChecked() and 1 or nil) + end + end) + end + ensureHelpText(smartAmmoSection, "MetaHuntGeneralSmartAmmoEnabledHelp", "Auto-equips arrows or bullets matching your ranged weapon.", -96) + + local reloadEnabled = type(MTHSmartAmmo_GetReloadEnabled) == "function" and MTHSmartAmmo_GetReloadEnabled() and true or false + local reloadToggle = ensureCheckbox(smartAmmoSection, "MetaHuntGeneralSmartAmmoReloadToggle", "Enable Auto-Fallback", -126, reloadEnabled) + if reloadToggle then + if not smartModuleEnabled then + reloadToggle:Disable() + end + reloadToggle:SetScript("OnClick", function() + if type(MTHSmartAmmo_SetReloadEnabled) == "function" then + MTHSmartAmmo_SetReloadEnabled(this:GetChecked() and 1 or nil) + end + end) + end + ensureHelpText(smartAmmoSection, "MetaHuntGeneralSmartAmmoReloadHelp", "Falls back to any available ammo when preferred ammo is missing.", -154) + end + + local stripSection = ensureSection("MetaHuntGeneralAutoStripBox", "Auto-Strip", topY - 202, 122, "left") + if stripSection then + local autoStripToggle = ensureCheckbox(stripSection, "MetaHuntGeneralAutoStripToggle", "Enable Auto-Strip on Combat Exit", -10, stripSaved["autostrip"] and true or false) + if autoStripToggle then + autoStripToggle:SetScript("OnClick", function() + if type(AutoStrip_SetAutoStripToggle) == "function" then + AutoStrip_SetAutoStripToggle(this:GetChecked()) + end + end) + end + + local displayToggle = ensureCheckbox(stripSection, "MetaHuntGeneralAutoStripDisplay", "Show Strip Button", -36, stripSaved["display"] and true or false) + if displayToggle then + displayToggle:SetScript("OnClick", function() + if type(AutoStrip_SetDisplayToggle) == "function" then + AutoStrip_SetDisplayToggle(this:GetChecked()) + end + end) + end + + ensureHelpText(stripSection, "MetaHuntGeneralAutoStripHelp", "Auto-strip unequips items when combat ends. Requires at least one empty bag slot.", -64) + end + + local antiSection = ensureSection("MetaHuntGeneralAntiDazeBox", "Anti-Daze", topY - 338, 96, "left") + if antiSection then + local antiDazeEnabled = (AntiDaze_GetEnabled and AntiDaze_GetEnabled()) or false + local antiToggle = ensureCheckbox(antiSection, "MetaHuntGeneralAntiDazeToggle", "Enable Anti-Daze", -10, antiDazeEnabled) + if antiToggle then + antiToggle:SetScript("OnClick", function() + local enabled = this:GetChecked() and true or false + if type(AntiDaze_SetEnabled) == "function" then + AntiDaze_SetEnabled(enabled, 1) + end + if DEFAULT_CHAT_FRAME then + if MTH and MTH.Print then + MTH:Print("AntiDaze " .. (enabled and "Enabled." or "Disabled.")) + else + DEFAULT_CHAT_FRAME:AddMessage("AntiDaze " .. (enabled and "Enabled." or "Disabled.")) + end + end + end) + end + + ensureHelpText(antiSection, "MetaHuntGeneralAntiDazeHelp", "Cancels Cheetah/Pack when dazed.", -40) + end + + local tooltipsSection = ensureSection("MetaHuntGeneralTooltipsBox", "Tooltips", topY, 356, "right") + if tooltipsSection then + local tooltipsStore = MTH and MTH.GetModuleSavedVariables and MTH:GetModuleSavedVariables("tooltips") + if type(tooltipsStore) ~= "table" then + tooltipsStore = {} + end + if tooltipsStore.beastTooltips == nil then + tooltipsStore.beastTooltips = true + end + if tooltipsStore.ammoVendorTooltips == nil then + tooltipsStore.ammoVendorTooltips = true + end + if tooltipsStore.foodItemTooltips == nil then + if type(FOM_Config) == "table" and FOM_Config.Tooltip ~= nil then + tooltipsStore.foodItemTooltips = FOM_Config.Tooltip and true or false + else + tooltipsStore.foodItemTooltips = true + end + end + if tooltipsStore.ownPetTooltips == nil then + tooltipsStore.ownPetTooltips = false + end + + local moduleEnabled = MTH and MTH.IsModuleEnabled and MTH:IsModuleEnabled("tooltips", true) + local foodLabel = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_FOOD", "Activate on food")) or "Activate on food on food" + local foodHelp = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_FOOD_HELP", "Show your current pet's food preference directly in item tooltips.")) or "Show your current pet's food preference directly in item tooltips." + + local moduleToggle = ensureCheckbox(tooltipsSection, "MetaHuntGeneralTooltipsModuleToggle", "Enable module", -10, moduleEnabled) + if moduleToggle then + moduleToggle:SetScript("OnClick", function() + local enabled = this:GetChecked() and true or false + if MTH and MTH.SetModuleEnabled then + local ok = MTH:SetModuleEnabled("tooltips", enabled) + if not ok then + this:SetChecked(moduleEnabled and true or false) + return + end + moduleEnabled = enabled + end + end) + end + + ensureHelpText(tooltipsSection, "MetaHuntGeneralTooltipsModuleHelp", "This module allows to enhance NPC tooltips with additional information useful for hunters.", -34) + + local beastToggle = ensureCheckbox(tooltipsSection, "MetaHuntGeneralTooltipsBeastToggle", "Activate on Beasts", -86, tooltipsStore.beastTooltips and true or false) + if beastToggle then + beastToggle:SetScript("OnClick", function() + local enabled = this:GetChecked() and true or false + tooltipsStore.beastTooltips = enabled + local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") + if tooltipsModule and tooltipsModule.SetBeastTooltipsEnabled then + tooltipsModule:SetBeastTooltipsEnabled(enabled) + end + end) + end + + ensureHelpText(tooltipsSection, "MetaHuntGeneralTooltipsBeastHelp", "When mouseover on a Beast, its tooltip will show if it can learn you any pet abilities. They appear Green if you already know them, and Red if you don't know them yet.", -110) + + local ownPetLabel = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_OWNPET", "Activate on my pet")) or "Activate on my pet" + local ownPetHelp = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_OWNPET_HELP", "Show your own pet status details in tooltip when hovering your pet context.")) or "Show your own pet status details in tooltip when hovering your pet context." + local ownPetToggle = ensureCheckbox(tooltipsSection, "MetaHuntGeneralTooltipsOwnPetToggle", ownPetLabel, -158, tooltipsStore.ownPetTooltips and true or false) + if ownPetToggle then + ownPetToggle:SetScript("OnClick", function() + local enabled = this:GetChecked() and true or false + tooltipsStore.ownPetTooltips = enabled + local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") + if tooltipsModule and tooltipsModule.SetOwnPetTooltipsEnabled then + tooltipsModule:SetOwnPetTooltipsEnabled(enabled) + end + end) + end + + ensureHelpText(tooltipsSection, "MetaHuntGeneralTooltipsOwnPetHelp", ownPetHelp, -182) + + local ammoToggle = ensureCheckbox(tooltipsSection, "MetaHuntGeneralTooltipsAmmoVendorToggle", "Activate on Vendors", -222, tooltipsStore.ammoVendorTooltips and true or false) + if ammoToggle then + ammoToggle:SetScript("OnClick", function() + local enabled = this:GetChecked() and true or false + tooltipsStore.ammoVendorTooltips = enabled + local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") + if tooltipsModule and tooltipsModule.SetAmmoVendorTooltipsEnabled then + tooltipsModule:SetAmmoVendorTooltipsEnabled(enabled) + end + end) + end + + ensureHelpText(tooltipsSection, "MetaHuntGeneralTooltipsVendorHelp", "When mouseover on a vendor, its tooltip will tell if it sells Arrows and/or Bullets.", -246) + + local foodToggle = ensureCheckbox(tooltipsSection, "MetaHuntGeneralTooltipsFoodToggle", foodLabel, -274, tooltipsStore.foodItemTooltips and true or false) + if foodToggle then + foodToggle:SetScript("OnClick", function() + local enabled = this:GetChecked() and true or false + tooltipsStore.foodItemTooltips = enabled + local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") + if tooltipsModule and tooltipsModule.SetFoodItemTooltipsEnabled then + tooltipsModule:SetFoodItemTooltipsEnabled(enabled) + end + end) + end + + ensureHelpText(tooltipsSection, "MetaHuntGeneralTooltipsFoodHelp", foodHelp, -298) + end + +end diff --git a/api/options.lua b/api/options.lua new file mode 100644 index 0000000..58d2835 --- /dev/null +++ b/api/options.lua @@ -0,0 +1,125 @@ +-- MetaHunt unified options window + +function MTH_GetFrame(name) + return getglobal(name) +end + +function MTH_OptionsRequire(moduleName, requiredGlobals) + local missing = {} + for i = 1, table.getn(requiredGlobals) do + local key = requiredGlobals[i] + if _G[key] == nil then + table.insert(missing, key) + end + end + + if table.getn(missing) == 0 then + return true + end + + local message = "[MTH OPTIONS] " .. tostring(moduleName) .. " missing dependencies: " .. table.concat(missing, ", ") + if MTH and MTH.Print then + MTH:Print(message, "error") + elseif type(MTH_Log) == "function" then + MTH_Log(message, "error") + elseif DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + DEFAULT_CHAT_FRAME:AddMessage(message) + end + + return false +end + +function MTH_CreateActionButton(parent, name, text, x, y, width, height, onClick) + local button = CreateFrame("Button", name, parent, "UIPanelButtonTemplate") + if not button then return nil end + button:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y) + button:SetWidth(width or 120) + button:SetHeight(height or 24) + button:SetText(text) + if onClick then + button:SetScript("OnClick", onClick) + end + return button +end + +-- Helper: Create a slider control +function MTH_CreateSlider(parent, name, label, minVal, maxVal, step, yOffset) + local slider = CreateFrame("Slider", name, parent, "OptionsSliderTemplate") + if not slider then return nil end + + slider:SetPoint("TOPLEFT", parent, "TOPLEFT", 15, yOffset) + slider:SetWidth(280) + slider:SetMinMaxValues(minVal, maxVal) + slider:SetValueStep(step) + slider:SetValue(minVal) + + -- Labels + local text = slider:CreateFontString(name.."Text", "ARTWORK", "GameFontNormalSmall") + text:SetPoint("BOTTOM", slider, "TOP", 0, 2) + text:SetText(label) + + local value = slider:CreateFontString(name.."Value", "ARTWORK", "GameFontHighlight") + value:SetPoint("TOP", slider, "BOTTOM", 0, 2) + value:SetText(tostring(minVal)) + + -- Hide default low/high labels from OptionsSliderTemplate + local lowLabel = getglobal(name.."Low") + local highLabel = getglobal(name.."High") + if lowLabel then lowLabel:Hide() end + if highLabel then highLabel:Hide() end + + slider:SetScript("OnValueChanged", function(self, newValue) + local activeSlider = self or this or slider + local current = tonumber(newValue) + if not current and activeSlider and activeSlider.GetValue then + current = tonumber(activeSlider:GetValue()) + end + if not current then + current = minVal + end + value:SetText(tostring(math.floor(current + 0.5))) + if slider.onChange then slider.onChange(current, activeSlider) end + end) + + return slider +end + +-- Helper: Create a checkbox control +MTH_CreateCheckbox = function(parent, name, label, yOffset, xOffset) + local check = CreateFrame("CheckButton", name, parent, "UICheckButtonTemplate") + if not check then return nil end + + check:SetPoint("TOPLEFT", parent, "TOPLEFT", xOffset or 15, yOffset) + + local text = check:CreateFontString(name.."Text", "ARTWORK", "GameFontNormalSmall") + text:SetPoint("LEFT", check, "RIGHT", 5, 0) + text:SetText(label) + + return check +end + +-- Helper: Clear all children from a container +MTH_ClearContainer = function(container) + if not container then return end + -- Hide all child frames + local children = { container:GetChildren() } + for i = 1, table.getn(children) do + local child = children[i] + if child then + child:Hide() + child:ClearAllPoints() + end + end + -- Hide all font strings and textures + local regions = { container:GetRegions() } + for i = 1, table.getn(regions) do + local region = regions[i] + if region and region ~= container then + region:Hide() + if region.ClearAllPoints then + region:ClearAllPoints() + end + end + end +end + diff --git a/api/options.xml b/api/options.xml new file mode 100644 index 0000000..d3bcd80 --- /dev/null +++ b/api/options.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + this:RegisterForDrag("LeftButton") + + + if ( this.isMoving ) then + this:StopMovingOrSizing(); + this.isMoving = false; + end + + + this:StartMoving(); + this.isMoving = true; + + + this:StopMovingOrSizing(); + this.isMoving = false; + + + + diff --git a/api/uilib.lua b/api/uilib.lua new file mode 100644 index 0000000..0111ca5 --- /dev/null +++ b/api/uilib.lua @@ -0,0 +1,125 @@ +MTH_UI = MTH_UI or {} + +MTH_UI.version = "0.1.0" +MTH_UI.pages = MTH_UI.pages or {} +MTH_UI.pageOrder = MTH_UI.pageOrder or {} +MTH_UI.callbacks = MTH_UI.callbacks or {} + +local function MTH_UI_SafeCall(handler, a1, a2, a3) + if type(handler) ~= "function" then + return false, "handler is not a function" + end + local ok, resultOrError = pcall(handler, a1, a2, a3) + if not ok then + if MTH and MTH.Print then + MTH:Print("[MTH_UI] " .. tostring(resultOrError), "error") + end + return false, resultOrError + end + return true, resultOrError +end + +local function MTH_UI_CopyTable(value) + if type(value) ~= "table" then return value end + local copy = {} + for key, nestedValue in pairs(value) do + copy[key] = MTH_UI_CopyTable(nestedValue) + end + return copy +end + +local function MTH_UI_ApplyDefaults(target, defaults) + if type(target) ~= "table" or type(defaults) ~= "table" then return target end + for key, defaultValue in pairs(defaults) do + if target[key] == nil then + target[key] = MTH_UI_CopyTable(defaultValue) + elseif type(target[key]) == "table" and type(defaultValue) == "table" then + MTH_UI_ApplyDefaults(target[key], defaultValue) + end + end + return target +end + +function MTH_UI.CloneTable(value) + return MTH_UI_CopyTable(value) +end + +function MTH_UI.MergeDefaults(target, defaults) + return MTH_UI_ApplyDefaults(target, defaults) +end + +function MTH_UI.ResolveValue(value, context) + if type(value) == "function" then + local ok, resolved = MTH_UI_SafeCall(value, context) + if ok then return resolved end + return nil + end + return value +end + +function MTH_UI.RegisterCallback(eventName, callbackKey, handler) + if type(eventName) ~= "string" or eventName == "" then return false end + if type(callbackKey) ~= "string" or callbackKey == "" then return false end + if type(handler) ~= "function" then return false end + + if not MTH_UI.callbacks[eventName] then + MTH_UI.callbacks[eventName] = {} + end + MTH_UI.callbacks[eventName][callbackKey] = handler + return true +end + +function MTH_UI.UnregisterCallback(eventName, callbackKey) + if not MTH_UI.callbacks[eventName] then return false end + if not MTH_UI.callbacks[eventName][callbackKey] then return false end + MTH_UI.callbacks[eventName][callbackKey] = nil + return true +end + +function MTH_UI.FireCallback(eventName, payload) + local listeners = MTH_UI.callbacks[eventName] + if not listeners then return end + for _, handler in pairs(listeners) do + MTH_UI_SafeCall(handler, payload) + end +end + +function MTH_UI.RegisterPage(pageId, pageDefinition) + if type(pageId) ~= "string" or pageId == "" then return false, "invalid page id" end + if type(pageDefinition) ~= "table" then return false, "page definition must be a table" end + + local rootType = pageDefinition.type or "group" + if rootType ~= "group" then + return false, "root page type must be group" + end + + local existing = MTH_UI.pages[pageId] + MTH_UI.pages[pageId] = MTH_UI_CopyTable(pageDefinition) + MTH_UI.pages[pageId].id = pageId + + if not existing then + table.insert(MTH_UI.pageOrder, pageId) + end + + MTH_UI.FireCallback("PageRegistered", pageId) + return true +end + +function MTH_UI.GetPage(pageId) + return MTH_UI.pages[pageId] +end + +function MTH_UI.ListPages() + local pageList = {} + for _, pageId in ipairs(MTH_UI.pageOrder) do + if MTH_UI.pages[pageId] then + table.insert(pageList, pageId) + end + end + return pageList +end + +function MTH_UI.NotifyPageChanged(pageId) + MTH_UI.FireCallback("PageChanged", pageId) +end + diff --git a/api/uirenderer.lua b/api/uirenderer.lua new file mode 100644 index 0000000..0e6d4bd --- /dev/null +++ b/api/uirenderer.lua @@ -0,0 +1,190 @@ +MTH_UIRenderer = MTH_UIRenderer or {} + +MTH_UIRenderer.activeWidgets = MTH_UIRenderer.activeWidgets or {} + +local function MTH_UIR_Clear(frame) + if not frame then return end + local widgets = frame._mthWidgets + if not widgets then return end + for _, widget in ipairs(widgets) do + if widget and widget.Hide then + widget:Hide() + end + end + frame._mthWidgets = {} +end + +local function MTH_UIR_AddWidget(frame, widget) + if not frame._mthWidgets then + frame._mthWidgets = {} + end + table.insert(frame._mthWidgets, widget) +end + +local function MTH_UIR_MakeLabel(frame, text, x, y, width, template) + local label = frame:CreateFontString(nil, "ARTWORK", template or "GameFontNormalSmall") + label:SetPoint("TOPLEFT", frame, "TOPLEFT", x, y) + label:SetWidth(width) + label:SetJustifyH("LEFT") + label:SetText(text or "") + MTH_UIR_AddWidget(frame, label) + return label +end + +local function MTH_UIR_SetCheckboxLabel(checkButton, text) + local textRegion = nil + local checkName = checkButton and checkButton.GetName and checkButton:GetName() + if type(checkName) == "string" and checkName ~= "" then + textRegion = getglobal(checkName .. "Text") + end + if not textRegion and checkButton and checkButton.Text then + textRegion = checkButton.Text + end + if textRegion then + textRegion:SetText(text or "") + end +end + +local function MTH_UIR_CreateControl(frame, key, option, context, x, y, width) + local optionType = option.type or "description" + local optionName = MTH_UI.ResolveValue(option.name, context) + if optionType == "description" then + local label = MTH_UIR_MakeLabel(frame, optionName or "", x, y, width, "GameFontHighlightSmall") + if label then label:SetTextColor(0.90, 0.90, 0.90) end + return 18 + end + + if optionType == "header" then + local label = MTH_UIR_MakeLabel(frame, optionName or "", x, y, width, "GameFontNormal") + if label then label:SetTextColor(1.00, 0.82, 0.00) end + return 24 + end + + if optionType == "toggle" then + local checkButton = CreateFrame("CheckButton", nil, frame, "UICheckButtonTemplate") + checkButton:SetPoint("TOPLEFT", frame, "TOPLEFT", x, y + 2) + checkButton:SetChecked(MTH_UI.ResolveValue(option.get, context) and 1 or nil) + MTH_UIR_SetCheckboxLabel(checkButton, optionName or "") + checkButton:SetScript("OnClick", function(self) + self = self or this + if not self then return end + if option.set then + option.set(context, self:GetChecked() == 1) + end + end) + if MTH_UI.ResolveValue(option.disabled, context) then + checkButton:Disable() + end + MTH_UIR_AddWidget(frame, checkButton) + return 24 + end + + if optionType == "execute" then + local button = CreateFrame("Button", nil, frame) + button:SetPoint("TOPLEFT", frame, "TOPLEFT", x, y) + button:SetHeight(20) + button:SetWidth(math.min(180, width)) + button:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, + tileSize = 16, + edgeSize = 16, + insets = { left = 3, right = 3, top = 3, bottom = 3 }, + }) + button:SetBackdropColor(0.1, 0.1, 0.1, 0.5) + button:SetBackdropBorderColor(0.4, 0.4, 0.4) + + local text = button:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + text:SetPoint("CENTER", button, "CENTER", 0, 0) + text:SetText(optionName or "Run") + text:SetTextColor(1.00, 0.82, 0.00) + + button:SetScript("OnEnter", function(self) self = self or this if self then self:SetBackdropBorderColor(0.6, 0.6, 0.6) end end) + button:SetScript("OnLeave", function(self) self = self or this if self then self:SetBackdropBorderColor(0.4, 0.4, 0.4) end end) + button:SetScript("OnClick", function() + if option.func then + option.func(context) + end + end) + if MTH_UI.ResolveValue(option.disabled, context) then + button:Disable() + end + MTH_UIR_AddWidget(frame, button) + return 24 + end + + if optionType == "select" then + local label = MTH_UIR_MakeLabel(frame, optionName or "Select", x, y, width, "GameFontNormalSmall") + if label then label:SetTextColor(1.00, 0.82, 0.00) end + + local button = CreateFrame("Button", nil, frame) + button:SetPoint("TOPLEFT", frame, "TOPLEFT", x, y - 16) + button:SetHeight(20) + button:SetWidth(math.min(220, width)) + button:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, + tileSize = 16, + edgeSize = 16, + insets = { left = 3, right = 3, top = 3, bottom = 3 }, + }) + button:SetBackdropColor(0.1, 0.1, 0.1, 0.5) + button:SetBackdropBorderColor(0.4, 0.4, 0.4) + + local currentValue = MTH_UI.ResolveValue(option.get, context) + local text = button:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") + text:SetPoint("CENTER", button, "CENTER", 0, 0) + text:SetText(tostring(currentValue or "Choose")) + text:SetTextColor(0.90, 0.90, 0.90) + + button:SetScript("OnEnter", function(self) self = self or this if self then self:SetBackdropBorderColor(0.6, 0.6, 0.6) end end) + button:SetScript("OnLeave", function(self) self = self or this if self then self:SetBackdropBorderColor(0.4, 0.4, 0.4) end end) + button:SetScript("OnClick", function() + if option.onClick then + option.onClick(context, option) + end + end) + if MTH_UI.ResolveValue(option.disabled, context) then + button:Disable() + end + MTH_UIR_AddWidget(frame, button) + return 42 + end + + MTH_UIR_MakeLabel(frame, optionName or ("Unsupported type: " .. tostring(optionType)), x, y, width, "GameFontDisableSmall") + return 18 +end + +function MTH_UIRenderer.RenderPage(frame, pageDefinition, context) + if not frame or not pageDefinition then return false, "invalid render target" end + + MTH_UIR_Clear(frame) + + local args = pageDefinition.args or {} + local orderedKeys = {} + for key in pairs(args) do + table.insert(orderedKeys, key) + end + table.sort(orderedKeys, function(leftKey, rightKey) + local leftOrder = (args[leftKey] and args[leftKey].order) or 9999 + local rightOrder = (args[rightKey] and args[rightKey].order) or 9999 + if leftOrder ~= rightOrder then return leftOrder < rightOrder end + return tostring(leftKey) < tostring(rightKey) + end) + + local cursorY = -8 + for _, key in ipairs(orderedKeys) do + local option = args[key] + if option then + local hidden = MTH_UI.ResolveValue(option.hidden, context) + if not hidden then + local consumedHeight = MTH_UIR_CreateControl(frame, key, option, context, 8, cursorY, frame:GetWidth() - 16) + cursorY = cursorY - consumedHeight + end + end + end + + return true +end diff --git a/data/ds-beasts-extra.lua b/data/ds-beasts-extra.lua new file mode 100644 index 0000000..d2ba109 --- /dev/null +++ b/data/ds-beasts-extra.lua @@ -0,0 +1,2101 @@ +-- MetaHunt Beast Extras (generated from pfQuest + pfQuest-turtle) +-- Adds beast NPC IDs missing from base mth-beasts using known beast-name dictionaries +if not MTH_DS then MTH_DS = {} end +if not MTH_DS_Beasts then MTH_DS_Beasts = {} end + +if not MTH_DS_Beasts[521] then + MTH_DS_Beasts[521] = { + ["name"] = "Lupos", + ["family"] = "Unknown", + ["lvl"] = "23", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 14400, + ["respawnMaxSeconds"] = 14400, + ["respawnSamples"] = 6, + ["coords"] = { + {14.9, 29, 10}, + {32.6, 25.8, 10}, + {64.3, 24, 10}, + }, + } +end + +if not MTH_DS_Beasts[728] then + MTH_DS_Beasts[728] = { + ["name"] = "Bhag'Thera", + ["family"] = "Cats", + ["lvl"] = "40", + ["fac"] = "Unknown", + ["abilities"] = "Dash 2", + ["attackSpeed"] = "1.5", + ["elite"] = true, + ["respawnMinSeconds"] = 240, + ["respawnMaxSeconds"] = 600, + ["respawnSamples"] = 2, + ["coords"] = { + {49.6, 24, 33}, + }, + } +end + +if not MTH_DS_Beasts[729] then + MTH_DS_Beasts[729] = { + ["name"] = "Sin'Dall", + ["family"] = "Cats", + ["lvl"] = "37", + ["fac"] = "Unknown", + ["abilities"] = "Dash 1", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 1, + ["coords"] = { + {32.2, 17.4, 33}, + }, + } +end + +if not MTH_DS_Beasts[1199] then + MTH_DS_Beasts[1199] = { + ["name"] = "Juvenile Snow Leopard", + ["family"] = "Unknown", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 30, + ["coords"] = { + {40.5, 62, 1}, + {38.8, 61, 1}, + {38.3, 60.9, 1}, + {38.6, 60.7, 1}, + {35.5, 60.6, 1}, + {36.7, 60.5, 1}, + {43.3, 60.3, 1}, + {39.8, 60.1, 1}, + {41.5, 60.1, 1}, + {43.6, 59.7, 1}, + {40.2, 58.9, 1}, + {45.4, 58.7, 1}, + {45.3, 58.5, 1}, + {42.4, 57.9, 1}, + {43, 56.5, 1}, + }, + } +end + +if not MTH_DS_Beasts[1225] then + MTH_DS_Beasts[1225] = { + ["name"] = "Ol' Sooty", + ["family"] = "Bears", + ["lvl"] = "20", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 3, + ["coords"] = { + {42.5, 64.7, 38}, + {20.3, 77.4, 5602}, + }, + } +end + +if not MTH_DS_Beasts[1516] then + MTH_DS_Beasts[1516] = { + ["name"] = "Konda", + ["family"] = "Gorillas", + ["lvl"] = "43", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["unique"] = true, + ["coords"] = { + {36.3, 63.9, 33}, + }, + } +end + +if not MTH_DS_Beasts[2275] then + MTH_DS_Beasts[2275] = { + ["name"] = "Enraged Stanley", + ["family"] = "Wolves", + ["lvl"] = "24", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.3", + ["unique"] = true, + ["coords"] = { + }, + } +end + +if not MTH_DS_Beasts[2321] then + MTH_DS_Beasts[2321] = { + ["name"] = "Foreststrider Fledgling", + ["family"] = "Tallstriders", + ["lvl"] = "11-13", + ["fac"] = "Unknown", + ["abilities"] = "Cower 1, Strider Presence", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 196, + ["coords"] = { + {34.8, 87.3, 148}, + {43.8, 70.7, 148}, + {43.1, 70.5, 148}, + {42.2, 70.1, 148}, + {44.3, 68.9, 148}, + {39, 68.8, 148}, + {41.4, 68.6, 148}, + {42.3, 68.5, 148}, + {38.4, 68.1, 148}, + {43.2, 68.1, 148}, + {41.7, 67.5, 148}, + {42.8, 67.3, 148}, + {43.9, 67.3, 148}, + {39.9, 67.1, 148}, + {38.8, 67, 148}, + {43.3, 66.1, 148}, + {41.6, 65.8, 148}, + {39.3, 65.6, 148}, + {42, 65.5, 148}, + {38.5, 65.4, 148}, + {38.5, 64.7, 148}, + {38.1, 63.9, 148}, + {38.2, 62.6, 148}, + {39.5, 62.5, 148}, + {39.1, 60.7, 148}, + {39.6, 59.7, 148}, + {38.2, 59.4, 148}, + {38.5, 58.2, 148}, + {45.2, 58, 148}, + {39.8, 57.4, 148}, + {38.2, 56.6, 148}, + {44.3, 55.8, 148}, + {42.9, 55.3, 148}, + {43.6, 55.1, 148}, + {45.7, 54.8, 148}, + {43.4, 53.5, 148}, + {46.1, 53, 148}, + {39, 48.7, 148}, + {38.2, 47.5, 148}, + {42.4, 47.5, 148}, + {41.9, 46, 148}, + {47.2, 44.8, 148}, + {43.3, 44.5, 148}, + {44.6, 44.3, 148}, + {43.9, 43.2, 148}, + {41.1, 43.2, 148}, + {45.6, 42.9, 148}, + {42.8, 42.6, 148}, + {47.2, 42.6, 148}, + {44.7, 42.6, 148}, + {41.3, 41.5, 148}, + {44.4, 41.4, 148}, + {47.3, 41.3, 148}, + {40.3, 41.3, 148}, + {45.4, 41.1, 148}, + {47.9, 41.1, 148}, + {43.4, 40.7, 148}, + {47.6, 40.1, 148}, + {40.1, 39.8, 148}, + {42.6, 39.7, 148}, + {39.3, 38.5, 148}, + {40.5, 38.4, 148}, + {40.5, 36.7, 148}, + {39.7, 36.6, 148}, + {38.8, 36.4, 148}, + {42.4, 35.1, 148}, + {40.4, 35, 148}, + {39.3, 34.8, 148}, + {41.5, 34.7, 148}, + {39.6, 33.9, 148}, + {41.9, 33.7, 148}, + {38.5, 33.5, 148}, + {42.8, 33.5, 148}, + {43.8, 33.4, 148}, + {44.6, 33.4, 148}, + {45.4, 32.2, 148}, + {44, 31.6, 148}, + {44.5, 29.9, 148}, + {43.9, 29.3, 148}, + {44.5, 28.8, 148}, + {43.7, 28.1, 148}, + {45.4, 27.4, 148}, + {42.8, 26.4, 148}, + {44.2, 26.3, 148}, + {46.8, 25.8, 148}, + {43.7, 25.8, 148}, + {44.6, 25.7, 148}, + {45.8, 24.9, 148}, + {44.6, 24.5, 148}, + {46.9, 24.5, 148}, + {47.9, 23.6, 148}, + {45.6, 23.4, 148}, + {44.2, 23.4, 148}, + {45.4, 22.9, 148}, + {48.9, 22.8, 148}, + {47.1, 22.5, 148}, + {45.9, 22.2, 148}, + {29.5, 31.1, 361}, + }, + } +end + +if not MTH_DS_Beasts[2560] then + MTH_DS_Beasts[2560] = { + ["name"] = "Highland Thrasher", + ["family"] = "Raptors", + ["lvl"] = "33-34", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 400, + ["respawnSamples"] = 131, + ["coords"] = { + {41, 69.9, 45}, + {39.5, 69.9, 45}, + {38.7, 69.1, 45}, + {39.7, 68.9, 45}, + {56, 68.8, 45}, + {37.4, 67.9, 45}, + {58.8, 67.3, 45}, + {38.4, 67.2, 45}, + {40.1, 67.1, 45}, + {36.9, 66.6, 45}, + {39.5, 65.9, 45}, + {35.4, 65.8, 45}, + {62.4, 65.3, 45}, + {31.3, 65.2, 45}, + {36.2, 65.1, 45}, + {39.2, 65, 45}, + {37.4, 64.9, 45}, + {41, 64.8, 45}, + {34.9, 64.3, 45}, + {36.5, 64, 45}, + {32.4, 64, 45}, + {34.1, 61.8, 45}, + {32.1, 60.3, 45}, + {54.5, 59.6, 45}, + {32.1, 59, 45}, + {19.8, 58, 45}, + {31.1, 57.6, 45}, + {54.1, 57.6, 45}, + {32.5, 56.9, 45}, + {32.6, 56.6, 45}, + {30.5, 56.4, 45}, + {30.1, 56.1, 45}, + {31.4, 55.9, 45}, + {21.9, 53.6, 45}, + {23.2, 49.7, 45}, + {19.1, 48.7, 45}, + {20.1, 48.2, 45}, + {20.8, 47.7, 45}, + {22, 47.7, 45}, + {18, 47.2, 45}, + {18.5, 46.3, 45}, + {20.1, 45, 45}, + {40.2, 41.1, 45}, + {41.7, 39.6, 45}, + {38.1, 38.7, 45}, + {43.9, 38.4, 45}, + {39.4, 38.4, 45}, + {37, 38.2, 45}, + {37.9, 36.9, 45}, + {40.2, 36.8, 45}, + {36.5, 36.7, 45}, + {43, 36.4, 45}, + {40.2, 35.3, 45}, + {41.2, 35.3, 45}, + {36.7, 35.2, 45}, + {46.5, 35.1, 45}, + {39.9, 34.2, 45}, + {43.9, 34, 45}, + {38, 33.5, 45}, + {37.8, 33.3, 45}, + {36.3, 33.1, 45}, + {41, 30, 45}, + {37.1, 29.9, 45}, + {39.9, 29.9, 45}, + }, + } +end + +if not MTH_DS_Beasts[2563] then + MTH_DS_Beasts[2563] = { + ["name"] = "Plains Creeper", + ["family"] = "Spiders", + ["lvl"] = "32-33", + ["fac"] = "Unknown", + ["abilities"] = "Bite 5, Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 400, + ["respawnSamples"] = 229, + ["coords"] = { + {42, 75.6, 45}, + {42, 72.7, 45}, + {39.7, 70.9, 45}, + {44.2, 70.5, 45}, + {40.8, 67.8, 45}, + {45.3, 67.4, 45}, + {34.9, 66.2, 45}, + {34, 64.9, 45}, + {45.1, 64.8, 45}, + {43.1, 64.5, 45}, + {45.7, 64.5, 45}, + {38.6, 62.9, 45}, + {43.5, 62.7, 45}, + {41.1, 62.5, 45}, + {41.7, 61.7, 45}, + {42.6, 61.5, 45}, + {50.2, 61.5, 45}, + {47.9, 61.2, 45}, + {32.7, 60.9, 45}, + {48.1, 60.6, 45}, + {42, 59.8, 45}, + {43, 59, 45}, + {41.2, 58.8, 45}, + {47.9, 57.6, 45}, + {43.1, 57.4, 45}, + {69, 56.3, 45}, + {69.8, 56.2, 45}, + {68.1, 56, 45}, + {69.7, 55.1, 45}, + {22.4, 54.6, 45}, + {32.8, 54.4, 45}, + {71.1, 54.3, 45}, + {68, 53.1, 45}, + {67, 52.6, 45}, + {18.1, 52.3, 45}, + {22.7, 52, 45}, + {40, 51.9, 45}, + {48.7, 50.8, 45}, + {18.1, 50.7, 45}, + {58.7, 50.7, 45}, + {65.6, 50.7, 45}, + {69, 50.6, 45}, + {61.6, 50.6, 45}, + {61.4, 50.6, 45}, + {19.9, 50.5, 45}, + {71.8, 50.5, 45}, + {69.9, 50.2, 45}, + {17.3, 49.8, 45}, + {59.8, 49.7, 45}, + {21.6, 49.6, 45}, + {69.9, 49.3, 45}, + {66.6, 49.3, 45}, + {61.6, 49.2, 45}, + {41.4, 48.7, 45}, + {71.8, 48.5, 45}, + {70, 48.3, 45}, + {23.8, 48.3, 45}, + {69, 47.9, 45}, + {73, 47.2, 45}, + {71.1, 47, 45}, + {73.7, 46.9, 45}, + {70.5, 46.7, 45}, + {60.6, 46.6, 45}, + {68.4, 46.5, 45}, + {39.4, 45.4, 45}, + {56, 45.4, 45}, + {17.2, 45.3, 45}, + {22.6, 45.2, 45}, + {57.8, 45.2, 45}, + {18.1, 44.7, 45}, + {16.9, 43.7, 45}, + {64.2, 43.7, 45}, + {19.4, 43.6, 45}, + {62.2, 42.6, 45}, + {17.2, 42.3, 45}, + {20.8, 42.2, 45}, + {50.6, 41.8, 45}, + {20, 41, 45}, + {45.8, 40.9, 45}, + {60.8, 39.7, 45}, + {44.7, 39.2, 45}, + {63.4, 38.3, 45}, + {23.6, 38.2, 45}, + {41.2, 38.2, 45}, + {48.3, 38, 45}, + {50.6, 38, 45}, + {26.4, 38, 45}, + {42.1, 37.2, 45}, + {69.9, 37.1, 45}, + {49.5, 36.7, 45}, + {51.2, 36.6, 45}, + {47.6, 36.4, 45}, + {20.7, 36.3, 45}, + {19.4, 35.9, 45}, + {49.9, 35.6, 45}, + {52.3, 35.5, 45}, + {18.2, 35.5, 45}, + {39.1, 35.4, 45}, + {68.9, 35.4, 45}, + {47.9, 35, 45}, + {29.1, 34.6, 45}, + {36.1, 33.9, 45}, + {57.9, 33.9, 45}, + {69.1, 33.8, 45}, + {55.3, 33.7, 45}, + {53.3, 33.3, 45}, + {56.4, 33.2, 45}, + {54.3, 32.5, 45}, + {37.7, 31.1, 45}, + {62.8, 30.1, 45}, + {26.6, 25.7, 45}, + {28.3, 20.9, 45}, + {80.7, 83.8, 267}, + {79.9, 81, 267}, + }, + } +end + +if not MTH_DS_Beasts[2578] then + MTH_DS_Beasts[2578] = { + ["name"] = "Young Mesa Buzzard", + ["family"] = "Carrion Birds", + ["lvl"] = "31-32", + ["fac"] = "Unknown", + ["abilities"] = "Dive 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 400, + ["respawnMaxSeconds"] = 400, + ["respawnSamples"] = 36, + ["coords"] = { + {43.4, 61.2, 45}, + {42.4, 56.6, 45}, + {68.4, 53.2, 45}, + {69.3, 52.4, 45}, + {68.8, 49.6, 45}, + {20.2, 47, 45}, + {73.9, 46.6, 45}, + {54.1, 46.2, 45}, + {17.9, 43.6, 45}, + {60, 41.5, 45}, + {66.5, 40.8, 45}, + {27.1, 38, 45}, + {19.8, 37.4, 45}, + {69.3, 37.2, 45}, + {25.3, 35.1, 45}, + {28.1, 34.4, 45}, + {26.7, 24.8, 45}, + {29.1, 19.8, 45}, + }, + } +end + +if not MTH_DS_Beasts[2931] then + MTH_DS_Beasts[2931] = { + ["name"] = "Zaricotl", + ["family"] = "Carrion Birds", + ["lvl"] = "55", + ["fac"] = "Unknown", + ["abilities"] = "Dive 3", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 38000, + ["respawnSamples"] = 2, + ["coords"] = { + {56.1, 61.7, 3}, + }, + } +end + +if not MTH_DS_Beasts[3257] then + MTH_DS_Beasts[3257] = { + ["name"] = "Ishamuhale", + ["family"] = "Raptors", + ["lvl"] = "19", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 2", + ["attackSpeed"] = "2.0", + ["unique"] = true, + ["coords"] = { + {59.7, 30.3, 17}, + }, + } +end + +if not MTH_DS_Beasts[3475] then + MTH_DS_Beasts[3475] = { + ["name"] = "Echeyakee", + ["family"] = "Cats", + ["lvl"] = "16", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["unique"] = true, + ["coords"] = { + {56.1, 17.4, 17}, + }, + } +end + +if not MTH_DS_Beasts[3503] then + MTH_DS_Beasts[3503] = { + ["name"] = "Silithid Protector", + ["family"] = "Crabs", + ["lvl"] = "18-19", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["unique"] = true, + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 1, + ["coords"] = { + {45.5, 72.5, 17}, + }, + } +end + +if not MTH_DS_Beasts[3619] then + MTH_DS_Beasts[3619] = { + ["name"] = "Ghost Saber", + ["family"] = "Cats", + ["lvl"] = "19-20", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3", + ["attackSpeed"] = "2.0", + ["unique"] = true, + ["coords"] = { + }, + } +end + +if not MTH_DS_Beasts[3812] then + MTH_DS_Beasts[3812] = { + ["name"] = "Clattering Crawler", + ["family"] = "Crabs", + ["lvl"] = "19-20", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 43, + ["coords"] = { + {26.9, 99.3, 148}, + {26.3, 98.8, 148}, + {26.9, 98, 148}, + {30, 97.9, 148}, + {28.4, 97.5, 148}, + {27, 96.7, 148}, + {27.9, 96.6, 148}, + {30, 96.6, 148}, + {29.5, 96, 148}, + {27.4, 95.9, 148}, + {24.9, 95.3, 148}, + {10, 35.4, 331}, + {10.1, 33.4, 331}, + {11.8, 32.7, 331}, + {10.6, 32.1, 331}, + {8.8, 31.1, 331}, + {12.9, 30.7, 331}, + {12.6, 30.3, 331}, + {10, 29.7, 331}, + {14, 29.4, 331}, + {11.8, 29.1, 331}, + {11.1, 28.6, 331}, + {13.5, 28.5, 331}, + {12.4, 28.4, 331}, + {15.4, 27.4, 331}, + {12, 27.3, 331}, + {12.9, 26.6, 331}, + {12.4, 22.9, 331}, + {12.9, 22.2, 331}, + {14.6, 21.4, 331}, + {13.5, 21.1, 331}, + {12.7, 18.8, 331}, + {9, 17.6, 331}, + {8.4, 17, 331}, + {9, 16.1, 331}, + {12.6, 16, 331}, + {10.8, 15.5, 331}, + {9.1, 14.6, 331}, + {10.2, 14.6, 331}, + {12.5, 14.5, 331}, + {11.9, 13.8, 331}, + {9.5, 13.7, 331}, + {6.7, 13, 331}, + }, + } +end + +if not MTH_DS_Beasts[4118] then + MTH_DS_Beasts[4118] = { + ["name"] = "Venomous Cloud Serpent", + ["family"] = "Unknown", + ["lvl"] = "26-28", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 15, + ["coords"] = { + {42.9, 94.8, 17}, + {41.6, 93.7, 17}, + {40, 92.9, 17}, + {51.2, 56.3, 400}, + {36.7, 55.3, 400}, + {52.6, 55.1, 400}, + {34.9, 54.4, 400}, + {37.8, 54, 400}, + {37.5, 53.8, 400}, + {33.2, 52.9, 400}, + {28, 42.2, 400}, + {21.3, 38.1, 400}, + {29.3, 28.1, 400}, + {26.4, 25.6, 400}, + {22.7, 23.7, 400}, + }, + } +end + +if not MTH_DS_Beasts[4250] then + MTH_DS_Beasts[4250] = { + ["name"] = "Galak Packhound", + ["family"] = "Hyenas", + ["lvl"] = "24", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 1, + ["coords"] = { + {25.1, 37.2, 400}, + }, + } +end + +if not MTH_DS_Beasts[4425] then + MTH_DS_Beasts[4425] = { + ["name"] = "Blind Hunter", + ["family"] = "Bats", + ["lvl"] = "32", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["elite"] = true, + ["respawnMinSeconds"] = 604800, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 1, + ["coords"] = { + {11, 30.3, 491}, + }, + } +end + +if not MTH_DS_Beasts[4511] then + MTH_DS_Beasts[4511] = { + ["name"] = "Agam'ar", + ["family"] = "Boars", + ["lvl"] = "24-25", + ["fac"] = "Unknown", + ["abilities"] = "Charge 3", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 15, + ["coords"] = { + {39.9, 61.7, 491}, + {60, 59.1, 491}, + {48.5, 58.6, 491}, + {57.8, 57.8, 491}, + {27, 52.7, 491}, + {34.9, 49.2, 491}, + {53.4, 47.7, 491}, + {28.2, 47.3, 491}, + {35.8, 46.3, 491}, + {43.5, 46.2, 491}, + {35, 43.2, 491}, + {42.7, 43, 491}, + {50.7, 40.8, 491}, + {50.6, 37.4, 491}, + {48.5, 37.2, 491}, + }, + } +end + +if not MTH_DS_Beasts[4512] then + MTH_DS_Beasts[4512] = { + ["name"] = "Rotting Agam'ar", + ["family"] = "Boars", + ["lvl"] = "28", + ["fac"] = "Unknown", + ["abilities"] = "Charge 3", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 3, + ["coords"] = { + {28.6, 57.6, 491}, + {54.1, 57.3, 491}, + {48.5, 57.1, 491}, + }, + } +end + +if not MTH_DS_Beasts[4514] then + MTH_DS_Beasts[4514] = { + ["name"] = "Raging Agam'ar", + ["family"] = "Boars", + ["lvl"] = "25-26", + ["fac"] = "Unknown", + ["abilities"] = "Charge 3", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 15, + ["coords"] = { + {44.9, 64.6, 491}, + {40.4, 64.2, 491}, + {26.8, 63.6, 491}, + {28.1, 63.3, 491}, + {47.4, 62.2, 491}, + {67.2, 61.9, 491}, + {60.4, 57.7, 491}, + {62.4, 57.5, 491}, + {28.3, 56.8, 491}, + {37.3, 54.9, 491}, + {52.2, 54.8, 491}, + {37.2, 48.4, 491}, + {28.6, 43.4, 491}, + {52.3, 40.7, 491}, + {45.7, 40.1, 491}, + }, + } +end + +if not MTH_DS_Beasts[4660] then + MTH_DS_Beasts[4660] = { + ["name"] = "Maraudine Bonepaw", + ["family"] = "Hyenas", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["unique"] = true, + ["coords"] = { + }, + } +end + +if not MTH_DS_Beasts[4693] then + MTH_DS_Beasts[4693] = { + ["name"] = "Dread Flyer", + ["family"] = "Carrion Birds", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "Dive 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 100, + ["coords"] = { + {63.4, 60.4, 405}, + {44.1, 59.2, 405}, + {61.9, 58.9, 405}, + {63.1, 57.3, 405}, + {42.4, 56.9, 405}, + {60.5, 56.4, 405}, + {44.1, 55.9, 405}, + {62.6, 55.1, 405}, + {44.1, 54.5, 405}, + {61.3, 54.4, 405}, + {42.3, 53.6, 405}, + {57.9, 53.1, 405}, + {43.9, 52.4, 405}, + {62.7, 52, 405}, + {60.1, 51.3, 405}, + {61.1, 51.2, 405}, + {57.5, 51.1, 405}, + {40.2, 51, 405}, + {58.5, 50.9, 405}, + {41.7, 50.3, 405}, + {58.2, 50.2, 405}, + {59.7, 49.6, 405}, + {53.6, 49.3, 405}, + {55.4, 49.1, 405}, + {62.8, 48.5, 405}, + {41.7, 48.5, 405}, + {58.9, 48.2, 405}, + {45.4, 48.1, 405}, + {61, 47.9, 405}, + {42.8, 47.7, 405}, + {58.4, 47.3, 405}, + {63.9, 47, 405}, + {45, 46.9, 405}, + {43.5, 46.8, 405}, + {55.4, 46.6, 405}, + {41.7, 45.8, 405}, + {64, 45.6, 405}, + {59.7, 45.6, 405}, + {51.3, 45.2, 405}, + {42.2, 44.7, 405}, + {61.9, 44.6, 405}, + {40.5, 43.2, 405}, + {41.9, 42, 405}, + {42.8, 40.9, 405}, + {39.2, 39.2, 405}, + {41.3, 38.9, 405}, + {35.8, 38.8, 405}, + {40.5, 37.4, 405}, + {73.8, 11.2, 405}, + {50.4, 82.9, 406}, + }, + } +end + +if not MTH_DS_Beasts[4824] then + MTH_DS_Beasts[4824] = { + ["name"] = "Aku'mai Fisher", + ["family"] = "Turtles", + ["lvl"] = "23-24", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3, Shell Shield 1", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 6, + ["coords"] = { + {23.3, 44.9, 719}, + {21.4, 44.1, 719}, + {20.3, 41.9, 719}, + {24.3, 41.7, 719}, + {20.6, 38.3, 719}, + {23, 38, 719}, + }, + } +end + +if not MTH_DS_Beasts[4825] then + MTH_DS_Beasts[4825] = { + ["name"] = "Aku'mai Snapjaw", + ["family"] = "Turtles", + ["lvl"] = "26-27", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4, Shell Shield 1", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 7, + ["coords"] = { + {73.1, 92.3, 719}, + {68.9, 90.9, 719}, + {57.7, 89.5, 719}, + {60.4, 89.4, 719}, + {72.2, 88.4, 719}, + {60.3, 85.5, 719}, + {57.3, 85.3, 719}, + }, + } +end + +if not MTH_DS_Beasts[5224] then + MTH_DS_Beasts[5224] = { + ["name"] = "Murk Slitherer", + ["family"] = "Serpents (Cobra)", + ["lvl"] = "45-46", + ["fac"] = "Unknown", + ["abilities"] = "Poison Spit 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 900, + ["respawnMaxSeconds"] = 900, + ["respawnSamples"] = 5, + ["coords"] = { + {73.7, 44.8, 8}, + {75.9, 44.6, 8}, + {77, 43.2, 8}, + {73.8, 42.6, 8}, + {72, 41.9, 8}, + }, + } +end + +if not MTH_DS_Beasts[5260] then + MTH_DS_Beasts[5260] = { + ["name"] = "Groddoc Ape", + ["family"] = "Gorillas", + ["lvl"] = "42-43", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 26, + ["coords"] = { + {61.7, 64.4, 357}, + {61.7, 64, 357}, + {58.8, 61.6, 357}, + {61.2, 61.4, 357}, + {60.6, 61.2, 357}, + {60.8, 60.8, 357}, + {57.4, 60.7, 357}, + {59.6, 60.5, 357}, + {58.9, 60.3, 357}, + {57.8, 60.3, 357}, + {59.8, 60.1, 357}, + {56.5, 60.1, 357}, + {58.6, 58.7, 357}, + {58.4, 58.1, 357}, + {57.8, 57.9, 357}, + {58, 57.9, 357}, + {57.2, 57.2, 357}, + {58.3, 55.6, 357}, + {57.9, 55.5, 357}, + {56.3, 54.9, 357}, + {56.9, 54.5, 357}, + {57.5, 54.3, 357}, + {73.4, 53.7, 357}, + {57.4, 53.5, 357}, + {68.3, 52.1, 357}, + {74.7, 51.4, 357}, + }, + } +end + +if not MTH_DS_Beasts[5432] then + MTH_DS_Beasts[5432] = { + ["name"] = "Giant Surf Glider", + ["family"] = "Unknown", + ["lvl"] = "48-50", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 1, + ["coords"] = { + {60.2, 81.6, 440}, + }, + } +end + +if not MTH_DS_Beasts[5842] then + MTH_DS_Beasts[5842] = { + ["name"] = "Takk the Leaper", + ["family"] = "Raptors", + ["lvl"] = "19", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 2", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["elite"] = true, + ["respawnMinSeconds"] = 14400, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 2, + ["coords"] = { + {59.6, 8.3, 17}, + }, + } +end + +if not MTH_DS_Beasts[5984] then + MTH_DS_Beasts[5984] = { + ["name"] = "Starving Snickerfang", + ["family"] = "Hyenas", + ["lvl"] = "45-46", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 48, + ["coords"] = { + {50.8, 28.1, 4}, + {43.9, 25.5, 4}, + {48.6, 24.5, 4}, + {45.5, 21.4, 4}, + {46.7, 20.9, 4}, + {47.8, 19.7, 4}, + {48.3, 17.8, 4}, + {56, 17.6, 4}, + }, + } +end + +if not MTH_DS_Beasts[6513] then + MTH_DS_Beasts[6513] = { + ["name"] = "Un'Goro Stomper", + ["family"] = "Gorillas", + ["lvl"] = "51-52", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 52, + ["coords"] = { + {62.6, 19.7, 490}, + {61.6, 18.1, 490}, + {63.3, 18.1, 490}, + {69, 17.6, 490}, + {66.2, 17.3, 490}, + {62.4, 17.3, 490}, + {67.4, 17.2, 490}, + {68.6, 16.9, 490}, + {63.3, 16.8, 490}, + {64.9, 16.8, 490}, + {60.4, 16.7, 490}, + {65.2, 16.6, 490}, + {67.7, 16.6, 490}, + {64.1, 16.3, 490}, + {68.1, 15.9, 490}, + {69.1, 15.8, 490}, + {65.9, 15.6, 490}, + {67.1, 15.3, 490}, + {68.8, 15.2, 490}, + {67.4, 15.1, 490}, + {65.6, 14.7, 490}, + {68.6, 14.4, 490}, + {66.4, 14.3, 490}, + {68.6, 14.2, 490}, + {67.7, 13.8, 490}, + {68.3, 13.1, 490}, + }, + } +end + +if not MTH_DS_Beasts[6514] then + MTH_DS_Beasts[6514] = { + ["name"] = "Un'Goro Gorilla", + ["family"] = "Gorillas", + ["lvl"] = "50-51", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 52, + ["coords"] = { + {62.6, 19.7, 490}, + {61.6, 18.1, 490}, + {63.3, 18.1, 490}, + {69, 17.6, 490}, + {66.2, 17.3, 490}, + {62.4, 17.3, 490}, + {67.4, 17.2, 490}, + {68.6, 16.9, 490}, + {63.3, 16.8, 490}, + {64.9, 16.8, 490}, + {60.4, 16.7, 490}, + {65.2, 16.6, 490}, + {67.7, 16.6, 490}, + {64.1, 16.3, 490}, + {68.1, 15.9, 490}, + {69.1, 15.8, 490}, + {65.9, 15.6, 490}, + {67.1, 15.3, 490}, + {68.8, 15.2, 490}, + {67.4, 15.1, 490}, + {65.6, 14.7, 490}, + {68.6, 14.4, 490}, + {66.4, 14.3, 490}, + {68.6, 14.2, 490}, + {67.7, 13.8, 490}, + {68.3, 13.1, 490}, + }, + } +end + +if not MTH_DS_Beasts[6516] then + MTH_DS_Beasts[6516] = { + ["name"] = "Un'Goro Thunderer", + ["family"] = "Gorillas", + ["lvl"] = "52-53", + ["fac"] = "Unknown", + ["abilities"] = "Thunderstomp 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 40, + ["coords"] = { + {69, 17.6, 490}, + {66.2, 17.3, 490}, + {67.4, 17.2, 490}, + {68.6, 16.9, 490}, + {64.9, 16.8, 490}, + {65.2, 16.6, 490}, + {67.7, 16.6, 490}, + {64.1, 16.3, 490}, + {68.1, 15.9, 490}, + {69.1, 15.8, 490}, + {65.9, 15.6, 490}, + {67.1, 15.3, 490}, + {68.8, 15.2, 490}, + {67.4, 15.1, 490}, + {65.6, 14.7, 490}, + {68.6, 14.4, 490}, + {66.4, 14.3, 490}, + {68.6, 14.2, 490}, + {67.7, 13.8, 490}, + {68.3, 13.1, 490}, + }, + } +end + +if not MTH_DS_Beasts[6585] then + MTH_DS_Beasts[6585] = { + ["name"] = "Uhk'loc", + ["family"] = "Gorillas", + ["lvl"] = "52-53", + ["fac"] = "Unknown", + ["abilities"] = "Bite 7", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 27000, + ["respawnSamples"] = 2, + ["coords"] = { + {68.5, 12.7, 490}, + }, + } +end + +if not MTH_DS_Beasts[7455] then + MTH_DS_Beasts[7455] = { + ["name"] = "Winterspring Owl", + ["family"] = "Owls", + ["lvl"] = "54-56", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7, Dive 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 333, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 44, + ["coords"] = { + {66.1, 54.2, 618}, + {66.4, 51.9, 618}, + {68.3, 51.5, 618}, + {67.3, 49.5, 618}, + {63.5, 48.3, 618}, + {55.4, 47.2, 618}, + {55.5, 46.7, 618}, + {59.1, 46.2, 618}, + {62.9, 45.4, 618}, + {63.2, 43.9, 618}, + {59.3, 43.6, 618}, + {57.7, 43.4, 618}, + {60.7, 41.2, 618}, + {58.1, 40, 618}, + {57.2, 37.6, 618}, + {64.2, 36.8, 618}, + {59.2, 35.5, 618}, + {56.4, 34.6, 618}, + {57.5, 34.3, 618}, + {63.2, 34.1, 618}, + {50.9, 34, 618}, + {60.9, 33.9, 618}, + {59.4, 33.7, 618}, + {61.3, 33.2, 618}, + {62.6, 33, 618}, + {64.9, 33, 618}, + {57.1, 32.9, 618}, + {61.2, 32.9, 618}, + {50.8, 32.8, 618}, + {67.3, 32, 618}, + {63.5, 31.5, 618}, + {68.3, 31.3, 618}, + {59.1, 30.9, 618}, + {58.1, 30.1, 618}, + {58.9, 29.9, 618}, + {55.2, 29.8, 618}, + {67.2, 29.3, 618}, + {55.8, 29.3, 618}, + {64.5, 29, 618}, + {59, 28.9, 618}, + {65.1, 28.8, 618}, + {63.3, 27.5, 618}, + {63.3, 27.2, 618}, + {65.5, 27, 618}, + }, + } +end + +if not MTH_DS_Beasts[7803] then + MTH_DS_Beasts[7803] = { + ["name"] = "Scorpid Duneburrower", + ["family"] = "Scorpids", + ["lvl"] = "46-47", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 3", + ["attackSpeed"] = "2.0", + ["unique"] = true, + ["coords"] = { + }, + } +end + +if not MTH_DS_Beasts[8277] then + MTH_DS_Beasts[8277] = { + ["name"] = "Rekk'tilac", + ["family"] = "Spiders", + ["lvl"] = "48", + ["fac"] = "Unknown", + ["abilities"] = "Bite 7", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 75600, + ["respawnMaxSeconds"] = 108000, + ["respawnSamples"] = 2, + ["coords"] = { + {61.9, 73.2, 51}, + }, + } +end + +if not MTH_DS_Beasts[8926] then + MTH_DS_Beasts[8926] = { + ["name"] = "Deep Stinger", + ["family"] = "Scorpids", + ["lvl"] = "50-52", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 3", + ["attackSpeed"] = "2.0", + ["coords"] = { + }, + } +end + +if not MTH_DS_Beasts[8927] then + MTH_DS_Beasts[8927] = { + ["name"] = "Dark Screecher", + ["family"] = "Bats", + ["lvl"] = "50-52", + ["fac"] = "Unknown", + ["abilities"] = "Dive 3, Screech 3", + ["attackSpeed"] = "2.0", + ["coords"] = { + }, + } +end + +if not MTH_DS_Beasts[9622] then + MTH_DS_Beasts[9622] = { + ["name"] = "U'cha", + ["family"] = "Gorillas", + ["lvl"] = "55", + ["fac"] = "Unknown", + ["abilities"] = "Thunderstomp 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 1, + ["coords"] = { + {68.1, 12.6, 490}, + }, + } +end + +if not MTH_DS_Beasts[9683] then + MTH_DS_Beasts[9683] = { + ["name"] = "Lar'Korwi Mate", + ["family"] = "Raptors", + ["lvl"] = "49-50", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 25, + ["respawnMaxSeconds"] = 25, + ["respawnSamples"] = 1, + ["coords"] = { + {66.3, 63.9, 490}, + }, + } +end + +if not MTH_DS_Beasts[9684] then + MTH_DS_Beasts[9684] = { + ["name"] = "Lar'Korwi", + ["family"] = "Raptors", + ["lvl"] = "56", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 5", + ["attackSpeed"] = "1.5", + ["coords"] = { + {31.7, 28.7, 440}, + {79.4, 49.9, 490}, + }, + } +end + +if not MTH_DS_Beasts[10200] then + MTH_DS_Beasts[10200] = { + ["name"] = "Rak'shiri", + ["family"] = "Cats", + ["lvl"] = "57", + ["fac"] = "Unknown", + ["abilities"] = "Dash 3", + ["attackSpeed"] = "1.5", + ["rare"] = true, + ["respawnMinSeconds"] = 37800, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 2, + ["coords"] = { + {51.1, 10.7, 618}, + }, + } +end + +if not MTH_DS_Beasts[10356] then + MTH_DS_Beasts[10356] = { + ["name"] = "Bayne", + ["family"] = "Wolves", + ["lvl"] = "10", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 3600, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 2, + ["coords"] = { + {45.6, 47.5, 85}, + }, + } +end + +if not MTH_DS_Beasts[10359] then + MTH_DS_Beasts[10359] = { + ["name"] = "Sri'skulk", + ["family"] = "Spiders", + ["lvl"] = "13", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 5400, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 2, + ["coords"] = { + {88.2, 51.4, 85}, + }, + } +end + +if not MTH_DS_Beasts[10741] then + MTH_DS_Beasts[10741] = { + ["name"] = "Sian-Rotam", + ["family"] = "Cats", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["unique"] = true, + ["coords"] = { + }, + } +end + +if not MTH_DS_Beasts[10882] then + MTH_DS_Beasts[10882] = { + ["name"] = "Arikara", + ["family"] = "Wind Serpents", + ["lvl"] = "28", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["unique"] = true, + ["coords"] = { + }, + } +end + +if not MTH_DS_Beasts[10981] then + MTH_DS_Beasts[10981] = { + ["name"] = "Frostwolf", + ["family"] = "Wolves", + ["lvl"] = "50-51", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 430, + ["respawnMaxSeconds"] = 430, + ["respawnSamples"] = 37, + ["coords"] = { + {54.3, 85, 2597}, + {53.1, 82.6, 2597}, + {54.9, 81.2, 2597}, + {55.2, 78.5, 2597}, + {50, 77.2, 2597}, + {52, 77.1, 2597}, + {52.4, 76.1, 2597}, + {53.8, 75.5, 2597}, + {50.7, 74.1, 2597}, + {51.4, 74, 2597}, + {53.2, 72.6, 2597}, + {48.9, 70.4, 2597}, + {49.4, 68.9, 2597}, + {53, 68.8, 2597}, + {53.4, 58.3, 2597}, + {46.7, 58.2, 2597}, + {46.5, 55.5, 2597}, + {45.2, 53.5, 2597}, + {44.9, 51.9, 2597}, + }, + } +end + +if not MTH_DS_Beasts[12037] then + MTH_DS_Beasts[12037] = { + ["name"] = "Ursol'lok", + ["family"] = "Bears", + ["lvl"] = "32", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.39", + ["rare"] = true, + ["respawnMinSeconds"] = 37800, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 2, + ["coords"] = { + {83.4, 48.5, 331}, + }, + } +end + +if not MTH_DS_Beasts[14222] then + MTH_DS_Beasts[14222] = { + ["name"] = "Araga", + ["family"] = "Cats", + ["lvl"] = "35", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.4", + ["rare"] = true, + ["respawnMinSeconds"] = 75600, + ["respawnMaxSeconds"] = 115200, + ["respawnSamples"] = 12, + ["coords"] = { + {39, 91.2, 36}, + {31, 86, 36}, + {30.2, 72.1, 36}, + {43, 28.3, 267}, + {36, 23.7, 267}, + {35.3, 11.5, 267}, + }, + } +end + +if not MTH_DS_Beasts[14223] then + MTH_DS_Beasts[14223] = { + ["name"] = "Cranky Benj", + ["family"] = "Turtles", + ["lvl"] = "32", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4, Shell Shield 1", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 115200, + ["respawnMaxSeconds"] = 180000, + ["respawnSamples"] = 8, + ["coords"] = { + {14.5, 54.1, 36}, + {20.9, 48, 36}, + {27.7, 41.2, 36}, + {36.7, 21.3, 36}, + {73.1, 42, 130}, + {77.5, 38, 130}, + {82, 33.4, 130}, + }, + } +end + +if not MTH_DS_Beasts[14234] then + MTH_DS_Beasts[14234] = { + ["name"] = "Hayoc", + ["family"] = "Wind Serpents", + ["lvl"] = "41", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 38000, + ["respawnMaxSeconds"] = 38000, + ["respawnSamples"] = 1, + ["coords"] = { + {52, 62.9, 15}, + }, + } +end + +if not MTH_DS_Beasts[15041] then + MTH_DS_Beasts[15041] = { + ["name"] = "Spawn of Mar'li", + ["family"] = "Spiders", + ["lvl"] = "59-60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.4", + ["coords"] = { + }, + } +end + +if not MTH_DS_Beasts[36507] then + MTH_DS_Beasts[36507] = { + ["name"] = "Spot", + ["family"] = "Wolves", + ["lvl"] = "1", + ["fac"] = "AH", + ["abilities"] = "Dash 1", + ["attackSpeed"] = "2.0", + ["coords"] = { + {68, 46.8, 15}, + }, + } +end + +if not MTH_DS_Beasts[36514] then + MTH_DS_Beasts[36514] = { + ["name"] = "Frostsaber Cub", + ["family"] = "Cats", + ["lvl"] = "1", + ["fac"] = "AH", + ["abilities"] = "Cower 6", + ["attackSpeed"] = "1.5", + ["coords"] = { + {48.9, 19, 618}, + }, + } +end + +if not MTH_DS_Beasts[37007] then + MTH_DS_Beasts[37007] = { + ["name"] = "Black Widow Hatchling", + ["family"] = "Spiders", + ["lvl"] = "1", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = { + {80.7, 62.8, 10}, + }, + } +end + +if not MTH_DS_Beasts[50624] then + MTH_DS_Beasts[50624] = { + ["name"] = "Silithid Swarmer", + ["family"] = "Scorpids", + ["lvl"] = "30-43", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 1", + ["attackSpeed"] = "2.0", + ["coords"] = { + {26.5, 30.9, 15}, + }, + } +end + +if not MTH_DS_Beasts[50627] then + MTH_DS_Beasts[50627] = { + ["name"] = "Silithid Swarmer", + ["family"] = "Scorpids", + ["lvl"] = "30-43", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 1", + ["attackSpeed"] = "2.0", + ["coords"] = { + {26.5, 30.9, 15}, + }, + } +end + +if not MTH_DS_Beasts[50630] then + MTH_DS_Beasts[50630] = { + ["name"] = "Silithid Swarmer", + ["family"] = "Scorpids", + ["lvl"] = "30-43", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 1", + ["attackSpeed"] = "2.0", + ["coords"] = { + {26.5, 30.9, 15}, + }, + } +end + +if not MTH_DS_Beasts[60697] then + MTH_DS_Beasts[60697] = { + ["name"] = "Soothound", + ["family"] = "Hyenas", + ["lvl"] = "51-52", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["untameable"] = true, + ["respawnMinSeconds"] = 480, + ["respawnMaxSeconds"] = 480, + ["respawnSamples"] = 5, + ["coords"] = { + {94.1, 62.8, 46}, + {96.3, 58.3, 46}, + {93.7, 58.1, 46}, + {93.7, 57.2, 46}, + {97.1, 51.2, 46}, + }, + } +end + +if not MTH_DS_Beasts[61096] then + MTH_DS_Beasts[61096] = { + ["name"] = "Venomflayer Serpent", + ["family"] = "Wind Serpents", + ["lvl"] = "57-58", + ["fac"] = "Unknown", + ["abilities"] = "TBD", + ["attackSpeed"] = "2.0", + ["untameable"] = true, + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 280, + ["respawnSamples"] = 17, + ["coords"] = { + {45.2, 42.4, 5121}, + {44.5, 41.6, 5121}, + {47.3, 40.1, 5121}, + {42.6, 39.1, 5121}, + {45.5, 38, 5121}, + {44.1, 37.2, 5121}, + {46.3, 37.1, 5121}, + {43.5, 36.1, 5121}, + {47, 35.3, 5121}, + {42, 34.6, 5121}, + {45.3, 33.7, 5121}, + {41.5, 32.1, 5121}, + {47.8, 21, 5121}, + {50.2, 20.7, 5121}, + {47.6, 20.1, 5121}, + {47.3, 18.8, 5121}, + {49.4, 18.2, 5121}, + }, + } +end + +if not MTH_DS_Beasts[61500] then + MTH_DS_Beasts[61500] = { + ["name"] = "Highvale Silverback", + ["family"] = "Gorillas", + ["lvl"] = "58", + ["fac"] = "Unknown", + ["abilities"] = "Thunderstomp 4", + ["attackSpeed"] = "1.5", + ["rare"] = true, + ["respawnMinSeconds"] = 108000, + ["respawnMaxSeconds"] = 108000, + ["respawnSamples"] = 1, + ["coords"] = { + {56.7, 48.3, 5121}, + }, + } +end + +if not MTH_DS_Beasts[61552] then + MTH_DS_Beasts[61552] = { + ["name"] = "Duskskitterer", + ["family"] = "Spiders", + ["lvl"] = "44", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.29", + ["rare"] = true, + ["respawnMinSeconds"] = 43200, + ["respawnMaxSeconds"] = 43200, + ["respawnSamples"] = 1, + ["coords"] = { + {45.6, 78, 5179}, + }, + } +end + +if not MTH_DS_Beasts[61554] then + MTH_DS_Beasts[61554] = { + ["name"] = "Dawnhowl", + ["family"] = "Wolves", + ["lvl"] = "40", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 43200, + ["respawnMaxSeconds"] = 43200, + ["respawnSamples"] = 2, + ["coords"] = { + {47.4, 92.2, 130}, + {48.9, 22.7, 5179}, + }, + } +end + +if not MTH_DS_Beasts[61699] then + MTH_DS_Beasts[61699] = { + ["name"] = "Shar'lan", + ["family"] = "Cats", + ["lvl"] = "8", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 150, + ["respawnMaxSeconds"] = 150, + ["respawnSamples"] = 2, + ["coords"] = { + {20.6, 8.9, 139}, + {64.2, 67, 5225}, + }, + } +end + +if not MTH_DS_Beasts[61975] then + MTH_DS_Beasts[61975] = { + ["name"] = "Hightusk Boar", + ["family"] = "Boars", + ["lvl"] = "33-34", + ["fac"] = "Unknown", + ["abilities"] = "Charge 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 13, + ["coords"] = { + {20.8, 54.9, 45}, + {20.4, 54.8, 45}, + {19.9, 54.4, 45}, + {20.8, 54.3, 45}, + {20.9, 53.7, 45}, + {19.8, 53.5, 45}, + {19.4, 53.4, 45}, + {20.5, 53.2, 45}, + {20.9, 52.8, 45}, + {19.3, 52.7, 45}, + {19.6, 52.3, 45}, + {20.1, 52.3, 45}, + {20.8, 52.2, 45}, + }, + } +end + +if not MTH_DS_Beasts[62066] then + MTH_DS_Beasts[62066] = { + ["name"] = "Cavernweb Broodmother", + ["family"] = "Spider", + ["lvl"] = "27", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 604800, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 1, + ["coords"] = { + {33.6, 44.3, 5601}, + }, + } +end + +if not MTH_DS_Beasts[62073] then + MTH_DS_Beasts[62073] = { + ["name"] = "Cavernweb Spider", + ["family"] = "Spider", + ["lvl"] = "26-27", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 604800, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 8, + ["coords"] = { + {29, 60, 5601}, + {27.5, 58.8, 5601}, + {31.9, 57.4, 5601}, + {27.9, 55.1, 5601}, + {32, 53.6, 5601}, + {31.7, 51.6, 5601}, + {31.9, 50.2, 5601}, + {35.3, 47.3, 5601}, + }, + } +end + +if not MTH_DS_Beasts[62074] then + MTH_DS_Beasts[62074] = { + ["name"] = "Cavernweb Creeper", + ["family"] = "Spider", + ["lvl"] = "26-27", + ["fac"] = "Unknown", + ["abilities"] = "Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 604800, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 6, + ["coords"] = { + {39.3, 64.8, 5601}, + {39.3, 64.5, 5601}, + {28.7, 57.7, 5601}, + {34, 50.9, 5601}, + {30.3, 48.6, 5601}, + {33.2, 47, 5601}, + }, + } +end + +if not MTH_DS_Beasts[62075] then + MTH_DS_Beasts[62075] = { + ["name"] = "Cavernweb Venomspitter", + ["family"] = "Spider", + ["lvl"] = "27-28", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 604800, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 3, + ["coords"] = { + {30.5, 53.7, 5601}, + {30.3, 50.6, 5601}, + {36.3, 48.3, 5601}, + }, + } +end + +if not MTH_DS_Beasts[62345] then + MTH_DS_Beasts[62345] = { + ["name"] = "Sorrowmore Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "29-31", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 45, + ["coords"] = { + {41.7, 65, 5561}, + {41.7, 62.9, 5561}, + {40.9, 62, 5561}, + {39.9, 59.6, 5561}, + {38.9, 57.8, 5561}, + {37.6, 57.7, 5561}, + {40.1, 57.7, 5561}, + {41, 57.2, 5561}, + {37.3, 57.1, 5561}, + {39.9, 56.9, 5561}, + {40.9, 56.6, 5561}, + {39, 56.4, 5561}, + {39.7, 56.2, 5561}, + {41.3, 56, 5561}, + {41.9, 55, 5561}, + {42, 53.6, 5561}, + {38.9, 53, 5561}, + {42.5, 52.5, 5561}, + {38.8, 52.3, 5561}, + {41.5, 51.6, 5561}, + {43, 51.3, 5561}, + {38.9, 50.9, 5561}, + {39.5, 50.8, 5561}, + {42.1, 50.6, 5561}, + {43, 50.3, 5561}, + {44, 50.2, 5561}, + {40.6, 50, 5561}, + {39.5, 49.4, 5561}, + {41.8, 49.3, 5561}, + {43, 49, 5561}, + {40.5, 48.8, 5561}, + {42.5, 48.8, 5561}, + {41.1, 48.5, 5561}, + {38.7, 48.2, 5561}, + {37.7, 48, 5561}, + {42.3, 47.9, 5561}, + {41.5, 47.8, 5561}, + {40.4, 47.1, 5561}, + {39.2, 46.8, 5561}, + {38.5, 46.8, 5561}, + {41.3, 24.9, 5561}, + {40.6, 23.7, 5561}, + {42.2, 22.7, 5561}, + {40.9, 22.5, 5561}, + {41.5, 21.8, 5561}, + }, + } +end + +if not MTH_DS_Beasts[80250] then + MTH_DS_Beasts[80250] = { + ["name"] = "Crimson Lynx", + ["family"] = "Cats", + ["lvl"] = "10", + ["fac"] = "Unknown", + ["abilities"] = "Claw 1", + ["attackSpeed"] = "2.0", + ["coords"] = { + {9.7, 16.2, 139}, + }, + } +end + +if not MTH_DS_Beasts[80257] then + MTH_DS_Beasts[80257] = { + ["name"] = "Red Fox", + ["family"] = "Foxes", + ["lvl"] = "10-11", + ["fac"] = "AH", + ["abilities"] = "Grace 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 42, + ["coords"] = { + {19.7, 83.7, 38}, + {22.3, 75.6, 38}, + {20.5, 71.3, 38}, + {28.6, 66.4, 38}, + {29.6, 58.5, 38}, + {42.7, 57.3, 38}, + {28.3, 56.2, 38}, + {32.7, 55.2, 38}, + {35.4, 52.9, 38}, + {41, 50.2, 38}, + {27.9, 49.9, 38}, + {30.2, 42.9, 38}, + {26.9, 41.7, 38}, + {35.4, 37.9, 38}, + {36.3, 37.5, 38}, + {33.7, 31.1, 38}, + {26.8, 31, 38}, + {38.6, 30.5, 38}, + {36, 29.4, 38}, + {32.5, 29, 38}, + {35.5, 28.1, 38}, + {8.6, 87.2, 5602}, + {10, 83, 5602}, + {9.1, 80.8, 5602}, + {13.2, 78.3, 5602}, + {13.7, 74.2, 5602}, + {20.4, 73.6, 5602}, + {13, 73, 5602}, + {15.3, 72.5, 5602}, + {16.7, 71.3, 5602}, + {19.5, 70, 5602}, + {12.8, 69.8, 5602}, + {14, 66.2, 5602}, + {12.3, 65.6, 5602}, + {16.7, 63.6, 5602}, + {17.1, 63.4, 5602}, + {15.8, 60.1, 5602}, + {12.3, 60.1, 5602}, + {18.3, 59.8, 5602}, + {17, 59.3, 5602}, + {15.2, 59.1, 5602}, + {16.7, 58.6, 5602}, + }, + } +end + +if not MTH_DS_Beasts[80259] then + MTH_DS_Beasts[80259] = { + ["name"] = "Mist Fox", + ["family"] = "Foxes", + ["lvl"] = "50-51", + ["fac"] = "Unknown", + ["abilities"] = "Bite 7, Grace 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 3, + ["coords"] = { + {46.5, 79, 41}, + {50, 77.8, 41}, + {52.4, 36.6, 41}, + }, + } +end + +if not MTH_DS_Beasts[80260] then + MTH_DS_Beasts[80260] = { + ["name"] = "Spirit Fox", + ["family"] = "Foxes", + ["lvl"] = "53", + ["fac"] = "Unknown", + ["abilities"] = "Grace 1", + ["attackSpeed"] = "1.2", + ["rare"] = true, + ["respawnMinSeconds"] = 172800, + ["respawnMaxSeconds"] = 172800, + ["respawnSamples"] = 1, + ["coords"] = { + {63.6, 15.8, 618}, + }, + } +end + +if not MTH_DS_Beasts[80920] then + MTH_DS_Beasts[80920] = { + ["name"] = "Amani Eagle", + ["family"] = "Owls", + ["lvl"] = "1", + ["fac"] = "AH", + ["abilities"] = "Screech 1, Claw 2", + ["attackSpeed"] = "2.0", + ["coords"] = { + {36.4, 18.3, 406}, + }, + } +end + +if not MTH_DS_Beasts[90980] then + MTH_DS_Beasts[90980] = { + ["name"] = "Zulian Panther", + ["family"] = "Cats", + ["lvl"] = "1", + ["fac"] = "AH", + ["abilities"] = "Dash 3", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["coords"] = { + {50.3, 28.9, 1977}, + }, + } +end + +if not MTH_DS_Beasts[90981] then + MTH_DS_Beasts[90981] = { + ["name"] = "Zulian Panther", + ["family"] = "Cats", + ["lvl"] = "1", + ["fac"] = "AH", + ["abilities"] = "Dash 3", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["coords"] = { + {50.3, 28.9, 1977}, + }, + } +end + +MTH_DS_BeastsExtraMeta = { + generated = "2026-02-15", + baseBeasts = 627, + knownBeastNames = 710, + addedIDs = 80, +} diff --git a/data/ds-beasts-families.lua b/data/ds-beasts-families.lua new file mode 100644 index 0000000..b650302 --- /dev/null +++ b/data/ds-beasts-families.lua @@ -0,0 +1,250 @@ +-- MetaHunt Beast Families Database +-- Food map sourced from Turtle WoW Hunter Pets wiki +-- Total: 19 families + +if not MTH_DS then MTH_DS = {} end + +MTH_DS_Families = { + ["Bats"] = { + ["named"] = 20, + ["coords"] = 18, + ["food"] = { + "fruit", + "fungus", + }, + ["abilities"] = { + "Cower", + "Dive", + "Screech", + }, + }, + ["Bears"] = { + ["named"] = 45, + ["coords"] = 45, + ["food"] = { + "meat", + "fruit", + "fish", + "fungus", + "cheese", + "bread", + }, + ["abilities"] = { + "Claw", + "Roar of Fortitude", + }, + }, + ["Boars"] = { + ["named"] = 45, + ["coords"] = 87, + ["food"] = { + "meat", + "fruit", + "fish", + "fungus", + "cheese", + "bread", + }, + ["abilities"] = { + "Charge", + "Dash", + }, + }, + ["Carrion Birds"] = { + ["named"] = 36, + ["coords"] = 101, + ["food"] = { + "meat", + }, + ["abilities"] = { + "Dive", + "Screech", + }, + }, + ["Cats"] = { + ["named"] = 85, + ["coords"] = 88, + ["food"] = { + "meat", + "fish", + }, + ["abilities"] = { + "Claw", + "Cower", + "Dash", + "Prowl", + }, + }, + ["Crabs"] = { + ["named"] = 32, + ["coords"] = 74, + ["food"] = { + "fruit", + "fish", + "fungus", + "bread", + }, + ["abilities"] = { + "Bubble Barrier", + "Claw", + }, + }, + ["Crocolisks"] = { + ["named"] = 27, + ["coords"] = 71, + ["food"] = { + "meat", + "fish", + }, + ["abilities"] = { + "BIte", + "Bite", + "Claw", + "Death Roll", + }, + }, + ["Foxes"] = { + ["named"] = 9, + ["coords"] = 51, + ["food"] = { + "meat", + "fruit", + }, + ["abilities"] = { + "Bite", + "Grace", + }, + }, + ["Gorillas"] = { + ["named"] = 24, + ["coords"] = 117, + ["food"] = { + "fruit", + "fungus", + }, + ["abilities"] = { + "Thunderstomp", + }, + }, + ["Hyenas"] = { + ["named"] = 26, + ["coords"] = 36, + ["food"] = { + "meat", + "fruit", + }, + ["abilities"] = { + "Bite", + "Dash", + "Packleader", + }, + }, + ["Owls"] = { + ["named"] = 16, + ["coords"] = 59, + ["food"] = { + "meat", + }, + ["abilities"] = { + "Claw", + "Dive", + "Screech", + }, + }, + ["Raptors"] = { + ["named"] = 51, + ["coords"] = 115, + ["food"] = { + "meat", + }, + ["abilities"] = { + "Savage Rend", + }, + }, + ["Scorpids"] = { + ["named"] = 38, + ["coords"] = 36, + ["food"] = { + "meat", + }, + ["abilities"] = { + "Claw", + "Scorpid Poison", + }, + }, + ["Serpents (Cobra)"] = { + ["named"] = 10, + ["coords"] = 13, + ["food"] = { + "meat", + "fish", + }, + ["abilities"] = { + "Bite", + "Poison Spit", + }, + }, + ["Spiders"] = { + ["named"] = 91, + ["coords"] = 213, + ["food"] = { + "meat", + }, + ["abilities"] = { + "Bite", + "Web", + }, + }, + ["Tallstriders"] = { + ["named"] = 15, + ["coords"] = 112, + ["food"] = { + "fruit", + "fungus", + "bread", + }, + ["abilities"] = { + "Bite", + "Cower", + "Strider Presence", + }, + }, + ["Turtles"] = { + ["named"] = 25, + ["coords"] = 42, + ["food"] = { + "fruit", + "fish", + "fungus", + }, + ["abilities"] = { + "Bite", + "Shell Shield", + }, + }, + ["Wind Serpents"] = { + ["named"] = 27, + ["coords"] = 42, + ["food"] = { + "fish", + "cheese", + "bread", + }, + ["abilities"] = { + "Dive", + "Lightning Breath", + }, + }, + ["Wolves"] = { + ["named"] = 81, + ["coords"] = 99, + ["food"] = { + "meat", + }, + ["abilities"] = { + "Bite", + "Dash", + "Furious Howl", + }, + }, +} \ No newline at end of file diff --git a/data/ds-beasts-html-enrichment.lua b/data/ds-beasts-html-enrichment.lua new file mode 100644 index 0000000..e2e311a --- /dev/null +++ b/data/ds-beasts-html-enrichment.lua @@ -0,0 +1,14520 @@ +-- Generated from Turtle DB Beasts HTML candidates (full columns) +if not MTH_DS then MTH_DS = {} end +if not MTH_DS_Beasts then MTH_DS_Beasts = {} end + +do + local entry = MTH_DS_Beasts[659] + if not entry then + MTH_DS_Beasts[659] = { + ["name"] = "El Pollo Grande", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[659] + end + + entry["name"] = "El Pollo Grande" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "The Black Chicken of Death" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[728] + if not entry then + MTH_DS_Beasts[728] = { + ["name"] = "Bhag'thera", + ["family"] = "Unknown", + ["lvl"] = "40", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[728] + end + + entry["name"] = "Bhag'thera" + entry["lvl"] = "40" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[730] + if not entry then + MTH_DS_Beasts[730] = { + ["name"] = "Tethis", + ["family"] = "Unknown", + ["lvl"] = "43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[730] + end + + entry["name"] = "Tethis" + entry["lvl"] = "43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[731] + if not entry then + MTH_DS_Beasts[731] = { + ["name"] = "King Bangalash", + ["family"] = "Unknown", + ["lvl"] = "43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[731] + end + + entry["name"] = "King Bangalash" + entry["lvl"] = "43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[756] + if not entry then + MTH_DS_Beasts[756] = { + ["name"] = "Skullsplitter Panther", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[756] + end + + entry["name"] = "Skullsplitter Panther" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[1087] + if not entry then + MTH_DS_Beasts[1087] = { + ["name"] = "Sawtooth Snapper", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1087] + end + + entry["name"] = "Sawtooth Snapper" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 8 } +end + +do + local entry = MTH_DS_Beasts[1088] + if not entry then + MTH_DS_Beasts[1088] = { + ["name"] = "Monstrous Crawler", + ["family"] = "Unknown", + ["lvl"] = "43-44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1088] + end + + entry["name"] = "Monstrous Crawler" + entry["lvl"] = "43-44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 8 } +end + +do + local entry = MTH_DS_Beasts[1511] + if not entry then + MTH_DS_Beasts[1511] = { + ["name"] = "Enraged Silverback Gorilla", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1511] + end + + entry["name"] = "Enraged Silverback Gorilla" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[1514] + if not entry then + MTH_DS_Beasts[1514] = { + ["name"] = "Mokk the Savage", + ["family"] = "Unknown", + ["lvl"] = "44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1514] + end + + entry["name"] = "Mokk the Savage" + entry["lvl"] = "44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1 } +end + +do + local entry = MTH_DS_Beasts[1516] + if not entry then + MTH_DS_Beasts[1516] = { + ["name"] = "Konda", + ["family"] = "Unknown", + ["lvl"] = "43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1516] + end + + entry["name"] = "Konda" + entry["lvl"] = "43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[1550] + if not entry then + MTH_DS_Beasts[1550] = { + ["name"] = "Thrashtail Basilisk", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1550] + end + + entry["name"] = "Thrashtail Basilisk" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[1551] + if not entry then + MTH_DS_Beasts[1551] = { + ["name"] = "Ironjaw Basilisk", + ["family"] = "Unknown", + ["lvl"] = "43-44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1551] + end + + entry["name"] = "Ironjaw Basilisk" + entry["lvl"] = "43-44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[1552] + if not entry then + MTH_DS_Beasts[1552] = { + ["name"] = "Scale Belly", + ["family"] = "Unknown", + ["lvl"] = "45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1552] + end + + entry["name"] = "Scale Belly" + entry["lvl"] = "45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[1558] + if not entry then + MTH_DS_Beasts[1558] = { + ["name"] = "Silverback Patriarch", + ["family"] = "Unknown", + ["lvl"] = "42-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1558] + end + + entry["name"] = "Silverback Patriarch" + entry["lvl"] = "42-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[1559] + if not entry then + MTH_DS_Beasts[1559] = { + ["name"] = "King Mukla", + ["family"] = "Unknown", + ["lvl"] = "51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1559] + end + + entry["name"] = "King Mukla" + entry["lvl"] = "51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[1713] + if not entry then + MTH_DS_Beasts[1713] = { + ["name"] = "Elder Shadowmaw Panther", + ["family"] = "Unknown", + ["lvl"] = "42-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1713] + end + + entry["name"] = "Elder Shadowmaw Panther" + entry["lvl"] = "42-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[1809] + if not entry then + MTH_DS_Beasts[1809] = { + ["name"] = "Carrion Vulture", + ["family"] = "Unknown", + ["lvl"] = "50-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1809] + end + + entry["name"] = "Carrion Vulture" + entry["lvl"] = "50-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 28 } +end + +do + local entry = MTH_DS_Beasts[1815] + if not entry then + MTH_DS_Beasts[1815] = { + ["name"] = "Diseased Black Bear", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1815] + end + + entry["name"] = "Diseased Black Bear" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 28 } +end + +do + local entry = MTH_DS_Beasts[1816] + if not entry then + MTH_DS_Beasts[1816] = { + ["name"] = "Diseased Grizzly", + ["family"] = "Unknown", + ["lvl"] = "55-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1816] + end + + entry["name"] = "Diseased Grizzly" + entry["lvl"] = "55-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 28 } +end + +do + local entry = MTH_DS_Beasts[1817] + if not entry then + MTH_DS_Beasts[1817] = { + ["name"] = "Diseased Wolf", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1817] + end + + entry["name"] = "Diseased Wolf" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 28 } +end + +do + local entry = MTH_DS_Beasts[1821] + if not entry then + MTH_DS_Beasts[1821] = { + ["name"] = "Carrion Lurker", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1821] + end + + entry["name"] = "Carrion Lurker" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 28 } +end + +do + local entry = MTH_DS_Beasts[1822] + if not entry then + MTH_DS_Beasts[1822] = { + ["name"] = "Venom Mist Lurker", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1822] + end + + entry["name"] = "Venom Mist Lurker" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 28 } +end + +do + local entry = MTH_DS_Beasts[1824] + if not entry then + MTH_DS_Beasts[1824] = { + ["name"] = "Plague Lurker", + ["family"] = "Unknown", + ["lvl"] = "54-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[1824] + end + + entry["name"] = "Plague Lurker" + entry["lvl"] = "54-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 28 } +end + +do + local entry = MTH_DS_Beasts[2505] + if not entry then + MTH_DS_Beasts[2505] = { + ["name"] = "Saltwater Snapjaw", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2505] + end + + entry["name"] = "Saltwater Snapjaw" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[2521] + if not entry then + MTH_DS_Beasts[2521] = { + ["name"] = "Skymane Gorilla", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2521] + end + + entry["name"] = "Skymane Gorilla" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[2522] + if not entry then + MTH_DS_Beasts[2522] = { + ["name"] = "Jaguero Stalker", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2522] + end + + entry["name"] = "Jaguero Stalker" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[2658] + if not entry then + MTH_DS_Beasts[2658] = { + ["name"] = "Razorbeak Gryphon", + ["family"] = "Unknown", + ["lvl"] = "43-45", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2658] + end + + entry["name"] = "Razorbeak Gryphon" + entry["lvl"] = "43-45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[2659] + if not entry then + MTH_DS_Beasts[2659] = { + ["name"] = "Razorbeak Skylord", + ["family"] = "Unknown", + ["lvl"] = "46-48", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2659] + end + + entry["name"] = "Razorbeak Skylord" + entry["lvl"] = "46-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[2680] + if not entry then + MTH_DS_Beasts[2680] = { + ["name"] = "Vilebranch Wolf Pup", + ["family"] = "Unknown", + ["lvl"] = "46-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2680] + end + + entry["name"] = "Vilebranch Wolf Pup" + entry["lvl"] = "46-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[2681] + if not entry then + MTH_DS_Beasts[2681] = { + ["name"] = "Vilebranch Raiding Wolf", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2681] + end + + entry["name"] = "Vilebranch Raiding Wolf" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[2686] + if not entry then + MTH_DS_Beasts[2686] = { + ["name"] = "Witherbark Broodguard", + ["family"] = "Unknown", + ["lvl"] = "44-45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2686] + end + + entry["name"] = "Witherbark Broodguard" + entry["lvl"] = "44-45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[2707] + if not entry then + MTH_DS_Beasts[2707] = { + ["name"] = "Shadra", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2707] + end + + entry["name"] = "Shadra" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "The Venom Queen" + entry["classification"] = "elite" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[2730] + if not entry then + MTH_DS_Beasts[2730] = { + ["name"] = "Rabid Crag Coyote", + ["family"] = "Unknown", + ["lvl"] = "42-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2730] + end + + entry["name"] = "Rabid Crag Coyote" + entry["lvl"] = "42-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 3 } +end + +do + local entry = MTH_DS_Beasts[2923] + if not entry then + MTH_DS_Beasts[2923] = { + ["name"] = "Mangy Silvermane", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2923] + end + + entry["name"] = "Mangy Silvermane" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[2924] + if not entry then + MTH_DS_Beasts[2924] = { + ["name"] = "Silvermane Wolf", + ["family"] = "Unknown", + ["lvl"] = "43-44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2924] + end + + entry["name"] = "Silvermane Wolf" + entry["lvl"] = "43-44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[2925] + if not entry then + MTH_DS_Beasts[2925] = { + ["name"] = "Silvermane Howler", + ["family"] = "Unknown", + ["lvl"] = "45-46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2925] + end + + entry["name"] = "Silvermane Howler" + entry["lvl"] = "45-46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[2926] + if not entry then + MTH_DS_Beasts[2926] = { + ["name"] = "Silvermane Stalker", + ["family"] = "Unknown", + ["lvl"] = "47-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2926] + end + + entry["name"] = "Silvermane Stalker" + entry["lvl"] = "47-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[2931] + if not entry then + MTH_DS_Beasts[2931] = { + ["name"] = "Zaricotl", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[2931] + end + + entry["name"] = "Zaricotl" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 3 } +end + +do + local entry = MTH_DS_Beasts[3581] + if not entry then + MTH_DS_Beasts[3581] = { + ["name"] = "Sewer Beast", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[3581] + end + + entry["name"] = "Sewer Beast" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 1519 } +end + +do + local entry = MTH_DS_Beasts[4242] + if not entry then + MTH_DS_Beasts[4242] = { + ["name"] = "Frostsaber Companion", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[4242] + end + + entry["name"] = "Frostsaber Companion" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 141 } +end + +do + local entry = MTH_DS_Beasts[4243] + if not entry then + MTH_DS_Beasts[4243] = { + ["name"] = "Nightshade", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[4243] + end + + entry["name"] = "Nightshade" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Jeen'ra's Pet" + entry["zoneIds"] = { 141 } +end + +do + local entry = MTH_DS_Beasts[4357] + if not entry then + MTH_DS_Beasts[4357] = { + ["name"] = "Bloodfen Lashtail", + ["family"] = "Unknown", + ["lvl"] = "40-41", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[4357] + end + + entry["name"] = "Bloodfen Lashtail" + entry["lvl"] = "40-41" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[4380] + if not entry then + MTH_DS_Beasts[4380] = { + ["name"] = "Darkmist Widow", + ["family"] = "Unknown", + ["lvl"] = "40", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[4380] + end + + entry["name"] = "Darkmist Widow" + entry["lvl"] = "40" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[4389] + if not entry then + MTH_DS_Beasts[4389] = { + ["name"] = "Murk Thresher", + ["family"] = "Unknown", + ["lvl"] = "41-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[4389] + end + + entry["name"] = "Murk Thresher" + entry["lvl"] = "41-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[4390] + if not entry then + MTH_DS_Beasts[4390] = { + ["name"] = "Elder Murk Thresher", + ["family"] = "Unknown", + ["lvl"] = "43-45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[4390] + end + + entry["name"] = "Elder Murk Thresher" + entry["lvl"] = "43-45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[4399] + if not entry then + MTH_DS_Beasts[4399] = { + ["name"] = "Mudrock Borer", + ["family"] = "Unknown", + ["lvl"] = "42-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[4399] + end + + entry["name"] = "Mudrock Borer" + entry["lvl"] = "42-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[4400] + if not entry then + MTH_DS_Beasts[4400] = { + ["name"] = "Mudrock Snapjaw", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[4400] + end + + entry["name"] = "Mudrock Snapjaw" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[4841] + if not entry then + MTH_DS_Beasts[4841] = { + ["name"] = "Deadmire", + ["family"] = "Unknown", + ["lvl"] = "45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[4841] + end + + entry["name"] = "Deadmire" + entry["lvl"] = "45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[5186] + if not entry then + MTH_DS_Beasts[5186] = { + ["name"] = "Basking Shark", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5186] + end + + entry["name"] = "Basking Shark" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 369 } +end + +do + local entry = MTH_DS_Beasts[5224] + if not entry then + MTH_DS_Beasts[5224] = { + ["name"] = "Murk Slitherer", + ["family"] = "Unknown", + ["lvl"] = "45-46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5224] + end + + entry["name"] = "Murk Slitherer" + entry["lvl"] = "45-46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 8 } +end + +do + local entry = MTH_DS_Beasts[5225] + if not entry then + MTH_DS_Beasts[5225] = { + ["name"] = "Murk Spitter", + ["family"] = "Unknown", + ["lvl"] = "46-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5225] + end + + entry["name"] = "Murk Spitter" + entry["lvl"] = "46-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 8 } +end + +do + local entry = MTH_DS_Beasts[5226] + if not entry then + MTH_DS_Beasts[5226] = { + ["name"] = "Murk Worm", + ["family"] = "Unknown", + ["lvl"] = "47-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5226] + end + + entry["name"] = "Murk Worm" + entry["lvl"] = "47-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 109 } +end + +do + local entry = MTH_DS_Beasts[5260] + if not entry then + MTH_DS_Beasts[5260] = { + ["name"] = "Groddoc Ape", + ["family"] = "Unknown", + ["lvl"] = "42-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5260] + end + + entry["name"] = "Groddoc Ape" + entry["lvl"] = "42-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5262] + if not entry then + MTH_DS_Beasts[5262] = { + ["name"] = "Groddoc Thunderer", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5262] + end + + entry["name"] = "Groddoc Thunderer" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5268] + if not entry then + MTH_DS_Beasts[5268] = { + ["name"] = "Ironfur Bear", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5268] + end + + entry["name"] = "Ironfur Bear" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5272] + if not entry then + MTH_DS_Beasts[5272] = { + ["name"] = "Grizzled Ironfur Bear", + ["family"] = "Unknown", + ["lvl"] = "44-45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5272] + end + + entry["name"] = "Grizzled Ironfur Bear" + entry["lvl"] = "44-45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5274] + if not entry then + MTH_DS_Beasts[5274] = { + ["name"] = "Ironfur Patriarch", + ["family"] = "Unknown", + ["lvl"] = "48-49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5274] + end + + entry["name"] = "Ironfur Patriarch" + entry["lvl"] = "48-49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5287] + if not entry then + MTH_DS_Beasts[5287] = { + ["name"] = "Longtooth Howler", + ["family"] = "Unknown", + ["lvl"] = "43-44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5287] + end + + entry["name"] = "Longtooth Howler" + entry["lvl"] = "43-44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5288] + if not entry then + MTH_DS_Beasts[5288] = { + ["name"] = "Rabid Longtooth", + ["family"] = "Unknown", + ["lvl"] = "47-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5288] + end + + entry["name"] = "Rabid Longtooth" + entry["lvl"] = "47-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5291] + if not entry then + MTH_DS_Beasts[5291] = { + ["name"] = "Hakkari Frostwing", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5291] + end + + entry["name"] = "Hakkari Frostwing" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 109 } +end + +do + local entry = MTH_DS_Beasts[5300] + if not entry then + MTH_DS_Beasts[5300] = { + ["name"] = "Frayfeather Hippogryph", + ["family"] = "Unknown", + ["lvl"] = "43-44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5300] + end + + entry["name"] = "Frayfeather Hippogryph" + entry["lvl"] = "43-44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5304] + if not entry then + MTH_DS_Beasts[5304] = { + ["name"] = "Frayfeather Stagwing", + ["family"] = "Unknown", + ["lvl"] = "44-45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5304] + end + + entry["name"] = "Frayfeather Stagwing" + entry["lvl"] = "44-45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5305] + if not entry then + MTH_DS_Beasts[5305] = { + ["name"] = "Frayfeather Skystormer", + ["family"] = "Unknown", + ["lvl"] = "45-46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5305] + end + + entry["name"] = "Frayfeather Skystormer" + entry["lvl"] = "45-46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5306] + if not entry then + MTH_DS_Beasts[5306] = { + ["name"] = "Frayfeather Patriarch", + ["family"] = "Unknown", + ["lvl"] = "46-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5306] + end + + entry["name"] = "Frayfeather Patriarch" + entry["lvl"] = "46-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5307] + if not entry then + MTH_DS_Beasts[5307] = { + ["name"] = "Vale Screecher", + ["family"] = "Unknown", + ["lvl"] = "41-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5307] + end + + entry["name"] = "Vale Screecher" + entry["lvl"] = "41-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5308] + if not entry then + MTH_DS_Beasts[5308] = { + ["name"] = "Rogue Vale Screecher", + ["family"] = "Unknown", + ["lvl"] = "44-46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5308] + end + + entry["name"] = "Rogue Vale Screecher" + entry["lvl"] = "44-46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5347] + if not entry then + MTH_DS_Beasts[5347] = { + ["name"] = "Antilus the Soarer", + ["family"] = "Unknown", + ["lvl"] = "48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5347] + end + + entry["name"] = "Antilus the Soarer" + entry["lvl"] = "48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5349] + if not entry then + MTH_DS_Beasts[5349] = { + ["name"] = "Arash-ethis", + ["family"] = "Unknown", + ["lvl"] = "49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5349] + end + + entry["name"] = "Arash-ethis" + entry["lvl"] = "49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5352] + if not entry then + MTH_DS_Beasts[5352] = { + ["name"] = "Old Grizzlegut", + ["family"] = "Unknown", + ["lvl"] = "43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5352] + end + + entry["name"] = "Old Grizzlegut" + entry["lvl"] = "43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5356] + if not entry then + MTH_DS_Beasts[5356] = { + ["name"] = "Snarler", + ["family"] = "Unknown", + ["lvl"] = "42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5356] + end + + entry["name"] = "Snarler" + entry["lvl"] = "42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[5419] + if not entry then + MTH_DS_Beasts[5419] = { + ["name"] = "Glasshide Basilisk", + ["family"] = "Unknown", + ["lvl"] = "42-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5419] + end + + entry["name"] = "Glasshide Basilisk" + entry["lvl"] = "42-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5420] + if not entry then + MTH_DS_Beasts[5420] = { + ["name"] = "Glasshide Gazer", + ["family"] = "Unknown", + ["lvl"] = "45-46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5420] + end + + entry["name"] = "Glasshide Gazer" + entry["lvl"] = "45-46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5421] + if not entry then + MTH_DS_Beasts[5421] = { + ["name"] = "Glasshide Petrifier", + ["family"] = "Unknown", + ["lvl"] = "48-49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5421] + end + + entry["name"] = "Glasshide Petrifier" + entry["lvl"] = "48-49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5423] + if not entry then + MTH_DS_Beasts[5423] = { + ["name"] = "Scorpid Tail Lasher", + ["family"] = "Unknown", + ["lvl"] = "43-44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5423] + end + + entry["name"] = "Scorpid Tail Lasher" + entry["lvl"] = "43-44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5424] + if not entry then + MTH_DS_Beasts[5424] = { + ["name"] = "Scorpid Dunestalker", + ["family"] = "Unknown", + ["lvl"] = "46-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5424] + end + + entry["name"] = "Scorpid Dunestalker" + entry["lvl"] = "46-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1 } +end + +do + local entry = MTH_DS_Beasts[5425] + if not entry then + MTH_DS_Beasts[5425] = { + ["name"] = "Starving Blisterpaw", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5425] + end + + entry["name"] = "Starving Blisterpaw" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5426] + if not entry then + MTH_DS_Beasts[5426] = { + ["name"] = "Blisterpaw Hyena", + ["family"] = "Unknown", + ["lvl"] = "44-45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5426] + end + + entry["name"] = "Blisterpaw Hyena" + entry["lvl"] = "44-45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5427] + if not entry then + MTH_DS_Beasts[5427] = { + ["name"] = "Rabid Blisterpaw", + ["family"] = "Unknown", + ["lvl"] = "47-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5427] + end + + entry["name"] = "Rabid Blisterpaw" + entry["lvl"] = "47-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5428] + if not entry then + MTH_DS_Beasts[5428] = { + ["name"] = "Roc", + ["family"] = "Unknown", + ["lvl"] = "41-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5428] + end + + entry["name"] = "Roc" + entry["lvl"] = "41-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5429] + if not entry then + MTH_DS_Beasts[5429] = { + ["name"] = "Fire Roc", + ["family"] = "Unknown", + ["lvl"] = "43-45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5429] + end + + entry["name"] = "Fire Roc" + entry["lvl"] = "43-45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5430] + if not entry then + MTH_DS_Beasts[5430] = { + ["name"] = "Searing Roc", + ["family"] = "Unknown", + ["lvl"] = "47-49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5430] + end + + entry["name"] = "Searing Roc" + entry["lvl"] = "47-49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5431] + if not entry then + MTH_DS_Beasts[5431] = { + ["name"] = "Surf Glider", + ["family"] = "Unknown", + ["lvl"] = "48-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5431] + end + + entry["name"] = "Surf Glider" + entry["lvl"] = "48-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5432] + if not entry then + MTH_DS_Beasts[5432] = { + ["name"] = "Giant Surf Glider", + ["family"] = "Unknown", + ["lvl"] = "48-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5432] + end + + entry["name"] = "Giant Surf Glider" + entry["lvl"] = "48-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[5434] + if not entry then + MTH_DS_Beasts[5434] = { + ["name"] = "Coral Shark", + ["family"] = "Unknown", + ["lvl"] = "46-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5434] + end + + entry["name"] = "Coral Shark" + entry["lvl"] = "46-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[5708] + if not entry then + MTH_DS_Beasts[5708] = { + ["name"] = "Spawn of Hakkar", + ["family"] = "Unknown", + ["lvl"] = "51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5708] + end + + entry["name"] = "Spawn of Hakkar" + entry["lvl"] = "51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 109 } +end + +do + local entry = MTH_DS_Beasts[5833] + if not entry then + MTH_DS_Beasts[5833] = { + ["name"] = "Margol the Rager", + ["family"] = "Unknown", + ["lvl"] = "48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5833] + end + + entry["name"] = "Margol the Rager" + entry["lvl"] = "48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 51 } +end + +do + local entry = MTH_DS_Beasts[5856] + if not entry then + MTH_DS_Beasts[5856] = { + ["name"] = "Glassweb Spider", + ["family"] = "Unknown", + ["lvl"] = "43-45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5856] + end + + entry["name"] = "Glassweb Spider" + entry["lvl"] = "43-45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 51 } +end + +do + local entry = MTH_DS_Beasts[5857] + if not entry then + MTH_DS_Beasts[5857] = { + ["name"] = "Searing Lava Spider", + ["family"] = "Unknown", + ["lvl"] = "45-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5857] + end + + entry["name"] = "Searing Lava Spider" + entry["lvl"] = "45-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 51 } +end + +do + local entry = MTH_DS_Beasts[5858] + if not entry then + MTH_DS_Beasts[5858] = { + ["name"] = "Greater Lava Spider", + ["family"] = "Unknown", + ["lvl"] = "47-49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5858] + end + + entry["name"] = "Greater Lava Spider" + entry["lvl"] = "47-49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 51 } +end + +do + local entry = MTH_DS_Beasts[5955] + if not entry then + MTH_DS_Beasts[5955] = { + ["name"] = "Tooga", + ["family"] = "Unknown", + ["lvl"] = "45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5955] + end + + entry["name"] = "Tooga" + entry["lvl"] = "45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[5982] + if not entry then + MTH_DS_Beasts[5982] = { + ["name"] = "Black Slayer", + ["family"] = "Unknown", + ["lvl"] = "46-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5982] + end + + entry["name"] = "Black Slayer" + entry["lvl"] = "46-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[5983] + if not entry then + MTH_DS_Beasts[5983] = { + ["name"] = "Bonepicker", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5983] + end + + entry["name"] = "Bonepicker" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[5984] + if not entry then + MTH_DS_Beasts[5984] = { + ["name"] = "Starving Snickerfang", + ["family"] = "Unknown", + ["lvl"] = "45-46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5984] + end + + entry["name"] = "Starving Snickerfang" + entry["lvl"] = "45-46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[5985] + if not entry then + MTH_DS_Beasts[5985] = { + ["name"] = "Snickerfang Hyena", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5985] + end + + entry["name"] = "Snickerfang Hyena" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[5988] + if not entry then + MTH_DS_Beasts[5988] = { + ["name"] = "Scorpok Stinger", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5988] + end + + entry["name"] = "Scorpok Stinger" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[5990] + if not entry then + MTH_DS_Beasts[5990] = { + ["name"] = "Redstone Basilisk", + ["family"] = "Unknown", + ["lvl"] = "47-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5990] + end + + entry["name"] = "Redstone Basilisk" + entry["lvl"] = "47-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[5991] + if not entry then + MTH_DS_Beasts[5991] = { + ["name"] = "Redstone Crystalhide", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5991] + end + + entry["name"] = "Redstone Crystalhide" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[5992] + if not entry then + MTH_DS_Beasts[5992] = { + ["name"] = "Ashmane Boar", + ["family"] = "Unknown", + ["lvl"] = "48-49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[5992] + end + + entry["name"] = "Ashmane Boar" + entry["lvl"] = "48-49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[6015] + if not entry then + MTH_DS_Beasts[6015] = { + ["name"] = "Torta", + ["family"] = "Unknown", + ["lvl"] = "45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6015] + end + + entry["name"] = "Torta" + entry["lvl"] = "45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[6347] + if not entry then + MTH_DS_Beasts[6347] = { + ["name"] = "Young Wavethrasher", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6347] + end + + entry["name"] = "Young Wavethrasher" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[6348] + if not entry then + MTH_DS_Beasts[6348] = { + ["name"] = "Wavethrasher", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6348] + end + + entry["name"] = "Wavethrasher" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[6349] + if not entry then + MTH_DS_Beasts[6349] = { + ["name"] = "Great Wavethrasher", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6349] + end + + entry["name"] = "Great Wavethrasher" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[6352] + if not entry then + MTH_DS_Beasts[6352] = { + ["name"] = "Coralshell Lurker", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6352] + end + + entry["name"] = "Coralshell Lurker" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[6369] + if not entry then + MTH_DS_Beasts[6369] = { + ["name"] = "Coralshell Tortoise", + ["family"] = "Unknown", + ["lvl"] = "50-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6369] + end + + entry["name"] = "Coralshell Tortoise" + entry["lvl"] = "50-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[6375] + if not entry then + MTH_DS_Beasts[6375] = { + ["name"] = "Thunderhead Hippogryph", + ["family"] = "Unknown", + ["lvl"] = "46-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6375] + end + + entry["name"] = "Thunderhead Hippogryph" + entry["lvl"] = "46-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[6377] + if not entry then + MTH_DS_Beasts[6377] = { + ["name"] = "Thunderhead Stagwing", + ["family"] = "Unknown", + ["lvl"] = "48-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6377] + end + + entry["name"] = "Thunderhead Stagwing" + entry["lvl"] = "48-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[6378] + if not entry then + MTH_DS_Beasts[6378] = { + ["name"] = "Thunderhead Skystormer", + ["family"] = "Unknown", + ["lvl"] = "50-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6378] + end + + entry["name"] = "Thunderhead Skystormer" + entry["lvl"] = "50-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[6379] + if not entry then + MTH_DS_Beasts[6379] = { + ["name"] = "Thunderhead Patriarch", + ["family"] = "Unknown", + ["lvl"] = "52-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6379] + end + + entry["name"] = "Thunderhead Patriarch" + entry["lvl"] = "52-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[6380] + if not entry then + MTH_DS_Beasts[6380] = { + ["name"] = "Thunderhead Consort", + ["family"] = "Unknown", + ["lvl"] = "52-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6380] + end + + entry["name"] = "Thunderhead Consort" + entry["lvl"] = "52-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[6498] + if not entry then + MTH_DS_Beasts[6498] = { + ["name"] = "Devilsaur", + ["family"] = "Unknown", + ["lvl"] = "54-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6498] + end + + entry["name"] = "Devilsaur" + entry["lvl"] = "54-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6499] + if not entry then + MTH_DS_Beasts[6499] = { + ["name"] = "Ironhide Devilsaur", + ["family"] = "Unknown", + ["lvl"] = "54-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6499] + end + + entry["name"] = "Ironhide Devilsaur" + entry["lvl"] = "54-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[6500] + if not entry then + MTH_DS_Beasts[6500] = { + ["name"] = "Tyrant Devilsaur", + ["family"] = "Unknown", + ["lvl"] = "54-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6500] + end + + entry["name"] = "Tyrant Devilsaur" + entry["lvl"] = "54-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6501] + if not entry then + MTH_DS_Beasts[6501] = { + ["name"] = "Stegodon", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6501] + end + + entry["name"] = "Stegodon" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6502] + if not entry then + MTH_DS_Beasts[6502] = { + ["name"] = "Plated Stegodon", + ["family"] = "Unknown", + ["lvl"] = "52-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6502] + end + + entry["name"] = "Plated Stegodon" + entry["lvl"] = "52-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6503] + if not entry then + MTH_DS_Beasts[6503] = { + ["name"] = "Spiked Stegodon", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6503] + end + + entry["name"] = "Spiked Stegodon" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6504] + if not entry then + MTH_DS_Beasts[6504] = { + ["name"] = "Thunderstomp Stegodon", + ["family"] = "Unknown", + ["lvl"] = "54-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6504] + end + + entry["name"] = "Thunderstomp Stegodon" + entry["lvl"] = "54-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6505] + if not entry then + MTH_DS_Beasts[6505] = { + ["name"] = "Ravasaur", + ["family"] = "Unknown", + ["lvl"] = "48-49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6505] + end + + entry["name"] = "Ravasaur" + entry["lvl"] = "48-49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6506] + if not entry then + MTH_DS_Beasts[6506] = { + ["name"] = "Ravasaur Runner", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6506] + end + + entry["name"] = "Ravasaur Runner" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6507] + if not entry then + MTH_DS_Beasts[6507] = { + ["name"] = "Ravasaur Hunter", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6507] + end + + entry["name"] = "Ravasaur Hunter" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6508] + if not entry then + MTH_DS_Beasts[6508] = { + ["name"] = "Venomhide Ravasaur", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6508] + end + + entry["name"] = "Venomhide Ravasaur" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 28 } +end + +do + local entry = MTH_DS_Beasts[6513] + if not entry then + MTH_DS_Beasts[6513] = { + ["name"] = "Un'Goro Stomper", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6513] + end + + entry["name"] = "Un'Goro Stomper" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6514] + if not entry then + MTH_DS_Beasts[6514] = { + ["name"] = "Un'Goro Gorilla", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6514] + end + + entry["name"] = "Un'Goro Gorilla" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[6516] + if not entry then + MTH_DS_Beasts[6516] = { + ["name"] = "Un'Goro Thunderer", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6516] + end + + entry["name"] = "Un'Goro Thunderer" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6581] + if not entry then + MTH_DS_Beasts[6581] = { + ["name"] = "Ravasaur Matriarch", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6581] + end + + entry["name"] = "Ravasaur Matriarch" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6583] + if not entry then + MTH_DS_Beasts[6583] = { + ["name"] = "Gruff", + ["family"] = "Unknown", + ["lvl"] = "57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6583] + end + + entry["name"] = "Gruff" + entry["lvl"] = "57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6584] + if not entry then + MTH_DS_Beasts[6584] = { + ["name"] = "King Mosh", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6584] + end + + entry["name"] = "King Mosh" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6585] + if not entry then + MTH_DS_Beasts[6585] = { + ["name"] = "Uhk'loc", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6585] + end + + entry["name"] = "Uhk'loc" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[6648] + if not entry then + MTH_DS_Beasts[6648] = { + ["name"] = "Antilos", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[6648] + end + + entry["name"] = "Antilos" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[7055] + if not entry then + MTH_DS_Beasts[7055] = { + ["name"] = "Blackrock Worg", + ["family"] = "Unknown", + ["lvl"] = "54-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7055] + end + + entry["name"] = "Blackrock Worg" + entry["lvl"] = "54-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[7097] + if not entry then + MTH_DS_Beasts[7097] = { + ["name"] = "Ironbeak Owl", + ["family"] = "Unknown", + ["lvl"] = "48-49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7097] + end + + entry["name"] = "Ironbeak Owl" + entry["lvl"] = "48-49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[7098] + if not entry then + MTH_DS_Beasts[7098] = { + ["name"] = "Ironbeak Screecher", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7098] + end + + entry["name"] = "Ironbeak Screecher" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[7099] + if not entry then + MTH_DS_Beasts[7099] = { + ["name"] = "Ironbeak Hunter", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7099] + end + + entry["name"] = "Ironbeak Hunter" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[7167] + if not entry then + MTH_DS_Beasts[7167] = { + ["name"] = "Polly", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7167] + end + + entry["name"] = "Polly" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 141 } +end + +do + local entry = MTH_DS_Beasts[7268] + if not entry then + MTH_DS_Beasts[7268] = { + ["name"] = "Sandfury Guardian", + ["family"] = "Unknown", + ["lvl"] = "45-46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7268] + end + + entry["name"] = "Sandfury Guardian" + entry["lvl"] = "45-46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 209 } +end + +do + local entry = MTH_DS_Beasts[7269] + if not entry then + MTH_DS_Beasts[7269] = { + ["name"] = "Scarab", + ["family"] = "Unknown", + ["lvl"] = "44-46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7269] + end + + entry["name"] = "Scarab" + entry["lvl"] = "44-46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 209 } +end + +do + local entry = MTH_DS_Beasts[7273] + if not entry then + MTH_DS_Beasts[7273] = { + ["name"] = "Gahz'rilla", + ["family"] = "Unknown", + ["lvl"] = "46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7273] + end + + entry["name"] = "Gahz'rilla" + entry["lvl"] = "46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[7376] + if not entry then + MTH_DS_Beasts[7376] = { + ["name"] = "Sky Shadow", + ["family"] = "Unknown", + ["lvl"] = "55-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7376] + end + + entry["name"] = "Sky Shadow" + entry["lvl"] = "55-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 41 } +end + +do + local entry = MTH_DS_Beasts[7405] + if not entry then + MTH_DS_Beasts[7405] = { + ["name"] = "Deadly Cleft Scorpid", + ["family"] = "Unknown", + ["lvl"] = "42-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7405] + end + + entry["name"] = "Deadly Cleft Scorpid" + entry["lvl"] = "42-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 70 } +end + +do + local entry = MTH_DS_Beasts[7430] + if not entry then + MTH_DS_Beasts[7430] = { + ["name"] = "Frostsaber Cub", + ["family"] = "Unknown", + ["lvl"] = "55-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7430] + end + + entry["name"] = "Frostsaber Cub" + entry["lvl"] = "55-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7431] + if not entry then + MTH_DS_Beasts[7431] = { + ["name"] = "Frostsaber", + ["family"] = "Unknown", + ["lvl"] = "56-57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7431] + end + + entry["name"] = "Frostsaber" + entry["lvl"] = "56-57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7432] + if not entry then + MTH_DS_Beasts[7432] = { + ["name"] = "Frostsaber Stalker", + ["family"] = "Unknown", + ["lvl"] = "59-60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7432] + end + + entry["name"] = "Frostsaber Stalker" + entry["lvl"] = "59-60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7433] + if not entry then + MTH_DS_Beasts[7433] = { + ["name"] = "Frostsaber Huntress", + ["family"] = "Unknown", + ["lvl"] = "58-59", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7433] + end + + entry["name"] = "Frostsaber Huntress" + entry["lvl"] = "58-59" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7434] + if not entry then + MTH_DS_Beasts[7434] = { + ["name"] = "Frostsaber Pride Watcher", + ["family"] = "Unknown", + ["lvl"] = "59-60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7434] + end + + entry["name"] = "Frostsaber Pride Watcher" + entry["lvl"] = "59-60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7443] + if not entry then + MTH_DS_Beasts[7443] = { + ["name"] = "Shardtooth Mauler", + ["family"] = "Unknown", + ["lvl"] = "55-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7443] + end + + entry["name"] = "Shardtooth Mauler" + entry["lvl"] = "55-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7444] + if not entry then + MTH_DS_Beasts[7444] = { + ["name"] = "Shardtooth Bear", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7444] + end + + entry["name"] = "Shardtooth Bear" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7445] + if not entry then + MTH_DS_Beasts[7445] = { + ["name"] = "Elder Shardtooth", + ["family"] = "Unknown", + ["lvl"] = "57-58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7445] + end + + entry["name"] = "Elder Shardtooth" + entry["lvl"] = "57-58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7446] + if not entry then + MTH_DS_Beasts[7446] = { + ["name"] = "Rabid Shardtooth", + ["family"] = "Unknown", + ["lvl"] = "59-60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7446] + end + + entry["name"] = "Rabid Shardtooth" + entry["lvl"] = "59-60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7447] + if not entry then + MTH_DS_Beasts[7447] = { + ["name"] = "Fledgling Chillwind", + ["family"] = "Unknown", + ["lvl"] = "53-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7447] + end + + entry["name"] = "Fledgling Chillwind" + entry["lvl"] = "53-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7448] + if not entry then + MTH_DS_Beasts[7448] = { + ["name"] = "Chillwind Chimaera", + ["family"] = "Unknown", + ["lvl"] = "55-57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7448] + end + + entry["name"] = "Chillwind Chimaera" + entry["lvl"] = "55-57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7449] + if not entry then + MTH_DS_Beasts[7449] = { + ["name"] = "Chillwind Ravager", + ["family"] = "Unknown", + ["lvl"] = "57-59", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7449] + end + + entry["name"] = "Chillwind Ravager" + entry["lvl"] = "57-59" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7455] + if not entry then + MTH_DS_Beasts[7455] = { + ["name"] = "Winterspring Owl", + ["family"] = "Unknown", + ["lvl"] = "54-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7455] + end + + entry["name"] = "Winterspring Owl" + entry["lvl"] = "54-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7456] + if not entry then + MTH_DS_Beasts[7456] = { + ["name"] = "Winterspring Screecher", + ["family"] = "Unknown", + ["lvl"] = "57-59", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7456] + end + + entry["name"] = "Winterspring Screecher" + entry["lvl"] = "57-59" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[7803] + if not entry then + MTH_DS_Beasts[7803] = { + ["name"] = "Scorpid Duneburrower", + ["family"] = "Unknown", + ["lvl"] = "46-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7803] + end + + entry["name"] = "Scorpid Duneburrower" + entry["lvl"] = "46-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[7808] + if not entry then + MTH_DS_Beasts[7808] = { + ["name"] = "Marauding Owlbeast", + ["family"] = "Unknown", + ["lvl"] = "46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7808] + end + + entry["name"] = "Marauding Owlbeast" + entry["lvl"] = "46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[7977] + if not entry then + MTH_DS_Beasts[7977] = { + ["name"] = "Gammerita", + ["family"] = "Unknown", + ["lvl"] = "48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[7977] + end + + entry["name"] = "Gammerita" + entry["lvl"] = "48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[8024] + if not entry then + MTH_DS_Beasts[8024] = { + ["name"] = "Sharpbeak's Father", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8024] + end + + entry["name"] = "Sharpbeak's Father" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[8025] + if not entry then + MTH_DS_Beasts[8025] = { + ["name"] = "Sharpbeak's Mother", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8025] + end + + entry["name"] = "Sharpbeak's Mother" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[8095] + if not entry then + MTH_DS_Beasts[8095] = { + ["name"] = "Sul'lithuz Sandcrawler", + ["family"] = "Unknown", + ["lvl"] = "45-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8095] + end + + entry["name"] = "Sul'lithuz Sandcrawler" + entry["lvl"] = "45-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 209 } +end + +do + local entry = MTH_DS_Beasts[8207] + if not entry then + MTH_DS_Beasts[8207] = { + ["name"] = "Greater Firebird", + ["family"] = "Unknown", + ["lvl"] = "46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8207] + end + + entry["name"] = "Greater Firebird" + entry["lvl"] = "46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[8208] + if not entry then + MTH_DS_Beasts[8208] = { + ["name"] = "Murderous Blisterpaw", + ["family"] = "Unknown", + ["lvl"] = "44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8208] + end + + entry["name"] = "Murderous Blisterpaw" + entry["lvl"] = "44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[8211] + if not entry then + MTH_DS_Beasts[8211] = { + ["name"] = "Old Cliff Jumper", + ["family"] = "Unknown", + ["lvl"] = "42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8211] + end + + entry["name"] = "Old Cliff Jumper" + entry["lvl"] = "42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[8213] + if not entry then + MTH_DS_Beasts[8213] = { + ["name"] = "Ironback", + ["family"] = "Unknown", + ["lvl"] = "51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8213] + end + + entry["name"] = "Ironback" + entry["lvl"] = "51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 47 } +end + +do + local entry = MTH_DS_Beasts[8277] + if not entry then + MTH_DS_Beasts[8277] = { + ["name"] = "Rekk'tilac", + ["family"] = "Unknown", + ["lvl"] = "48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8277] + end + + entry["name"] = "Rekk'tilac" + entry["lvl"] = "48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 51 } +end + +do + local entry = MTH_DS_Beasts[8299] + if not entry then + MTH_DS_Beasts[8299] = { + ["name"] = "Spiteflayer", + ["family"] = "Unknown", + ["lvl"] = "52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8299] + end + + entry["name"] = "Spiteflayer" + entry["lvl"] = "52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[8300] + if not entry then + MTH_DS_Beasts[8300] = { + ["name"] = "Ravage", + ["family"] = "Unknown", + ["lvl"] = "51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8300] + end + + entry["name"] = "Ravage" + entry["lvl"] = "51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[8301] + if not entry then + MTH_DS_Beasts[8301] = { + ["name"] = "Clack the Reaver", + ["family"] = "Unknown", + ["lvl"] = "53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8301] + end + + entry["name"] = "Clack the Reaver" + entry["lvl"] = "53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[8302] + if not entry then + MTH_DS_Beasts[8302] = { + ["name"] = "Deatheye", + ["family"] = "Unknown", + ["lvl"] = "49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8302] + end + + entry["name"] = "Deatheye" + entry["lvl"] = "49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[8303] + if not entry then + MTH_DS_Beasts[8303] = { + ["name"] = "Grunter", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8303] + end + + entry["name"] = "Grunter" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[8311] + if not entry then + MTH_DS_Beasts[8311] = { + ["name"] = "Slime Maggot", + ["family"] = "Unknown", + ["lvl"] = "45-46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8311] + end + + entry["name"] = "Slime Maggot" + entry["lvl"] = "45-46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 109 } +end + +do + local entry = MTH_DS_Beasts[8336] + if not entry then + MTH_DS_Beasts[8336] = { + ["name"] = "Hakkari Sapper", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8336] + end + + entry["name"] = "Hakkari Sapper" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 109 } +end + +do + local entry = MTH_DS_Beasts[8600] + if not entry then + MTH_DS_Beasts[8600] = { + ["name"] = "Plaguebat", + ["family"] = "Unknown", + ["lvl"] = "53-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8600] + end + + entry["name"] = "Plaguebat" + entry["lvl"] = "53-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 139 } +end + +do + local entry = MTH_DS_Beasts[8601] + if not entry then + MTH_DS_Beasts[8601] = { + ["name"] = "Noxious Plaguebat", + ["family"] = "Unknown", + ["lvl"] = "54-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8601] + end + + entry["name"] = "Noxious Plaguebat" + entry["lvl"] = "54-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 139 } +end + +do + local entry = MTH_DS_Beasts[8602] + if not entry then + MTH_DS_Beasts[8602] = { + ["name"] = "Monstrous Plaguebat", + ["family"] = "Unknown", + ["lvl"] = "56-58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8602] + end + + entry["name"] = "Monstrous Plaguebat" + entry["lvl"] = "56-58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 139 } +end + +do + local entry = MTH_DS_Beasts[8603] + if not entry then + MTH_DS_Beasts[8603] = { + ["name"] = "Carrion Grub", + ["family"] = "Unknown", + ["lvl"] = "54-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8603] + end + + entry["name"] = "Carrion Grub" + entry["lvl"] = "54-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 139 } +end + +do + local entry = MTH_DS_Beasts[8605] + if not entry then + MTH_DS_Beasts[8605] = { + ["name"] = "Carrion Devourer", + ["family"] = "Unknown", + ["lvl"] = "56-57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8605] + end + + entry["name"] = "Carrion Devourer" + entry["lvl"] = "56-57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 139 } +end + +do + local entry = MTH_DS_Beasts[8612] + if not entry then + MTH_DS_Beasts[8612] = { + ["name"] = "Screecher Spirit", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8612] + end + + entry["name"] = "Screecher Spirit" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 4 } +end + +do + local entry = MTH_DS_Beasts[8660] + if not entry then + MTH_DS_Beasts[8660] = { + ["name"] = "The Evalcharr", + ["family"] = "Unknown", + ["lvl"] = "48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8660] + end + + entry["name"] = "The Evalcharr" + entry["lvl"] = "48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8759] + if not entry then + MTH_DS_Beasts[8759] = { + ["name"] = "Mosshoof Runner", + ["family"] = "Unknown", + ["lvl"] = "45-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8759] + end + + entry["name"] = "Mosshoof Runner" + entry["lvl"] = "45-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8760] + if not entry then + MTH_DS_Beasts[8760] = { + ["name"] = "Mosshoof Stag", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8760] + end + + entry["name"] = "Mosshoof Stag" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8761] + if not entry then + MTH_DS_Beasts[8761] = { + ["name"] = "Mosshoof Courser", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8761] + end + + entry["name"] = "Mosshoof Courser" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8762] + if not entry then + MTH_DS_Beasts[8762] = { + ["name"] = "Timberweb Recluse", + ["family"] = "Unknown", + ["lvl"] = "47-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8762] + end + + entry["name"] = "Timberweb Recluse" + entry["lvl"] = "47-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8763] + if not entry then + MTH_DS_Beasts[8763] = { + ["name"] = "Mistwing Rogue", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8763] + end + + entry["name"] = "Mistwing Rogue" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8764] + if not entry then + MTH_DS_Beasts[8764] = { + ["name"] = "Mistwing Ravager", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8764] + end + + entry["name"] = "Mistwing Ravager" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8925] + if not entry then + MTH_DS_Beasts[8925] = { + ["name"] = "Dredge Worm", + ["family"] = "Unknown", + ["lvl"] = "50-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8925] + end + + entry["name"] = "Dredge Worm" + entry["lvl"] = "50-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8926] + if not entry then + MTH_DS_Beasts[8926] = { + ["name"] = "Deep Stinger", + ["family"] = "Unknown", + ["lvl"] = "50-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8926] + end + + entry["name"] = "Deep Stinger" + entry["lvl"] = "50-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8927] + if not entry then + MTH_DS_Beasts[8927] = { + ["name"] = "Dark Screecher", + ["family"] = "Unknown", + ["lvl"] = "50-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8927] + end + + entry["name"] = "Dark Screecher" + entry["lvl"] = "50-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8928] + if not entry then + MTH_DS_Beasts[8928] = { + ["name"] = "Burrowing Thundersnout", + ["family"] = "Unknown", + ["lvl"] = "50-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8928] + end + + entry["name"] = "Burrowing Thundersnout" + entry["lvl"] = "50-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8932] + if not entry then + MTH_DS_Beasts[8932] = { + ["name"] = "Borer Beetle", + ["family"] = "Unknown", + ["lvl"] = "50-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8932] + end + + entry["name"] = "Borer Beetle" + entry["lvl"] = "50-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[8933] + if not entry then + MTH_DS_Beasts[8933] = { + ["name"] = "Cave Creeper", + ["family"] = "Unknown", + ["lvl"] = "50-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8933] + end + + entry["name"] = "Cave Creeper" + entry["lvl"] = "50-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 28 } +end + +do + local entry = MTH_DS_Beasts[8956] + if not entry then + MTH_DS_Beasts[8956] = { + ["name"] = "Angerclaw Bear", + ["family"] = "Unknown", + ["lvl"] = "47-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8956] + end + + entry["name"] = "Angerclaw Bear" + entry["lvl"] = "47-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[8957] + if not entry then + MTH_DS_Beasts[8957] = { + ["name"] = "Angerclaw Grizzly", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8957] + end + + entry["name"] = "Angerclaw Grizzly" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[8958] + if not entry then + MTH_DS_Beasts[8958] = { + ["name"] = "Angerclaw Mauler", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8958] + end + + entry["name"] = "Angerclaw Mauler" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[8959] + if not entry then + MTH_DS_Beasts[8959] = { + ["name"] = "Felpaw Wolf", + ["family"] = "Unknown", + ["lvl"] = "47-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8959] + end + + entry["name"] = "Felpaw Wolf" + entry["lvl"] = "47-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[8960] + if not entry then + MTH_DS_Beasts[8960] = { + ["name"] = "Felpaw Scavenger", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8960] + end + + entry["name"] = "Felpaw Scavenger" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[8961] + if not entry then + MTH_DS_Beasts[8961] = { + ["name"] = "Felpaw Ravager", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[8961] + end + + entry["name"] = "Felpaw Ravager" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[9032] + if not entry then + MTH_DS_Beasts[9032] = { + ["name"] = "Hedrum the Creeper", + ["family"] = "Unknown", + ["lvl"] = "53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9032] + end + + entry["name"] = "Hedrum the Creeper" + entry["lvl"] = "53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[9162] + if not entry then + MTH_DS_Beasts[9162] = { + ["name"] = "Young Diemetradon", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9162] + end + + entry["name"] = "Young Diemetradon" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[9163] + if not entry then + MTH_DS_Beasts[9163] = { + ["name"] = "Diemetradon", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9163] + end + + entry["name"] = "Diemetradon" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[9164] + if not entry then + MTH_DS_Beasts[9164] = { + ["name"] = "Elder Diemetradon", + ["family"] = "Unknown", + ["lvl"] = "54-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9164] + end + + entry["name"] = "Elder Diemetradon" + entry["lvl"] = "54-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[9165] + if not entry then + MTH_DS_Beasts[9165] = { + ["name"] = "Fledgling Pterrordax", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9165] + end + + entry["name"] = "Fledgling Pterrordax" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[9166] + if not entry then + MTH_DS_Beasts[9166] = { + ["name"] = "Pterrordax", + ["family"] = "Unknown", + ["lvl"] = "50-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9166] + end + + entry["name"] = "Pterrordax" + entry["lvl"] = "50-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[9167] + if not entry then + MTH_DS_Beasts[9167] = { + ["name"] = "Frenzied Pterrordax", + ["family"] = "Unknown", + ["lvl"] = "52-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9167] + end + + entry["name"] = "Frenzied Pterrordax" + entry["lvl"] = "52-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[9274] + if not entry then + MTH_DS_Beasts[9274] = { + ["name"] = "Dadanga", + ["family"] = "Unknown", + ["lvl"] = "45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9274] + end + + entry["name"] = "Dadanga" + entry["lvl"] = "45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[9297] + if not entry then + MTH_DS_Beasts[9297] = { + ["name"] = "Enraged Wyvern", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9297] + end + + entry["name"] = "Enraged Wyvern" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[9318] + if not entry then + MTH_DS_Beasts[9318] = { + ["name"] = "Incendosaur", + ["family"] = "Unknown", + ["lvl"] = "47-49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9318] + end + + entry["name"] = "Incendosaur" + entry["lvl"] = "47-49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 51 } +end + +do + local entry = MTH_DS_Beasts[9416] + if not entry then + MTH_DS_Beasts[9416] = { + ["name"] = "Scarshield Worg", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9416] + end + + entry["name"] = "Scarshield Worg" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Scarshield Legion" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[9521] + if not entry then + MTH_DS_Beasts[9521] = { + ["name"] = "Enraged Felbat", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9521] + end + + entry["name"] = "Enraged Felbat" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[9526] + if not entry then + MTH_DS_Beasts[9526] = { + ["name"] = "Enraged Gryphon", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9526] + end + + entry["name"] = "Enraged Gryphon" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[9527] + if not entry then + MTH_DS_Beasts[9527] = { + ["name"] = "Enraged Hippogryph", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9527] + end + + entry["name"] = "Enraged Hippogryph" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[9622] + if not entry then + MTH_DS_Beasts[9622] = { + ["name"] = "U'cha", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9622] + end + + entry["name"] = "U'cha" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[9683] + if not entry then + MTH_DS_Beasts[9683] = { + ["name"] = "Lar'korwi Mate", + ["family"] = "Unknown", + ["lvl"] = "49-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9683] + end + + entry["name"] = "Lar'korwi Mate" + entry["lvl"] = "49-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[9684] + if not entry then + MTH_DS_Beasts[9684] = { + ["name"] = "Lar'korwi", + ["family"] = "Unknown", + ["lvl"] = "56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9684] + end + + entry["name"] = "Lar'korwi" + entry["lvl"] = "56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[9690] + if not entry then + MTH_DS_Beasts[9690] = { + ["name"] = "Ember Worg", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9690] + end + + entry["name"] = "Ember Worg" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[9691] + if not entry then + MTH_DS_Beasts[9691] = { + ["name"] = "Venomtip Scorpid", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9691] + end + + entry["name"] = "Venomtip Scorpid" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[9694] + if not entry then + MTH_DS_Beasts[9694] = { + ["name"] = "Slavering Ember Worg", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9694] + end + + entry["name"] = "Slavering Ember Worg" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[9695] + if not entry then + MTH_DS_Beasts[9695] = { + ["name"] = "Deathlash Scorpid", + ["family"] = "Unknown", + ["lvl"] = "54-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9695] + end + + entry["name"] = "Deathlash Scorpid" + entry["lvl"] = "54-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[9696] + if not entry then + MTH_DS_Beasts[9696] = { + ["name"] = "Bloodaxe Worg", + ["family"] = "Unknown", + ["lvl"] = "56-57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9696] + end + + entry["name"] = "Bloodaxe Worg" + entry["lvl"] = "56-57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Scarshield Legion" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[9697] + if not entry then + MTH_DS_Beasts[9697] = { + ["name"] = "Giant Ember Worg", + ["family"] = "Unknown", + ["lvl"] = "55-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9697] + end + + entry["name"] = "Giant Ember Worg" + entry["lvl"] = "55-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[9698] + if not entry then + MTH_DS_Beasts[9698] = { + ["name"] = "Firetail Scorpid", + ["family"] = "Unknown", + ["lvl"] = "56-57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9698] + end + + entry["name"] = "Firetail Scorpid" + entry["lvl"] = "56-57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[9701] + if not entry then + MTH_DS_Beasts[9701] = { + ["name"] = "Spire Scorpid", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[9701] + end + + entry["name"] = "Spire Scorpid" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[10077] + if not entry then + MTH_DS_Beasts[10077] = { + ["name"] = "Deathmaw", + ["family"] = "Unknown", + ["lvl"] = "53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10077] + end + + entry["name"] = "Deathmaw" + entry["lvl"] = "53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[10177] + if not entry then + MTH_DS_Beasts[10177] = { + ["name"] = "Spire Scarab", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10177] + end + + entry["name"] = "Spire Scarab" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[10200] + if not entry then + MTH_DS_Beasts[10200] = { + ["name"] = "Rak'shiri", + ["family"] = "Unknown", + ["lvl"] = "57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10200] + end + + entry["name"] = "Rak'shiri" + entry["lvl"] = "57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[10204] + if not entry then + MTH_DS_Beasts[10204] = { + ["name"] = "Misha", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10204] + end + + entry["name"] = "Misha" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 405 } +end + +do + local entry = MTH_DS_Beasts[10220] + if not entry then + MTH_DS_Beasts[10220] = { + ["name"] = "Halycon", + ["family"] = "Unknown", + ["lvl"] = "59", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10220] + end + + entry["name"] = "Halycon" + entry["lvl"] = "59" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[10221] + if not entry then + MTH_DS_Beasts[10221] = { + ["name"] = "Bloodaxe Worg Pup", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10221] + end + + entry["name"] = "Bloodaxe Worg Pup" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[10268] + if not entry then + MTH_DS_Beasts[10268] = { + ["name"] = "Gizrul the Slavener", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10268] + end + + entry["name"] = "Gizrul the Slavener" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[10374] + if not entry then + MTH_DS_Beasts[10374] = { + ["name"] = "Spire Spider", + ["family"] = "Unknown", + ["lvl"] = "57-58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10374] + end + + entry["name"] = "Spire Spider" + entry["lvl"] = "57-58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[10375] + if not entry then + MTH_DS_Beasts[10375] = { + ["name"] = "Spire Spiderling", + ["family"] = "Unknown", + ["lvl"] = "55-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10375] + end + + entry["name"] = "Spire Spiderling" + entry["lvl"] = "55-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[10376] + if not entry then + MTH_DS_Beasts[10376] = { + ["name"] = "Crystal Fang", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10376] + end + + entry["name"] = "Crystal Fang" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[10430] + if not entry then + MTH_DS_Beasts[10430] = { + ["name"] = "The Beast", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10430] + end + + entry["name"] = "The Beast" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "boss" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[10577] + if not entry then + MTH_DS_Beasts[10577] = { + ["name"] = "Crypt Scarab", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10577] + end + + entry["name"] = "Crypt Scarab" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[10596] + if not entry then + MTH_DS_Beasts[10596] = { + ["name"] = "Mother Smolderweb", + ["family"] = "Unknown", + ["lvl"] = "59", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10596] + end + + entry["name"] = "Mother Smolderweb" + entry["lvl"] = "59" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[10619] + if not entry then + MTH_DS_Beasts[10619] = { + ["name"] = "Glacier", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10619] + end + + entry["name"] = "Glacier" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Rivern's Guardian" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[10717] + if not entry then + MTH_DS_Beasts[10717] = { + ["name"] = "Temporal Parasite", + ["family"] = "Unknown", + ["lvl"] = "55-57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10717] + end + + entry["name"] = "Temporal Parasite" + entry["lvl"] = "55-57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[10737] + if not entry then + MTH_DS_Beasts[10737] = { + ["name"] = "Shy-Rotam", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10737] + end + + entry["name"] = "Shy-Rotam" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[10741] + if not entry then + MTH_DS_Beasts[10741] = { + ["name"] = "Sian-Rotam", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10741] + end + + entry["name"] = "Sian-Rotam" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[10806] + if not entry then + MTH_DS_Beasts[10806] = { + ["name"] = "Ursius", + ["family"] = "Unknown", + ["lvl"] = "56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10806] + end + + entry["name"] = "Ursius" + entry["lvl"] = "56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[10807] + if not entry then + MTH_DS_Beasts[10807] = { + ["name"] = "Brumeran", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10807] + end + + entry["name"] = "Brumeran" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[10942] + if not entry then + MTH_DS_Beasts[10942] = { + ["name"] = "Nessy", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10942] + end + + entry["name"] = "Nessy" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["classification"] = "elite" + entry["zoneIds"] = { 369 } +end + +do + local entry = MTH_DS_Beasts[10979] + if not entry then + MTH_DS_Beasts[10979] = { + ["name"] = "Scarlet Hound", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10979] + end + + entry["name"] = "Scarlet Hound" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[10981] + if not entry then + MTH_DS_Beasts[10981] = { + ["name"] = "Frostwolf", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10981] + end + + entry["name"] = "Frostwolf" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 2597 } +end + +do + local entry = MTH_DS_Beasts[10985] + if not entry then + MTH_DS_Beasts[10985] = { + ["name"] = "Muzzled Frostwolf", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10985] + end + + entry["name"] = "Muzzled Frostwolf" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[10988] + if not entry then + MTH_DS_Beasts[10988] = { + ["name"] = "Kodo Spirit", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10988] + end + + entry["name"] = "Kodo Spirit" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[10989] + if not entry then + MTH_DS_Beasts[10989] = { + ["name"] = "Tamed Alterac Ram", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10989] + end + + entry["name"] = "Tamed Alterac Ram" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 2597 } +end + +do + local entry = MTH_DS_Beasts[10990] + if not entry then + MTH_DS_Beasts[10990] = { + ["name"] = "Alterac Ram", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[10990] + end + + entry["name"] = "Alterac Ram" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 2597 } +end + +do + local entry = MTH_DS_Beasts[11024] + if not entry then + MTH_DS_Beasts[11024] = { + ["name"] = "Della", + ["family"] = "Unknown", + ["lvl"] = "49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11024] + end + + entry["name"] = "Della" + entry["lvl"] = "49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["subname"] = "Jessir's Pet" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[11181] + if not entry then + MTH_DS_Beasts[11181] = { + ["name"] = "Shi'alune", + ["family"] = "Unknown", + ["lvl"] = "56", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11181] + end + + entry["name"] = "Shi'alune" + entry["lvl"] = "56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Kaerbrus' Pet" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[11357] + if not entry then + MTH_DS_Beasts[11357] = { + ["name"] = "Son of Hakkar", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11357] + end + + entry["name"] = "Son of Hakkar" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[11359] + if not entry then + MTH_DS_Beasts[11359] = { + ["name"] = "Soulflayer", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11359] + end + + entry["name"] = "Soulflayer" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[11360] + if not entry then + MTH_DS_Beasts[11360] = { + ["name"] = "Zulian Cub", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11360] + end + + entry["name"] = "Zulian Cub" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[11361] + if not entry then + MTH_DS_Beasts[11361] = { + ["name"] = "Zulian Tiger", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11361] + end + + entry["name"] = "Zulian Tiger" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[11365] + if not entry then + MTH_DS_Beasts[11365] = { + ["name"] = "Zulian Panther", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11365] + end + + entry["name"] = "Zulian Panther" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[11368] + if not entry then + MTH_DS_Beasts[11368] = { + ["name"] = "Bloodseeker Bat", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11368] + end + + entry["name"] = "Bloodseeker Bat" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[11370] + if not entry then + MTH_DS_Beasts[11370] = { + ["name"] = "Razzashi Broodwidow", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11370] + end + + entry["name"] = "Razzashi Broodwidow" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[11371] + if not entry then + MTH_DS_Beasts[11371] = { + ["name"] = "Razzashi Serpent", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11371] + end + + entry["name"] = "Razzashi Serpent" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[11372] + if not entry then + MTH_DS_Beasts[11372] = { + ["name"] = "Razzashi Adder", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11372] + end + + entry["name"] = "Razzashi Adder" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[11373] + if not entry then + MTH_DS_Beasts[11373] = { + ["name"] = "Razzashi Cobra", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11373] + end + + entry["name"] = "Razzashi Cobra" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[11374] + if not entry then + MTH_DS_Beasts[11374] = { + ["name"] = "Hooktooth Frenzy", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11374] + end + + entry["name"] = "Hooktooth Frenzy" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[11497] + if not entry then + MTH_DS_Beasts[11497] = { + ["name"] = "The Razza", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11497] + end + + entry["name"] = "The Razza" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[11614] + if not entry then + MTH_DS_Beasts[11614] = { + ["name"] = "Bloodshot", + ["family"] = "Unknown", + ["lvl"] = "54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11614] + end + + entry["name"] = "Bloodshot" + entry["lvl"] = "54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[11671] + if not entry then + MTH_DS_Beasts[11671] = { + ["name"] = "Core Hound", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11671] + end + + entry["name"] = "Core Hound" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 2717 } +end + +do + local entry = MTH_DS_Beasts[11672] + if not entry then + MTH_DS_Beasts[11672] = { + ["name"] = "Core Rager", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11672] + end + + entry["name"] = "Core Rager" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 2717 } +end + +do + local entry = MTH_DS_Beasts[11673] + if not entry then + MTH_DS_Beasts[11673] = { + ["name"] = "Ancient Core Hound", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11673] + end + + entry["name"] = "Ancient Core Hound" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 2717 } +end + +do + local entry = MTH_DS_Beasts[11710] + if not entry then + MTH_DS_Beasts[11710] = { + ["name"] = "Mirador", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11710] + end + + entry["name"] = "Mirador" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 28 } +end + +do + local entry = MTH_DS_Beasts[11735] + if not entry then + MTH_DS_Beasts[11735] = { + ["name"] = "Stonelash Scorpid", + ["family"] = "Unknown", + ["lvl"] = "54-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11735] + end + + entry["name"] = "Stonelash Scorpid" + entry["lvl"] = "54-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[11736] + if not entry then + MTH_DS_Beasts[11736] = { + ["name"] = "Stonelash Pincer", + ["family"] = "Unknown", + ["lvl"] = "56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11736] + end + + entry["name"] = "Stonelash Pincer" + entry["lvl"] = "56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[11737] + if not entry then + MTH_DS_Beasts[11737] = { + ["name"] = "Stonelash Flayer", + ["family"] = "Unknown", + ["lvl"] = "57-58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11737] + end + + entry["name"] = "Stonelash Flayer" + entry["lvl"] = "57-58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[11738] + if not entry then + MTH_DS_Beasts[11738] = { + ["name"] = "Sand Skitterer", + ["family"] = "Unknown", + ["lvl"] = "55-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11738] + end + + entry["name"] = "Sand Skitterer" + entry["lvl"] = "55-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[11739] + if not entry then + MTH_DS_Beasts[11739] = { + ["name"] = "Rock Stalker", + ["family"] = "Unknown", + ["lvl"] = "57-58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11739] + end + + entry["name"] = "Rock Stalker" + entry["lvl"] = "57-58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[11740] + if not entry then + MTH_DS_Beasts[11740] = { + ["name"] = "Dredge Striker", + ["family"] = "Unknown", + ["lvl"] = "55-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11740] + end + + entry["name"] = "Dredge Striker" + entry["lvl"] = "55-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[11741] + if not entry then + MTH_DS_Beasts[11741] = { + ["name"] = "Dredge Crusher", + ["family"] = "Unknown", + ["lvl"] = "57-58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11741] + end + + entry["name"] = "Dredge Crusher" + entry["lvl"] = "57-58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[11785] + if not entry then + MTH_DS_Beasts[11785] = { + ["name"] = "Ambereye Basilisk", + ["family"] = "Unknown", + ["lvl"] = "40-41", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11785] + end + + entry["name"] = "Ambereye Basilisk" + entry["lvl"] = "40-41" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 405 } +end + +do + local entry = MTH_DS_Beasts[11786] + if not entry then + MTH_DS_Beasts[11786] = { + ["name"] = "Ambereye Reaver", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11786] + end + + entry["name"] = "Ambereye Reaver" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 405 } +end + +do + local entry = MTH_DS_Beasts[11788] + if not entry then + MTH_DS_Beasts[11788] = { + ["name"] = "Rock Worm", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11788] + end + + entry["name"] = "Rock Worm" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 405 } +end + +do + local entry = MTH_DS_Beasts[11789] + if not entry then + MTH_DS_Beasts[11789] = { + ["name"] = "Deep Borer", + ["family"] = "Unknown", + ["lvl"] = "46-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11789] + end + + entry["name"] = "Deep Borer" + entry["lvl"] = "46-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 349 } +end + +do + local entry = MTH_DS_Beasts[11871] + if not entry then + MTH_DS_Beasts[11871] = { + ["name"] = "Grinning Dog", + ["family"] = "Unknown", + ["lvl"] = "48", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11871] + end + + entry["name"] = "Grinning Dog" + entry["lvl"] = "48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 1637 } +end + +do + local entry = MTH_DS_Beasts[11885] + if not entry then + MTH_DS_Beasts[11885] = { + ["name"] = "Blighthound", + ["family"] = "Unknown", + ["lvl"] = "58-59", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11885] + end + + entry["name"] = "Blighthound" + entry["lvl"] = "58-59" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 139 } +end + +do + local entry = MTH_DS_Beasts[11896] + if not entry then + MTH_DS_Beasts[11896] = { + ["name"] = "Borelgore", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11896] + end + + entry["name"] = "Borelgore" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 139 } +end + +do + local entry = MTH_DS_Beasts[11897] + if not entry then + MTH_DS_Beasts[11897] = { + ["name"] = "Duskwing", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11897] + end + + entry["name"] = "Duskwing" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 139 } +end + +do + local entry = MTH_DS_Beasts[11956] + if not entry then + MTH_DS_Beasts[11956] = { + ["name"] = "Great Bear Spirit", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11956] + end + + entry["name"] = "Great Bear Spirit" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 493 } +end + +do + local entry = MTH_DS_Beasts[11957] + if not entry then + MTH_DS_Beasts[11957] = { + ["name"] = "Great Cat Spirit", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11957] + end + + entry["name"] = "Great Cat Spirit" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 493 } +end + +do + local entry = MTH_DS_Beasts[11982] + if not entry then + MTH_DS_Beasts[11982] = { + ["name"] = "Magmadar", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[11982] + end + + entry["name"] = "Magmadar" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "boss" + entry["zoneIds"] = { 2717 } +end + +do + local entry = MTH_DS_Beasts[12121] + if not entry then + MTH_DS_Beasts[12121] = { + ["name"] = "Draka", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[12121] + end + + entry["name"] = "Draka" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 2597 } +end + +do + local entry = MTH_DS_Beasts[12122] + if not entry then + MTH_DS_Beasts[12122] = { + ["name"] = "Duros", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[12122] + end + + entry["name"] = "Duros" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 2597 } +end + +do + local entry = MTH_DS_Beasts[12124] + if not entry then + MTH_DS_Beasts[12124] = { + ["name"] = "Great Shark", + ["family"] = "Unknown", + ["lvl"] = "52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[12124] + end + + entry["name"] = "Great Shark" + entry["lvl"] = "52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[12125] + if not entry then + MTH_DS_Beasts[12125] = { + ["name"] = "Mammoth Shark", + ["family"] = "Unknown", + ["lvl"] = "54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[12125] + end + + entry["name"] = "Mammoth Shark" + entry["lvl"] = "54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[12218] + if not entry then + MTH_DS_Beasts[12218] = { + ["name"] = "Vile Larva", + ["family"] = "Unknown", + ["lvl"] = "45-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[12218] + end + + entry["name"] = "Vile Larva" + entry["lvl"] = "45-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 349 } +end + +do + local entry = MTH_DS_Beasts[12257] + if not entry then + MTH_DS_Beasts[12257] = { + ["name"] = "Mechanical Yeti", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[12257] + end + + entry["name"] = "Mechanical Yeti" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[12418] + if not entry then + MTH_DS_Beasts[12418] = { + ["name"] = "Gordok Hyena", + ["family"] = "Unknown", + ["lvl"] = "52-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[12418] + end + + entry["name"] = "Gordok Hyena" + entry["lvl"] = "52-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[12800] + if not entry then + MTH_DS_Beasts[12800] = { + ["name"] = "Chimaerok", + ["family"] = "Unknown", + ["lvl"] = "60-61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[12800] + end + + entry["name"] = "Chimaerok" + entry["lvl"] = "60-61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[12801] + if not entry then + MTH_DS_Beasts[12801] = { + ["name"] = "Arcane Chimaerok", + ["family"] = "Unknown", + ["lvl"] = "60-62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[12801] + end + + entry["name"] = "Arcane Chimaerok" + entry["lvl"] = "60-62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[12802] + if not entry then + MTH_DS_Beasts[12802] = { + ["name"] = "Chimaerok Devourer", + ["family"] = "Unknown", + ["lvl"] = "61-62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[12802] + end + + entry["name"] = "Chimaerok Devourer" + entry["lvl"] = "61-62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[12803] + if not entry then + MTH_DS_Beasts[12803] = { + ["name"] = "Lord Lakmaeran", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[12803] + end + + entry["name"] = "Lord Lakmaeran" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[13036] + if not entry then + MTH_DS_Beasts[13036] = { + ["name"] = "Gordok Mastiff", + ["family"] = "Unknown", + ["lvl"] = "57-59", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13036] + end + + entry["name"] = "Gordok Mastiff" + entry["lvl"] = "57-59" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 2557 } +end + +do + local entry = MTH_DS_Beasts[13160] + if not entry then + MTH_DS_Beasts[13160] = { + ["name"] = "Carrion Swarmer", + ["family"] = "Unknown", + ["lvl"] = "56-58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13160] + end + + entry["name"] = "Carrion Swarmer" + entry["lvl"] = "56-58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 2557 } +end + +do + local entry = MTH_DS_Beasts[13161] + if not entry then + MTH_DS_Beasts[13161] = { + ["name"] = "Aerie Gryphon", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13161] + end + + entry["name"] = "Aerie Gryphon" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[13178] + if not entry then + MTH_DS_Beasts[13178] = { + ["name"] = "War Rider", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13178] + end + + entry["name"] = "War Rider" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[13221] + if not entry then + MTH_DS_Beasts[13221] = { + ["name"] = "Ryson's Eye in the Sky", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13221] + end + + entry["name"] = "Ryson's Eye in the Sky" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["classification"] = "elite" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[13323] + if not entry then + MTH_DS_Beasts[13323] = { + ["name"] = "Subterranean Diemetradon", + ["family"] = "Unknown", + ["lvl"] = "46-48", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13323] + end + + entry["name"] = "Subterranean Diemetradon" + entry["lvl"] = "46-48" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 349 } +end + +do + local entry = MTH_DS_Beasts[13533] + if not entry then + MTH_DS_Beasts[13533] = { + ["name"] = "Spewed Larva", + ["family"] = "Unknown", + ["lvl"] = "45-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13533] + end + + entry["name"] = "Spewed Larva" + entry["lvl"] = "45-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 349 } +end + +do + local entry = MTH_DS_Beasts[13596] + if not entry then + MTH_DS_Beasts[13596] = { + ["name"] = "Rotgrip", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13596] + end + + entry["name"] = "Rotgrip" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 349 } +end + +do + local entry = MTH_DS_Beasts[13599] + if not entry then + MTH_DS_Beasts[13599] = { + ["name"] = "Stolid Snapjaw", + ["family"] = "Unknown", + ["lvl"] = "46-47", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13599] + end + + entry["name"] = "Stolid Snapjaw" + entry["lvl"] = "46-47" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 349 } +end + +do + local entry = MTH_DS_Beasts[13618] + if not entry then + MTH_DS_Beasts[13618] = { + ["name"] = "Stabled Frostwolf", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13618] + end + + entry["name"] = "Stabled Frostwolf" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 2597 } +end + +do + local entry = MTH_DS_Beasts[13676] + if not entry then + MTH_DS_Beasts[13676] = { + ["name"] = "Stabled Alterac Ram", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13676] + end + + entry["name"] = "Stabled Alterac Ram" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 2597 } +end + +do + local entry = MTH_DS_Beasts[13837] + if not entry then + MTH_DS_Beasts[13837] = { + ["name"] = "Captured Stallion", + ["family"] = "Unknown", + ["lvl"] = "40", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13837] + end + + entry["name"] = "Captured Stallion" + entry["lvl"] = "40" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 405 } +end + +do + local entry = MTH_DS_Beasts[13896] + if not entry then + MTH_DS_Beasts[13896] = { + ["name"] = "Scalebeard", + ["family"] = "Unknown", + ["lvl"] = "52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[13896] + end + + entry["name"] = "Scalebeard" + entry["lvl"] = "52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 16 } +end + +do + local entry = MTH_DS_Beasts[14123] + if not entry then + MTH_DS_Beasts[14123] = { + ["name"] = "Steeljaw Snapper", + ["family"] = "Unknown", + ["lvl"] = "42-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14123] + end + + entry["name"] = "Steeljaw Snapper" + entry["lvl"] = "42-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[14234] + if not entry then + MTH_DS_Beasts[14234] = { + ["name"] = "Hayoc", + ["family"] = "Unknown", + ["lvl"] = "41", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14234] + end + + entry["name"] = "Hayoc" + entry["lvl"] = "41" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[14237] + if not entry then + MTH_DS_Beasts[14237] = { + ["name"] = "Oozeworm", + ["family"] = "Unknown", + ["lvl"] = "42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14237] + end + + entry["name"] = "Oozeworm" + entry["lvl"] = "42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[14282] + if not entry then + MTH_DS_Beasts[14282] = { + ["name"] = "Frostwolf Bloodhound", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14282] + end + + entry["name"] = "Frostwolf Bloodhound" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 2597 } +end + +do + local entry = MTH_DS_Beasts[14283] + if not entry then + MTH_DS_Beasts[14283] = { + ["name"] = "Stormpike Owl", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14283] + end + + entry["name"] = "Stormpike Owl" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 2597 } +end + +do + local entry = MTH_DS_Beasts[14306] + if not entry then + MTH_DS_Beasts[14306] = { + ["name"] = "Eskhandar", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14306] + end + + entry["name"] = "Eskhandar" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[14308] + if not entry then + MTH_DS_Beasts[14308] = { + ["name"] = "Ferra", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14308] + end + + entry["name"] = "Ferra" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 2557 } +end + +do + local entry = MTH_DS_Beasts[14339] + if not entry then + MTH_DS_Beasts[14339] = { + ["name"] = "Death Howl", + ["family"] = "Unknown", + ["lvl"] = "49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14339] + end + + entry["name"] = "Death Howl" + entry["lvl"] = "49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[14343] + if not entry then + MTH_DS_Beasts[14343] = { + ["name"] = "Olm the Wise", + ["family"] = "Unknown", + ["lvl"] = "52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14343] + end + + entry["name"] = "Olm the Wise" + entry["lvl"] = "52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["classification"] = "rare" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[14344] + if not entry then + MTH_DS_Beasts[14344] = { + ["name"] = "Mongress", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14344] + end + + entry["name"] = "Mongress" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[14472] + if not entry then + MTH_DS_Beasts[14472] = { + ["name"] = "Gretheer", + ["family"] = "Unknown", + ["lvl"] = "57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14472] + end + + entry["name"] = "Gretheer" + entry["lvl"] = "57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[14476] + if not entry then + MTH_DS_Beasts[14476] = { + ["name"] = "Krellack", + ["family"] = "Unknown", + ["lvl"] = "56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14476] + end + + entry["name"] = "Krellack" + entry["lvl"] = "56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[14477] + if not entry then + MTH_DS_Beasts[14477] = { + ["name"] = "Grubthor", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14477] + end + + entry["name"] = "Grubthor" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[14490] + if not entry then + MTH_DS_Beasts[14490] = { + ["name"] = "Rippa", + ["family"] = "Unknown", + ["lvl"] = "44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14490] + end + + entry["name"] = "Rippa" + entry["lvl"] = "44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[14491] + if not entry then + MTH_DS_Beasts[14491] = { + ["name"] = "Kurmokk", + ["family"] = "Unknown", + ["lvl"] = "42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14491] + end + + entry["name"] = "Kurmokk" + entry["lvl"] = "42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 33 } +end + +do + local entry = MTH_DS_Beasts[14532] + if not entry then + MTH_DS_Beasts[14532] = { + ["name"] = "Razzashi Venombrood", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14532] + end + + entry["name"] = "Razzashi Venombrood" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[14566] + if not entry then + MTH_DS_Beasts[14566] = { + ["name"] = "Ancient Equine Spirit", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14566] + end + + entry["name"] = "Ancient Equine Spirit" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[14568] + if not entry then + MTH_DS_Beasts[14568] = { + ["name"] = "Darkreaver's Fallen Charger", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14568] + end + + entry["name"] = "Darkreaver's Fallen Charger" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[14821] + if not entry then + MTH_DS_Beasts[14821] = { + ["name"] = "Razzashi Raptor", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14821] + end + + entry["name"] = "Razzashi Raptor" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[14880] + if not entry then + MTH_DS_Beasts[14880] = { + ["name"] = "Razzashi Skitterer", + ["family"] = "Unknown", + ["lvl"] = "56-58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14880] + end + + entry["name"] = "Razzashi Skitterer" + entry["lvl"] = "56-58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[14943] + if not entry then + MTH_DS_Beasts[14943] = { + ["name"] = "Guse's War Rider", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14943] + end + + entry["name"] = "Guse's War Rider" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[14944] + if not entry then + MTH_DS_Beasts[14944] = { + ["name"] = "Jeztor's War Rider", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14944] + end + + entry["name"] = "Jeztor's War Rider" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[14945] + if not entry then + MTH_DS_Beasts[14945] = { + ["name"] = "Mulverick's War Rider", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14945] + end + + entry["name"] = "Mulverick's War Rider" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[14946] + if not entry then + MTH_DS_Beasts[14946] = { + ["name"] = "Slidore's Gryphon", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14946] + end + + entry["name"] = "Slidore's Gryphon" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 532 } +end + +do + local entry = MTH_DS_Beasts[14947] + if not entry then + MTH_DS_Beasts[14947] = { + ["name"] = "Ichman's Gryphon", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14947] + end + + entry["name"] = "Ichman's Gryphon" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[14948] + if not entry then + MTH_DS_Beasts[14948] = { + ["name"] = "Vipore's Gryphon", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14948] + end + + entry["name"] = "Vipore's Gryphon" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[14965] + if not entry then + MTH_DS_Beasts[14965] = { + ["name"] = "Frenzied Bloodseeker Bat", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14965] + end + + entry["name"] = "Frenzied Bloodseeker Bat" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[14988] + if not entry then + MTH_DS_Beasts[14988] = { + ["name"] = "Ohgan", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[14988] + end + + entry["name"] = "Ohgan" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "boss" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[15041] + if not entry then + MTH_DS_Beasts[15041] = { + ["name"] = "Spawn of Mar'li", + ["family"] = "Unknown", + ["lvl"] = "59-60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15041] + end + + entry["name"] = "Spawn of Mar'li" + entry["lvl"] = "59-60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[15043] + if not entry then + MTH_DS_Beasts[15043] = { + ["name"] = "Zulian Crocolisk", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15043] + end + + entry["name"] = "Zulian Crocolisk" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[15067] + if not entry then + MTH_DS_Beasts[15067] = { + ["name"] = "Zulian Stalker", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15067] + end + + entry["name"] = "Zulian Stalker" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[15068] + if not entry then + MTH_DS_Beasts[15068] = { + ["name"] = "Zulian Guardian", + ["family"] = "Unknown", + ["lvl"] = "57-60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15068] + end + + entry["name"] = "Zulian Guardian" + entry["lvl"] = "57-60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[15101] + if not entry then + MTH_DS_Beasts[15101] = { + ["name"] = "Zulian Prowler", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15101] + end + + entry["name"] = "Zulian Prowler" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[15114] + if not entry then + MTH_DS_Beasts[15114] = { + ["name"] = "Gahz'ranka", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15114] + end + + entry["name"] = "Gahz'ranka" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "boss" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[15172] + if not entry then + MTH_DS_Beasts[15172] = { + ["name"] = "Glibb", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15172] + end + + entry["name"] = "Glibb" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["subname"] = "Brann Bronzebeard's Pet Monkey" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[15196] + if not entry then + MTH_DS_Beasts[15196] = { + ["name"] = "Deathclasp", + ["family"] = "Unknown", + ["lvl"] = "59", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15196] + end + + entry["name"] = "Deathclasp" + entry["lvl"] = "59" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 1377 } +end + +do + local entry = MTH_DS_Beasts[15204] + if not entry then + MTH_DS_Beasts[15204] = { + ["name"] = "High Marshal Whirlaxis", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15204] + end + + entry["name"] = "High Marshal Whirlaxis" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Abyssal High Council" + entry["classification"] = "boss" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[15220] + if not entry then + MTH_DS_Beasts[15220] = { + ["name"] = "The Duke of Zephyrs", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15220] + end + + entry["name"] = "The Duke of Zephyrs" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Abyssal Council" + entry["classification"] = "elite" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[15505] + if not entry then + MTH_DS_Beasts[15505] = { + ["name"] = "Canal Frenzy", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15505] + end + + entry["name"] = "Canal Frenzy" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 509 } +end + +do + local entry = MTH_DS_Beasts[15554] + if not entry then + MTH_DS_Beasts[15554] = { + ["name"] = "Number Two", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15554] + end + + entry["name"] = "Number Two" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[15571] + if not entry then + MTH_DS_Beasts[15571] = { + ["name"] = "Maws", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15571] + end + + entry["name"] = "Maws" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["classification"] = "boss" + entry["zoneIds"] = { 2717 } +end + +do + local entry = MTH_DS_Beasts[15721] + if not entry then + MTH_DS_Beasts[15721] = { + ["name"] = "Mechanical Greench", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15721] + end + + entry["name"] = "Mechanical Greench" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[15952] + if not entry then + MTH_DS_Beasts[15952] = { + ["name"] = "Maexxna", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15952] + end + + entry["name"] = "Maexxna" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "boss" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[15974] + if not entry then + MTH_DS_Beasts[15974] = { + ["name"] = "Dread Creeper", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15974] + end + + entry["name"] = "Dread Creeper" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[15975] + if not entry then + MTH_DS_Beasts[15975] = { + ["name"] = "Carrion Spinner", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15975] + end + + entry["name"] = "Carrion Spinner" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[15976] + if not entry then + MTH_DS_Beasts[15976] = { + ["name"] = "Venom Stalker", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15976] + end + + entry["name"] = "Venom Stalker" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[15977] + if not entry then + MTH_DS_Beasts[15977] = { + ["name"] = "Infectious Skitterer", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[15977] + end + + entry["name"] = "Infectious Skitterer" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[16036] + if not entry then + MTH_DS_Beasts[16036] = { + ["name"] = "Frenzied Bat", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[16036] + end + + entry["name"] = "Frenzied Bat" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[16037] + if not entry then + MTH_DS_Beasts[16037] = { + ["name"] = "Plagued Bat", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[16037] + end + + entry["name"] = "Plagued Bat" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[16056] + if not entry then + MTH_DS_Beasts[16056] = { + ["name"] = "Diseased Maggot", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[16056] + end + + entry["name"] = "Diseased Maggot" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[16057] + if not entry then + MTH_DS_Beasts[16057] = { + ["name"] = "Rotting Maggot", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[16057] + end + + entry["name"] = "Rotting Maggot" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[16095] + if not entry then + MTH_DS_Beasts[16095] = { + ["name"] = "Gnashjaw", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[16095] + end + + entry["name"] = "Gnashjaw" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Malgen Longspear's Pet" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[16098] + if not entry then + MTH_DS_Beasts[16098] = { + ["name"] = "Empyrean", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[16098] + end + + entry["name"] = "Empyrean" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[16117] + if not entry then + MTH_DS_Beasts[16117] = { + ["name"] = "Plagued Swine", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[16117] + end + + entry["name"] = "Plagued Swine" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 139 } +end + +do + local entry = MTH_DS_Beasts[16232] + if not entry then + MTH_DS_Beasts[16232] = { + ["name"] = "Caravan Mule", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[16232] + end + + entry["name"] = "Caravan Mule" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 139 } +end + +do + local entry = MTH_DS_Beasts[16448] + if not entry then + MTH_DS_Beasts[16448] = { + ["name"] = "Plagued Deathhound", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[16448] + end + + entry["name"] = "Plagued Deathhound" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[16453] + if not entry then + MTH_DS_Beasts[16453] = { + ["name"] = "Necro Stalker", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[16453] + end + + entry["name"] = "Necro Stalker" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 533 } +end + +do + local entry = MTH_DS_Beasts[16509] + if not entry then + MTH_DS_Beasts[16509] = { + ["name"] = "Argent Warhorse", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[16509] + end + + entry["name"] = "Argent Warhorse" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[17055] + if not entry then + MTH_DS_Beasts[17055] = { + ["name"] = "Maexxna Spiderling", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[17055] + end + + entry["name"] = "Maexxna Spiderling" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 490 } +end + +do + local entry = MTH_DS_Beasts[17660] + if not entry then + MTH_DS_Beasts[17660] = { + ["name"] = "Skeletal Gryphon", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[17660] + end + + entry["name"] = "Skeletal Gryphon" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[29001] + if not entry then + MTH_DS_Beasts[29001] = { + ["name"] = "Greater Feral Spirit", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[29001] + end + + entry["name"] = "Greater Feral Spirit" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 493 } +end + +do + local entry = MTH_DS_Beasts[49004] + if not entry then + MTH_DS_Beasts[49004] = { + ["name"] = "Mallon The Moontouched", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[49004] + end + + entry["name"] = "Mallon The Moontouched" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[50060] + if not entry then + MTH_DS_Beasts[50060] = { + ["name"] = "Turtlhu", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[50060] + end + + entry["name"] = "Turtlhu" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[51539] + if not entry then + MTH_DS_Beasts[51539] = { + ["name"] = "Nerubling", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[51539] + end + + entry["name"] = "Nerubling" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[52145] + if not entry then + MTH_DS_Beasts[52145] = { + ["name"] = "Incindis", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[52145] + end + + entry["name"] = "Incindis" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Ancient Incendosaur" + entry["classification"] = "boss" + entry["zoneIds"] = { 2717 } +end + +do + local entry = MTH_DS_Beasts[52146] + if not entry then + MTH_DS_Beasts[52146] = { + ["name"] = "Small Incendic Egg", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[52146] + end + + entry["name"] = "Small Incendic Egg" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 532 } +end + +do + local entry = MTH_DS_Beasts[52147] + if not entry then + MTH_DS_Beasts[52147] = { + ["name"] = "Large Incendic Egg", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[52147] + end + + entry["name"] = "Large Incendic Egg" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 814 } +end + +do + local entry = MTH_DS_Beasts[52148] + if not entry then + MTH_DS_Beasts[52148] = { + ["name"] = "Spawn of Incindis", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[52148] + end + + entry["name"] = "Spawn of Incindis" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 532 } +end + +do + local entry = MTH_DS_Beasts[52149] + if not entry then + MTH_DS_Beasts[52149] = { + ["name"] = "Flameskin Incendosaur", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[52149] + end + + entry["name"] = "Flameskin Incendosaur" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 800 } +end + +do + local entry = MTH_DS_Beasts[59997] + if not entry then + MTH_DS_Beasts[59997] = { + ["name"] = "Red Owl", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[59997] + end + + entry["name"] = "Red Owl" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["classification"] = "boss" + entry["zoneIds"] = { 2717 } +end + +do + local entry = MTH_DS_Beasts[59998] + if not entry then + MTH_DS_Beasts[59998] = { + ["name"] = "Blue Owl", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[59998] + end + + entry["name"] = "Blue Owl" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["classification"] = "boss" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[60369] + if not entry then + MTH_DS_Beasts[60369] = { + ["name"] = "Pack Ally", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60369] + end + + entry["name"] = "Pack Ally" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[60491] + if not entry then + MTH_DS_Beasts[60491] = { + ["name"] = "Putridfang", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60491] + end + + entry["name"] = "Putridfang" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["subname"] = "Valdos Madhound's Pet" + entry["zoneIds"] = { 1497 } +end + +do + local entry = MTH_DS_Beasts[60492] + if not entry then + MTH_DS_Beasts[60492] = { + ["name"] = "Angerclaw", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60492] + end + + entry["name"] = "Angerclaw" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["subname"] = "Noel Bearfinger's Pet" + entry["zoneIds"] = { 1497 } +end + +do + local entry = MTH_DS_Beasts[60493] + if not entry then + MTH_DS_Beasts[60493] = { + ["name"] = "Blackest Widow", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60493] + end + + entry["name"] = "Blackest Widow" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["subname"] = "Vaya Spidersbite's Pet" + entry["zoneIds"] = { 1497 } +end + +do + local entry = MTH_DS_Beasts[60494] + if not entry then + MTH_DS_Beasts[60494] = { + ["name"] = "Sorrowclaw", + ["family"] = "Unknown", + ["lvl"] = "41", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60494] + end + + entry["name"] = "Sorrowclaw" + entry["lvl"] = "41" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 8 } +end + +do + local entry = MTH_DS_Beasts[60501] + if not entry then + MTH_DS_Beasts[60501] = { + ["name"] = "Glasseye", + ["family"] = "Unknown", + ["lvl"] = "53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60501] + end + + entry["name"] = "Glasseye" + entry["lvl"] = "53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[60539] + if not entry then + MTH_DS_Beasts[60539] = { + ["name"] = "Bristlefur", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60539] + end + + entry["name"] = "Bristlefur" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 215 } +end + +do + local entry = MTH_DS_Beasts[60545] + if not entry then + MTH_DS_Beasts[60545] = { + ["name"] = "Icepoint Whiteclaw", + ["family"] = "Unknown", + ["lvl"] = "44-46", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60545] + end + + entry["name"] = "Icepoint Whiteclaw" + entry["lvl"] = "44-46" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 1 } +end + +do + local entry = MTH_DS_Beasts[60604] + if not entry then + MTH_DS_Beasts[60604] = { + ["name"] = "Wicked Skitterer", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60604] + end + + entry["name"] = "Wicked Skitterer" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 35 } +end + +do + local entry = MTH_DS_Beasts[60605] + if not entry then + MTH_DS_Beasts[60605] = { + ["name"] = "Ice Crawler", + ["family"] = "Unknown", + ["lvl"] = "41-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60605] + end + + entry["name"] = "Ice Crawler" + entry["lvl"] = "41-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 1 } +end + +do + local entry = MTH_DS_Beasts[60632] + if not entry then + MTH_DS_Beasts[60632] = { + ["name"] = "Clampjaw", + ["family"] = "Unknown", + ["lvl"] = "44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60632] + end + + entry["name"] = "Clampjaw" + entry["lvl"] = "44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[60679] + if not entry then + MTH_DS_Beasts[60679] = { + ["name"] = "Spirit of the Turtle", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60679] + end + + entry["name"] = "Spirit of the Turtle" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 532 } +end + +do + local entry = MTH_DS_Beasts[60683] + if not entry then + MTH_DS_Beasts[60683] = { + ["name"] = "The Greater Spirit of Harmony", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60683] + end + + entry["name"] = "The Greater Spirit of Harmony" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "elite" + entry["zoneIds"] = { 229 } +end + +do + local entry = MTH_DS_Beasts[60697] + if not entry then + MTH_DS_Beasts[60697] = { + ["name"] = "Soothound", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60697] + end + + entry["name"] = "Soothound" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[60755] + if not entry then + MTH_DS_Beasts[60755] = { + ["name"] = "Viceclaw", + ["family"] = "Unknown", + ["lvl"] = "45", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60755] + end + + entry["name"] = "Viceclaw" + entry["lvl"] = "45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 400 } +end + +do + local entry = MTH_DS_Beasts[60776] + if not entry then + MTH_DS_Beasts[60776] = { + ["name"] = "Valril the Seeker", + ["family"] = "Unknown", + ["lvl"] = "52", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60776] + end + + entry["name"] = "Valril the Seeker" + entry["lvl"] = "52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[60777] + if not entry then + MTH_DS_Beasts[60777] = { + ["name"] = "Gazush the Rabid", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "H", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60777] + end + + entry["name"] = "Gazush the Rabid" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "H" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[60785] + if not entry then + MTH_DS_Beasts[60785] = { + ["name"] = "Eperius", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60785] + end + + entry["name"] = "Eperius" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 45 } +end + +do + local entry = MTH_DS_Beasts[60873] + if not entry then + MTH_DS_Beasts[60873] = { + ["name"] = "Young Blackrock Worg", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[60873] + end + + entry["name"] = "Young Blackrock Worg" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[61072] + if not entry then + MTH_DS_Beasts[61072] = { + ["name"] = "Coastal Ripper", + ["family"] = "Unknown", + ["lvl"] = "56-57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61072] + end + + entry["name"] = "Coastal Ripper" + entry["lvl"] = "56-57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61074] + if not entry then + MTH_DS_Beasts[61074] = { + ["name"] = "Highvale Gorilla", + ["family"] = "Unknown", + ["lvl"] = "56-57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61074] + end + + entry["name"] = "Highvale Gorilla" + entry["lvl"] = "56-57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61075] + if not entry then + MTH_DS_Beasts[61075] = { + ["name"] = "Rockshell Crawler", + ["family"] = "Unknown", + ["lvl"] = "56-57", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61075] + end + + entry["name"] = "Rockshell Crawler" + entry["lvl"] = "56-57" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61076] + if not entry then + MTH_DS_Beasts[61076] = { + ["name"] = "Sandshell Crawler", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61076] + end + + entry["name"] = "Sandshell Crawler" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61077] + if not entry then + MTH_DS_Beasts[61077] = { + ["name"] = "Highvale Chimpanzee", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61077] + end + + entry["name"] = "Highvale Chimpanzee" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61078] + if not entry then + MTH_DS_Beasts[61078] = { + ["name"] = "Highvale Monkey", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61078] + end + + entry["name"] = "Highvale Monkey" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61080] + if not entry then + MTH_DS_Beasts[61080] = { + ["name"] = "Elder Highvale Gorilla", + ["family"] = "Unknown", + ["lvl"] = "57-59", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61080] + end + + entry["name"] = "Elder Highvale Gorilla" + entry["lvl"] = "57-59" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61082] + if not entry then + MTH_DS_Beasts[61082] = { + ["name"] = "Highvale Thunderer", + ["family"] = "Unknown", + ["lvl"] = "56-58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61082] + end + + entry["name"] = "Highvale Thunderer" + entry["lvl"] = "56-58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61090] + if not entry then + MTH_DS_Beasts[61090] = { + ["name"] = "Brushtail Cobra", + ["family"] = "Unknown", + ["lvl"] = "55-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61090] + end + + entry["name"] = "Brushtail Cobra" + entry["lvl"] = "55-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61091] + if not entry then + MTH_DS_Beasts[61091] = { + ["name"] = "Brushtail Adder", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61091] + end + + entry["name"] = "Brushtail Adder" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61092] + if not entry then + MTH_DS_Beasts[61092] = { + ["name"] = "Gravelhide Basilisk", + ["family"] = "Unknown", + ["lvl"] = "54-56", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61092] + end + + entry["name"] = "Gravelhide Basilisk" + entry["lvl"] = "54-56" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61095] + if not entry then + MTH_DS_Beasts[61095] = { + ["name"] = "Venomflayer Screecher", + ["family"] = "Unknown", + ["lvl"] = "53-55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61095] + end + + entry["name"] = "Venomflayer Screecher" + entry["lvl"] = "53-55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61096] + if not entry then + MTH_DS_Beasts[61096] = { + ["name"] = "Venomflayer Serpent", + ["family"] = "Unknown", + ["lvl"] = "57-58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61096] + end + + entry["name"] = "Venomflayer Serpent" + entry["lvl"] = "57-58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61097] + if not entry then + MTH_DS_Beasts[61097] = { + ["name"] = "Deepwater Hammerhead", + ["family"] = "Unknown", + ["lvl"] = "56-58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61097] + end + + entry["name"] = "Deepwater Hammerhead" + entry["lvl"] = "56-58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[61098] + if not entry then + MTH_DS_Beasts[61098] = { + ["name"] = "King Morogo Thunderfoot", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61098] + end + + entry["name"] = "King Morogo Thunderfoot" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61155] + if not entry then + MTH_DS_Beasts[61155] = { + ["name"] = "Shaewyn", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61155] + end + + entry["name"] = "Shaewyn" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 361 } +end + +do + local entry = MTH_DS_Beasts[61170] + if not entry then + MTH_DS_Beasts[61170] = { + ["name"] = "Father Bear", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61170] + end + + entry["name"] = "Father Bear" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 331 } +end + +do + local entry = MTH_DS_Beasts[61172] + if not entry then + MTH_DS_Beasts[61172] = { + ["name"] = "The Winter Patriarch", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61172] + end + + entry["name"] = "The Winter Patriarch" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["zoneIds"] = { 215 } +end + +do + local entry = MTH_DS_Beasts[61206] + if not entry then + MTH_DS_Beasts[61206] = { + ["name"] = "Skitterweb Crawler", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61206] + end + + entry["name"] = "Skitterweb Crawler" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 532 } +end + +do + local entry = MTH_DS_Beasts[61207] + if not entry then + MTH_DS_Beasts[61207] = { + ["name"] = "Skitterweb Darkfang", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61207] + end + + entry["name"] = "Skitterweb Darkfang" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 532 } +end + +do + local entry = MTH_DS_Beasts[61208] + if not entry then + MTH_DS_Beasts[61208] = { + ["name"] = "Skitterweb Venomfang", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61208] + end + + entry["name"] = "Skitterweb Venomfang" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 532 } +end + +do + local entry = MTH_DS_Beasts[61209] + if not entry then + MTH_DS_Beasts[61209] = { + ["name"] = "Skitterweb Leaper", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61209] + end + + entry["name"] = "Skitterweb Leaper" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 532 } +end + +do + local entry = MTH_DS_Beasts[61221] + if not entry then + MTH_DS_Beasts[61221] = { + ["name"] = "Brood Queen Araxxna", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61221] + end + + entry["name"] = "Brood Queen Araxxna" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "boss" + entry["zoneIds"] = { 532 } +end + +do + local entry = MTH_DS_Beasts[61228] + if not entry then + MTH_DS_Beasts[61228] = { + ["name"] = "Duskpelt Stalker", + ["family"] = "Unknown", + ["lvl"] = "41-43", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61228] + end + + entry["name"] = "Duskpelt Stalker" + entry["lvl"] = "41-43" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[61229] + if not entry then + MTH_DS_Beasts[61229] = { + ["name"] = "Starving Duskpelt", + ["family"] = "Unknown", + ["lvl"] = "43-45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61229] + end + + entry["name"] = "Starving Duskpelt" + entry["lvl"] = "43-45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[61231] + if not entry then + MTH_DS_Beasts[61231] = { + ["name"] = "Vilewing Bat", + ["family"] = "Unknown", + ["lvl"] = "42-44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61231] + end + + entry["name"] = "Vilewing Bat" + entry["lvl"] = "42-44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[61232] + if not entry then + MTH_DS_Beasts[61232] = { + ["name"] = "Hollow Web Spider", + ["family"] = "Unknown", + ["lvl"] = "42-44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61232] + end + + entry["name"] = "Hollow Web Spider" + entry["lvl"] = "42-44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[61233] + if not entry then + MTH_DS_Beasts[61233] = { + ["name"] = "Hollow Web Venomspitter", + ["family"] = "Unknown", + ["lvl"] = "42-45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61233] + end + + entry["name"] = "Hollow Web Venomspitter" + entry["lvl"] = "42-45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[61332] + if not entry then + MTH_DS_Beasts[61332] = { + ["name"] = "Misthoof Stag", + ["family"] = "Unknown", + ["lvl"] = "60-61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61332] + end + + entry["name"] = "Misthoof Stag" + entry["lvl"] = "60-61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[61333] + if not entry then + MTH_DS_Beasts[61333] = { + ["name"] = "Misthoof Sprinter", + ["family"] = "Unknown", + ["lvl"] = "58-60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61333] + end + + entry["name"] = "Misthoof Sprinter" + entry["lvl"] = "58-60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[61336] + if not entry then + MTH_DS_Beasts[61336] = { + ["name"] = "Greathorn Owl", + ["family"] = "Unknown", + ["lvl"] = "58-60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61336] + end + + entry["name"] = "Greathorn Owl" + entry["lvl"] = "58-60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[61337] + if not entry then + MTH_DS_Beasts[61337] = { + ["name"] = "Greathorn Hunter", + ["family"] = "Unknown", + ["lvl"] = "60-61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61337] + end + + entry["name"] = "Greathorn Hunter" + entry["lvl"] = "60-61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[61401] + if not entry then + MTH_DS_Beasts[61401] = { + ["name"] = "Greater Vilewing Bat", + ["family"] = "Unknown", + ["lvl"] = "43-45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61401] + end + + entry["name"] = "Greater Vilewing Bat" + entry["lvl"] = "43-45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[61480] + if not entry then + MTH_DS_Beasts[61480] = { + ["name"] = "Brilliant Wind Serpent", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61480] + end + + entry["name"] = "Brilliant Wind Serpent" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 215 } +end + +do + local entry = MTH_DS_Beasts[61488] + if not entry then + MTH_DS_Beasts[61488] = { + ["name"] = "Azurebeak", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61488] + end + + entry["name"] = "Azurebeak" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[61490] + if not entry then + MTH_DS_Beasts[61490] = { + ["name"] = "Whitestripe Tiger", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61490] + end + + entry["name"] = "Whitestripe Tiger" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 46 } +end + +do + local entry = MTH_DS_Beasts[61500] + if not entry then + MTH_DS_Beasts[61500] = { + ["name"] = "Highvale Silverback", + ["family"] = "Unknown", + ["lvl"] = "58", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61500] + end + + entry["name"] = "Highvale Silverback" + entry["lvl"] = "58" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 440 } +end + +do + local entry = MTH_DS_Beasts[61515] + if not entry then + MTH_DS_Beasts[61515] = { + ["name"] = "Ripjaw", + ["family"] = "Unknown", + ["lvl"] = "51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61515] + end + + entry["name"] = "Ripjaw" + entry["lvl"] = "51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Scourge of the Reef" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[61516] + if not entry then + MTH_DS_Beasts[61516] = { + ["name"] = "Embereye", + ["family"] = "Unknown", + ["lvl"] = "51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61516] + end + + entry["name"] = "Embereye" + entry["lvl"] = "51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[61546] + if not entry then + MTH_DS_Beasts[61546] = { + ["name"] = "Ursoc", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61546] + end + + entry["name"] = "Ursoc" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "boss" + entry["zoneIds"] = { 14 } +end + +do + local entry = MTH_DS_Beasts[61547] + if not entry then + MTH_DS_Beasts[61547] = { + ["name"] = "Goldrinn", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61547] + end + + entry["name"] = "Goldrinn" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "boss" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[61552] + if not entry then + MTH_DS_Beasts[61552] = { + ["name"] = "Duskskitterer", + ["family"] = "Unknown", + ["lvl"] = "44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61552] + end + + entry["name"] = "Duskskitterer" + entry["lvl"] = "44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[61586] + if not entry then + MTH_DS_Beasts[61586] = { + ["name"] = "Cenerron", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61586] + end + + entry["name"] = "Cenerron" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["subname"] = "Feralkin Druid" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[61589] + if not entry then + MTH_DS_Beasts[61589] = { + ["name"] = "Voltra", + ["family"] = "Unknown", + ["lvl"] = "45", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61589] + end + + entry["name"] = "Voltra" + entry["lvl"] = "45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Velos' Pet" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[61596] + if not entry then + MTH_DS_Beasts[61596] = { + ["name"] = "Venomlash Chimaera", + ["family"] = "Unknown", + ["lvl"] = "42-44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61596] + end + + entry["name"] = "Venomlash Chimaera" + entry["lvl"] = "42-44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[61597] + if not entry then + MTH_DS_Beasts[61597] = { + ["name"] = "Dunebound Chimaera", + ["family"] = "Unknown", + ["lvl"] = "42-44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61597] + end + + entry["name"] = "Dunebound Chimaera" + entry["lvl"] = "42-44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[61599] + if not entry then + MTH_DS_Beasts[61599] = { + ["name"] = "Darksaber", + ["family"] = "Unknown", + ["lvl"] = "42-44", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61599] + end + + entry["name"] = "Darksaber" + entry["lvl"] = "42-44" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 357 } +end + +do + local entry = MTH_DS_Beasts[61603] + if not entry then + MTH_DS_Beasts[61603] = { + ["name"] = "Thunder", + ["family"] = "Unknown", + ["lvl"] = "41", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61603] + end + + entry["name"] = "Thunder" + entry["lvl"] = "41" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 130 } +end + +do + local entry = MTH_DS_Beasts[61604] + if not entry then + MTH_DS_Beasts[61604] = { + ["name"] = "Gilneas Stable Horse", + ["family"] = "Unknown", + ["lvl"] = "41", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61604] + end + + entry["name"] = "Gilneas Stable Horse" + entry["lvl"] = "41" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 815 } +end + +do + local entry = MTH_DS_Beasts[61932] + if not entry then + MTH_DS_Beasts[61932] = { + ["name"] = "Vampiric Gloomwing", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61932] + end + + entry["name"] = "Vampiric Gloomwing" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 814 } +end + +do + local entry = MTH_DS_Beasts[61933] + if not entry then + MTH_DS_Beasts[61933] = { + ["name"] = "Greater Gloomwing", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61933] + end + + entry["name"] = "Greater Gloomwing" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 814 } +end + +do + local entry = MTH_DS_Beasts[61935] + if not entry then + MTH_DS_Beasts[61935] = { + ["name"] = "Duskfang Creeper", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[61935] + end + + entry["name"] = "Duskfang Creeper" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 814 } +end + +do + local entry = MTH_DS_Beasts[62004] + if not entry then + MTH_DS_Beasts[62004] = { + ["name"] = "Lord Rygel", + ["family"] = "Unknown", + ["lvl"] = "59", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[62004] + end + + entry["name"] = "Lord Rygel" + entry["lvl"] = "59" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["subname"] = "Frog Noble" + entry["classification"] = "elite" + entry["zoneIds"] = { 331 } +end + +do + local entry = MTH_DS_Beasts[62112] + if not entry then + MTH_DS_Beasts[62112] = { + ["name"] = "Spirit of Eskhandar", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[62112] + end + + entry["name"] = "Spirit of Eskhandar" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "boss" + entry["zoneIds"] = { 532 } +end + +do + local entry = MTH_DS_Beasts[62249] + if not entry then + MTH_DS_Beasts[62249] = { + ["name"] = "Old Feralclaw", + ["family"] = "Unknown", + ["lvl"] = "41", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[62249] + end + + entry["name"] = "Old Feralclaw" + entry["lvl"] = "41" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 15 } +end + +do + local entry = MTH_DS_Beasts[62499] + if not entry then + MTH_DS_Beasts[62499] = { + ["name"] = "Sandfury Scorpid", + ["family"] = "Unknown", + ["lvl"] = "45", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[62499] + end + + entry["name"] = "Sandfury Scorpid" + entry["lvl"] = "45" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 209 } +end + +do + local entry = MTH_DS_Beasts[62509] + if not entry then + MTH_DS_Beasts[62509] = { + ["name"] = "Gnaws", + ["family"] = "Unknown", + ["lvl"] = "41", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[62509] + end + + entry["name"] = "Gnaws" + entry["lvl"] = "41" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare elite" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[62518] + if not entry then + MTH_DS_Beasts[62518] = { + ["name"] = "Bristlebelly", + ["family"] = "Unknown", + ["lvl"] = "40", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[62518] + end + + entry["name"] = "Bristlebelly" + entry["lvl"] = "40" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "A" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["subname"] = "Leander's Companion" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[62640] + if not entry then + MTH_DS_Beasts[62640] = { + ["name"] = "Arashna", + ["family"] = "Unknown", + ["lvl"] = "40", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[62640] + end + + entry["name"] = "Arashna" + entry["lvl"] = "40" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 38 } +end + +do + local entry = MTH_DS_Beasts[65110] + if not entry then + MTH_DS_Beasts[65110] = { + ["name"] = "Darkwater Python", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[65110] + end + + entry["name"] = "Darkwater Python" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 269 } +end + +do + local entry = MTH_DS_Beasts[65111] + if not entry then + MTH_DS_Beasts[65111] = { + ["name"] = "Murkwater Crocolisk", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[65111] + end + + entry["name"] = "Murkwater Crocolisk" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 269 } +end + +do + local entry = MTH_DS_Beasts[65112] + if not entry then + MTH_DS_Beasts[65112] = { + ["name"] = "Blackfang Tarantula", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[65112] + end + + entry["name"] = "Blackfang Tarantula" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 269 } +end + +do + local entry = MTH_DS_Beasts[65120] + if not entry then + MTH_DS_Beasts[65120] = { + ["name"] = "Poison Cloud", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[65120] + end + + entry["name"] = "Poison Cloud" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "boss" + entry["zoneIds"] = { 2717 } +end + +do + local entry = MTH_DS_Beasts[65122] + if not entry then + MTH_DS_Beasts[65122] = { + ["name"] = "Rotmaw", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[65122] + end + + entry["name"] = "Rotmaw" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 269 } +end + +do + local entry = MTH_DS_Beasts[66003] + if not entry then + MTH_DS_Beasts[66003] = { + ["name"] = "An Unknown Entity", + ["family"] = "Unknown", + ["lvl"] = "63", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[66003] + end + + entry["name"] = "An Unknown Entity" + entry["lvl"] = "63" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["classification"] = "boss" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[80255] + if not entry then + MTH_DS_Beasts[80255] = { + ["name"] = "Brown Fox", + ["family"] = "Unknown", + ["lvl"] = "41-42", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[80255] + end + + entry["name"] = "Brown Fox" + entry["lvl"] = "41-42" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 369 } +end + +do + local entry = MTH_DS_Beasts[80259] + if not entry then + MTH_DS_Beasts[80259] = { + ["name"] = "Mist Fox", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[80259] + end + + entry["name"] = "Mist Fox" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 41 } +end + +do + local entry = MTH_DS_Beasts[80260] + if not entry then + MTH_DS_Beasts[80260] = { + ["name"] = "Spirit Fox", + ["family"] = "Unknown", + ["lvl"] = "53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[80260] + end + + entry["name"] = "Spirit Fox" + entry["lvl"] = "53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "rare" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[80321] + if not entry then + MTH_DS_Beasts[80321] = { + ["name"] = "Emiga", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[80321] + end + + entry["name"] = "Emiga" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "friendly" + entry["reactAColor"] = "green" + entry["reactH"] = "friendly" + entry["reactHColor"] = "green" + entry["subname"] = "Rodfather's Minion" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[91819] + if not entry then + MTH_DS_Beasts[91819] = { + ["name"] = "Hazzuri Tiger", + ["family"] = "Unknown", + ["lvl"] = "50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91819] + end + + entry["name"] = "Hazzuri Tiger" + entry["lvl"] = "50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91825] + if not entry then + MTH_DS_Beasts[91825] = { + ["name"] = "Young Bengal Tiger", + ["family"] = "Unknown", + ["lvl"] = "48-49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91825] + end + + entry["name"] = "Young Bengal Tiger" + entry["lvl"] = "48-49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91826] + if not entry then + MTH_DS_Beasts[91826] = { + ["name"] = "Bengal Matriarch", + ["family"] = "Unknown", + ["lvl"] = "49-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91826] + end + + entry["name"] = "Bengal Matriarch" + entry["lvl"] = "49-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91827] + if not entry then + MTH_DS_Beasts[91827] = { + ["name"] = "Bengal Tiger", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91827] + end + + entry["name"] = "Bengal Tiger" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91828] + if not entry then + MTH_DS_Beasts[91828] = { + ["name"] = "Bengal Alpha", + ["family"] = "Unknown", + ["lvl"] = "53-54", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91828] + end + + entry["name"] = "Bengal Alpha" + entry["lvl"] = "53-54" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91829] + if not entry then + MTH_DS_Beasts[91829] = { + ["name"] = "Jungleback Stomper", + ["family"] = "Unknown", + ["lvl"] = "50-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91829] + end + + entry["name"] = "Jungleback Stomper" + entry["lvl"] = "50-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91830] + if not entry then + MTH_DS_Beasts[91830] = { + ["name"] = "Jungleback Thrasher", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91830] + end + + entry["name"] = "Jungleback Thrasher" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91831] + if not entry then + MTH_DS_Beasts[91831] = { + ["name"] = "Bright Crawler", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91831] + end + + entry["name"] = "Bright Crawler" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91832] + if not entry then + MTH_DS_Beasts[91832] = { + ["name"] = "Island Pincer", + ["family"] = "Unknown", + ["lvl"] = "48-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91832] + end + + entry["name"] = "Island Pincer" + entry["lvl"] = "48-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91833] + if not entry then + MTH_DS_Beasts[91833] = { + ["name"] = "Rock Crawler", + ["family"] = "Unknown", + ["lvl"] = "49-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91833] + end + + entry["name"] = "Rock Crawler" + entry["lvl"] = "49-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 40 } +end + +do + local entry = MTH_DS_Beasts[91837] + if not entry then + MTH_DS_Beasts[91837] = { + ["name"] = "Shank Reef Ripper", + ["family"] = "Unknown", + ["lvl"] = "49-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91837] + end + + entry["name"] = "Shank Reef Ripper" + entry["lvl"] = "49-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91838] + if not entry then + MTH_DS_Beasts[91838] = { + ["name"] = "Deepshell Snapper", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91838] + end + + entry["name"] = "Deepshell Snapper" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91864] + if not entry then + MTH_DS_Beasts[91864] = { + ["name"] = "Deepwater Shark", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91864] + end + + entry["name"] = "Deepwater Shark" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 618 } +end + +do + local entry = MTH_DS_Beasts[91914] + if not entry then + MTH_DS_Beasts[91914] = { + ["name"] = "Undead Frenzy", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91914] + end + + entry["name"] = "Undead Frenzy" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 309 } +end + +do + local entry = MTH_DS_Beasts[91917] + if not entry then + MTH_DS_Beasts[91917] = { + ["name"] = "Corpsemuncher", + ["family"] = "Unknown", + ["lvl"] = "62", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91917] + end + + entry["name"] = "Corpsemuncher" + entry["lvl"] = "62" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "boss" + entry["zoneIds"] = { 800 } +end + +do + local entry = MTH_DS_Beasts[91960] + if not entry then + MTH_DS_Beasts[91960] = { + ["name"] = "Junglepaw Panther", + ["family"] = "Unknown", + ["lvl"] = "48-50", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91960] + end + + entry["name"] = "Junglepaw Panther" + entry["lvl"] = "48-50" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91961] + if not entry then + MTH_DS_Beasts[91961] = { + ["name"] = "Junglepaw Shadow Panther", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91961] + end + + entry["name"] = "Junglepaw Shadow Panther" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91962] + if not entry then + MTH_DS_Beasts[91962] = { + ["name"] = "Saltsnap Crocolisk", + ["family"] = "Unknown", + ["lvl"] = "49-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91962] + end + + entry["name"] = "Saltsnap Crocolisk" + entry["lvl"] = "49-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91963] + if not entry then + MTH_DS_Beasts[91963] = { + ["name"] = "Deepstone Basilisk", + ["family"] = "Unknown", + ["lvl"] = "52-53", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91963] + end + + entry["name"] = "Deepstone Basilisk" + entry["lvl"] = "52-53" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91964] + if not entry then + MTH_DS_Beasts[91964] = { + ["name"] = "Saltjaw Basilisk", + ["family"] = "Unknown", + ["lvl"] = "49-51", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91964] + end + + entry["name"] = "Saltjaw Basilisk" + entry["lvl"] = "49-51" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[91966] + if not entry then + MTH_DS_Beasts[91966] = { + ["name"] = "Venomous Jungle Serpent", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[91966] + end + + entry["name"] = "Venomous Jungle Serpent" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[92146] + if not entry then + MTH_DS_Beasts[92146] = { + ["name"] = "Strand Crawler", + ["family"] = "Unknown", + ["lvl"] = "48-49", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[92146] + end + + entry["name"] = "Strand Crawler" + entry["lvl"] = "48-49" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[92147] + if not entry then + MTH_DS_Beasts[92147] = { + ["name"] = "Silver Coast Crawler", + ["family"] = "Unknown", + ["lvl"] = "51-52", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[92147] + end + + entry["name"] = "Silver Coast Crawler" + entry["lvl"] = "51-52" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "neutral" + entry["reactAColor"] = "orange" + entry["reactH"] = "neutral" + entry["reactHColor"] = "orange" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[92163] + if not entry then + MTH_DS_Beasts[92163] = { + ["name"] = "Letashaz", + ["family"] = "Unknown", + ["lvl"] = "55", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[92163] + end + + entry["name"] = "Letashaz" + entry["lvl"] = "55" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 0 } +end + +do + local entry = MTH_DS_Beasts[93105] + if not entry then + MTH_DS_Beasts[93105] = { + ["name"] = "Shadow Creeper", + ["family"] = "Unknown", + ["lvl"] = "61", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[93105] + end + + entry["name"] = "Shadow Creeper" + entry["lvl"] = "61" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 35 } +end + +do + local entry = MTH_DS_Beasts[161208] + if not entry then + MTH_DS_Beasts[161208] = { + ["name"] = "The Black Widow", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[161208] + end + + entry["name"] = "The Black Widow" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["classification"] = "elite" + entry["zoneIds"] = { 27 } +end + +do + local entry = MTH_DS_Beasts[161212] + if not entry then + MTH_DS_Beasts[161212] = { + ["name"] = "Spiderling", + ["family"] = "Unknown", + ["lvl"] = "60", + ["fac"] = "AH", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = {}, + } + entry = MTH_DS_Beasts[161212] + end + + entry["name"] = "Spiderling" + entry["lvl"] = "60" + local currentFac = entry["fac"] + if not currentFac or currentFac == "" or currentFac == "Unknown" or currentFac == "unknown" then + entry["fac"] = "AH" + end + entry["type"] = "Beast" + entry["reactA"] = "hostile" + entry["reactAColor"] = "red" + entry["reactH"] = "hostile" + entry["reactHColor"] = "red" + entry["zoneIds"] = { 309 } +end + diff --git a/data/ds-beasts.lua b/data/ds-beasts.lua new file mode 100644 index 0000000..b242abd --- /dev/null +++ b/data/ds-beasts.lua @@ -0,0 +1,8840 @@ +-- MetaHunt Beast Database +-- TWoW spreadsheet + pfQuest spawn coordinates +-- Total: 627 beasts + +if not MTH_DS then MTH_DS = {} end + +MTH_DS_Beasts = { + [30] = { + ["name"] = "Forest Spider", + ["family"] = "Spiders", + ["lvl"] = "Unknown", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 41, + ["coords"] = { + }, + }, + [43] = { + ["name"] = "Mine Spider", + ["family"] = "Spiders", + ["lvl"] = "8-9", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 16, + ["coords"] = { + {60.0, 48.7, 12}, + }, + }, + [69] = { + ["name"] = "Timber Wolf", + ["family"] = "Wolves", + ["lvl"] = "2", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 70, + ["coords"] = { + {49.5, 49.3, 12}, + }, + }, + [113] = { + ["name"] = "Stonetusk Boar", + ["family"] = "Boars", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 104, + ["coords"] = { + {40.9, 91.9, 12}, + }, + }, + [118] = { + ["name"] = "Prowler", + ["family"] = "Wolves", + ["lvl"] = "9-10", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 168, + ["coords"] = { + {83.0, 85.5, 12}, + }, + }, + [119] = { + ["name"] = "Longsnout", + ["family"] = "Boars", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 19, + ["coords"] = { + {24.6, 88.5, 12}, + }, + }, + [154] = { + ["name"] = "Greater Fleshripper", + ["family"] = "Carrion Birds", + ["lvl"] = "16-17", + ["fac"] = "Unknown", + ["abilities"] = "Screech 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 26, + ["coords"] = { + {58.8, 64.2, 40}, + }, + }, + [157] = { + ["name"] = "Goretusk", + ["family"] = "Boars", + ["lvl"] = "14-15", + ["fac"] = "Unknown", + ["abilities"] = "Charge 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 110, + ["coords"] = { + {35.7, 80.8, 40}, + }, + }, + [199] = { + ["name"] = "Young Fleshripper", + ["family"] = "Carrion Birds", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 74, + ["coords"] = { + {16.6, 77.1, 12}, + }, + }, + [213] = { + ["name"] = "Starving Dire Wolf", + ["family"] = "Wolves", + ["lvl"] = "19-20", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 90, + ["coords"] = { + {12.6, 66.8, 10}, + }, + }, + [217] = { + ["name"] = "Venom Web Spider", + ["family"] = "Spiders", + ["lvl"] = "19-20", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 69, + ["coords"] = { + {15.7, 72.6, 10}, + }, + }, + [299] = { + ["name"] = "Young Wolf", + ["family"] = "Wolves", + ["lvl"] = "1", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 88, + ["coords"] = { + {42.6, 61.0, 12}, + }, + }, + [330] = { + ["name"] = "Princess", + ["family"] = "Boars", + ["lvl"] = "9", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 2, + ["coords"] = { + {69.7, 79.3, 12}, + }, + }, + [345] = { + ["name"] = "Bellygrub", + ["family"] = "Boars", + ["lvl"] = "24", + ["fac"] = "Unknown", + ["abilities"] = "Charge 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 2, + ["coords"] = { + {15.7, 49.3, 44}, + }, + }, + [390] = { + ["name"] = "Porcine Entourage", + ["family"] = "Boars", + ["lvl"] = "7", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 4, + ["coords"] = { + {69.7, 79.2, 12}, + }, + }, + [428] = { + ["name"] = "Dire Condor", + ["family"] = "Carrion Birds", + ["lvl"] = "18-19", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 41, + ["coords"] = { + {60.8, 78.3, 44}, + }, + }, + [442] = { + ["name"] = "Tarantula", + ["family"] = "Spiders", + ["lvl"] = "15-16", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 55, + ["coords"] = { + {98.2, 11.6, 10}, + }, + }, + [454] = { + ["name"] = "Young Goretusk", + ["family"] = "Boars", + ["lvl"] = "12-13", + ["fac"] = "Unknown", + ["abilities"] = "Charge 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 25, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 96, + ["coords"] = { + {20.8, 86.9, 12}, + }, + }, + [462] = { + ["name"] = "Vultros", + ["family"] = "Carrion Birds", + ["lvl"] = "26", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 75600, + ["respawnMaxSeconds"] = 108000, + ["respawnSamples"] = 32, + ["coords"] = { + {62.0, 75.5, 40}, + }, + }, + [471] = { + ["name"] = "Mother Fang", + ["family"] = "Spiders", + ["lvl"] = "10", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 3600, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 2, + ["coords"] = { + {62.1, 48.1, 12}, + }, + }, + [505] = { + ["name"] = "Greater Tarantula", + ["family"] = "Spiders", + ["lvl"] = "19-20", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 26, + ["coords"] = { + {67.2, 76.6, 44}, + }, + }, + [524] = { + ["name"] = "Rockhide Boar", + ["family"] = "Boars", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 180, + ["coords"] = { + {45.0, 89.6, 12}, + }, + }, + [525] = { + ["name"] = "Mangy Wolf", + ["family"] = "Wolves", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.7", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 174, + ["coords"] = { + {23.6, 77.0, 12}, + }, + }, + [539] = { + ["name"] = "Pygmy Venom Web Spider", + ["family"] = "Spiders", + ["lvl"] = "18-19", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 87, + ["coords"] = { + {13.2, 70.3, 10}, + }, + }, + [547] = { + ["name"] = "Great Goretusk", + ["family"] = "Boars", + ["lvl"] = "16-17", + ["fac"] = "Unknown", + ["abilities"] = "Charge 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 95, + ["coords"] = { + {67.5, 70.9, 40}, + }, + }, + [565] = { + ["name"] = "Rabid Dire Wolf", + ["family"] = "Wolves", + ["lvl"] = "20-21", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 285, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 70, + ["coords"] = { + {12.9, 69.2, 10}, + }, + }, + [569] = { + ["name"] = "Green Recluse", + ["family"] = "Spiders", + ["lvl"] = "21-22", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3, Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 50, + ["coords"] = { + {16.5, 69.5, 10}, + }, + }, + [574] = { + ["name"] = "Naraxis", + ["family"] = "Spiders", + ["lvl"] = "27", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4, Web", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 27000, + ["respawnSamples"] = 2, + ["coords"] = { + {86.4, 47.6, 10}, + }, + }, + [616] = { + ["name"] = "Chatter", + ["family"] = "Spiders", + ["lvl"] = "23", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 14400, + ["respawnMaxSeconds"] = 19800, + ["respawnSamples"] = 2, + ["coords"] = { + {53.1, 37.1, 44}, + }, + }, + [628] = { + ["name"] = "Black Ravager", + ["family"] = "Wolves", + ["lvl"] = "24-25", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 20, + ["coords"] = { + {52.7, 63.2, 10}, + }, + }, + [681] = { + ["name"] = "Young Stranglethorn Tiger", + ["family"] = "Cats", + ["lvl"] = "30-31", + ["fac"] = "Unknown", + ["abilities"] = "Cower 3", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 48, + ["coords"] = { + {34.7, 17.5, 33}, + }, + }, + [682] = { + ["name"] = "Stranglethorn Tiger", + ["family"] = "Cats", + ["lvl"] = "32-33", + ["fac"] = "Unknown", + ["abilities"] = "Dash 1", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 49, + ["coords"] = { + {47.2, 18.5, 33}, + }, + }, + [683] = { + ["name"] = "Young Panther", + ["family"] = "Cats", + ["lvl"] = "30-31", + ["fac"] = "Unknown", + ["abilities"] = "Cower 3", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 49, + ["coords"] = { + {35.1, 15.1, 33}, + }, + }, + [684] = { + ["name"] = "Shadowmaw Panther", + ["family"] = "Cats", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "Prowl 1", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 102, + ["coords"] = { + {39.3, 45.5, 33}, + }, + }, + [685] = { + ["name"] = "Stranglethorn Raptor", + ["family"] = "Raptors", + ["lvl"] = "33-34", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 28, + ["coords"] = { + {27.5, 17.7, 33}, + }, + }, + [686] = { + ["name"] = "Lashtail Raptor", + ["family"] = "Raptors", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 58, + ["coords"] = { + {37.6, 27.9, 33}, + }, + }, + [687] = { + ["name"] = "Jungle Stalker", + ["family"] = "Raptors", + ["lvl"] = "40-41", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 86, + ["coords"] = { + {23.2, 51.5, 33}, + }, + }, + [704] = { + ["name"] = "Ragged Timber Wolf", + ["family"] = "Wolves", + ["lvl"] = "2", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 48, + ["coords"] = { + {24.0, 79.7, 1}, + }, + }, + [705] = { + ["name"] = "Ragged Young Wolf", + ["family"] = "Wolves", + ["lvl"] = "1", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 555, + ["respawnMaxSeconds"] = 555, + ["respawnSamples"] = 118, + ["coords"] = { + {22.6, 78.2, 1}, + }, + }, + [708] = { + ["name"] = "Small Crag Boar", + ["family"] = "Boars", + ["lvl"] = "3", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 120, + ["coords"] = { + {24.7, 79.3, 1}, + }, + }, + [730] = { + ["name"] = "Tethis", + ["family"] = "Raptors", + ["lvl"] = "43", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 4", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 240, + ["respawnMaxSeconds"] = 600, + ["respawnSamples"] = 4, + ["coords"] = { + {28.7, 44.8, 33}, + }, + }, + [731] = { + ["name"] = "King Bangalash", + ["family"] = "Cats", + ["lvl"] = "43", + ["fac"] = "Unknown", + ["abilities"] = "Claw 6, Dash 2", + ["attackSpeed"] = "1.4", + ["elite"] = true, + ["respawnMinSeconds"] = 240, + ["respawnMaxSeconds"] = 600, + ["respawnSamples"] = 2, + ["coords"] = { + {38.2, 35.6, 33}, + }, + }, + [736] = { + ["name"] = "Panther", + ["family"] = "Cats", + ["lvl"] = "32-33", + ["fac"] = "Unknown", + ["abilities"] = "Cower 3", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 31, + ["coords"] = { + {28.2, 16.6, 33}, + }, + }, + [767] = { + ["name"] = "Swamp Jaguar", + ["family"] = "Cats", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "Dash 1", + ["attackSpeed"] = "1.2", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 92, + ["coords"] = { + {27.2, 64.0, 8}, + }, + }, + [768] = { + ["name"] = "Shadow Panther", + ["family"] = "Cats", + ["lvl"] = "39-40", + ["fac"] = "Unknown", + ["abilities"] = "Prowl 1", + ["attackSpeed"] = "1.6", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 12, + ["coords"] = { + {65.4, 67.9, 8}, + }, + }, + [769] = { + ["name"] = "Deathstrike Tarantula", + ["family"] = "Spiders", + ["lvl"] = "40-41", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 76, + ["coords"] = { + {81.1, 89.1, 8}, + }, + }, + [772] = { + ["name"] = "Stranglethorn Tigress", + ["family"] = "Cats", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 129, + ["coords"] = { + {39.5, 41.7, 33}, + }, + }, + [822] = { + ["name"] = "Young Forest Bear", + ["family"] = "Bears", + ["lvl"] = "8-9", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2", + ["attackSpeed"] = "2.5", + ["respawnMinSeconds"] = 25, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 98, + ["coords"] = { + {25.5, 90.7, 12}, + }, + }, + [830] = { + ["name"] = "Sand Crawler", + ["family"] = "Crabs", + ["lvl"] = "13-14", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 34, + ["coords"] = { + {39.6, 12.9, 40}, + }, + }, + [831] = { + ["name"] = "Sea Crawler", + ["family"] = "Crabs", + ["lvl"] = "15-16", + ["fac"] = "Unknown", + ["abilities"] = "Bubble Barrier 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 24, + ["coords"] = { + {31.1, 24.2, 40}, + }, + }, + [833] = { + ["name"] = "Coyote Packleader", + ["family"] = "Wolves", + ["lvl"] = "11-12", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2, Furious Howl 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 42, + ["coords"] = { + {35.2, 42.7, 40}, + }, + }, + [834] = { + ["name"] = "Coyote", + ["family"] = "Wolves", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 95, + ["coords"] = { + {19.7, 84.0, 12}, + }, + }, + [854] = { + ["name"] = "Young Jungle Stalker", + ["family"] = "Raptors", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 4, + ["coords"] = { + {31.3, 44.7, 33}, + }, + }, + [855] = { + ["name"] = "Young Stranglethorn Raptor", + ["family"] = "Raptors", + ["lvl"] = "30-31", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 3, + ["coords"] = { + {24.2, 16.3, 33}, + }, + }, + [856] = { + ["name"] = "Young Lashtail Raptor", + ["family"] = "Raptors", + ["lvl"] = "33-34", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 2, + ["coords"] = { + {30.6, 23.5, 33}, + }, + }, + [858] = { + ["name"] = "Sorrow Spinner", + ["family"] = "Spiders", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 54, + ["coords"] = { + {24.9, 64.0, 8}, + }, + }, + [922] = { + ["name"] = "Silt Crawler", + ["family"] = "Crabs", + ["lvl"] = "40-41", + ["fac"] = "Unknown", + ["abilities"] = "Claw 6, Bubble Barrier 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 25, + ["coords"] = { + {80.5, 93.9, 8}, + }, + }, + [923] = { + ["name"] = "Young Black Ravager", + ["family"] = "Wolves", + ["lvl"] = "23-24", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 80, + ["coords"] = { + {22.0, 78.3, 10}, + }, + }, + [930] = { + ["name"] = "Black Widow Hatchling", + ["family"] = "Spiders", + ["lvl"] = "24-25", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 108, + ["coords"] = { + {80.7, 62.8, 10}, + }, + }, + [949] = { + ["name"] = "Carrion Recluse", + ["family"] = "Spiders", + ["lvl"] = "25-26", + ["fac"] = "Unknown", + ["abilities"] = "Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 12, + ["coords"] = { + {21.5, 46.3, 10}, + }, + }, + [976] = { + ["name"] = "Kurzen War Tiger", + ["family"] = "Cats", + ["lvl"] = "32-33", + ["fac"] = "Unknown", + ["abilities"] = "Dash 1", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 2, + ["coords"] = { + {45.9, 10.0, 33}, + }, + }, + [977] = { + ["name"] = "Kurzen War Panther", + ["family"] = "Cats", + ["lvl"] = "32-33", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 2, + ["coords"] = { + {45.7, 9.7, 33}, + }, + }, + [1015] = { + ["name"] = "Highland Raptor", + ["family"] = "Raptors", + ["lvl"] = "23-24", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 54, + ["coords"] = { + {57.8, 37.6, 11}, + }, + }, + [1016] = { + ["name"] = "Highland Lashtail", + ["family"] = "Raptors", + ["lvl"] = "24-25", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 38, + ["coords"] = { + {57.8, 35.4, 11}, + }, + }, + [1017] = { + ["name"] = "Highland Scytheclaw", + ["family"] = "Raptors", + ["lvl"] = "25-26", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 43, + ["coords"] = { + {56.4, 31.4, 11}, + }, + }, + [1018] = { + ["name"] = "Highland Razormaw", + ["family"] = "Raptors", + ["lvl"] = "27-28", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 105, + ["coords"] = { + {72.0, 39.9, 11}, + }, + }, + [1019] = { + ["name"] = "Elder Razormaw", + ["family"] = "Raptors", + ["lvl"] = "29", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 62, + ["coords"] = { + {69.3, 32.9, 11}, + }, + }, + [1020] = { + ["name"] = "Mottled Raptor", + ["family"] = "Raptors", + ["lvl"] = "22-23", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 54, + ["coords"] = { + {23.0, 59.8, 11}, + }, + }, + [1021] = { + ["name"] = "Mottled Screecher", + ["family"] = "Raptors", + ["lvl"] = "24-25", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 32, + ["coords"] = { + {23.7, 60.4, 11}, + }, + }, + [1022] = { + ["name"] = "Mottled Scytheclaw", + ["family"] = "Raptors", + ["lvl"] = "25-26", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 28, + ["coords"] = { + {35.6, 51.9, 11}, + }, + }, + [1023] = { + ["name"] = "Mottled Razormaw", + ["family"] = "Raptors", + ["lvl"] = "26-27", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 18, + ["coords"] = { + {33.7, 49.8, 11}, + }, + }, + [1082] = { + ["name"] = "Sawtooth Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "38-39", + ["fac"] = "Unknown", + ["abilities"] = "Death Roll 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 50, + ["coords"] = { + {56.1, 56.8, 8}, + }, + }, + [1084] = { + ["name"] = "Young Sawtooth Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "Death Roll 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 40, + ["coords"] = { + {28.6, 56.0, 8}, + }, + }, + [1087] = { + ["name"] = "Sawtooth Snapper", + ["family"] = "Crocolisks", + ["lvl"] = "41-42", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6, Death Roll 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 60, + ["coords"] = { + {82.6, 99.5, 8}, + }, + }, + [1088] = { + ["name"] = "Monstrous Crawler", + ["family"] = "Crabs", + ["lvl"] = "43-44", + ["fac"] = "Unknown", + ["abilities"] = "Claw 6, Bubble Barrier 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 32, + ["coords"] = { + {78.9, 98.1, 8}, + }, + }, + [1108] = { + ["name"] = "Mistvale Gorilla", + ["family"] = "Gorillas", + ["lvl"] = "32-33", + ["fac"] = "Unknown", + ["abilities"] = "Thunderstomp 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 22, + ["coords"] = { + {36.7, 18.3, 33}, + }, + }, + [1109] = { + ["name"] = "Fleshripper", + ["family"] = "Carrion Birds", + ["lvl"] = "13-14", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 96, + ["coords"] = { + {37.0, 69.1, 40}, + }, + }, + [1111] = { + ["name"] = "Leech Stalker", + ["family"] = "Spiders", + ["lvl"] = "21-22", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 105, + ["coords"] = { + {56.1, 67.9, 11}, + }, + }, + [1112] = { + ["name"] = "Leech Widow", + ["family"] = "Spiders", + ["lvl"] = "24", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 14400, + ["respawnMaxSeconds"] = 27000, + ["respawnSamples"] = 4, + ["coords"] = { + {47.3, 59.2, 11}, + }, + }, + [1114] = { + ["name"] = "Jungle Thunderer", + ["family"] = "Gorillas", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "Thunderstomp 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 84, + ["coords"] = { + {47.7, 38.0, 33}, + }, + }, + [1125] = { + ["name"] = "Crag Boar", + ["family"] = "Boars", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 146, + ["coords"] = { + {42.2, 67.6, 1}, + }, + }, + [1126] = { + ["name"] = "Large Crag Boar", + ["family"] = "Boars", + ["lvl"] = "6-7", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 84, + ["coords"] = { + {47.3, 63.9, 1}, + }, + }, + [1127] = { + ["name"] = "Elder Crag Boar", + ["family"] = "Boars", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 178, + ["coords"] = { + {70.7, 61.6, 1}, + }, + }, + [1128] = { + ["name"] = "Young Black Bear", + ["family"] = "Bears", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 60, + ["coords"] = { + {41.9, 66.8, 1}, + }, + }, + [1129] = { + ["name"] = "Black Bear", + ["family"] = "Bears", + ["lvl"] = "6-7", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.5", + ["coords"] = { + }, + }, + [1130] = { + ["name"] = "Bjarn", + ["family"] = "Bears", + ["lvl"] = "12", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 5400, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 2, + ["coords"] = { + {55.6, 58.5, 1}, + }, + }, + [1131] = { + ["name"] = "Winter Wolf", + ["family"] = "Wolves", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 46, + ["coords"] = { + {50.7, 52.5, 1}, + }, + }, + [1132] = { + ["name"] = "Timber", + ["family"] = "Wolves", + ["lvl"] = "10", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 3600, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 2, + ["coords"] = { + {34.2, 41.8, 1}, + }, + }, + [1133] = { + ["name"] = "Starving Winter Wolf", + ["family"] = "Wolves", + ["lvl"] = "8-9", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 9000, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 2, + ["coords"] = { + {34.2, 41.9, 1}, + }, + }, + [1138] = { + ["name"] = "Snow Tracker Wolf", + ["family"] = "Wolves", + ["lvl"] = "6-7", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 20, + ["coords"] = { + {44.6, 47.1, 1}, + }, + }, + [1140] = { + ["name"] = "Razormaw Matriarch", + ["family"] = "Raptors", + ["lvl"] = "30", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 3", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 27000, + ["respawnSamples"] = 3, + ["coords"] = { + {69.9, 29.2, 11}, + }, + }, + [1150] = { + ["name"] = "River Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "30-31", + ["fac"] = "Unknown", + ["abilities"] = "Death Roll 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 26, + ["coords"] = { + {39.9, 14.4, 33}, + }, + }, + [1152] = { + ["name"] = "Snapjaw Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 54, + ["coords"] = { + {37.9, 32.6, 33}, + }, + }, + [1184] = { + ["name"] = "Cliff Lurker", + ["family"] = "Spiders", + ["lvl"] = "13-14", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 44, + ["coords"] = { + {55.3, 78.3, 38}, + }, + }, + [1185] = { + ["name"] = "Wood Lurker", + ["family"] = "Spiders", + ["lvl"] = "17-18", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 33, + ["coords"] = { + {78.9, 40.9, 38}, + }, + }, + [1186] = { + ["name"] = "Elder Black Bear", + ["family"] = "Bears", + ["lvl"] = "11-12", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 72, + ["coords"] = { + {20.7, 77.4, 38}, + }, + }, + [1188] = { + ["name"] = "Grizzled Black Bear", + ["family"] = "Bears", + ["lvl"] = "13-14", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 86, + ["coords"] = { + {63.2, 80.2, 38}, + }, + }, + [1189] = { + ["name"] = "Black Bear Patriarch", + ["family"] = "Bears", + ["lvl"] = "16-17", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 66, + ["coords"] = { + {37.9, 63.4, 38}, + }, + }, + [1190] = { + ["name"] = "Mountain Boar", + ["family"] = "Boars", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 78, + ["coords"] = { + {29.9, 46.8, 38}, + }, + }, + [1191] = { + ["name"] = "Mangy Mountain Boar", + ["family"] = "Boars", + ["lvl"] = "14-15", + ["fac"] = "Unknown", + ["abilities"] = "Charge 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 114, + ["coords"] = { + {52.5, 74.7, 38}, + }, + }, + [1192] = { + ["name"] = "Elder Mountain Boar", + ["family"] = "Boars", + ["lvl"] = "16-17", + ["fac"] = "Unknown", + ["abilities"] = "Charge 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 57, + ["coords"] = { + {74.8, 50.7, 38}, + }, + }, + [1194] = { + ["name"] = "Mountain Buzzard", + ["family"] = "Carrion Birds", + ["lvl"] = "15-16", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 100, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 61, + ["coords"] = { + {78.7, 70.3, 38}, + }, + }, + [1195] = { + ["name"] = "Forest Lurker", + ["family"] = "Spiders", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 106, + ["coords"] = { + {19.3, 83.5, 38}, + }, + }, + [1196] = { + ["name"] = "Ice Claw Bear", + ["family"] = "Bears", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Claw 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 97, + ["coords"] = { + {46.3, 63.3, 1}, + }, + }, + [1201] = { + ["name"] = "Snow Leopard", + ["family"] = "Cats", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 144, + ["coords"] = { + {47.3, 63.3, 1}, + }, + }, + [1216] = { + ["name"] = "Shore Crawler", + ["family"] = "Crabs", + ["lvl"] = "17-18", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 44, + ["coords"] = { + {36.7, 88.4, 40}, + }, + }, + [1258] = { + ["name"] = "Black Ravager Mastiff", + ["family"] = "Wolves", + ["lvl"] = "25-26", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4, Furious Howl 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 12, + ["coords"] = { + {50.8, 60.5, 10}, + }, + }, + [1400] = { + ["name"] = "Wetlands Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "23-24", + ["fac"] = "Unknown", + ["abilities"] = "Death Roll 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 116, + ["coords"] = { + {19.3, 58.2, 11}, + }, + }, + [1417] = { + ["name"] = "Young Wetlands Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "21-22", + ["fac"] = "Unknown", + ["abilities"] = "Death Roll 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 198, + ["coords"] = { + {63.4, 75.3, 11}, + }, + }, + [1504] = { + ["name"] = "Young Night Web Spider", + ["family"] = "Spiders", + ["lvl"] = "2-3", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 60, + ["coords"] = { + {27.9, 60.5, 85}, + }, + }, + [1505] = { + ["name"] = "Night Web Spider", + ["family"] = "Spiders", + ["lvl"] = "3-4", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 49, + ["coords"] = { + {24.5, 60.6, 85}, + }, + }, + [1508] = { + ["name"] = "Young Scavenger", + ["family"] = "Wolves", + ["lvl"] = "1", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 180, + ["respawnMaxSeconds"] = 180, + ["respawnSamples"] = 36, + ["coords"] = { + {33.6, 68.6, 85}, + }, + }, + [1509] = { + ["name"] = "Ragged Scavenger", + ["family"] = "Wolves", + ["lvl"] = "2-3", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 38, + ["coords"] = { + {35.6, 65.8, 85}, + }, + }, + [1511] = { + ["name"] = "Enraged Silverback Gorilla", + ["family"] = "Gorillas", + ["lvl"] = "41-42", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = { + }, + }, + [1512] = { + ["name"] = "Duskbat", + ["family"] = "Bats", + ["lvl"] = "1-2", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 180, + ["respawnMaxSeconds"] = 180, + ["respawnSamples"] = 80, + ["coords"] = { + {31.5, 72.5, 85}, + }, + }, + [1513] = { + ["name"] = "Mangy Duskbat", + ["family"] = "Bats", + ["lvl"] = "3-4", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 42, + ["coords"] = { + {34.9, 66.8, 85}, + }, + }, + [1547] = { + ["name"] = "Decrepit Darkhound", + ["family"] = "Wolves", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 160, + ["coords"] = { + {70.6, 65.1, 85}, + }, + }, + [1548] = { + ["name"] = "Cursed Darkhound", + ["family"] = "Wolves", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 372, + ["coords"] = { + {20.3, 61.2, 28}, + }, + }, + [1549] = { + ["name"] = "Ravenous Darkhound", + ["family"] = "Wolves", + ["lvl"] = "9-10", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 80, + ["coords"] = { + {32.5, 30.8, 28}, + }, + }, + [1553] = { + ["name"] = "Greater Duskbat", + ["family"] = "Bats", + ["lvl"] = "6-7", + ["fac"] = "Unknown", + ["abilities"] = "Cower 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 240, + ["coords"] = { + {54.2, 65.9, 85}, + }, + }, + [1554] = { + ["name"] = "Vampiric Duskbat", + ["family"] = "Bats", + ["lvl"] = "8-9", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 332, + ["coords"] = { + {25.3, 61.6, 28}, + }, + }, + [1555] = { + ["name"] = "Vicious Night Web Spider", + ["family"] = "Spiders", + ["lvl"] = "9-10", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 71, + ["coords"] = { + {86.8, 57.0, 85}, + }, + }, + [1557] = { + ["name"] = "Elder Mistvale Gorilla", + ["family"] = "Gorillas", + ["lvl"] = "40-41", + ["fac"] = "Unknown", + ["abilities"] = "Thunderstomp 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 56, + ["coords"] = { + {32.3, 68.4, 33}, + }, + }, + [1558] = { + ["name"] = "Silverback Patriarch", + ["family"] = "Gorillas", + ["lvl"] = "42-43", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 9, + ["coords"] = { + {36.3, 63.9, 33}, + }, + }, + [1688] = { + ["name"] = "Night Web Matriarch", + ["family"] = "Spiders", + ["lvl"] = "5", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 2, + ["coords"] = { + {24.0, 58.2, 85}, + }, + }, + [1689] = { + ["name"] = "Scarred Crag Boar", + ["family"] = "Boars", + ["lvl"] = "9-10", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 87, + ["coords"] = { + {70.9, 61.3, 1}, + }, + }, + [1693] = { + ["name"] = "Loch Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "14-15", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2, Death Roll 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 81, + ["coords"] = { + {58.8, 57.6, 38}, + }, + }, + [1713] = { + ["name"] = "Elder Shadowmaw Panther", + ["family"] = "Cats", + ["lvl"] = "42-43", + ["fac"] = "Unknown", + ["abilities"] = "Dash 2, Prowl 2", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 18, + ["coords"] = { + {31.2, 58.5, 33}, + }, + }, + [1765] = { + ["name"] = "Worg", + ["family"] = "Wolves", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2, Furious Howl 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 36, + ["coords"] = { + {51.8, 77.2, 85}, + }, + }, + [1766] = { + ["name"] = "Mottled Worg", + ["family"] = "Wolves", + ["lvl"] = "11-12", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 80, + ["coords"] = { + {44.5, 26.8, 130}, + }, + }, + [1778] = { + ["name"] = "Ferocious Grizzled Bear", + ["family"] = "Bears", + ["lvl"] = "11-12", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 78, + ["coords"] = { + {44.1, 52.0, 130}, + }, + }, + [1780] = { + ["name"] = "Moss Stalker", + ["family"] = "Spiders", + ["lvl"] = "12-13", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 30, + ["coords"] = { + {34.2, 17.3, 130}, + }, + }, + [1781] = { + ["name"] = "Mist Creeper", + ["family"] = "Spiders", + ["lvl"] = "13-14", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 51, + ["coords"] = { + {23.6, 81.4, 85}, + }, + }, + [1797] = { + ["name"] = "Giant Grizzled Bear", + ["family"] = "Bears", + ["lvl"] = "12-13", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 90, + ["coords"] = { + {55.5, 71.0, 130}, + }, + }, + [1809] = { + ["name"] = "Carrion Vulture", + ["family"] = "Carrion Birds", + ["lvl"] = "50-52", + ["fac"] = "Unknown", + ["abilities"] = "Dive 3, Screech 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 315, + ["respawnMaxSeconds"] = 315, + ["respawnSamples"] = 3, + ["coords"] = { + {32.3, 62.4, 28}, + }, + }, + [1815] = { + ["name"] = "Diseased Black Bear", + ["family"] = "Bears", + ["lvl"] = "51-52", + ["fac"] = "Unknown", + ["abilities"] = "Roar of Fortitude", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 315, + ["respawnMaxSeconds"] = 315, + ["respawnSamples"] = 81, + ["coords"] = { + {36.3, 68.0, 28}, + }, + }, + [1816] = { + ["name"] = "Diseased Grizzly", + ["family"] = "Bears", + ["lvl"] = "55-56", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7, Roar of Fortitude", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 315, + ["respawnMaxSeconds"] = 315, + ["respawnSamples"] = 86, + ["coords"] = { + {55.2, 64.5, 28}, + }, + }, + [1817] = { + ["name"] = "Diseased Wolf", + ["family"] = "Wolves", + ["lvl"] = "53-54", + ["fac"] = "Unknown", + ["abilities"] = "Bite 7", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 315, + ["respawnMaxSeconds"] = 315, + ["respawnSamples"] = 69, + ["coords"] = { + {52.5, 70.4, 28}, + }, + }, + [1822] = { + ["name"] = "Venom Mist Lurker", + ["family"] = "Spiders", + ["lvl"] = "50-51", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 315, + ["respawnMaxSeconds"] = 315, + ["respawnSamples"] = 58, + ["coords"] = { + {35.5, 66.8, 28}, + }, + }, + [1824] = { + ["name"] = "Plague Lurker", + ["family"] = "Spiders", + ["lvl"] = "54-55", + ["fac"] = "Unknown", + ["abilities"] = "Bite 7", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 315, + ["respawnMaxSeconds"] = 315, + ["respawnSamples"] = 64, + ["coords"] = { + {54.9, 62.2, 28}, + }, + }, + [1922] = { + ["name"] = "Gray Forest Wolf", + ["family"] = "Wolves", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "1.7", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 109, + ["coords"] = { + {60.4, 71.3, 12}, + }, + }, + [1923] = { + ["name"] = "Bloodsnout Worg", + ["family"] = "Wolves", + ["lvl"] = "16-17", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 66, + ["coords"] = { + {50.2, 84.9, 130}, + }, + }, + [1961] = { + ["name"] = "Mangeclaw", + ["family"] = "Bears", + ["lvl"] = "11", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 270, + ["respawnSamples"] = 2, + ["coords"] = { + {78.3, 37.8, 1}, + }, + }, + [1984] = { + ["name"] = "Young Thistle Boar", + ["family"] = "Boars", + ["lvl"] = "1-2", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 60, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 48, + ["coords"] = { + {60.6, 46.3, 141}, + }, + }, + [1985] = { + ["name"] = "Thistle Boar", + ["family"] = "Boars", + ["lvl"] = "2-3", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 41, + ["coords"] = { + {55.5, 39.4, 141}, + }, + }, + [1986] = { + ["name"] = "Webwood Spider", + ["family"] = "Spiders", + ["lvl"] = "3-4", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 88, + ["coords"] = { + {56.9, 34.5, 141}, + }, + }, + [1994] = { + ["name"] = "Githyiss the Vile", + ["family"] = "Spiders", + ["lvl"] = "5", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 2, + ["coords"] = { + {56.6, 26.3, 141}, + }, + }, + [1995] = { + ["name"] = "Strigid Owl", + ["family"] = "Owls", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "Claw 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 72, + ["coords"] = { + {55.4, 66.0, 141}, + }, + }, + [1996] = { + ["name"] = "Strigid Screecher", + ["family"] = "Owls", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 37, + ["coords"] = { + {53.1, 77.9, 141}, + }, + }, + [1997] = { + ["name"] = "Strigid Hunter", + ["family"] = "Owls", + ["lvl"] = "8-9", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 13, + ["coords"] = { + {38.5, 47.8, 141}, + }, + }, + [1998] = { + ["name"] = "Webwood Lurker", + ["family"] = "Spiders", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 74, + ["coords"] = { + {52.6, 67.4, 141}, + }, + }, + [1999] = { + ["name"] = "Webwood Venomfang", + ["family"] = "Spiders", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 62, + ["coords"] = { + {45.8, 78.9, 141}, + }, + }, + [2000] = { + ["name"] = "Webwood Silkspinner", + ["family"] = "Spiders", + ["lvl"] = "8-9", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 32, + ["coords"] = { + {43.7, 47.3, 141}, + }, + }, + [2001] = { + ["name"] = "Giant Webwood Spider", + ["family"] = "Spiders", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 4, + ["coords"] = { + {41.2, 28.9, 141}, + }, + }, + [2031] = { + ["name"] = "Young Nightsaber", + ["family"] = "Cats", + ["lvl"] = "1", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 50, + ["coords"] = { + {58.4, 46.7, 141}, + }, + }, + [2032] = { + ["name"] = "Mangy Nightsaber", + ["family"] = "Cats", + ["lvl"] = "2", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 48, + ["coords"] = { + {61.2, 39.1, 141}, + }, + }, + [2033] = { + ["name"] = "Elder Nightsaber", + ["family"] = "Cats", + ["lvl"] = "8-9", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.4", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 30, + ["coords"] = { + {46.9, 45.6, 141}, + }, + }, + [2034] = { + ["name"] = "Feral Nightsaber", + ["family"] = "Cats", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 6, + ["coords"] = { + {38.0, 29.3, 141}, + }, + }, + [2042] = { + ["name"] = "Nightsaber", + ["family"] = "Cats", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "Cower 1", + ["attackSpeed"] = "1.4", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 82, + ["coords"] = { + {54.8, 65.1, 141}, + }, + }, + [2043] = { + ["name"] = "Nightsaber Stalker", + ["family"] = "Cats", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.4", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 64, + ["coords"] = { + {50.0, 79.2, 141}, + }, + }, + [2069] = { + ["name"] = "Moonstalker", + ["family"] = "Cats", + ["lvl"] = "14-15", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 99, + ["coords"] = { + {37.5, 79.0, 148}, + }, + }, + [2070] = { + ["name"] = "Moonstalker Runt", + ["family"] = "Cats", + ["lvl"] = "10-17", + ["fac"] = "Unknown", + ["abilities"] = "Cower 1", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 124, + ["coords"] = { + {37.5, 93.0, 148}, + }, + }, + [2071] = { + ["name"] = "Moonstalker Matriarch", + ["family"] = "Cats", + ["lvl"] = "19-20", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 24, + ["coords"] = { + {37.2, 93.4, 148}, + }, + }, + [2089] = { + ["name"] = "Giant Wetlands Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "25-26", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4, Death Roll 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 26, + ["coords"] = { + {15.3, 29.2, 11}, + }, + }, + [2163] = { + ["name"] = "Thistle Bear", + ["family"] = "Bears", + ["lvl"] = "11-12", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 163, + ["coords"] = { + {44.7, 60.2, 148}, + }, + }, + [2165] = { + ["name"] = "Grizzled Thistle Bear", + ["family"] = "Bears", + ["lvl"] = "16-17", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 41, + ["coords"] = { + {43.0, 82.4, 148}, + }, + }, + [2172] = { + ["name"] = "Strider Clutchmother", + ["family"] = "Tallstriders", + ["lvl"] = "20", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 19800, + ["respawnMaxSeconds"] = 19800, + ["respawnSamples"] = 1, + ["coords"] = { + {34.8, 87.3, 148}, + }, + }, + [2175] = { + ["name"] = "Shadowclaw", + ["family"] = "Cats", + ["lvl"] = "13", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["rare"] = true, + ["respawnMinSeconds"] = 5400, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 2, + ["coords"] = { + {40.3, 40.5, 148}, + }, + }, + [2231] = { + ["name"] = "Pygmy Tide Crawler", + ["family"] = "Crabs", + ["lvl"] = "9-10", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 92, + ["coords"] = { + {35.4, 51.2, 148}, + }, + }, + [2232] = { + ["name"] = "Tide Crawler", + ["family"] = "Crabs", + ["lvl"] = "12-14", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 27, + ["coords"] = { + {36.6, 69.8, 148}, + }, + }, + [2233] = { + ["name"] = "Encrusted Tide Crawler", + ["family"] = "Crabs", + ["lvl"] = "18-20", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 30, + ["coords"] = { + {32.3, 83.7, 148}, + }, + }, + [2234] = { + ["name"] = "Young Reef Crawler", + ["family"] = "Crabs", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Bubble Barrier 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 17, + ["coords"] = { + {35.1, 56.1, 148}, + }, + }, + [2235] = { + ["name"] = "Reef Crawler", + ["family"] = "Crabs", + ["lvl"] = "15-17", + ["fac"] = "Unknown", + ["abilities"] = "Bubble Barrier 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 36, + ["coords"] = { + {35.5, 77.5, 148}, + }, + }, + [2236] = { + ["name"] = "Raging Reef Crawler", + ["family"] = "Crabs", + ["lvl"] = "20-21", + ["fac"] = "Unknown", + ["abilities"] = "Bubble Barrier 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 33, + ["coords"] = { + {29.8, 91.1, 148}, + }, + }, + [2237] = { + ["name"] = "Moonstalker Sire", + ["family"] = "Cats", + ["lvl"] = "17-18", + ["fac"] = "Unknown", + ["abilities"] = "Cower 2", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 47, + ["coords"] = { + {39.3, 94.5, 148}, + }, + }, + [2322] = { + ["name"] = "Foreststrider", + ["family"] = "Tallstriders", + ["lvl"] = "14-16", + ["fac"] = "Unknown", + ["abilities"] = "Strider Presence", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 48, + ["coords"] = { + {37.6, 78.8, 148}, + }, + }, + [2323] = { + ["name"] = "Giant Foreststrider", + ["family"] = "Tallstriders", + ["lvl"] = "17-19", + ["fac"] = "Unknown", + ["abilities"] = "Cower 2, Strider Presence", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 68, + ["coords"] = { + {37.1, 95.6, 148}, + }, + }, + [2348] = { + ["name"] = "Elder Moss Creeper", + ["family"] = "Spiders", + ["lvl"] = "26-27", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 60, + ["coords"] = { + {12.2, 40.3, 45}, + }, + }, + [2349] = { + ["name"] = "Giant Moss Creeper", + ["family"] = "Spiders", + ["lvl"] = "24-25", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4, Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 394, + ["coords"] = { + {19.7, 99.2, 36}, + }, + }, + [2350] = { + ["name"] = "Forest Moss Creeper", + ["family"] = "Spiders", + ["lvl"] = "20-21", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3, Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 148, + ["coords"] = { + {57.3, 99.3, 36}, + }, + }, + [2351] = { + ["name"] = "Gray Bear", + ["family"] = "Bears", + ["lvl"] = "21-22", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 164, + ["coords"] = { + {65.8, 98.1, 36}, + }, + }, + [2354] = { + ["name"] = "Vicious Gray Bear", + ["family"] = "Bears", + ["lvl"] = "22-23", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 120, + ["coords"] = { + {71.1, 98.8, 36}, + }, + }, + [2356] = { + ["name"] = "Elder Gray Bear", + ["family"] = "Bears", + ["lvl"] = "25-26", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 324, + ["coords"] = { + {20.7, 98.1, 36}, + }, + }, + [2384] = { + ["name"] = "Starving Mountain Lion", + ["family"] = "Cats", + ["lvl"] = "23-24", + ["fac"] = "Unknown", + ["abilities"] = "Cower 2", + ["attackSpeed"] = "1.2", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 120, + ["coords"] = { + {39.0, 99.4, 36}, + }, + }, + [2385] = { + ["name"] = "Feral Mountain Lion", + ["family"] = "Cats", + ["lvl"] = "27-28", + ["fac"] = "Unknown", + ["abilities"] = "Cower 3", + ["attackSpeed"] = "1.2", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 38, + ["coords"] = { + {6.7, 49.7, 45}, + }, + }, + [2406] = { + ["name"] = "Mountain Lion", + ["family"] = "Cats", + ["lvl"] = "32-33", + ["fac"] = "Unknown", + ["abilities"] = "Prowl 1", + ["attackSpeed"] = "1.4", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 114, + ["coords"] = { + {41.4, 98.5, 28}, + }, + }, + [2407] = { + ["name"] = "Hulking Mountain Lion", + ["family"] = "Cats", + ["lvl"] = "33-34", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.4", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 84, + ["coords"] = { + {42.5, 99.6, 28}, + }, + }, + [2408] = { + ["name"] = "Snapjaw", + ["family"] = "Turtles", + ["lvl"] = "30-31", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4, Shell Shield 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 262, + ["coords"] = { + {45.5, 99.3, 28}, + }, + }, + [2476] = { + ["name"] = "Large Loch Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "22", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3", + ["attackSpeed"] = "1.48", + ["rare"] = true, + ["respawnMinSeconds"] = 19800, + ["respawnMaxSeconds"] = 19800, + ["respawnSamples"] = 3, + ["coords"] = { + {58.9, 31.1, 38}, + }, + }, + [2505] = { + ["name"] = "Saltwater Snapjaw", + ["family"] = "Turtles", + ["lvl"] = "49-50", + ["fac"] = "Unknown", + ["abilities"] = "Bite 7", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 350, + ["respawnMaxSeconds"] = 350, + ["respawnSamples"] = 120, + ["coords"] = { + {77.4, 86.5, 47}, + }, + }, + [2521] = { + ["name"] = "Skymane Gorilla", + ["family"] = "Gorillas", + ["lvl"] = "50", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 34, + ["coords"] = { + {39.1, 86.6, 33}, + }, + }, + [2522] = { + ["name"] = "Jaguero Stalker", + ["family"] = "Cats", + ["lvl"] = "50", + ["fac"] = "Unknown", + ["abilities"] = "Cower 5, Prowl 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 28, + ["coords"] = { + {38.6, 86.6, 33}, + }, + }, + [2544] = { + ["name"] = "Southern Sand Crawler", + ["family"] = "Crabs", + ["lvl"] = "40-41", + ["fac"] = "Unknown", + ["abilities"] = "Bubble Barrier 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 24, + ["coords"] = { + {31.2, 88.1, 33}, + }, + }, + [2559] = { + ["name"] = "Highland Strider", + ["family"] = "Raptors", + ["lvl"] = "30-31", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 400, + ["respawnSamples"] = 236, + ["coords"] = { + {42.8, 79.6, 45}, + }, + }, + [2561] = { + ["name"] = "Highland Fleshstalker", + ["family"] = "Raptors", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 400, + ["respawnMaxSeconds"] = 400, + ["respawnSamples"] = 38, + ["coords"] = { + {48.0, 82.4, 45}, + }, + }, + [2565] = { + ["name"] = "Giant Plains Creeper", + ["family"] = "Spiders", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "Bite 5, Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 400, + ["respawnMaxSeconds"] = 400, + ["respawnSamples"] = 85, + ["coords"] = { + {47.8, 77.2, 45}, + }, + }, + [2579] = { + ["name"] = "Mesa Buzzard", + ["family"] = "Carrion Birds", + ["lvl"] = "34-35", + ["fac"] = "Unknown", + ["abilities"] = "Dive 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 400, + ["respawnMaxSeconds"] = 400, + ["respawnSamples"] = 27, + ["coords"] = { + {57.2, 71.7, 45}, + }, + }, + [2580] = { + ["name"] = "Elder Mesa Buzzard", + ["family"] = "Carrion Birds", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 400, + ["respawnMaxSeconds"] = 400, + ["respawnSamples"] = 16, + ["coords"] = { + {49.1, 82.5, 45}, + }, + }, + [2635] = { + ["name"] = "Elder Saltwater Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "38", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 1800, + ["respawnMaxSeconds"] = 1800, + ["respawnSamples"] = 4, + ["coords"] = { + {33.4, 32.5, 33}, + }, + }, + [2680] = { + ["name"] = "Vilebranch Wolf Pup", + ["family"] = "Wolves", + ["lvl"] = "46-47", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 350, + ["respawnMaxSeconds"] = 350, + ["respawnSamples"] = 14, + ["coords"] = { + {66.7, 72.4, 47}, + }, + }, + [2681] = { + ["name"] = "Vilebranch Raiding Wolf", + ["family"] = "Wolves", + ["lvl"] = "50-51", + ["fac"] = "Unknown", + ["abilities"] = "Bite 7, Dash 3", + ["attackSpeed"] = "1.7", + ["elite"] = true, + ["respawnMinSeconds"] = 350, + ["respawnMaxSeconds"] = 350, + ["respawnSamples"] = 36, + ["coords"] = { + {86.0, 22.2, 45}, + }, + }, + [2686] = { + ["name"] = "Witherbark Broodguard", + ["family"] = "Spiders", + ["lvl"] = "44-45", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 350, + ["respawnMaxSeconds"] = 350, + ["respawnSamples"] = 42, + ["coords"] = { + {53.2, 12.6, 45}, + }, + }, + [2727] = { + ["name"] = "Crag Coyote", + ["family"] = "Wolves", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "Bite 5, Dash 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 65, + ["coords"] = { + {53.2, 52.0, 3}, + }, + }, + [2728] = { + ["name"] = "Feral Crag Coyote", + ["family"] = "Wolves", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "Dash 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 78, + ["coords"] = { + {48.6, 76.2, 3}, + }, + }, + [2729] = { + ["name"] = "Elder Crag Coyote", + ["family"] = "Wolves", + ["lvl"] = "39-40", + ["fac"] = "Unknown", + ["abilities"] = "Dash 1, Furious Howl 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 94, + ["coords"] = { + {93.1, 85.3, 1}, + }, + }, + [2730] = { + ["name"] = "Rabid Crag Coyote", + ["family"] = "Wolves", + ["lvl"] = "42-43", + ["fac"] = "Unknown", + ["abilities"] = "Dash 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 17, + ["coords"] = { + {69.4, 36.2, 3}, + }, + }, + [2731] = { + ["name"] = "Ridge Stalker", + ["family"] = "Cats", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "Cower 4, Prowl 1", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 58, + ["coords"] = { + {38.1, 46.5, 3}, + }, + }, + [2732] = { + ["name"] = "Ridge Huntress", + ["family"] = "Cats", + ["lvl"] = "38-39", + ["fac"] = "Unknown", + ["abilities"] = "Cower 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 83, + ["coords"] = { + {46.9, 76.7, 3}, + }, + }, + [2734] = { + ["name"] = "Ridge Stalker Patriarch", + ["family"] = "Cats", + ["lvl"] = "40-41", + ["fac"] = "Unknown", + ["abilities"] = "Dash 2, Prowl 2", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 44, + ["coords"] = { + {25.4, 79.3, 3}, + }, + }, + [2753] = { + ["name"] = "Barnabus", + ["family"] = "Wolves", + ["lvl"] = "38", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 72000, + ["respawnMaxSeconds"] = 75600, + ["respawnSamples"] = 2, + ["coords"] = { + {46.3, 74.3, 3}, + }, + }, + [2829] = { + ["name"] = "Starving Buzzard", + ["family"] = "Carrion Birds", + ["lvl"] = "35-37", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 16, + ["coords"] = { + {63.9, 46.0, 3}, + }, + }, + [2830] = { + ["name"] = "Buzzard", + ["family"] = "Carrion Birds", + ["lvl"] = "37-39", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 18, + ["coords"] = { + {67.2, 86.3, 3}, + }, + }, + [2831] = { + ["name"] = "Giant Buzzard", + ["family"] = "Carrion Birds", + ["lvl"] = "39-41", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 34, + ["coords"] = { + {25.4, 78.8, 3}, + }, + }, + [2850] = { + ["name"] = "Broken Tooth", + ["family"] = "Cats", + ["lvl"] = "37", + ["fac"] = "Unknown", + ["abilities"] = "Dash 1", + ["attackSpeed"] = "1.0", + ["rare"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 28800, + ["respawnSamples"] = 8, + ["coords"] = { + {44.0, 38.4, 3}, + }, + }, + [2923] = { + ["name"] = "Mangy Silvermane", + ["family"] = "Wolves", + ["lvl"] = "41-42", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 350, + ["respawnSamples"] = 44, + ["coords"] = { + {20.3, 57.2, 47}, + }, + }, + [2924] = { + ["name"] = "Silvermane Wolf", + ["family"] = "Wolves", + ["lvl"] = "43-44", + ["fac"] = "Unknown", + ["abilities"] = "Furious Howl 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 350, + ["respawnMaxSeconds"] = 350, + ["respawnSamples"] = 64, + ["coords"] = { + {27.9, 69.4, 47}, + }, + }, + [2925] = { + ["name"] = "Silvermane Howler", + ["family"] = "Wolves", + ["lvl"] = "45-46", + ["fac"] = "Unknown", + ["abilities"] = "Furious Howl 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 350, + ["respawnMaxSeconds"] = 350, + ["respawnSamples"] = 20, + ["coords"] = { + {41.3, 67.1, 47}, + }, + }, + [2926] = { + ["name"] = "Silvermane Stalker", + ["family"] = "Wolves", + ["lvl"] = "47-48", + ["fac"] = "Unknown", + ["abilities"] = "Dash 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 350, + ["respawnMaxSeconds"] = 350, + ["respawnSamples"] = 120, + ["coords"] = { + {72.0, 63.0, 47}, + }, + }, + [2954] = { + ["name"] = "Bristleback Battleboar", + ["family"] = "Boars", + ["lvl"] = "4-5", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 375, + ["respawnMaxSeconds"] = 375, + ["respawnSamples"] = 28, + ["coords"] = { + {61.0, 86.5, 215}, + }, + }, + [2955] = { + ["name"] = "Plainstrider", + ["family"] = "Tallstriders", + ["lvl"] = "1-2", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 233, + ["respawnMaxSeconds"] = 233, + ["respawnSamples"] = 194, + ["coords"] = { + {53.1, 88.9, 215}, + }, + }, + [2956] = { + ["name"] = "Adult Plainstrider", + ["family"] = "Tallstriders", + ["lvl"] = "6-7", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 375, + ["respawnMaxSeconds"] = 375, + ["respawnSamples"] = 194, + ["coords"] = { + {37.4, 55.7, 17}, + }, + }, + [2957] = { + ["name"] = "Elder Plainstrider", + ["family"] = "Tallstriders", + ["lvl"] = "8-9", + ["fac"] = "Unknown", + ["abilities"] = "Cower 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 375, + ["respawnMaxSeconds"] = 375, + ["respawnSamples"] = 249, + ["coords"] = { + {39.3, 58.7, 17}, + }, + }, + [2958] = { + ["name"] = "Prairie Wolf", + ["family"] = "Wolves", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 375, + ["respawnMaxSeconds"] = 375, + ["respawnSamples"] = 236, + ["coords"] = { + {34.1, 79.3, 215}, + }, + }, + [2959] = { + ["name"] = "Prairie Stalker", + ["family"] = "Wolves", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 375, + ["respawnMaxSeconds"] = 375, + ["respawnSamples"] = 150, + ["coords"] = { + {36.6, 56.3, 17}, + }, + }, + [2960] = { + ["name"] = "Prairie Wolf Alpha", + ["family"] = "Wolves", + ["lvl"] = "9-10", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2, Furious Howl 1", + ["attackSpeed"] = "1.4", + ["respawnMinSeconds"] = 375, + ["respawnMaxSeconds"] = 375, + ["respawnSamples"] = 283, + ["coords"] = { + {38.5, 62.3, 17}, + }, + }, + [2961] = { + ["name"] = "Mountain Cougar", + ["family"] = "Cats", + ["lvl"] = "3", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 233, + ["respawnMaxSeconds"] = 233, + ["respawnSamples"] = 73, + ["coords"] = { + {56.1, 94.7, 215}, + }, + }, + [2966] = { + ["name"] = "Battleboar", + ["family"] = "Boars", + ["lvl"] = "3-4", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 233, + ["respawnMaxSeconds"] = 233, + ["respawnSamples"] = 92, + ["coords"] = { + {59.5, 90.5, 215}, + }, + }, + [2969] = { + ["name"] = "Wiry Swoop", + ["family"] = "Carrion Birds", + ["lvl"] = "5-7", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 375, + ["respawnMaxSeconds"] = 375, + ["respawnSamples"] = 17, + ["coords"] = { + {34.2, 78.3, 215}, + }, + }, + [2970] = { + ["name"] = "Swoop", + ["family"] = "Carrion Birds", + ["lvl"] = "7-9", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 375, + ["respawnMaxSeconds"] = 375, + ["respawnSamples"] = 35, + ["coords"] = { + {57.7, 68.4, 215}, + }, + }, + [2971] = { + ["name"] = "Taloned Swoop", + ["family"] = "Carrion Birds", + ["lvl"] = "8-10", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 375, + ["respawnMaxSeconds"] = 375, + ["respawnSamples"] = 23, + ["coords"] = { + {38.6, 58.1, 17}, + }, + }, + [3035] = { + ["name"] = "Flatland Cougar", + ["family"] = "Cats", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Cower 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 375, + ["respawnMaxSeconds"] = 375, + ["respawnSamples"] = 194, + ["coords"] = { + {37.5, 54.1, 17}, + }, + }, + [3068] = { + ["name"] = "Mazzranache", + ["family"] = "Tallstriders", + ["lvl"] = "9", + ["fac"] = "Unknown", + ["abilities"] = "Cower 1", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 9000, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 1, + ["coords"] = { + {50.4, 42.6, 215}, + }, + }, + [3098] = { + ["name"] = "Mottled Boar", + ["family"] = "Boars", + ["lvl"] = "1-2", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 180, + ["respawnMaxSeconds"] = 180, + ["respawnSamples"] = 136, + ["coords"] = { + {42.7, 72.7, 14}, + }, + }, + [3099] = { + ["name"] = "Dire Mottled Boar", + ["family"] = "Boars", + ["lvl"] = "6-7", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 250, + ["coords"] = { + {51.8, 78.1, 14}, + }, + }, + [3100] = { + ["name"] = "Elder Mottled Boar", + ["family"] = "Boars", + ["lvl"] = "8-9", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 312, + ["coords"] = { + {37.8, 55.8, 14}, + }, + }, + [3106] = { + ["name"] = "Pygmy Surf Crawler", + ["family"] = "Crabs", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "Claw 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 110, + ["coords"] = { + {51.5, 84.0, 14}, + }, + }, + [3107] = { + ["name"] = "Surf Crawler", + ["family"] = "Crabs", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 159, + ["coords"] = { + {64.6, 98.2, 14}, + }, + }, + [3108] = { + ["name"] = "Encrusted Surf Crawler", + ["family"] = "Crabs", + ["lvl"] = "9-10", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 39, + ["coords"] = { + {57.8, 96.6, 14}, + }, + }, + [3110] = { + ["name"] = "Dreadmaw Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "9-11", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 192, + ["coords"] = { + {38.5, 71.5, 14}, + }, + }, + [3121] = { + ["name"] = "Durotar Tiger", + ["family"] = "Cats", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Cower 1", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 88, + ["coords"] = { + {64.0, 97.3, 14}, + }, + }, + [3122] = { + ["name"] = "Bloodtalon Taillasher", + ["family"] = "Raptors", + ["lvl"] = "6-8", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 133, + ["coords"] = { + {63.1, 94.4, 14}, + }, + }, + [3123] = { + ["name"] = "Bloodtalon Scythemaw", + ["family"] = "Raptors", + ["lvl"] = "8-10", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 94, + ["coords"] = { + {37.2, 54.9, 14}, + }, + }, + [3124] = { + ["name"] = "Scorpid Worker", + ["family"] = "Scorpids", + ["lvl"] = "3", + ["fac"] = "Unknown", + ["abilities"] = "Claw 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 122, + ["coords"] = { + {44.0, 73.7, 14}, + }, + }, + [3125] = { + ["name"] = "Clattering Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 142, + ["coords"] = { + {54.8, 80.3, 14}, + }, + }, + [3126] = { + ["name"] = "Armored Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 128, + ["coords"] = { + {41.1, 51.7, 14}, + }, + }, + [3127] = { + ["name"] = "Venomtail Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "9-10", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2, Scorpid Poison 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 126, + ["coords"] = { + {35.9, 56.6, 14}, + }, + }, + [3225] = { + ["name"] = "Corrupted Mottled Boar", + ["family"] = "Boars", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 2, + ["coords"] = { + {36.9, 40.1, 14}, + }, + }, + [3226] = { + ["name"] = "Corrupted Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 5, + ["coords"] = { + {36.4, 48.4, 14}, + }, + }, + [3227] = { + ["name"] = "Corrupted Bloodtalon Scythemaw", + ["family"] = "Raptors", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 270, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 6, + ["coords"] = { + {37.6, 56.1, 14}, + }, + }, + [3228] = { + ["name"] = "Corrupted Surf Crawler", + ["family"] = "Crabs", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Bubble Barrier 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 2, + ["coords"] = { + {60.4, 21.8, 14}, + }, + }, + [3231] = { + ["name"] = "Corrupted Dreadmaw Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "11-12", + ["fac"] = "Unknown", + ["abilities"] = "Death Roll 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 8, + ["coords"] = { + {34.3, 52.7, 14}, + }, + }, + [3241] = { + ["name"] = "Savannah Patriarch", + ["family"] = "Cats", + ["lvl"] = "15-16", + ["fac"] = "Unknown", + ["abilities"] = "Cower 2", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 7, + ["coords"] = { + {61.6, 32.7, 17}, + }, + }, + [3243] = { + ["name"] = "Savannah Highmane", + ["family"] = "Cats", + ["lvl"] = "12-13", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 12, + ["coords"] = { + {54.9, 30.5, 17}, + }, + }, + [3244] = { + ["name"] = "Greater Plainstrider", + ["family"] = "Tallstriders", + ["lvl"] = "11-12", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 186, + ["coords"] = { + {52.9, 33.6, 17}, + }, + }, + [3245] = { + ["name"] = "Ornery Plainstrider", + ["family"] = "Tallstriders", + ["lvl"] = "16-17", + ["fac"] = "Unknown", + ["abilities"] = "Cower 2, Strider Presence", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 184, + ["coords"] = { + {59.6, 53.4, 17}, + }, + }, + [3246] = { + ["name"] = "Fleeting Plainstrider", + ["family"] = "Tallstriders", + ["lvl"] = "12-13", + ["fac"] = "Unknown", + ["abilities"] = "Cower 1, Strider Presence", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 315, + ["coords"] = { + {35.8, 63.2, 14}, + }, + }, + [3247] = { + ["name"] = "Thunderhawk Hatchling", + ["family"] = "Wind Serpents", + ["lvl"] = "18-20", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 14, + ["coords"] = { + {48.5, 56.7, 17}, + }, + }, + [3249] = { + ["name"] = "Greater Thunderhawk", + ["family"] = "Wind Serpents", + ["lvl"] = "23-24", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 16, + ["coords"] = { + {45.5, 81.2, 17}, + }, + }, + [3250] = { + ["name"] = "Silithid Creeper", + ["family"] = "Scorpids", + ["lvl"] = "20-21", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 54, + ["coords"] = { + {26.9, 29.7, 15}, + }, + }, + [3252] = { + ["name"] = "Silithid Swarmer", + ["family"] = "Scorpids", + ["lvl"] = "21-22", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 62, + ["coords"] = { + {26.5, 30.9, 15}, + }, + }, + [3254] = { + ["name"] = "Sunscale Lashtail", + ["family"] = "Raptors", + ["lvl"] = "11-13", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 9, + ["coords"] = { + {52.9, 33.0, 17}, + }, + }, + [3255] = { + ["name"] = "Sunscale Screecher", + ["family"] = "Raptors", + ["lvl"] = "13-15", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 339, + ["coords"] = { + {60.3, 47.1, 17}, + }, + }, + [3256] = { + ["name"] = "Sunscale Scytheclaw", + ["family"] = "Raptors", + ["lvl"] = "16-18", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 183, + ["coords"] = { + {57.0, 54.5, 17}, + }, + }, + [3281] = { + ["name"] = "Sarkoth", + ["family"] = "Scorpids", + ["lvl"] = "4", + ["fac"] = "Unknown", + ["abilities"] = "Claw 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 4, + ["coords"] = { + {40.5, 66.8, 14}, + }, + }, + [3415] = { + ["name"] = "Savannah Huntress", + ["family"] = "Cats", + ["lvl"] = "11-12", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 95, + ["coords"] = { + {50.0, 31.8, 17}, + }, + }, + [3416] = { + ["name"] = "Savannah Matriarch", + ["family"] = "Cats", + ["lvl"] = "17-18", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.7", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 10, + ["coords"] = { + {58.8, 35.7, 17}, + }, + }, + [3424] = { + ["name"] = "Thunderhawk Cloudscraper", + ["family"] = "Wind Serpents", + ["lvl"] = "20-22", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 6, + ["coords"] = { + {45.5, 67.0, 17}, + }, + }, + [3425] = { + ["name"] = "Savannah Prowler", + ["family"] = "Cats", + ["lvl"] = "14-15", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.4", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 206, + ["coords"] = { + {59.2, 41.0, 17}, + }, + }, + [3461] = { + ["name"] = "Oasis Snapjaw", + ["family"] = "Turtles", + ["lvl"] = "15-16", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 74, + ["coords"] = { + {55.6, 43.6, 17}, + }, + }, + [3472] = { + ["name"] = "Washte Pawne", + ["family"] = "Wind Serpents", + ["lvl"] = "25", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 5, + ["coords"] = { + {43.2, 80.9, 17}, + }, + }, + [3566] = { + ["name"] = "Flatland Prowler", + ["family"] = "Cats", + ["lvl"] = "9", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 375, + ["respawnMaxSeconds"] = 375, + ["respawnSamples"] = 268, + ["coords"] = { + {37.5, 57.5, 17}, + }, + }, + [3581] = { + ["name"] = "Sewer Beast", + ["family"] = "Crocolisks", + ["lvl"] = "50", + ["fac"] = "Unknown", + ["abilities"] = "Bite 7, Death Roll 5", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 7286, + ["respawnMaxSeconds"] = 7286, + ["respawnSamples"] = 7, + ["coords"] = { + {71.1, 68.0, 1519}, + }, + }, + [3630] = { + ["name"] = "Deviate Coiler", + ["family"] = "Wind Serpents", + ["lvl"] = "15-16", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 2", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 275, + ["respawnMaxSeconds"] = 275, + ["respawnSamples"] = 12, + ["coords"] = { + {46.1, 35.3, 17}, + }, + }, + [3631] = { + ["name"] = "Deviate Stinglash", + ["family"] = "Wind Serpents", + ["lvl"] = "16-17", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 2", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 275, + ["respawnMaxSeconds"] = 275, + ["respawnSamples"] = 21, + ["coords"] = { + {48.5, 34.5, 17}, + }, + }, + [3632] = { + ["name"] = "Deviate Creeper", + ["family"] = "Raptors", + ["lvl"] = "15-16", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 275, + ["respawnMaxSeconds"] = 275, + ["respawnSamples"] = 36, + ["coords"] = { + {45.9, 35.6, 17}, + }, + }, + [3633] = { + ["name"] = "Deviate Slayer", + ["family"] = "Raptors", + ["lvl"] = "16-17", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 275, + ["respawnMaxSeconds"] = 275, + ["respawnSamples"] = 42, + ["coords"] = { + {48.3, 34.3, 17}, + }, + }, + [3634] = { + ["name"] = "Deviate Stalker", + ["family"] = "Raptors", + ["lvl"] = "15-17", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 275, + ["respawnMaxSeconds"] = 275, + ["respawnSamples"] = 12, + ["coords"] = { + {46.5, 35.0, 17}, + }, + }, + [3636] = { + ["name"] = "Deviate Ravager", + ["family"] = "Raptors", + ["lvl"] = "18-19", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 16, + ["coords"] = { + {47.0, 41.0, 718}, + }, + }, + [3637] = { + ["name"] = "Deviate Guardian", + ["family"] = "Raptors", + ["lvl"] = "18-19", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 14, + ["coords"] = { + {52.0, 40.2, 718}, + }, + }, + [3653] = { + ["name"] = "Kresh", + ["family"] = "Turtles", + ["lvl"] = "20", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3, Shell Shield 1", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 604800, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 1, + ["coords"] = { + {40.2, 35.6, 718}, + }, + }, + [3809] = { + ["name"] = "Ashenvale Bear", + ["family"] = "Bears", + ["lvl"] = "21-22", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 188, + ["coords"] = { + {66.8, 87.7, 331}, + }, + }, + [3810] = { + ["name"] = "Elder Ashenvale Bear", + ["family"] = "Bears", + ["lvl"] = "25-26", + ["fac"] = "Unknown", + ["abilities"] = "Claw 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 102, + ["coords"] = { + {63.2, 68.3, 331}, + }, + }, + [3811] = { + ["name"] = "Giant Ashenvale Bear", + ["family"] = "Bears", + ["lvl"] = "29-30", + ["fac"] = "Unknown", + ["abilities"] = "Roar of Fortitude", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 3, + ["coords"] = { + {82.9, 48.4, 331}, + }, + }, + [3814] = { + ["name"] = "Spined Crawler", + ["family"] = "Crabs", + ["lvl"] = "20-21", + ["fac"] = "Unknown", + ["abilities"] = "Bubble Barrier 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 54, + ["coords"] = { + {32.1, 99.7, 148}, + }, + }, + [3819] = { + ["name"] = "Wildthorn Stalker", + ["family"] = "Spiders", + ["lvl"] = "20-21", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 254, + ["coords"] = { + {64.2, 86.6, 331}, + }, + }, + [3820] = { + ["name"] = "Wildthorn Venomspitter", + ["family"] = "Spiders", + ["lvl"] = "24-25", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 28, + ["coords"] = { + {57.2, 78.5, 331}, + }, + }, + [3821] = { + ["name"] = "Wildthorn Lurker", + ["family"] = "Spiders", + ["lvl"] = "28-29", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4, Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 73, + ["coords"] = { + {88.7, 68.3, 331}, + }, + }, + [3823] = { + ["name"] = "Ghostpaw Runner", + ["family"] = "Wolves", + ["lvl"] = "19-20", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 165, + ["coords"] = { + {41.7, 97.8, 148}, + }, + }, + [3824] = { + ["name"] = "Ghostpaw Howler", + ["family"] = "Wolves", + ["lvl"] = "23-24", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 34, + ["coords"] = { + {57.5, 78.5, 331}, + }, + }, + [3825] = { + ["name"] = "Ghostpaw Alpha", + ["family"] = "Wolves", + ["lvl"] = "27-28", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4, Furious Howl 2", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 26, + ["coords"] = { + {66.6, 70.1, 331}, + }, + }, + [3861] = { + ["name"] = "Bleak Worg", + ["family"] = "Wolves", + ["lvl"] = "18-19", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 8, + ["coords"] = { + {82.6, 77.9, 209}, + }, + }, + [3862] = { + ["name"] = "Slavering Worg", + ["family"] = "Wolves", + ["lvl"] = "18-19", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.2", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 8, + ["coords"] = { + {70.9, 67.3, 209}, + }, + }, + [3866] = { + ["name"] = "Vile Bat", + ["family"] = "Bats", + ["lvl"] = "22-23", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 604800, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 1, + ["coords"] = { + {81.1, 71.7, 209}, + }, + }, + [3868] = { + ["name"] = "Blood Seeker", + ["family"] = "Bats", + ["lvl"] = "23-24", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 604800, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 1, + ["coords"] = { + {82.2, 73.9, 209}, + }, + }, + [4005] = { + ["name"] = "Deepmoss Creeper", + ["family"] = "Spiders", + ["lvl"] = "16-17", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 46, + ["coords"] = { + {63.8, 89.0, 406}, + }, + }, + [4006] = { + ["name"] = "Deepmoss Webspinner", + ["family"] = "Spiders", + ["lvl"] = "19-20", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3, Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 90, + ["coords"] = { + {55.9, 71.5, 406}, + }, + }, + [4007] = { + ["name"] = "Deepmoss Venomspitter", + ["family"] = "Spiders", + ["lvl"] = "17-18", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 110, + ["coords"] = { + {54.5, 74.4, 406}, + }, + }, + [4040] = { + ["name"] = "Cave Stalker", + ["family"] = "Spiders", + ["lvl"] = "21-22", + ["fac"] = "Unknown", + ["abilities"] = "Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 51, + ["coords"] = { + {46.7, 63.8, 11}, + }, + }, + [4067] = { + ["name"] = "Twilight Runner", + ["family"] = "Cats", + ["lvl"] = "23-24", + ["fac"] = "Unknown", + ["abilities"] = "Cower 2", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 30, + ["coords"] = { + {43.8, 27.5, 406}, + }, + }, + [4117] = { + ["name"] = "Cloud Serpent", + ["family"] = "Wind Serpents", + ["lvl"] = "25-26", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 6, + ["coords"] = { + {45.4, 94.6, 17}, + }, + }, + [4119] = { + ["name"] = "Elder Cloud Serpent", + ["family"] = "Wind Serpents", + ["lvl"] = "27-29", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 40, + ["coords"] = { + {55.5, 51.9, 400}, + }, + }, + [4124] = { + ["name"] = "Needles Cougar", + ["family"] = "Cats", + ["lvl"] = "27-28", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.2", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 48, + ["coords"] = { + {41.0, 95.5, 17}, + }, + }, + [4126] = { + ["name"] = "Crag Stalker", + ["family"] = "Cats", + ["lvl"] = "25-26", + ["fac"] = "Unknown", + ["abilities"] = "Cower 3", + ["attackSpeed"] = "1.6", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 56, + ["coords"] = { + {44.5, 97.4, 17}, + }, + }, + [4127] = { + ["name"] = "Hecklefang Hyena", + ["family"] = "Hyenas", + ["lvl"] = "15-16", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 218, + ["coords"] = { + {36.0, 14.4, 14}, + }, + }, + [4128] = { + ["name"] = "Hecklefang Stalker", + ["family"] = "Hyenas", + ["lvl"] = "22-23", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 540, + ["respawnSamples"] = 88, + ["coords"] = { + {28.2, 48.7, 15}, + }, + }, + [4129] = { + ["name"] = "Hecklefang Snarler", + ["family"] = "Hyenas", + ["lvl"] = "18-19", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 28, + ["coords"] = { + {50.4, 59.1, 17}, + }, + }, + [4139] = { + ["name"] = "Scorpid Terror", + ["family"] = "Scorpids", + ["lvl"] = "33-34", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 31, + ["coords"] = { + {73.5, 89.8, 400}, + }, + }, + [4140] = { + ["name"] = "Scorpid Reaver", + ["family"] = "Scorpids", + ["lvl"] = "31-32", + ["fac"] = "Unknown", + ["abilities"] = "Claw 4, Scorpid Poison 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 67, + ["coords"] = { + {71.8, 73.7, 400}, + }, + }, + [4142] = { + ["name"] = "Sparkleshell Tortoise", + ["family"] = "Turtles", + ["lvl"] = "30-31", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 45, + ["coords"] = { + {72.7, 68.6, 400}, + }, + }, + [4143] = { + ["name"] = "Sparkleshell Snapper", + ["family"] = "Turtles", + ["lvl"] = "34-35", + ["fac"] = "Unknown", + ["abilities"] = "Bite 5", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 54, + ["coords"] = { + {88.3, 79.1, 400}, + }, + }, + [4144] = { + ["name"] = "Sparkleshell Borer", + ["family"] = "Turtles", + ["lvl"] = "32-33", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 6, + ["coords"] = { + {81.7, 89.2, 400}, + }, + }, + [4154] = { + ["name"] = "Salt Flats Scavenger", + ["family"] = "Carrion Birds", + ["lvl"] = "30-32", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 26, + ["coords"] = { + {71.2, 90.0, 400}, + }, + }, + [4158] = { + ["name"] = "Salt Flats Vulture", + ["family"] = "Carrion Birds", + ["lvl"] = "32-34", + ["fac"] = "Unknown", + ["abilities"] = "Screech 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 12, + ["coords"] = { + {78.0, 89.8, 400}, + }, + }, + [4248] = { + ["name"] = "Pesterhide Hyena", + ["family"] = "Hyenas", + ["lvl"] = "26-27", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 58, + ["coords"] = { + {46.8, 97.2, 17}, + }, + }, + [4249] = { + ["name"] = "Pesterhide Snarler", + ["family"] = "Hyenas", + ["lvl"] = "28-29", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 94, + ["coords"] = { + {37.9, 91.3, 17}, + }, + }, + [4263] = { + ["name"] = "Deepmoss Hatchling", + ["family"] = "Spiders", + ["lvl"] = "14", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["coords"] = { + }, + }, + [4264] = { + ["name"] = "Deepmoss Matriarch", + ["family"] = "Spiders", + ["lvl"] = "22", + ["fac"] = "Unknown", + ["abilities"] = "Web", + ["attackSpeed"] = "2.0", + ["coords"] = { + }, + }, + [4304] = { + ["name"] = "Scarlet Tracking Hound", + ["family"] = "Hyenas", + ["lvl"] = "33-34", + ["fac"] = "Unknown", + ["abilities"] = "Dash 1", + ["attackSpeed"] = "1.5", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 7, + ["coords"] = { + {29.8, 87.3, 5135}, + }, + }, + [4316] = { + ["name"] = "Kolkar Packhound", + ["family"] = "Hyenas", + ["lvl"] = "13", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 13, + ["coords"] = { + {55.3, 46.5, 17}, + }, + }, + [4341] = { + ["name"] = "Drywallow Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "BIte 5, Death Roll 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 162, + ["coords"] = { + {59.5, 35.1, 15}, + }, + }, + [4342] = { + ["name"] = "Drywallow Vicejaw", + ["family"] = "Crocolisks", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 242, + ["coords"] = { + {41.9, 45.5, 15}, + }, + }, + [4343] = { + ["name"] = "Drywallow Snapper", + ["family"] = "Crocolisks", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "Claw 5, Death Roll 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 368, + ["coords"] = { + {48.9, 65.6, 15}, + }, + }, + [4344] = { + ["name"] = "Mottled Drywallow Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "38-39", + ["fac"] = "Unknown", + ["abilities"] = "Bite 5", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 236, + ["coords"] = { + {41.1, 60.6, 15}, + }, + }, + [4345] = { + ["name"] = "Drywallow Daggermaw", + ["family"] = "Crocolisks", + ["lvl"] = "40-41", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6, Death Roll 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 146, + ["coords"] = { + {50.5, 80.9, 15}, + }, + }, + [4351] = { + ["name"] = "Bloodfen Raptor", + ["family"] = "Raptors", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 162, + ["coords"] = { + {63.6, 40.2, 15}, + }, + }, + [4352] = { + ["name"] = "Bloodfen Screecher", + ["family"] = "Raptors", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 57, + ["coords"] = { + {51.6, 22.0, 15}, + }, + }, + [4355] = { + ["name"] = "Bloodfen Scytheclaw", + ["family"] = "Raptors", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 209, + ["coords"] = { + {49.5, 63.7, 15}, + }, + }, + [4356] = { + ["name"] = "Bloodfen Razormaw", + ["family"] = "Raptors", + ["lvl"] = "39-40", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 50, + ["coords"] = { + {34.2, 73.2, 15}, + }, + }, + [4357] = { + ["name"] = "Bloodfen Lashtail", + ["family"] = "Raptors", + ["lvl"] = "40-41", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 64, + ["coords"] = { + {36.4, 66.1, 15}, + }, + }, + [4376] = { + ["name"] = "Darkmist Spider", + ["family"] = "Spiders", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 76, + ["coords"] = { + {34.6, 25.3, 15}, + }, + }, + [4377] = { + ["name"] = "Darkmist Lurker", + ["family"] = "Spiders", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 24, + ["coords"] = { + {33.0, 22.8, 15}, + }, + }, + [4378] = { + ["name"] = "Darkmist Recluse", + ["family"] = "Spiders", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 68, + ["coords"] = { + {34.9, 23.1, 15}, + }, + }, + [4379] = { + ["name"] = "Darkmist Silkspinner", + ["family"] = "Spiders", + ["lvl"] = "38-39", + ["fac"] = "Unknown", + ["abilities"] = "Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 96, + ["coords"] = { + {34.8, 22.8, 15}, + }, + }, + [4380] = { + ["name"] = "Darkmist Widow", + ["family"] = "Spiders", + ["lvl"] = "40", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 27000, + ["respawnSamples"] = 4, + ["coords"] = { + {31.2, 20.4, 15}, + }, + }, + [4396] = { + ["name"] = "Mudrock Tortoise", + ["family"] = "Turtles", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 60, + ["coords"] = { + {64.8, 40.8, 15}, + }, + }, + [4397] = { + ["name"] = "Mudrock Spikeshell", + ["family"] = "Turtles", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 180, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 158, + ["coords"] = { + {65.0, 42.2, 15}, + }, + }, + [4398] = { + ["name"] = "Mudrock Burrower", + ["family"] = "Turtles", + ["lvl"] = "39-40", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 20, + ["coords"] = { + {58.9, 51.5, 15}, + }, + }, + [4399] = { + ["name"] = "Mudrock Borer", + ["family"] = "Turtles", + ["lvl"] = "42-43", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 120, + ["respawnSamples"] = 11, + ["coords"] = { + {60.9, 65.6, 15}, + }, + }, + [4400] = { + ["name"] = "Mudrock Snapjaw", + ["family"] = "Turtles", + ["lvl"] = "41-42", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 120, + ["respawnSamples"] = 12, + ["coords"] = { + {57.0, 62.0, 15}, + }, + }, + [4411] = { + ["name"] = "Darkfang Lurker", + ["family"] = "Spiders", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "Bite 5", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 136, + ["coords"] = { + {34.0, 46.0, 15}, + }, + }, + [4412] = { + ["name"] = "Darkfang Creeper", + ["family"] = "Spiders", + ["lvl"] = "38-39", + ["fac"] = "Unknown", + ["abilities"] = "Bite 5", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 110, + ["coords"] = { + {44.4, 53.5, 15}, + }, + }, + [4413] = { + ["name"] = "Darkfang Spider", + ["family"] = "Spiders", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "Bite 5", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 79, + ["coords"] = { + {57.3, 37.7, 15}, + }, + }, + [4414] = { + ["name"] = "Darkfang Venomspitter", + ["family"] = "Spiders", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 201, + ["coords"] = { + {46.7, 42.6, 15}, + }, + }, + [4415] = { + ["name"] = "Giant Darkfang Spider", + ["family"] = "Spiders", + ["lvl"] = "40-41", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 28, + ["coords"] = { + {52.6, 67.2, 15}, + }, + }, + [4538] = { + ["name"] = "Kraul Bat", + ["family"] = "Bats", + ["lvl"] = "30-31", + ["fac"] = "Unknown", + ["abilities"] = "Cower 3, Dive 1", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 13, + ["coords"] = { + {18.8, 59.7, 491}, + }, + }, + [4539] = { + ["name"] = "Greater Kraul Bat", + ["family"] = "Bats", + ["lvl"] = "32", + ["fac"] = "Unknown", + ["abilities"] = "Cower 3, Dive 1", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 7, + ["coords"] = { + {10.5, 55.5, 491}, + }, + }, + [4548] = { + ["name"] = "Steelsnap", + ["family"] = "Hyenas", + ["lvl"] = "30", + ["fac"] = "Unknown", + ["abilities"] = "Packleader", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 2, + ["coords"] = { + {37.9, 91.2, 17}, + }, + }, + [4662] = { + ["name"] = "Magram Bonepaw", + ["family"] = "Hyenas", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "Dash 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 10, + ["coords"] = { + {65.4, 80.7, 405}, + }, + }, + [4688] = { + ["name"] = "Bonepaw Hyena", + ["family"] = "Hyenas", + ["lvl"] = "33-35", + ["fac"] = "Unknown", + ["abilities"] = "Dash 1, Packleader", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 3, + ["coords"] = { + {37.0, 72.7, 405}, + }, + }, + [4689] = { + ["name"] = "Starving Bonepaw", + ["family"] = "Hyenas", + ["lvl"] = "30-32", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 3, + ["coords"] = { + {71.2, 34.8, 405}, + }, + }, + [4690] = { + ["name"] = "Rabid Bonepaw", + ["family"] = "Hyenas", + ["lvl"] = "36-38", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 2, + ["coords"] = { + {43.1, 50.3, 405}, + }, + }, + [4692] = { + ["name"] = "Dread Swoop", + ["family"] = "Carrion Birds", + ["lvl"] = "32-33", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 207, + ["coords"] = { + {38.5, 75.7, 405}, + }, + }, + [4694] = { + ["name"] = "Dread Ripper", + ["family"] = "Carrion Birds", + ["lvl"] = "39-40", + ["fac"] = "Unknown", + ["abilities"] = "Screech 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 20, + ["coords"] = { + {55.2, 85.7, 405}, + }, + }, + [4695] = { + ["name"] = "Carrion Horror", + ["family"] = "Carrion Birds", + ["lvl"] = "35-37", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 30, + ["coords"] = { + {53.6, 63.6, 405}, + }, + }, + [4696] = { + ["name"] = "Scorpashi Snapper", + ["family"] = "Scorpids", + ["lvl"] = "30-31", + ["fac"] = "Unknown", + ["abilities"] = "Claw 4, Scorpid Poison 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 165, + ["coords"] = { + {53.1, 68.9, 405}, + }, + }, + [4697] = { + ["name"] = "Scorpashi Lasher", + ["family"] = "Scorpids", + ["lvl"] = "34-35", + ["fac"] = "Unknown", + ["abilities"] = "Claw 5, Scorpid Poison 5", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 26, + ["coords"] = { + {38.0, 74.5, 405}, + }, + }, + [4699] = { + ["name"] = "Scorpashi Venomlash", + ["family"] = "Scorpids", + ["lvl"] = "38-39", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 15, + ["coords"] = { + {50.7, 92.4, 405}, + }, + }, + [4821] = { + ["name"] = "Skittering Crustacean", + ["family"] = "Crabs", + ["lvl"] = "22-23", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 11, + ["coords"] = { + {35.6, 32.9, 719}, + }, + }, + [4822] = { + ["name"] = "Snapping Crustacean", + ["family"] = "Crabs", + ["lvl"] = "23-24", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3, Bubble Barrier 2", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 18, + ["coords"] = { + {20.2, 57.1, 719}, + }, + }, + [4823] = { + ["name"] = "Barbed Crustacean", + ["family"] = "Crabs", + ["lvl"] = "25-26", + ["fac"] = "Unknown", + ["abilities"] = "Claw 4, Bubble Barrier 2", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 3, + ["coords"] = { + {45.5, 91.3, 719}, + }, + }, + [4841] = { + ["name"] = "Deadmire", + ["family"] = "Crocolisks", + ["lvl"] = "45", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6, Death Roll 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 360, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 1, + ["coords"] = { + {48.8, 56.7, 15}, + }, + }, + [4861] = { + ["name"] = "Shrike Bat", + ["family"] = "Bats", + ["lvl"] = "38-39", + ["fac"] = "Unknown", + ["abilities"] = "Cower 4, Dive 1, Screech 2", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 8, + ["coords"] = { + {40.3, 65.5, 1337}, + }, + }, + [4887] = { + ["name"] = "Ghamoo-Ra", + ["family"] = "Turtles", + ["lvl"] = "25", + ["fac"] = "Unknown", + ["abilities"] = "Bite 4, Shell Shield 1", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 604800, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 1, + ["coords"] = { + {22.4, 41.5, 719}, + }, + }, + [4950] = { + ["name"] = "Spot", + ["family"] = "Wolves", + ["lvl"] = "35", + ["fac"] = "A", + ["abilities"] = "Dash 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 540, + ["respawnMaxSeconds"] = 540, + ["respawnSamples"] = 1, + ["coords"] = { + {68.0, 46.8, 15}, + }, + }, + [5048] = { + ["name"] = "Deviate Adder", + ["family"] = "Serpents (Cobra)", + ["lvl"] = "18-19", + ["fac"] = "Unknown", + ["abilities"] = "Poison Spit 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 9, + ["coords"] = { + {32.3, 43.1, 718}, + }, + }, + [5053] = { + ["name"] = "Deviate Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "18-19", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3, Death Roll 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 16, + ["coords"] = { + {85.8, 83.0, 718}, + }, + }, + [5056] = { + ["name"] = "Deviate Dreadfang", + ["family"] = "Wind Serpents", + ["lvl"] = "20-21", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 2", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 12, + ["coords"] = { + {85.5, 81.0, 718}, + }, + }, + [5225] = { + ["name"] = "Murk Spitter", + ["family"] = "Serpents (Cobra)", + ["lvl"] = "46-47", + ["fac"] = "Unknown", + ["abilities"] = "Poison Spit 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 900, + ["respawnMaxSeconds"] = 900, + ["respawnSamples"] = 6, + ["coords"] = { + {76.7, 46.2, 8}, + }, + }, + [5262] = { + ["name"] = "Groddoc Thunderer", + ["family"] = "Gorillas", + ["lvl"] = "49-50", + ["fac"] = "Unknown", + ["abilities"] = "Thunderstomp 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 74, + ["coords"] = { + {48.8, 35.4, 357}, + }, + }, + [5268] = { + ["name"] = "Ironfur Bear", + ["family"] = "Bears", + ["lvl"] = "41-42", + ["fac"] = "Unknown", + ["abilities"] = "Claw 6, Roar of Fortitude", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 137, + ["coords"] = { + {61.3, 61.6, 357}, + }, + }, + [5272] = { + ["name"] = "Grizzled Ironfur Bear", + ["family"] = "Bears", + ["lvl"] = "44-45", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 32, + ["coords"] = { + {70.7, 63.8, 357}, + }, + }, + [5274] = { + ["name"] = "Ironfur Patriarch", + ["family"] = "Bears", + ["lvl"] = "48-49", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 64, + ["coords"] = { + {48.9, 37.5, 357}, + }, + }, + [5286] = { + ["name"] = "Longtooth Runner", + ["family"] = "Wolves", + ["lvl"] = "40-41", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6, Dash 2, Furious Howl 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 204, + ["coords"] = { + {30.7, 52.6, 357}, + }, + }, + [5287] = { + ["name"] = "Longtooth Howler", + ["family"] = "Wolves", + ["lvl"] = "43-44", + ["fac"] = "Unknown", + ["abilities"] = "Furious Howl 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 38, + ["coords"] = { + {69.6, 63.5, 357}, + }, + }, + [5288] = { + ["name"] = "Rabid Longtooth", + ["family"] = "Wolves", + ["lvl"] = "47-48", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 56, + ["coords"] = { + {48.8, 37.1, 357}, + }, + }, + [5291] = { + ["name"] = "Hakkari Frostwing", + ["family"] = "Wind Serpents", + ["lvl"] = "49-50", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 5", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 7, + ["coords"] = { + {24.8, 64.8, 1477}, + }, + }, + [5307] = { + ["name"] = "Vale Screecher", + ["family"] = "Wind Serpents", + ["lvl"] = "41-43", + ["fac"] = "Unknown", + ["abilities"] = "Dive 2, Lightning Breath 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 8, + ["coords"] = { + {60.0, 61.3, 357}, + }, + }, + [5308] = { + ["name"] = "Rogue Vale Screecher", + ["family"] = "Wind Serpents", + ["lvl"] = "44-46", + ["fac"] = "Unknown", + ["abilities"] = "Dive 2, Lightning Breath 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 40, + ["coords"] = { + {47.9, 59.2, 357}, + }, + }, + [5349] = { + ["name"] = "Arash-ethis", + ["family"] = "Wind Serpents", + ["lvl"] = "49", + ["fac"] = "Unknown", + ["abilities"] = "Dive 2, Lightning Breath 5", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 27000, + ["respawnSamples"] = 2, + ["coords"] = { + {44.8, 25.0, 357}, + }, + }, + [5352] = { + ["name"] = "Old Grizzlegut", + ["family"] = "Bears", + ["lvl"] = "43", + ["fac"] = "Unknown", + ["abilities"] = "Claw 6", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 115200, + ["respawnMaxSeconds"] = 180000, + ["respawnSamples"] = 2, + ["coords"] = { + {59.5, 59.4, 357}, + }, + }, + [5356] = { + ["name"] = "Snarler", + ["family"] = "Wolves", + ["lvl"] = "42", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6, Furious Howl 3", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 27000, + ["respawnSamples"] = 2, + ["coords"] = { + {80.2, 39.6, 357}, + }, + }, + [5422] = { + ["name"] = "Scorpid Hunter", + ["family"] = "Scorpids", + ["lvl"] = "40-41", + ["fac"] = "Unknown", + ["abilities"] = "Claw 6, Scorpid Poison 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 180, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 102, + ["coords"] = { + {78.3, 99.7, 400}, + }, + }, + [5423] = { + ["name"] = "Scorpid Tail Lasher", + ["family"] = "Scorpids", + ["lvl"] = "43-44", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 191, + ["coords"] = { + {54.1, 54.3, 440}, + }, + }, + [5424] = { + ["name"] = "Scorpid Dunestalker", + ["family"] = "Scorpids", + ["lvl"] = "46-47", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 215, + ["coords"] = { + {49.3, 76.3, 440}, + }, + }, + [5425] = { + ["name"] = "Starving Blisterpaw", + ["family"] = "Hyenas", + ["lvl"] = "41-42", + ["fac"] = "Unknown", + ["abilities"] = "Dash 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 125, + ["coords"] = { + {54.5, 38.7, 440}, + }, + }, + [5426] = { + ["name"] = "Blisterpaw Hyena", + ["family"] = "Hyenas", + ["lvl"] = "44-45", + ["fac"] = "Unknown", + ["abilities"] = "Dash 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 303, + ["coords"] = { + {51.2, 63.4, 440}, + }, + }, + [5427] = { + ["name"] = "Rabid Blisterpaw", + ["family"] = "Hyenas", + ["lvl"] = "47-48", + ["fac"] = "Unknown", + ["abilities"] = "Dash 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 203, + ["coords"] = { + {32.9, 79.1, 440}, + }, + }, + [5428] = { + ["name"] = "Roc", + ["family"] = "Carrion Birds", + ["lvl"] = "41-43", + ["fac"] = "Unknown", + ["abilities"] = "Dive 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 18, + ["coords"] = { + {49.2, 37.1, 440}, + }, + }, + [5429] = { + ["name"] = "Fire Roc", + ["family"] = "Carrion Birds", + ["lvl"] = "43-45", + ["fac"] = "Unknown", + ["abilities"] = "Dive 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 86, + ["coords"] = { + {49.8, 53.7, 440}, + }, + }, + [5430] = { + ["name"] = "Searing Roc", + ["family"] = "Carrion Birds", + ["lvl"] = "47-49", + ["fac"] = "Unknown", + ["abilities"] = "Dive 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 18, + ["coords"] = { + {32.0, 74.9, 440}, + }, + }, + [5431] = { + ["name"] = "Surf Glider", + ["family"] = "Turtles", + ["lvl"] = "48-50", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 215, + ["coords"] = { + {53.1, 97.9, 440}, + }, + }, + [5708] = { + ["name"] = "Spawn of Hakkar", + ["family"] = "Wind Serpents", + ["lvl"] = "51", + ["fac"] = "Unknown", + ["abilities"] = "Dive 3, Lightning Breath 5", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 604800, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 2, + ["coords"] = { + {60.3, 59.5, 1477}, + }, + }, + [5755] = { + ["name"] = "Deviate Viper", + ["family"] = "Serpents (Cobra)", + ["lvl"] = "19-20", + ["fac"] = "Unknown", + ["abilities"] = "Poison Spit 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 8, + ["coords"] = { + {28.8, 40.3, 718}, + }, + }, + [5756] = { + ["name"] = "Deviate Venomwing", + ["family"] = "Wind Serpents", + ["lvl"] = "20-21", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 2", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 14, + ["coords"] = { + {86.9, 81.3, 718}, + }, + }, + [5762] = { + ["name"] = "Deviate Moccasin", + ["family"] = "Serpents (Cobra)", + ["lvl"] = "20-21", + ["fac"] = "Unknown", + ["abilities"] = "Poison Spit 1", + ["attackSpeed"] = "2.0", + ["coords"] = { + }, + }, + [5766] = { + ["name"] = "Savannah Cub", + ["family"] = "Cats", + ["lvl"] = "4-5", + ["fac"] = "Unknown", + ["abilities"] = "Cower 1", + ["attackSpeed"] = "1.2", + ["respawnMinSeconds"] = 413, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 9, + ["coords"] = { + {58.9, 35.7, 17}, + }, + }, + [5807] = { + ["name"] = "The Rake", + ["family"] = "Cats", + ["lvl"] = "10", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.2", + ["rare"] = true, + ["respawnMinSeconds"] = 9000, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 1, + ["coords"] = { + {53.4, 17.0, 215}, + }, + }, + [5823] = { + ["name"] = "Death Flayer", + ["family"] = "Scorpids", + ["lvl"] = "11", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2, Scorpid Poison 1", + ["attackSpeed"] = "1.6", + ["rare"] = true, + ["respawnMinSeconds"] = 1651, + ["respawnMaxSeconds"] = 5400, + ["respawnSamples"] = 3, + ["coords"] = { + {38.2, 51.1, 14}, + }, + }, + [5828] = { + ["name"] = "Humar the Pridelord", + ["family"] = "Cats", + ["lvl"] = "23", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.3", + ["rare"] = true, + ["respawnMinSeconds"] = 54000, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 1, + ["coords"] = { + {62.1, 33.3, 17}, + }, + }, + [5829] = { + ["name"] = "Snort the Heckler", + ["family"] = "Hyenas", + ["lvl"] = "17", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 2, + ["coords"] = { + {41.2, 22.2, 17}, + }, + }, + [5834] = { + ["name"] = "Azzere the Skyblade", + ["family"] = "Wind Serpents", + ["lvl"] = "25", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 14400, + ["respawnMaxSeconds"] = 19800, + ["respawnSamples"] = 2, + ["coords"] = { + {45.0, 63.6, 17}, + }, + }, + [5856] = { + ["name"] = "Glassweb Spider", + ["family"] = "Spiders", + ["lvl"] = "43-45", + ["fac"] = "Unknown", + ["abilities"] = "Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 500, + ["respawnMaxSeconds"] = 500, + ["respawnSamples"] = 39, + ["coords"] = { + {64.6, 75.3, 51}, + }, + }, + [5857] = { + ["name"] = "Searing Lava Spider", + ["family"] = "Spiders", + ["lvl"] = "45-47", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 500, + ["respawnMaxSeconds"] = 500, + ["respawnSamples"] = 52, + ["coords"] = { + {57.6, 83.9, 51}, + }, + }, + [5858] = { + ["name"] = "Greater Lava Spider", + ["family"] = "Spiders", + ["lvl"] = "47-49", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 500, + ["respawnSamples"] = 109, + ["coords"] = { + {12.7, 17.9, 46}, + }, + }, + [5865] = { + ["name"] = "Dishu", + ["family"] = "Cats", + ["lvl"] = "13", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["rare"] = true, + ["respawnMinSeconds"] = 5400, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 2, + ["coords"] = { + {50.9, 18.2, 17}, + }, + }, + [5937] = { + ["name"] = "Vile Sting", + ["family"] = "Scorpids", + ["lvl"] = "35", + ["fac"] = "Unknown", + ["abilities"] = "Claw 5, Scorpid Poison 2", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 38000, + ["respawnMaxSeconds"] = 38000, + ["respawnSamples"] = 1, + ["coords"] = { + {71.5, 63.6, 400}, + }, + }, + [5982] = { + ["name"] = "Black Slayer", + ["family"] = "Carrion Birds", + ["lvl"] = "46-48", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 360, + ["respawnSamples"] = 32, + ["coords"] = { + {54.9, 44.6, 4}, + }, + }, + [5985] = { + ["name"] = "Snickerfang Hyena", + ["family"] = "Hyenas", + ["lvl"] = "49-50", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 31, + ["coords"] = { + {52.4, 57.2, 4}, + }, + }, + [5988] = { + ["name"] = "Scorpok Stinger", + ["family"] = "Scorpids", + ["lvl"] = "50-51", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 75, + ["coords"] = { + {58.6, 41.0, 4}, + }, + }, + [5992] = { + ["name"] = "Ashmane Boar", + ["family"] = "Boars", + ["lvl"] = "48-49", + ["fac"] = "Unknown", + ["abilities"] = "Charge 5", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 50, + ["coords"] = { + {64.3, 54.7, 4}, + }, + }, + [6013] = { + ["name"] = "Wayward Buzzard", + ["family"] = "Carrion Birds", + ["lvl"] = "35-37", + ["fac"] = "Unknown", + ["abilities"] = "Dive 1", + ["attackSpeed"] = "2.0", + ["coords"] = { + }, + }, + [6352] = { + ["name"] = "Coralshell Lurker", + ["family"] = "Turtles", + ["lvl"] = "53-54", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 180, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 92, + ["coords"] = { + {65.8, 97.0, 16}, + }, + }, + [6369] = { + ["name"] = "Coralshell Tortoise", + ["family"] = "Turtles", + ["lvl"] = "50-52", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 333, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 8, + ["coords"] = { + {87.5, 16.5, 16}, + }, + }, + [6505] = { + ["name"] = "Ravasaur", + ["family"] = "Raptors", + ["lvl"] = "48-49", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 5", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 118, + ["coords"] = { + {61.9, 83.2, 490}, + }, + }, + [6506] = { + ["name"] = "Ravasaur Runner", + ["family"] = "Raptors", + ["lvl"] = "49-50", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 118, + ["coords"] = { + {61.9, 83.2, 490}, + }, + }, + [6507] = { + ["name"] = "Ravasaur Hunter", + ["family"] = "Raptors", + ["lvl"] = "49-50", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 5", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 82, + ["coords"] = { + {71.6, 61.5, 490}, + }, + }, + [6508] = { + ["name"] = "Venomhide Ravasaur", + ["family"] = "Raptors", + ["lvl"] = "50-51", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 82, + ["coords"] = { + {71.6, 61.5, 490}, + }, + }, + [6581] = { + ["name"] = "Ravasaur Matriarch", + ["family"] = "Raptors", + ["lvl"] = "50", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 5", + ["attackSpeed"] = "1.23", + ["rare"] = true, + ["respawnMinSeconds"] = 54000, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 1, + ["coords"] = { + {62.3, 65.9, 490}, + }, + }, + [6788] = { + ["name"] = "Den Mother", + ["family"] = "Bears", + ["lvl"] = "18-19", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 4, + ["coords"] = { + {51.5, 38.3, 148}, + }, + }, + [6789] = { + ["name"] = "Thistle Cub", + ["family"] = "Bears", + ["lvl"] = "9-10", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 413, + ["respawnSamples"] = 16, + ["coords"] = { + {51.4, 38.3, 148}, + }, + }, + [7022] = { + ["name"] = "Venomlash Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "39-40", + ["fac"] = "Unknown", + ["abilities"] = "Claw 5", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 4, + ["coords"] = { + {36.5, 60.5, 1337}, + }, + }, + [7055] = { + ["name"] = "Blackrock Worg", + ["family"] = "Wolves", + ["lvl"] = "54-55", + ["fac"] = "Unknown", + ["abilities"] = "Dash 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 500, + ["respawnMaxSeconds"] = 500, + ["respawnSamples"] = 11, + ["coords"] = { + {49.2, 58.5, 46}, + }, + }, + [7078] = { + ["name"] = "Cleft Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 30, + ["coords"] = { + {39.4, 63.6, 1337}, + }, + }, + [7097] = { + ["name"] = "Ironbeak Owl", + ["family"] = "Owls", + ["lvl"] = "48-49", + ["fac"] = "Unknown", + ["abilities"] = "Dive 2, Screech 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 35, + ["coords"] = { + {54.8, 88.1, 148}, + }, + }, + [7098] = { + ["name"] = "Ironbeak Screecher", + ["family"] = "Owls", + ["lvl"] = "52-53", + ["fac"] = "Unknown", + ["abilities"] = "Dive 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 28, + ["coords"] = { + {53.7, 27.0, 361}, + }, + }, + [7099] = { + ["name"] = "Ironbeak Hunter", + ["family"] = "Owls", + ["lvl"] = "50-51", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7, Dive 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 40, + ["coords"] = { + {44.9, 69.9, 361}, + }, + }, + [7268] = { + ["name"] = "Sandfury Guardian", + ["family"] = "Serpents (Cobra)", + ["lvl"] = "45-46", + ["fac"] = "Unknown", + ["abilities"] = "Poison Spit 2", + ["attackSpeed"] = "1.7", + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 10, + ["coords"] = { + {22.1, 47.3, 1176}, + }, + }, + [7319] = { + ["name"] = "Lady Sathrah", + ["family"] = "Spiders", + ["lvl"] = "12", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 4, + ["coords"] = { + {48.1, 25.1, 141}, + }, + }, + [7405] = { + ["name"] = "Deadly Cleft Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "42-43", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 10, + ["coords"] = { + {34.8, 38.4, 1337}, + }, + }, + [7430] = { + ["name"] = "Frostsaber Cub", + ["family"] = "Cats", + ["lvl"] = "55-56", + ["fac"] = "Unknown", + ["abilities"] = "Cower 6", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 333, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 20, + ["coords"] = { + {48.9, 19.0, 618}, + }, + }, + [7431] = { + ["name"] = "Frostsaber", + ["family"] = "Cats", + ["lvl"] = "56-57", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 333, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 44, + ["coords"] = { + {51.1, 22.3, 618}, + }, + }, + [7432] = { + ["name"] = "Frostsaber Stalker", + ["family"] = "Cats", + ["lvl"] = "59-60", + ["fac"] = "Unknown", + ["abilities"] = "Dash 3, Prowl 3", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 333, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 89, + ["coords"] = { + {51.5, 21.5, 618}, + }, + }, + [7433] = { + ["name"] = "Frostsaber Huntress", + ["family"] = "Cats", + ["lvl"] = "58-59", + ["fac"] = "Unknown", + ["abilities"] = "Dash 3", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 333, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 54, + ["coords"] = { + {50.4, 16.6, 618}, + }, + }, + [7434] = { + ["name"] = "Frostsaber Pride Watcher", + ["family"] = "Cats", + ["lvl"] = "59-60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 333, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 10, + ["coords"] = { + {49.3, 12.3, 618}, + }, + }, + [7443] = { + ["name"] = "Shardtooth Mauler", + ["family"] = "Bears", + ["lvl"] = "55-56", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 111, + ["coords"] = { + {66.7, 54.4, 618}, + }, + }, + [7444] = { + ["name"] = "Shardtooth Bear", + ["family"] = "Bears", + ["lvl"] = "53-54", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 333, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 68, + ["coords"] = { + {30.2, 47.2, 618}, + }, + }, + [7445] = { + ["name"] = "Elder Shardtooth", + ["family"] = "Bears", + ["lvl"] = "57-58", + ["fac"] = "Unknown", + ["abilities"] = "Claw 8, Roar of Fortitude", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 91, + ["coords"] = { + {60.8, 64.0, 618}, + }, + }, + [7446] = { + ["name"] = "Rabid Shardtooth", + ["family"] = "Bears", + ["lvl"] = "59-60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 43, + ["coords"] = { + {63.4, 74.4, 618}, + }, + }, + [7456] = { + ["name"] = "Winterspring Screecher", + ["family"] = "Owls", + ["lvl"] = "57-59", + ["fac"] = "Unknown", + ["abilities"] = "Claw 8, Dive 3, Screech 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 333, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 37, + ["coords"] = { + {60.3, 64.5, 618}, + }, + }, + [8207] = { + ["name"] = "Greater Firebird", + ["family"] = "Carrion Birds", + ["lvl"] = "46", + ["fac"] = "Unknown", + ["abilities"] = "Dive 2", + ["attackSpeed"] = "1.27", + ["rare"] = true, + ["respawnMinSeconds"] = 75600, + ["respawnMaxSeconds"] = 108000, + ["respawnSamples"] = 2, + ["coords"] = { + {54.2, 39.9, 440}, + }, + }, + [8208] = { + ["name"] = "Murderous Blisterpaw", + ["family"] = "Hyenas", + ["lvl"] = "44", + ["fac"] = "Unknown", + ["abilities"] = "Dash 2", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 115200, + ["respawnMaxSeconds"] = 180000, + ["respawnSamples"] = 2, + ["coords"] = { + {47.4, 25.0, 440}, + }, + }, + [8211] = { + ["name"] = "Old Cliff Jumper", + ["family"] = "Wolves", + ["lvl"] = "42", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6, Dash 2", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 37800, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 4, + ["coords"] = { + {11.9, 53.7, 47}, + }, + }, + [8213] = { + ["name"] = "Ironback", + ["family"] = "Turtles", + ["lvl"] = "51", + ["fac"] = "Unknown", + ["abilities"] = "Bite 7", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 37800, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 2, + ["coords"] = { + {81.5, 49.2, 47}, + }, + }, + [8299] = { + ["name"] = "Spiteflayer", + ["family"] = "Carrion Birds", + ["lvl"] = "52", + ["fac"] = "Unknown", + ["abilities"] = "Dive 3", + ["attackSpeed"] = "1.2", + ["rare"] = true, + ["respawnMinSeconds"] = 37800, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 2, + ["coords"] = { + {61.1, 35.4, 4}, + }, + }, + [8300] = { + ["name"] = "Ravage", + ["family"] = "Hyenas", + ["lvl"] = "51", + ["fac"] = "Unknown", + ["abilities"] = "Dash 3", + ["attackSpeed"] = "1.3", + ["rare"] = true, + ["respawnMinSeconds"] = 37800, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 2, + ["coords"] = { + {61.1, 36.6, 4}, + }, + }, + [8301] = { + ["name"] = "Clack the Reaver", + ["family"] = "Scorpids", + ["lvl"] = "53", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 37800, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 2, + ["coords"] = { + {48.3, 38.7, 4}, + }, + }, + [8303] = { + ["name"] = "Grunter", + ["family"] = "Boars", + ["lvl"] = "50", + ["fac"] = "Unknown", + ["abilities"] = "Charge 5, Dash 3", + ["attackSpeed"] = "1.23", + ["rare"] = true, + ["respawnMinSeconds"] = 115200, + ["respawnMaxSeconds"] = 180000, + ["respawnSamples"] = 2, + ["coords"] = { + {56.1, 31.1, 4}, + }, + }, + [8336] = { + ["name"] = "Hakkari Sapper", + ["family"] = "Wind Serpents", + ["lvl"] = "49-50", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 5", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 7, + ["coords"] = { + {24.8, 64.8, 1477}, + }, + }, + [8600] = { + ["name"] = "Plaguebat", + ["family"] = "Bats", + ["lvl"] = "53-55", + ["fac"] = "Unknown", + ["abilities"] = "Cower 5, Dive 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 345, + ["respawnMaxSeconds"] = 345, + ["respawnSamples"] = 298, + ["coords"] = { + {84.1, 69.0, 28}, + }, + }, + [8601] = { + ["name"] = "Noxious Plaguebat", + ["family"] = "Bats", + ["lvl"] = "54-56", + ["fac"] = "Unknown", + ["abilities"] = "Cower 5", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 345, + ["respawnMaxSeconds"] = 345, + ["respawnSamples"] = 175, + ["coords"] = { + {72.2, 72.8, 139}, + }, + }, + [8602] = { + ["name"] = "Monstrous Plaguebat", + ["family"] = "Bats", + ["lvl"] = "56-58", + ["fac"] = "Unknown", + ["abilities"] = "Cower 6, Screech 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 345, + ["respawnMaxSeconds"] = 345, + ["respawnSamples"] = 148, + ["coords"] = { + {67.7, 50.9, 139}, + }, + }, + [8762] = { + ["name"] = "Timberweb Recluse", + ["family"] = "Spiders", + ["lvl"] = "47-48", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6, Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 333, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 24, + ["coords"] = { + {24.4, 49.8, 16}, + }, + }, + [8933] = { + ["name"] = "Cave Creeper", + ["family"] = "Spiders", + ["lvl"] = "50-52", + ["fac"] = "Unknown", + ["abilities"] = "Bite 7", + ["attackSpeed"] = "2.0", + ["coords"] = { + }, + }, + [8956] = { + ["name"] = "Angerclaw Bear", + ["family"] = "Bears", + ["lvl"] = "47-48", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 97, + ["coords"] = { + {57.4, 25.5, 331}, + }, + }, + [8958] = { + ["name"] = "Angerclaw Mauler", + ["family"] = "Bears", + ["lvl"] = "49-50", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7, Roar of Fortitude", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 82, + ["coords"] = { + {43.8, 67.6, 361}, + }, + }, + [8959] = { + ["name"] = "Felpaw Wolf", + ["family"] = "Wolves", + ["lvl"] = "47-48", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6, Furious Howl 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 117, + ["coords"] = { + {57.2, 27.5, 331}, + }, + }, + [8960] = { + ["name"] = "Felpaw Scavenger", + ["family"] = "Wolves", + ["lvl"] = "49-50", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 110, + ["coords"] = { + {52.3, 62.0, 148}, + }, + }, + [8961] = { + ["name"] = "Felpaw Ravager", + ["family"] = "Wolves", + ["lvl"] = "51-52", + ["fac"] = "Unknown", + ["abilities"] = "Bite 7", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 35, + ["coords"] = { + {53.8, 27.7, 361}, + }, + }, + [9416] = { + ["name"] = "Scarshield Worg", + ["family"] = "Wolves", + ["lvl"] = "53-54", + ["fac"] = "Unknown", + ["abilities"] = "Dash 3", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 11, + ["coords"] = { + {62.7, 62.1, 1583}, + }, + }, + [9690] = { + ["name"] = "Ember Worg", + ["family"] = "Wolves", + ["lvl"] = "51-52", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 500, + ["respawnMaxSeconds"] = 500, + ["respawnSamples"] = 7, + ["coords"] = { + {84.1, 60.6, 46}, + }, + }, + [9691] = { + ["name"] = "Venomtip Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "52-53", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 500, + ["respawnMaxSeconds"] = 500, + ["respawnSamples"] = 11, + ["coords"] = { + {86.8, 59.7, 46}, + }, + }, + [9694] = { + ["name"] = "Slavering Ember Worg", + ["family"] = "Wolves", + ["lvl"] = "53-54", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 500, + ["respawnMaxSeconds"] = 500, + ["respawnSamples"] = 5, + ["coords"] = { + {58.4, 63.9, 46}, + }, + }, + [9695] = { + ["name"] = "Deathlash Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "54-55", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7, Scorpid Poison 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 500, + ["respawnMaxSeconds"] = 500, + ["respawnSamples"] = 12, + ["coords"] = { + {54.2, 67.0, 46}, + }, + }, + [9696] = { + ["name"] = "Bloodaxe Worg", + ["family"] = "Wolves", + ["lvl"] = "56-57", + ["fac"] = "Unknown", + ["abilities"] = "Bite 8, Dash 3, Furious Howl 4", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 16, + ["coords"] = { + {53.3, 79.7, 1583}, + }, + }, + [9697] = { + ["name"] = "Giant Ember Worg", + ["family"] = "Wolves", + ["lvl"] = "55-56", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 500, + ["respawnMaxSeconds"] = 500, + ["respawnSamples"] = 10, + ["coords"] = { + {24.5, 69.2, 46}, + }, + }, + [9698] = { + ["name"] = "Firetail Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "56-57", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 500, + ["respawnMaxSeconds"] = 500, + ["respawnSamples"] = 9, + ["coords"] = { + {25.6, 60.9, 46}, + }, + }, + [10077] = { + ["name"] = "Deathmaw", + ["family"] = "Wolves", + ["lvl"] = "53", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.2", + ["rare"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 27000, + ["respawnSamples"] = 3, + ["coords"] = { + {86.6, 49.6, 46}, + }, + }, + [10221] = { + ["name"] = "Bloodaxe Worg Pup", + ["family"] = "Wolves", + ["lvl"] = "52-53", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 3600, + ["respawnMaxSeconds"] = 3600, + ["respawnSamples"] = 12, + ["coords"] = { + {37.4, 84.6, 1583}, + }, + }, + [10357] = { + ["name"] = "Ressan the Needler", + ["family"] = "Bats", + ["lvl"] = "11", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 5400, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 2, + ["coords"] = { + {42.9, 67.6, 85}, + }, + }, + [10375] = { + ["name"] = "Spire Spiderling", + ["family"] = "Spiders", + ["lvl"] = "55-56", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 13, + ["coords"] = { + {62.6, 76.4, 1583}, + }, + }, + [10644] = { + ["name"] = "Mist Howler", + ["family"] = "Wolves", + ["lvl"] = "22", + ["fac"] = "Unknown", + ["abilities"] = "Furious Howl 1", + ["attackSpeed"] = "1.3", + ["rare"] = true, + ["respawnMinSeconds"] = 30600, + ["respawnMaxSeconds"] = 38000, + ["respawnSamples"] = 2, + ["coords"] = { + {20.9, 36.9, 331}, + }, + }, + [10737] = { + ["name"] = "Shy-Rotam", + ["family"] = "Cats", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["elite"] = true, + ["coords"] = { + {49.5, 9.7, 618}, + }, + }, + [10806] = { + ["name"] = "Ursius", + ["family"] = "Bears", + ["lvl"] = "56", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.4", + ["elite"] = true, + ["respawnMinSeconds"] = 333, + ["respawnMaxSeconds"] = 333, + ["respawnSamples"] = 1, + ["coords"] = { + {64.0, 24.1, 618}, + }, + }, + [11357] = { + ["name"] = "Son of Hakkar", + ["family"] = "Wind Serpents", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 6", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 60, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 20, + ["coords"] = { + {45.5, 45.3, 1977}, + }, + }, + [11360] = { + ["name"] = "Zulian Cub", + ["family"] = "Cats", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 12, + ["coords"] = { + {59.7, 41.8, 1977}, + }, + }, + [11361] = { + ["name"] = "Zulian Tiger", + ["family"] = "Cats", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 35, + ["coords"] = { + {54.3, 50.6, 1977}, + }, + }, + [11365] = { + ["name"] = "Zulian Panther", + ["family"] = "Cats", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "Dash 3", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 52, + ["coords"] = { + {50.3, 28.9, 1977}, + }, + }, + [11368] = { + ["name"] = "Bloodseeker Bat", + ["family"] = "Bats", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.0", + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 42, + ["coords"] = { + {34.9, 77.4, 1977}, + }, + }, + [11370] = { + ["name"] = "Razzashi Broodwidow", + ["family"] = "Spiders", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "Web", + ["attackSpeed"] = "2.7", + ["elite"] = true, + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 7, + ["coords"] = { + {47.2, 80.5, 1977}, + }, + }, + [11371] = { + ["name"] = "Razzashi Serpent", + ["family"] = "Serpents (Cobra)", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "Poison Spit 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 33, + ["coords"] = { + {51.0, 60.7, 1977}, + }, + }, + [11372] = { + ["name"] = "Razzashi Adder", + ["family"] = "Serpents (Cobra)", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "Poison Spit 3", + ["attackSpeed"] = "1.4", + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 29, + ["coords"] = { + {50.8, 60.3, 1977}, + }, + }, + [11735] = { + ["name"] = "Stonelash Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "54-55", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 272, + ["coords"] = { + {10.4, 42.7, 490}, + }, + }, + [11736] = { + ["name"] = "Stonelash Pincer", + ["family"] = "Scorpids", + ["lvl"] = "56", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 112, + ["coords"] = { + {7.5, 57.7, 490}, + }, + }, + [11737] = { + ["name"] = "Stonelash Flayer", + ["family"] = "Scorpids", + ["lvl"] = "57-58", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 259, + ["coords"] = { + {9.7, 73.4, 490}, + }, + }, + [11738] = { + ["name"] = "Sand Skitterer", + ["family"] = "Spiders", + ["lvl"] = "55-56", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 273, + ["coords"] = { + {9.3, 42.6, 490}, + }, + }, + [11739] = { + ["name"] = "Rock Stalker", + ["family"] = "Spiders", + ["lvl"] = "57-58", + ["fac"] = "Unknown", + ["abilities"] = "Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 252, + ["coords"] = { + {35.3, 91.1, 1377}, + }, + }, + [11921] = { + ["name"] = "Besseleth", + ["family"] = "Spiders", + ["lvl"] = "21", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3, Web", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 2, + ["coords"] = { + {53.8, 73.0, 406}, + }, + }, + [12431] = { + ["name"] = "Gorefang", + ["family"] = "Wolves", + ["lvl"] = "13", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 5400, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 2, + ["coords"] = { + {59.5, 7.6, 130}, + }, + }, + [12432] = { + ["name"] = "Old Vicejaw", + ["family"] = "Bears", + ["lvl"] = "14", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 5400, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 2, + ["coords"] = { + {53.8, 51.9, 130}, + }, + }, + [12433] = { + ["name"] = "Krethis Shadowspinner", + ["family"] = "Spiders", + ["lvl"] = "15", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 9000, + ["respawnMaxSeconds"] = 9000, + ["respawnSamples"] = 1, + ["coords"] = { + {24.0, 79.7, 85}, + }, + }, + [13599] = { + ["name"] = "Stolid Snapjaw", + ["family"] = "Turtles", + ["lvl"] = "46-47", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 26, + ["coords"] = { + {20.2, 88.2, 2100}, + }, + }, + [14123] = { + ["name"] = "Steeljaw Snapper", + ["family"] = "Turtles", + ["lvl"] = "42-43", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 89, + ["coords"] = { + {73.3, 43.0, 440}, + }, + }, + [14228] = { + ["name"] = "Giggler", + ["family"] = "Hyenas", + ["lvl"] = "34", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 37800, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 2, + ["coords"] = { + {61.3, 28.9, 405}, + }, + }, + [14232] = { + ["name"] = "Dart", + ["family"] = "Raptors", + ["lvl"] = "38", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 27000, + ["respawnSamples"] = 2, + ["coords"] = { + {48.1, 16.0, 15}, + }, + }, + [14233] = { + ["name"] = "Ripscale", + ["family"] = "Crocolisks", + ["lvl"] = "39", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 37800, + ["respawnMaxSeconds"] = 38000, + ["respawnSamples"] = 4, + ["coords"] = { + {49.2, 57.4, 15}, + }, + }, + [14266] = { + ["name"] = "Shanda the Spinner", + ["family"] = "Spiders", + ["lvl"] = "25", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 14400, + ["respawnMaxSeconds"] = 19800, + ["respawnSamples"] = 3, + ["coords"] = { + {78.1, 52.4, 38}, + }, + }, + [14268] = { + ["name"] = "Lord Condar", + ["family"] = "Carrion Birds", + ["lvl"] = "16", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 14400, + ["respawnMaxSeconds"] = 14400, + ["respawnSamples"] = 3, + ["coords"] = { + {68.6, 76.4, 38}, + }, + }, + [14279] = { + ["name"] = "Creepthess", + ["family"] = "Spiders", + ["lvl"] = "24", + ["fac"] = "Unknown", + ["abilities"] = "Bite 3", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 30600, + ["respawnMaxSeconds"] = 38000, + ["respawnSamples"] = 3, + ["coords"] = { + {38.6, 58.4, 267}, + }, + }, + [14280] = { + ["name"] = "Big Samras", + ["family"] = "Bears", + ["lvl"] = "27", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 37800, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 2, + ["coords"] = { + {86.0, 39.9, 267}, + }, + }, + [14283] = { + ["name"] = "Stormpike Owl", + ["family"] = "Owls", + ["lvl"] = "53-54", + ["fac"] = "A", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 120, + ["respawnSamples"] = 16, + ["coords"] = { + {49.7, 42.2, 2597}, + }, + }, + [14339] = { + ["name"] = "Death Howl", + ["family"] = "Wolves", + ["lvl"] = "49", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6, Furious Howl 3", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 75600, + ["respawnMaxSeconds"] = 108000, + ["respawnSamples"] = 3, + ["coords"] = { + {50.0, 77.1, 361}, + }, + }, + [14343] = { + ["name"] = "Olm the Wise", + ["family"] = "Owls", + ["lvl"] = "52", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7, Dive 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 75600, + ["respawnMaxSeconds"] = 108000, + ["respawnSamples"] = 3, + ["coords"] = { + {50.2, 32.8, 361}, + }, + }, + [14344] = { + ["name"] = "Mongress", + ["family"] = "Bears", + ["lvl"] = "50", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 115200, + ["respawnMaxSeconds"] = 180000, + ["respawnSamples"] = 2, + ["coords"] = { + {42.1, 75.2, 361}, + }, + }, + [14472] = { + ["name"] = "Gretheer", + ["family"] = "Spiders", + ["lvl"] = "57", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.18", + ["rare"] = true, + ["respawnMinSeconds"] = 115200, + ["respawnMaxSeconds"] = 180000, + ["respawnSamples"] = 2, + ["coords"] = { + {39.3, 55.4, 1377}, + }, + }, + [14476] = { + ["name"] = "Krellack", + ["family"] = "Scorpids", + ["lvl"] = "56", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 4", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 37800, + ["respawnMaxSeconds"] = 54000, + ["respawnSamples"] = 2, + ["coords"] = { + {63.2, 16.7, 1377}, + }, + }, + [14491] = { + ["name"] = "Kurmokk", + ["family"] = "Gorillas", + ["lvl"] = "42", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 115200, + ["respawnMaxSeconds"] = 180000, + ["respawnSamples"] = 2, + ["coords"] = { + {35.4, 56.6, 33}, + }, + }, + [14532] = { + ["name"] = "Razzashi Venombrood", + ["family"] = "Spiders", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 31, + ["coords"] = { + {49.5, 81.6, 1977}, + }, + }, + [14821] = { + ["name"] = "Razzashi Raptor", + ["family"] = "Raptors", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 6", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 32, + ["coords"] = { + {57.4, 83.2, 1977}, + }, + }, + [14965] = { + ["name"] = "Frenzied Bloodseeker Bat", + ["family"] = "Bats", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.0", + ["coords"] = { + }, + }, + [15043] = { + ["name"] = "Zulian Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "Death Roll 6", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 7200, + ["respawnMaxSeconds"] = 7200, + ["respawnSamples"] = 41, + ["coords"] = { + {41.6, 48.6, 1977}, + }, + }, + [15101] = { + ["name"] = "Zulian Prowler", + ["family"] = "Cats", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.6", + ["coords"] = { + }, + }, + [16117] = { + ["name"] = "Plagued Swine", + ["family"] = "Boars", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "Charge 6", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 345, + ["respawnMaxSeconds"] = 345, + ["respawnSamples"] = 50, + ["coords"] = { + {18.1, 35.3, 139}, + }, + }, + [40000] = { + ["name"] = "Red Fox", + ["family"] = "Foxes", + ["lvl"] = "1", + ["fac"] = "AH", + ["abilities"] = "Grace 1", + ["attackSpeed"] = "2.0", + ["coords"] = { + }, + }, + [60494] = { + ["name"] = "Sorrowclaw", + ["family"] = "Cats", + ["lvl"] = "41", + ["fac"] = "Unknown", + ["abilities"] = "Dash 2", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 1, + ["coords"] = { + {70.6, 31.4, 8}, + }, + }, + [60848] = { + ["name"] = "Bristleback Boar", + ["family"] = "Boars", + ["lvl"] = "9-10", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.9", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 10, + ["coords"] = { + {38.0, 35.7, 17}, + }, + }, + [61074] = { + ["name"] = "Highvale Gorilla", + ["family"] = "Gorillas", + ["lvl"] = "56-57", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 31, + ["coords"] = { + {49.7, 58.0, 5121}, + }, + }, + [61075] = { + ["name"] = "Rockshell Crawler", + ["family"] = "Crabs", + ["lvl"] = "56-57", + ["fac"] = "Unknown", + ["abilities"] = "Claw 8, Bubble Barrier 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 48, + ["coords"] = { + {36.9, 53.5, 5121}, + }, + }, + [61076] = { + ["name"] = "Sandshell Crawler", + ["family"] = "Crabs", + ["lvl"] = "53-54", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7, Bubble Barrier 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 280, + ["respawnSamples"] = 21, + ["coords"] = { + {46.9, 89.9, 5121}, + }, + }, + [61077] = { + ["name"] = "Highvale Chimpanzee", + ["family"] = "Gorillas", + ["lvl"] = "53-54", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 280, + ["respawnSamples"] = 18, + ["coords"] = { + {51.9, 58.2, 5121}, + }, + }, + [61078] = { + ["name"] = "Highvale Monkey", + ["family"] = "Gorillas", + ["lvl"] = "53-54", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 280, + ["respawnSamples"] = 23, + ["coords"] = { + {51.3, 85.8, 5121}, + }, + }, + [61080] = { + ["name"] = "Elder Highvale Gorilla", + ["family"] = "Gorillas", + ["lvl"] = "57-59", + ["fac"] = "Unknown", + ["abilities"] = "Thunderstomp 4", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 35, + ["coords"] = { + {53.3, 58.6, 5121}, + }, + }, + [61082] = { + ["name"] = "Highvale Thunderer", + ["family"] = "Gorillas", + ["lvl"] = "56-58", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.8", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 23, + ["coords"] = { + {55.2, 58.2, 5121}, + }, + }, + [61090] = { + ["name"] = "Brushtail Cobra", + ["family"] = "Serpents (Cobra)", + ["lvl"] = "55-56", + ["fac"] = "Unknown", + ["abilities"] = "Poison Spit 2, Bite 8", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 280, + ["respawnSamples"] = 33, + ["coords"] = { + {55.9, 69.3, 5121}, + }, + }, + [61091] = { + ["name"] = "Brushtail Adder", + ["family"] = "Serpents (Cobra)", + ["lvl"] = "53-54", + ["fac"] = "Unknown", + ["abilities"] = "Poison Spit 2, Bite 7", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 280, + ["respawnSamples"] = 15, + ["coords"] = { + {49.4, 86.4, 5121}, + }, + }, + [61095] = { + ["name"] = "Venomflayer Screecher", + ["family"] = "Wind Serpents", + ["lvl"] = "53-55", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 5", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 8, + ["coords"] = { + {47.3, 87.3, 5121}, + }, + }, + [61098] = { + ["name"] = "King Morogo Thunderfoot", + ["family"] = "Gorillas", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 280, + ["respawnSamples"] = 1, + ["coords"] = { + {53.9, 48.6, 5121}, + }, + }, + [61206] = { + ["name"] = "Skitterweb Crawler", + ["family"] = "Spiders", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 604800, + ["respawnMaxSeconds"] = 604800, + ["respawnSamples"] = 21, + ["coords"] = { + {65.3, 66.4, 3457}, + }, + }, + [61218] = { + ["name"] = "Deviate Coiler Hatchling", + ["family"] = "Wind Serpents", + ["lvl"] = "11", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 1", + ["attackSpeed"] = "2.0", + ["elite"] = true, + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 5, + ["coords"] = { + {45.9, 36.2, 17}, + }, + }, + [61227] = { + ["name"] = "Duskpelt Prowler", + ["family"] = "Wolves", + ["lvl"] = "39-41", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 53, + ["coords"] = { + {47.0, 91.4, 130}, + }, + }, + [61228] = { + ["name"] = "Duskpelt Stalker", + ["family"] = "Wolves", + ["lvl"] = "41-43", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 29, + ["coords"] = { + {56.5, 76.1, 5179}, + }, + }, + [61229] = { + ["name"] = "Starving Duskpelt", + ["family"] = "Wolves", + ["lvl"] = "43-45", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 41, + ["coords"] = { + {26.4, 74.1, 5179}, + }, + }, + [61230] = { + ["name"] = "Vilewing Batling", + ["family"] = "Bats", + ["lvl"] = "39-40", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 50, + ["coords"] = { + {44.3, 90.1, 130}, + }, + }, + [61231] = { + ["name"] = "Vilewing Bat", + ["family"] = "Bats", + ["lvl"] = "42-44", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 20, + ["coords"] = { + {40.3, 65.4, 5179}, + }, + }, + [61232] = { + ["name"] = "Hollow Web Spider", + ["family"] = "Spiders", + ["lvl"] = "42-44", + ["fac"] = "Unknown", + ["abilities"] = "Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 16, + ["coords"] = { + {46.7, 78.2, 5179}, + }, + }, + [61233] = { + ["name"] = "Hollow Web Venomspitter", + ["family"] = "Spiders", + ["lvl"] = "42-45", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 10, + ["coords"] = { + {43.4, 77.8, 5179}, + }, + }, + [61336] = { + ["name"] = "Greathorn Owl", + ["family"] = "Owls", + ["lvl"] = "58-60", + ["fac"] = "Unknown", + ["abilities"] = "Screech 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 49, + ["coords"] = { + {63.0, 87.6, 616}, + }, + }, + [61337] = { + ["name"] = "Greathorn Hunter", + ["family"] = "Owls", + ["lvl"] = "60-61", + ["fac"] = "Unknown", + ["abilities"] = "Screech 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 16, + ["coords"] = { + {71.2, 48.7, 616}, + }, + }, + [61401] = { + ["name"] = "Greater Vilewing Bat", + ["family"] = "Bats", + ["lvl"] = "43-45", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 16, + ["coords"] = { + {54.0, 89.8, 5179}, + }, + }, + [61488] = { + ["name"] = "Azurebeak", + ["family"] = "Owls", + ["lvl"] = "60", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 108000, + ["respawnMaxSeconds"] = 108000, + ["respawnSamples"] = 1, + ["coords"] = { + {23.0, 37.1, 616}, + }, + }, + [61599] = { + ["name"] = "Darksaber", + ["family"] = "Cats", + ["lvl"] = "42-44", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 13, + ["coords"] = { + {84.0, 38.4, 357}, + }, + }, + [61656] = { + ["name"] = "Ashfeather Buzzard", + ["family"] = "Carrion Birds", + ["lvl"] = "4-6", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 23, + ["coords"] = { + {99.9, 53.5, 14}, + }, + }, + [61657] = { + ["name"] = "Ashfeather Swooper", + ["family"] = "Carrion Birds", + ["lvl"] = "5-7", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 32, + ["coords"] = { + {57.0, 72.7, 5536}, + }, + }, + [61658] = { + ["name"] = "Ashpaw Hyena", + ["family"] = "Hyenas", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 15, + ["coords"] = { + {45.1, 46.4, 5536}, + }, + }, + [61659] = { + ["name"] = "Ashpaw Hunter", + ["family"] = "Hyenas", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Bite 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 11, + ["coords"] = { + {59.8, 52.4, 5536}, + }, + }, + [61660] = { + ["name"] = "Stonetail Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "4-6", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 21, + ["coords"] = { + {99.4, 53.0, 14}, + }, + }, + [61661] = { + ["name"] = "Stonetail Lasher", + ["family"] = "Scorpids", + ["lvl"] = "6-7", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 4, + ["coords"] = { + {46.9, 76.6, 5536}, + }, + }, + [61662] = { + ["name"] = "Blackvenom Scorpid", + ["family"] = "Scorpids", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Scorpid Poison 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 12, + ["coords"] = { + {55.9, 75.6, 5536}, + }, + }, + [61680] = { + ["name"] = "Muckreef Crawler", + ["family"] = "Crabs", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 70, + ["coords"] = { + {98.8, 37.9, 14}, + }, + }, + [61681] = { + ["name"] = "Young Muckreef Crawler", + ["family"] = "Crabs", + ["lvl"] = "3-4", + ["fac"] = "Unknown", + ["abilities"] = "Claw 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 71, + ["coords"] = { + {95.8, 67.9, 14}, + }, + }, + [61686] = { + ["name"] = "Ashfeather Scavenger", + ["family"] = "Carrion Birds", + ["lvl"] = "3", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 38, + ["coords"] = { + {99.4, 65.2, 14}, + }, + }, + [61689] = { + ["name"] = "Young Thalassian Boar", + ["family"] = "Boars", + ["lvl"] = "1-2", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 91, + ["coords"] = { + {6.0, 27.9, 139}, + }, + }, + [61690] = { + ["name"] = "Young Crimson Lynx", + ["family"] = "Cats", + ["lvl"] = "1-2", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 77, + ["coords"] = { + {8.9, 27.7, 139}, + }, + }, + [61691] = { + ["name"] = "Forest Hawkstrider", + ["family"] = "Tallstriders", + ["lvl"] = "3-4", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 24, + ["coords"] = { + {10.7, 18.5, 139}, + }, + }, + [61692] = { + ["name"] = "Thalassian Boar", + ["family"] = "Boars", + ["lvl"] = "2-3", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 22, + ["coords"] = { + {48.7, 76.0, 5225}, + }, + }, + [61693] = { + ["name"] = "Crimson Lynx", + ["family"] = "Cats", + ["lvl"] = "3-4", + ["fac"] = "Unknown", + ["abilities"] = "Claw 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 22, + ["coords"] = { + {9.7, 16.2, 139}, + }, + }, + [61695] = { + ["name"] = "Ivory Hawkstrider", + ["family"] = "Tallstriders", + ["lvl"] = "5-7", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 59, + ["coords"] = { + {16.9, 12.5, 139}, + }, + }, + [61696] = { + ["name"] = "Elder Thalassian Boar", + ["family"] = "Boars", + ["lvl"] = "5-7", + ["fac"] = "Unknown", + ["abilities"] = "Charge 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 59, + ["coords"] = { + {13.5, 12.9, 139}, + }, + }, + [61697] = { + ["name"] = "Bright Lynx", + ["family"] = "Cats", + ["lvl"] = "5-6", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 40, + ["coords"] = { + {18.9, 12.2, 139}, + }, + }, + [61698] = { + ["name"] = "Bright Lynx Matriarch", + ["family"] = "Cats", + ["lvl"] = "6-7", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 15, + ["coords"] = { + {18.2, 11.5, 139}, + }, + }, + [61700] = { + ["name"] = "Elder Crimson Lynx", + ["family"] = "Cats", + ["lvl"] = "6-8", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 33, + ["coords"] = { + {35.2, 98.1, 2040}, + }, + }, + [61704] = { + ["name"] = "Farstride Crawler", + ["family"] = "Crabs", + ["lvl"] = "6-8", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 44, + ["coords"] = { + {17.7, 86.9, 2040}, + }, + }, + [61716] = { + ["name"] = "Crimson Hawkstrider", + ["family"] = "Tallstriders", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 41, + ["coords"] = { + {0.5, 88.8, 2040}, + }, + }, + [61717] = { + ["name"] = "Thalassian Fox", + ["family"] = "Foxes", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 21, + ["coords"] = { + {0.2, 64.3, 2040}, + }, + }, + [61774] = { + ["name"] = "Brilliant Mana Wyrm", + ["family"] = "Wind Serpents", + ["lvl"] = "6-7", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 38, + ["coords"] = { + {11.0, 97.4, 2040}, + }, + }, + [61775] = { + ["name"] = "Emerald Mana Wyrm", + ["family"] = "Wind Serpents", + ["lvl"] = "6-7", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 25, + ["coords"] = { + {1.3, 95.4, 2040}, + }, + }, + [61776] = { + ["name"] = "Lavender Mana Wyrm", + ["family"] = "Wind Serpents", + ["lvl"] = "8", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 18, + ["coords"] = { + {35.7, 76.6, 5225}, + }, + }, + [61789] = { + ["name"] = "Moonfeather", + ["family"] = "Tallstriders", + ["lvl"] = "9", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.4", + ["respawnMinSeconds"] = 7300, + ["respawnMaxSeconds"] = 10000, + ["respawnSamples"] = 2, + ["coords"] = { + {26.5, 58.4, 5225}, + }, + }, + [62121] = { + ["name"] = "Goldbristle Boar", + ["family"] = "Boars", + ["lvl"] = "29-30", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 120, + ["respawnSamples"] = 41, + ["coords"] = { + {40.5, 71.7, 5581}, + }, + }, + [62122] = { + ["name"] = "Burly Goldbristle Boar", + ["family"] = "Boars", + ["lvl"] = "29-30", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 79, + ["coords"] = { + {23.9, 93.4, 1}, + }, + }, + [62123] = { + ["name"] = "Goldpelt Bear", + ["family"] = "Bears", + ["lvl"] = "28-30", + ["fac"] = "Unknown", + ["abilities"] = "Roar of Fortitude", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 120, + ["respawnSamples"] = 20, + ["coords"] = { + {41.7, 72.5, 5581}, + }, + }, + [62124] = { + ["name"] = "Goldpelt Grizzly", + ["family"] = "Bears", + ["lvl"] = "31-32", + ["fac"] = "Unknown", + ["abilities"] = "Roar of Fortitude", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 82, + ["coords"] = { + {56.0, 75.9, 5581}, + }, + }, + [62125] = { + ["name"] = "Elder Timber Wolf", + ["family"] = "Wolves", + ["lvl"] = "29-30", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 120, + ["respawnSamples"] = 32, + ["coords"] = { + {57.7, 72.1, 5581}, + }, + }, + [62126] = { + ["name"] = "Timber Wolf Alpha", + ["family"] = "Wolves", + ["lvl"] = "31-32", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 61, + ["coords"] = { + {58.9, 76.3, 5581}, + }, + }, + [62223] = { + ["name"] = "Grimscale Raptor", + ["family"] = "Raptors", + ["lvl"] = "31-33", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 27, + ["coords"] = { + {49.8, 67.5, 5602}, + }, + }, + [62224] = { + ["name"] = "Grimscale Screecher", + ["family"] = "Raptors", + ["lvl"] = "33-34", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 14, + ["coords"] = { + {49.4, 66.9, 5602}, + }, + }, + [62225] = { + ["name"] = "Grimscale Thrasher", + ["family"] = "Raptors", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "Savage Rend 4", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 27, + ["coords"] = { + {55.1, 37.7, 5602}, + }, + }, + [62226] = { + ["name"] = "Stonehide Boar", + ["family"] = "Boars", + ["lvl"] = "32-34", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 82, + ["coords"] = { + {95.4, 68.5, 11}, + }, + }, + [62227] = { + ["name"] = "Elder Stonehide Boar", + ["family"] = "Boars", + ["lvl"] = "37-38", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 76, + ["coords"] = { + {63.2, 77.0, 5602}, + }, + }, + [62247] = { + ["name"] = "Brown Recluse", + ["family"] = "Spiders", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "Web", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 33, + ["coords"] = { + {54.6, 66.0, 5602}, + }, + }, + [62248] = { + ["name"] = "Greater Brown Recluse", + ["family"] = "Spiders", + ["lvl"] = "39-40", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 120, + ["respawnSamples"] = 42, + ["coords"] = { + {46.6, 93.9, 5602}, + }, + }, + [62256] = { + ["name"] = "Clearwater Snapjaw", + ["family"] = "Turtles", + ["lvl"] = "32-33", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 28, + ["coords"] = { + {58.4, 53.2, 5602}, + }, + }, + [62257] = { + ["name"] = "Sickly Clearwater Snapjaw", + ["family"] = "Turtles", + ["lvl"] = "35-36", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 120, + ["respawnSamples"] = 32, + ["coords"] = { + {46.2, 91.7, 5602}, + }, + }, + [62258] = { + ["name"] = "Razortooth Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "33-34", + ["fac"] = "Unknown", + ["abilities"] = "Death Roll 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 15, + ["coords"] = { + {59.4, 51.5, 5602}, + }, + }, + [62259] = { + ["name"] = "Giant Razortooth Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "36-37", + ["fac"] = "Unknown", + ["abilities"] = "Death Roll 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 120, + ["respawnSamples"] = 18, + ["coords"] = { + {50.7, 87.8, 5602}, + }, + }, + [62260] = { + ["name"] = "Forest Boar", + ["family"] = "Boars", + ["lvl"] = "23-25", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 22, + ["coords"] = { + {63.4, 99.3, 36}, + }, + }, + [62261] = { + ["name"] = "Elder Forest Boar", + ["family"] = "Boars", + ["lvl"] = "26-27", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 9, + ["coords"] = { + {7.5, 52.2, 45}, + }, + }, + [62262] = { + ["name"] = "Mesa Boar", + ["family"] = "Boars", + ["lvl"] = "21-23", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 6, + ["coords"] = { + {42.5, 94.8, 17}, + }, + }, + [62271] = { + ["name"] = "Young Goldbristle Boar", + ["family"] = "Boars", + ["lvl"] = "26-28", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 120, + ["respawnSamples"] = 26, + ["coords"] = { + {60.1, 14.4, 1519}, + }, + }, + [62272] = { + ["name"] = "Young Timber Wolf", + ["family"] = "Wolves", + ["lvl"] = "26-28", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 120, + ["respawnSamples"] = 18, + ["coords"] = { + {64.1, 11.4, 1519}, + }, + }, + [62338] = { + ["name"] = "Stormwing Buzzard", + ["family"] = "Carrion Birds", + ["lvl"] = "27-30", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 22, + ["coords"] = { + {33.3, 79.0, 5561}, + }, + }, + [62339] = { + ["name"] = "Stormwing Vulture", + ["family"] = "Carrion Birds", + ["lvl"] = "29-31", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 8, + ["coords"] = { + {29.1, 60.9, 5561}, + }, + }, + [62340] = { + ["name"] = "Ragged Crag Coyote", + ["family"] = "Wolves", + ["lvl"] = "28-31", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 16, + ["coords"] = { + {47.2, 91.2, 5561}, + }, + }, + [62341] = { + ["name"] = "Slavering Crag Coyote", + ["family"] = "Wolves", + ["lvl"] = "30-32", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 21, + ["coords"] = { + {0.7, 1.8, 40}, + }, + }, + [62342] = { + ["name"] = "Mistbark Spider", + ["family"] = "Spiders", + ["lvl"] = "29-31", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 28, + ["coords"] = { + {31.8, 56.0, 5561}, + }, + }, + [62343] = { + ["name"] = "Mistbark Webweaver", + ["family"] = "Spiders", + ["lvl"] = "30-32", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 13, + ["coords"] = { + {30.6, 54.7, 5561}, + }, + }, + [62344] = { + ["name"] = "Mother Mistbark", + ["family"] = "Spiders", + ["lvl"] = "32", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 1, + ["coords"] = { + {31.2, 53.2, 5561}, + }, + }, + [62642] = { + ["name"] = "Razorscale", + ["family"] = "Raptors", + ["lvl"] = "35", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "1.36", + ["rare"] = true, + ["respawnMinSeconds"] = 50400, + ["respawnMaxSeconds"] = 50400, + ["respawnSamples"] = 1, + ["coords"] = { + {49.1, 67.2, 5602}, + }, + }, + [80252] = { + ["name"] = "Young Arctic Fox", + ["family"] = "Foxes", + ["lvl"] = "7-8", + ["fac"] = "Unknown", + ["abilities"] = "Bite 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 280, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 30, + ["coords"] = { + {75.7, 60.2, 1}, + }, + }, + [80254] = { + ["name"] = "Young Brown Fox", + ["family"] = "Foxes", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Grace 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 22, + ["coords"] = { + {21.6, 88.9, 12}, + }, + }, + [80256] = { + ["name"] = "Silver Fox", + ["family"] = "Foxes", + ["lvl"] = "27-28", + ["fac"] = "Unknown", + ["abilities"] = "Grace 1", + ["attackSpeed"] = "1.3", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 20, + ["coords"] = { + {44.1, 76.7, 10}, + }, + }, + [80258] = { + ["name"] = "Young Mist Fox", + ["family"] = "Foxes", + ["lvl"] = "10-11", + ["fac"] = "Unknown", + ["abilities"] = "Grace 1", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 120, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 11, + ["coords"] = { + {39.1, 29.9, 85}, + }, + }, + [80466] = { + ["name"] = "Tirisfal Plagued Bear", + ["family"] = "Bears", + ["lvl"] = "7-9", + ["fac"] = "Unknown", + ["abilities"] = "Claw 2", + ["attackSpeed"] = "2.5", + ["respawnMinSeconds"] = 180, + ["respawnMaxSeconds"] = 180, + ["respawnSamples"] = 39, + ["coords"] = { + {62.6, 58.9, 85}, + }, + }, + [80815] = { + ["name"] = "Amani Eagle", + ["family"] = "Owls", + ["lvl"] = "10-12", + ["fac"] = "Unknown", + ["abilities"] = "Screech 1, Claw 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 2500, + ["respawnSamples"] = 7, + ["coords"] = { + {36.4, 18.3, 406}, + }, + }, + [80816] = { + ["name"] = "Hinterlands Eagle", + ["family"] = "Owls", + ["lvl"] = "35-40", + ["fac"] = "Unknown", + ["abilities"] = "Dive 2, Screech 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 4, + ["coords"] = { + {21.2, 56.8, 47}, + }, + }, + [91819] = { + ["name"] = "Hazzuri Tiger", + ["family"] = "Cats", + ["lvl"] = "50", + ["fac"] = "Unknown", + ["abilities"] = "Cower 5, Prowl 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 594, + ["respawnSamples"] = 10, + ["coords"] = { + {30.0, 6.0, 408}, + }, + }, + [91825] = { + ["name"] = "Young Bengal Tiger", + ["family"] = "Cats", + ["lvl"] = "48-49", + ["fac"] = "Unknown", + ["abilities"] = "Cower 5, Prowl 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 21, + ["coords"] = { + {59.6, 68.5, 409}, + }, + }, + [91826] = { + ["name"] = "Bengal Matriarch", + ["family"] = "Cats", + ["lvl"] = "49-51", + ["fac"] = "Unknown", + ["abilities"] = "Cower 6", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 4, + ["coords"] = { + {39.6, 56.6, 409}, + }, + }, + [91827] = { + ["name"] = "Bengal Tiger", + ["family"] = "Cats", + ["lvl"] = "52-53", + ["fac"] = "Unknown", + ["abilities"] = "Cower 6", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 15, + ["coords"] = { + {59.7, 71.8, 409}, + }, + }, + [91828] = { + ["name"] = "Bengal Alpha", + ["family"] = "Cats", + ["lvl"] = "53-54", + ["fac"] = "Unknown", + ["abilities"] = "Cower 6", + ["attackSpeed"] = "1.5", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 8, + ["coords"] = { + {41.4, 59.9, 409}, + }, + }, + [91829] = { + ["name"] = "Jungleback Stomper", + ["family"] = "Gorillas", + ["lvl"] = "50-51", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 11, + ["coords"] = { + {62.0, 64.9, 409}, + }, + }, + [91830] = { + ["name"] = "Jungleback Thrasher", + ["family"] = "Gorillas", + ["lvl"] = "52-53", + ["fac"] = "Unknown", + ["abilities"] = "Thunderstomp 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 5, + ["coords"] = { + {40.4, 63.7, 409}, + }, + }, + [91831] = { + ["name"] = "Bright Crawler", + ["family"] = "Crabs", + ["lvl"] = "52-53", + ["fac"] = "Unknown", + ["abilities"] = "Claw 6, Bubble Barrier 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 32, + ["coords"] = { + {29.6, 70.3, 409}, + }, + }, + [91832] = { + ["name"] = "Island Pincer", + ["family"] = "Crabs", + ["lvl"] = "48-50", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7, Bubble Barrier 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 13, + ["coords"] = { + {62.1, 17.8, 409}, + }, + }, + [91833] = { + ["name"] = "Rock Crawler", + ["family"] = "Crabs", + ["lvl"] = "49-51", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7, Bubble Barrier 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 26, + ["coords"] = { + {3.9, 97.0, 40}, + }, + }, + [91838] = { + ["name"] = "Deepshell Snapper", + ["family"] = "Turtles", + ["lvl"] = "51-52", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 16, + ["coords"] = { + {41.8, 89.3, 408}, + }, + }, + [91960] = { + ["name"] = "Junglepaw Panther", + ["family"] = "Cats", + ["lvl"] = "48-50", + ["fac"] = "Unknown", + ["abilities"] = "Cower 5, Prowl 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 29, + ["coords"] = { + {66.1, 63.4, 408}, + }, + }, + [91961] = { + ["name"] = "Junglepaw Shadow Panther", + ["family"] = "Cats", + ["lvl"] = "52-53", + ["fac"] = "Unknown", + ["abilities"] = "Cower 5, Prowl 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 28, + ["coords"] = { + {52.9, 78.4, 408}, + }, + }, + [91962] = { + ["name"] = "Saltsnap Crocolisk", + ["family"] = "Crocolisks", + ["lvl"] = "49-51", + ["fac"] = "Unknown", + ["abilities"] = "Bite 6, Death Roll 5", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 12, + ["coords"] = { + {72.6, 67.4, 408}, + }, + }, + [91966] = { + ["name"] = "Venomous Jungle Serpent", + ["family"] = "Wind Serpents", + ["lvl"] = "51-52", + ["fac"] = "Unknown", + ["abilities"] = "Lightning Breath 5", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 525, + ["respawnSamples"] = 24, + ["coords"] = { + {56.7, 57.4, 408}, + }, + }, + [91984] = { + ["name"] = "Tirisclaw Bear", + ["family"] = "Bears", + ["lvl"] = "15-18", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 11, + ["coords"] = { + {23.6, 68.0, 85}, + }, + }, + [91985] = { + ["name"] = "Tirisclaw Cub", + ["family"] = "Bears", + ["lvl"] = "13-14", + ["fac"] = "Unknown", + ["abilities"] = "Claw 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 3, + ["coords"] = { + {23.7, 68.1, 85}, + }, + }, + [91987] = { + ["name"] = "Cragtusk Boar", + ["family"] = "Boars", + ["lvl"] = "14-17", + ["fac"] = "Unknown", + ["abilities"] = "Charge 2", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 30, + ["coords"] = { + {23.9, 79.6, 85}, + }, + }, + [91989] = { + ["name"] = "Graypaw Wolf", + ["family"] = "Wolves", + ["lvl"] = "16-18", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 28, + ["coords"] = { + {19.9, 76.4, 85}, + }, + }, + [91990] = { + ["name"] = "Graypaw Alpha", + ["family"] = "Wolves", + ["lvl"] = "18", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["rare"] = true, + ["respawnMinSeconds"] = 19200, + ["respawnMaxSeconds"] = 19200, + ["respawnSamples"] = 1, + ["coords"] = { + {27.0, 58.4, 85}, + }, + }, + [92115] = { + ["name"] = "Enraged Sharpclaw", + ["family"] = "Bears", + ["lvl"] = "33", + ["fac"] = "Unknown", + ["abilities"] = "None", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 18000, + ["respawnMaxSeconds"] = 18000, + ["respawnSamples"] = 24, + ["coords"] = { + {47.0, 48.9, 5077}, + }, + }, + [92146] = { + ["name"] = "Strand Crawler", + ["family"] = "Crabs", + ["lvl"] = "48-49", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7, Bubble Barrier 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 61, + ["coords"] = { + {0.9, 66.3, 33}, + }, + }, + [92147] = { + ["name"] = "Silver Coast Crawler", + ["family"] = "Crabs", + ["lvl"] = "51-52", + ["fac"] = "Unknown", + ["abilities"] = "Claw 7, Bubble Barrier 3", + ["attackSpeed"] = "2.0", + ["respawnMinSeconds"] = 300, + ["respawnMaxSeconds"] = 300, + ["respawnSamples"] = 36, + ["coords"] = { + {42.5, 85.6, 408}, + }, + }, +} \ No newline at end of file diff --git a/data/ds-creature-coords.lua b/data/ds-creature-coords.lua new file mode 100644 index 0000000..b177c39 --- /dev/null +++ b/data/ds-creature-coords.lua @@ -0,0 +1,13044 @@ +-- MetaHunt Creature Coordinate Fallback (generated from pfQuest + pfQuest-turtle) +-- Scope: creature IDs referenced by item drop sources +-- Coverage: 475/475 creature IDs with coordinates +if not MTH_DS then MTH_DS = {} end + +MTH_DS_CreatureCoords = { + [40] = { + {40.8, 81.4, 12}, + {41.9, 81.4, 12}, + {39.9, 81, 12}, + {41, 81, 12}, + {40.9, 80.7, 12}, + {40.9, 80.4, 12}, + {41.2, 80.4, 12}, + {41.1, 80.3, 12}, + {40.5, 80.2, 12}, + {39.9, 80.1, 12}, + {41.6, 80.1, 12}, + {41, 79.9, 12}, + {41.4, 79.9, 12}, + {40.2, 79.6, 12}, + {41.3, 79.2, 12}, + {39.8, 79.1, 12}, + {41.2, 78.7, 12}, + {41.7, 78.7, 12}, + {39.9, 78.6, 12}, + {40.3, 78.2, 12}, + {41.5, 78, 12}, + {40.3, 77.7, 12}, + {40.8, 77.4, 12}, + {63.7, 61.1, 12}, + {62.9, 60, 12}, + {64.8, 59.9, 12}, + {58, 59.8, 12}, + {60.9, 59.7, 12}, + {62, 58.3, 12}, + {63.8, 58.2, 12}, + {64.5, 57.4, 12}, + {60.1, 57.4, 12}, + {64.6, 56.9, 12}, + {64.3, 56.8, 12}, + {63.1, 56.6, 12}, + {64.8, 56.4, 12}, + {61.1, 56.4, 12}, + {63.6, 55.4, 12}, + {62, 55.2, 12}, + {62.9, 53.9, 12}, + {61.5, 53.3, 12}, + {60.7, 52.8, 12}, + {61.5, 52, 12}, + {61.4, 51.5, 12}, + {61.2, 50.9, 12}, + {60.6, 50.8, 12}, + {61.5, 50.2, 12}, + {61.1, 49.9, 12}, + {61.2, 49.6, 12}, + {60.3, 49.6, 12}, + {60.8, 49.5, 12}, + {61, 49.2, 12}, + {41.9, 81.3, 12}, + {39.9, 81.1, 12}, + {41.1, 81, 12}, + {40.8, 80.4, 12}, + {40.4, 80.3, 12}, + {40, 80.1, 12}, + {41.6, 80, 12}, + {41.3, 79.3, 12}, + {41, 78.2, 12}, + {41.5, 77.9, 12}, + {40.8, 77.5, 12}, + {62, 55.3, 12}, + {62.8, 53.8, 12}, + {61.5, 51.9, 12}, + }, + [43] = { + {60, 48.7, 12}, + {62, 47.9, 12}, + {60.2, 47.6, 12}, + {61.7, 47.5, 12}, + {61.1, 47.3, 12}, + {61.9, 47.1, 12}, + {61.6, 46.9, 12}, + {60.5, 46.9, 12}, + {61.8, 47.5, 12}, + }, + [46] = { + {71.1, 12.6, 10}, + {62.2, 11.2, 10}, + {62.2, 11.1, 10}, + {58.6, 9.8, 10}, + {55.4, 7.7, 10}, + {52.3, 86.7, 12}, + {75.6, 86.6, 12}, + {76.3, 86.5, 12}, + {77.8, 86.1, 12}, + {77.6, 85.9, 12}, + {76.8, 85.9, 12}, + {75.9, 85.9, 12}, + {76.6, 85.6, 12}, + {68.6, 85.5, 12}, + {68.6, 85.4, 12}, + {74.5, 85.2, 12}, + {56.4, 85.2, 12}, + {77.2, 85.2, 12}, + {76.2, 84.9, 12}, + {65.8, 84.4, 12}, + {57, 84.2, 12}, + {70, 84.1, 12}, + {67.8, 83.8, 12}, + {64.7, 83, 12}, + {63.4, 82.8, 12}, + {76.7, 82.8, 12}, + {59.3, 82.4, 12}, + {61.2, 82.4, 12}, + {61.9, 80.6, 12}, + {79.3, 58.1, 12}, + {78.3, 58, 12}, + {79.3, 57.2, 12}, + {79.3, 56.9, 12}, + {78.3, 56.7, 12}, + {79.4, 56.4, 12}, + {78.5, 56.2, 12}, + {78.8, 55.7, 12}, + {79.3, 55.2, 12}, + {79.6, 55.2, 12}, + {78.9, 55, 12}, + {79.2, 54.2, 12}, + {79.3, 54.1, 12}, + {79.5, 50.7, 12}, + {79.5, 48.2, 12}, + {79.4, 48, 12}, + {79.4, 47.7, 12}, + {79.2, 47.4, 12}, + {79.5, 47.2, 12}, + {79.5, 46.3, 12}, + {80, 46.3, 12}, + {79.1, 46, 12}, + {79.5, 45.4, 12}, + {79.3, 45.2, 12}, + {80.3, 45.2, 12}, + {79.2, 45.2, 12}, + {77.9, 45.1, 12}, + {78.6, 44.9, 12}, + {78.2, 44.9, 12}, + {78.8, 44.5, 12}, + {77.9, 44.4, 12}, + {77.7, 44.3, 12}, + {77.3, 44.1, 12}, + {79.5, 43.5, 12}, + {78.6, 43.2, 12}, + {78.6, 42.1, 12}, + {74.7, 85.3, 12}, + {67.9, 83.7, 12}, + {79.4, 58, 12}, + {78.4, 56.6, 12}, + }, + [60] = { + {64.6, 56.7, 12}, + }, + [68] = { + {32.1, 49.9, 12}, + {32.5, 49.5, 12}, + {71.3, 91.1, 1519}, + {72.3, 90, 1519}, + {70, 89.2, 1519}, + {71.5, 87.4, 1519}, + {67.8, 81.4, 1519}, + {64.2, 77.1, 1519}, + {62.8, 74.9, 1519}, + {64.2, 73.6, 1519}, + {59.9, 68.8, 1519}, + {55.1, 68.1, 1519}, + {60.6, 68, 1519}, + {55.4, 67.9, 1519}, + {62.5, 64.2, 1519}, + {48.8, 62, 1519}, + {62.4, 61.8, 1519}, + {42.5, 60.1, 1519}, + {43, 59.7, 1519}, + {38.5, 58.5, 1519}, + {38.4, 58.2, 1519}, + {59.7, 45.6, 1519}, + {59.9, 45.2, 1519}, + {55.4, 44.7, 1519}, + {55.2, 44.3, 1519}, + {72.2, 87.4, 1519}, + {69.4, 84.1, 1519}, + {68.2, 82.4, 1519}, + {69.3, 81.4, 1519}, + {66, 77.7, 1519}, + {62.3, 77.2, 1519}, + {66.6, 77, 1519}, + {62.6, 77, 1519}, + {45.2, 74.3, 1519}, + {68, 74.1, 1519}, + {57.6, 72.6, 1519}, + {68, 72.3, 1519}, + {52.5, 70.9, 1519}, + {52.9, 70.6, 1519}, + {49.5, 69.7, 1519}, + {49.4, 69.3, 1519}, + {65.9, 59.7, 1519}, + {66.1, 59.4, 1519}, + {55.9, 58.2, 1519}, + {52.6, 55.9, 1519}, + {42.4, 52.6, 1519}, + {31.6, 51.6, 1519}, + {32.2, 51.6, 1519}, + {41.7, 51.2, 1519}, + {43.8, 50.9, 1519}, + {30.3, 50.6, 1519}, + {40.5, 49.8, 1519}, + {43.1, 49.6, 1519}, + {30.3, 49.4, 1519}, + {42.4, 47.6, 1519}, + {39.1, 47.3, 1519}, + {42.5, 46.1, 1519}, + {59.5, 46, 1519}, + {29.5, 45.9, 1519}, + {43.3, 44.3, 1519}, + {43.8, 43.8, 1519}, + {42.7, 42.9, 1519}, + {30.3, 42.4, 1519}, + {34.1, 42.1, 1519}, + {30.3, 41.2, 1519}, + {41.9, 40.5, 1519}, + {39.4, 40.5, 1519}, + {30.3, 37.2, 1519}, + {34.1, 36.1, 1519}, + {30.3, 36, 1519}, + {27.2, 34.7, 1519}, + {37.4, 34.2, 1519}, + {37.2, 33.8, 1519}, + {30.3, 32.1, 1519}, + {27.2, 32.1, 1519}, + {30.3, 30.9, 1519}, + {33.4, 30.8, 1519}, + {33, 30.8, 1519}, + {65.4, 30, 1519}, + {67.2, 29.4, 1519}, + {65.9, 29.3, 1519}, + {65.7, 28.9, 1519}, + {64.8, 28.5, 1519}, + {68.4, 27.6, 1519}, + {30.3, 27, 1519}, + {66.3, 26.6, 1519}, + {30.3, 25.8, 1519}, + {67.4, 25.7, 1519}, + {27.2, 22.1, 1519}, + {27.2, 21.1, 1519}, + {34.8, 100, 5581}, + {32.7, 99.5, 5581}, + {39, 99.1, 5581}, + {37.6, 99.1, 5581}, + {32.7, 97.3, 5581}, + {34.8, 96.7, 5581}, + {32.7, 96.7, 5581}, + {31.1, 96, 5581}, + {36.6, 95.7, 5581}, + {36.5, 95.5, 5581}, + {32.7, 94.6, 5581}, + {31.1, 94.6, 5581}, + {32.8, 94, 5581}, + {34.4, 93.9, 5581}, + {34.2, 93.9, 5581}, + {51.6, 93.5, 5581}, + {52.5, 93.1, 5581}, + {51.8, 93.1, 5581}, + {51.7, 92.9, 5581}, + {51.3, 92.7, 5581}, + {53.2, 92.2, 5581}, + {32.7, 91.8, 5581}, + {52.1, 91.7, 5581}, + {32.8, 91.2, 5581}, + {52.7, 91.2, 5581}, + {31.1, 89.2, 5581}, + {31.1, 88.7, 5581}, + }, + [79] = { + {40.9, 77.5, 12}, + }, + [97] = { + {24.3, 93.6, 12}, + {26.1, 91.7, 12}, + {26.2, 91.7, 12}, + {25.3, 89.3, 12}, + {30.9, 89.1, 12}, + {25.1, 88.9, 12}, + {27.8, 88.1, 12}, + {28.3, 87.4, 12}, + {26.5, 86.6, 12}, + {27.7, 86.6, 12}, + {27.7, 86.1, 12}, + {30.8, 82.8, 12}, + {31.6, 80.6, 12}, + {68.6, 46.5, 12}, + {67.8, 46.4, 12}, + {68.4, 45.3, 12}, + {68.4, 44.9, 12}, + {66.4, 41.3, 12}, + {66.3, 40.6, 12}, + {68.8, 39.5, 12}, + {66.6, 39.4, 12}, + {68.1, 39.4, 12}, + {65.9, 39.3, 12}, + {70.8, 39.2, 12}, + {69.2, 39, 12}, + {68.7, 38.2, 12}, + {67.4, 26, 40}, + {67.2, 25.6, 40}, + {27.7, 88.3, 12}, + }, + [99] = { + {30.8, 64.7, 12}, + }, + [116] = { + {46.6, 85.5, 12}, + {49.7, 83.8, 12}, + {48.7, 83.3, 12}, + {50.5, 83.1, 12}, + {47.8, 83.1, 12}, + {50.6, 83, 12}, + {46.8, 83, 12}, + {68.4, 82, 12}, + {70.7, 81.2, 12}, + {47.7, 81.1, 12}, + {49.7, 80.9, 12}, + {68.1, 80.5, 12}, + {89.4, 80.2, 12}, + {88.8, 80, 12}, + {90, 80, 12}, + {89.6, 79.3, 12}, + {89.2, 79.2, 12}, + {90.2, 79.1, 12}, + {70.7, 78.9, 12}, + {89.9, 78.2, 12}, + {88.8, 78.2, 12}, + {67.8, 78.1, 12}, + {68.3, 77.7, 12}, + {68.6, 77.5, 12}, + {71.3, 76.7, 12}, + {68.7, 76.7, 12}, + {45.6, 76.2, 12}, + {46.6, 75.2, 12}, + {44.3, 75.2, 12}, + {47.6, 74.1, 12}, + {45.7, 73.8, 12}, + {30.9, 65.2, 12}, + {31.2, 65.1, 12}, + {30.6, 65, 12}, + {30.7, 64.9, 12}, + {31.3, 64.9, 12}, + {30.2, 64.7, 12}, + {30.3, 64.5, 12}, + {55.3, 59.9, 12}, + {28.6, 59.8, 12}, + {52.2, 59.4, 12}, + {52.1, 59.2, 12}, + {52.5, 59.1, 12}, + {29.1, 58.9, 12}, + {29.2, 58.3, 12}, + {29.9, 58.2, 12}, + {31, 57.5, 12}, + {70.7, 81.5, 12}, + {47.5, 81.2, 12}, + {67.7, 78, 12}, + {68.5, 77.4, 12}, + {68.6, 76.7, 12}, + {46.3, 75.4, 12}, + {44.4, 74.9, 12}, + {47.5, 74.1, 12}, + {45.9, 73.8, 12}, + }, + [118] = { + {83, 85.5, 12}, + {85.6, 85.5, 12}, + {83.6, 84.1, 12}, + {82.2, 84, 12}, + {85.5, 83.9, 12}, + {80.2, 83.2, 12}, + {85, 82.9, 12}, + {79.2, 82.8, 12}, + {81.3, 82.5, 12}, + {87.4, 81.8, 12}, + {80.1, 81.8, 12}, + {80.4, 81.7, 12}, + {90.4, 81.7, 12}, + {87.6, 81.3, 12}, + {80.8, 81.2, 12}, + {80.3, 81.1, 12}, + {80.6, 80.2, 12}, + {79.3, 79.7, 12}, + {91.2, 79.4, 12}, + {87.1, 79.4, 12}, + {80.6, 78.8, 12}, + {87.9, 78.3, 12}, + {80.3, 78.2, 12}, + {80.3, 78.1, 12}, + {77.9, 77.9, 12}, + {81.5, 77.9, 12}, + {76.2, 77.9, 12}, + {82.1, 77.6, 12}, + {87.1, 77.4, 12}, + {88.8, 77.4, 12}, + {90.3, 77.4, 12}, + {77.3, 76.5, 12}, + {79.1, 76.3, 12}, + {75.6, 76, 12}, + {76.2, 76, 12}, + {89.6, 75, 12}, + {77.4, 74.5, 12}, + {75.5, 74.3, 12}, + {85.9, 72.3, 12}, + {88, 72.1, 12}, + {83.8, 72, 12}, + {88.4, 70.9, 12}, + {87.1, 69.2, 12}, + {87.3, 68.8, 12}, + {87.4, 68.5, 12}, + {88.7, 68.2, 12}, + {86.9, 66.1, 12}, + {72.3, 65.8, 12}, + {71.8, 65.3, 12}, + {72.4, 65.2, 12}, + {72.3, 64.8, 12}, + {86.4, 64.2, 12}, + {86.4, 63.8, 12}, + {87.8, 63.8, 12}, + {76.2, 63.3, 12}, + {78.4, 62.7, 12}, + {85, 62.4, 12}, + {79.3, 62.3, 12}, + {85.1, 62, 12}, + {77.7, 61.7, 12}, + {85.9, 61.3, 12}, + {84.6, 61.2, 12}, + {80.2, 60.9, 12}, + {82.1, 60.8, 12}, + {78, 60.7, 12}, + {83.2, 59.6, 12}, + {66.6, 45.2, 12}, + {65.4, 43.3, 12}, + {70.5, 41.9, 12}, + {77.5, 40.9, 12}, + {78.9, 40.9, 12}, + {69.9, 40.8, 12}, + {76.6, 40.6, 12}, + {72.1, 40.5, 12}, + {73.7, 40.5, 12}, + {75.2, 40.4, 12}, + {73.6, 40, 12}, + {75.2, 39.7, 12}, + {70.3, 39.6, 12}, + {73.6, 38.1, 12}, + {74.2, 37.7, 12}, + {71.4, 37.5, 12}, + {75.4, 37.3, 12}, + {2.8, 83.1, 44}, + {75.5, 74.2, 12}, + {88.7, 68.6, 12}, + {79.3, 62.2, 12}, + {85, 62, 12}, + {80, 60.7, 12}, + {81.9, 60.7, 12}, + {83.2, 59.7, 12}, + }, + [199] = { + {16.8, 76.8, 12}, + {53.7, 43.9, 40}, + {51.4, 42.2, 40}, + {61.3, 40.2, 40}, + {48.4, 40, 40}, + {60, 39.8, 40}, + {54.2, 39.8, 40}, + {57.1, 39.7, 40}, + {49.4, 37.2, 40}, + {48.9, 35.9, 40}, + {52.7, 35.7, 40}, + {62, 35.1, 40}, + {43, 33.7, 40}, + {59.8, 32.3, 40}, + {38.1, 32.1, 40}, + {48.4, 30.2, 40}, + {61, 29.3, 40}, + {46, 29, 40}, + {60.9, 28, 40}, + {62.8, 27.8, 40}, + {48.6, 27.8, 40}, + {51.9, 27.7, 40}, + {36.3, 26.5, 40}, + {42.9, 25.1, 40}, + {49.5, 24.7, 40}, + {56.2, 24.6, 40}, + {60.1, 24.2, 40}, + {44.6, 20.1, 40}, + {38.2, 19.9, 40}, + {43.6, 18.8, 40}, + {52.4, 18.7, 40}, + {47.8, 18.2, 40}, + {55.2, 17.6, 40}, + {50.5, 16.5, 40}, + {41.9, 16.5, 40}, + {59, 13.6, 40}, + {16.6, 77.1, 12}, + {61.8, 35.3, 40}, + {48.8, 30.9, 40}, + {45.8, 29.4, 40}, + {48.4, 27.8, 40}, + {52.4, 27.7, 40}, + {50.6, 16.3, 40}, + {58.8, 13.9, 40}, + }, + [285] = { + {52, 68.7, 12}, + {55.8, 67.7, 12}, + {50.3, 67.6, 12}, + {56.8, 67.4, 12}, + {56.9, 67.4, 12}, + {50.7, 67.1, 12}, + {51.2, 67.1, 12}, + {54.6, 66.9, 12}, + {50.2, 66.9, 12}, + {56.9, 66.9, 12}, + {54.9, 66.8, 12}, + {54.3, 66.8, 12}, + {55.4, 66.8, 12}, + {56, 66.8, 12}, + {54.4, 66.8, 12}, + {53.7, 66.8, 12}, + {55.6, 66.7, 12}, + {56.5, 66.7, 12}, + {54.5, 66.7, 12}, + {56.6, 66.5, 12}, + {55, 66.4, 12}, + {49.8, 66.4, 12}, + {54, 66.4, 12}, + {54.4, 66.3, 12}, + {55.4, 66.3, 12}, + {56.7, 66.3, 12}, + {50.6, 66.2, 12}, + {50.4, 66.2, 12}, + {55.3, 66.2, 12}, + {55.2, 66.1, 12}, + {56.7, 65.9, 12}, + {53.8, 65.5, 12}, + {55, 65.2, 12}, + {55.9, 67.6, 12}, + {57, 67.3, 12}, + {50.2, 66.8, 12}, + {54.9, 66.7, 12}, + {56.4, 66.7, 12}, + {54.6, 66.3, 12}, + {55.3, 66.3, 12}, + {54.8, 65.3, 12}, + {52.7, 65.2, 12}, + }, + [327] = { + {41.7, 78, 12}, + }, + [330] = { + {69.7, 79.6, 12}, + {69.7, 79.3, 12}, + }, + [390] = { + {69.7, 79.4, 12}, + {69.6, 79.1, 12}, + {69.7, 79.2, 12}, + }, + [471] = { + {62.1, 48.1, 12}, + }, + [473] = { + {71.1, 80.6, 12}, + }, + [474] = { + {50.7, 83.2, 12}, + {89.4, 79.7, 12}, + {29, 62.6, 12}, + {28.9, 62.2, 12}, + {29.7, 61, 12}, + {29.1, 60.9, 12}, + {52.6, 58.8, 12}, + {29.5, 58.2, 12}, + {72.5, 56.4, 12}, + {74, 55.6, 12}, + {72.3, 55, 12}, + {73.1, 54.2, 12}, + {74.5, 54, 12}, + {76.7, 53.3, 12}, + {73.4, 52.5, 12}, + {74.6, 52.4, 12}, + {74.9, 52.1, 12}, + {71.7, 52, 12}, + {74.1, 51.8, 12}, + {77.2, 51.7, 12}, + {73.6, 51.2, 12}, + {74.2, 50.9, 12}, + {73.4, 49.8, 12}, + {71.7, 49.7, 12}, + {74.5, 49.3, 12}, + {76.8, 48.6, 12}, + {76, 47.4, 12}, + {74.4, 47, 12}, + {72.6, 46.4, 12}, + {76.6, 53.4, 12}, + }, + [475] = { + {37.9, 87, 12}, + {39, 85.1, 12}, + {37.6, 84.9, 12}, + {38.3, 84.4, 12}, + {39.9, 84.1, 12}, + {36.6, 84, 12}, + {38, 83.9, 12}, + {38.7, 83.8, 12}, + {37.4, 83.4, 12}, + {39.3, 83.2, 12}, + {39.7, 82.9, 12}, + {39.1, 82.6, 12}, + {38.1, 82.6, 12}, + {40.3, 82.5, 12}, + {36.1, 82.5, 12}, + {38.5, 82.3, 12}, + {39.6, 82.1, 12}, + {38.8, 81.8, 12}, + {38, 81.6, 12}, + {40.6, 81.6, 12}, + {38.9, 81.5, 12}, + {38.5, 81.5, 12}, + {39, 81.3, 12}, + {40.5, 81.2, 12}, + {40.9, 81.1, 12}, + {39, 80.7, 12}, + {37.3, 80.6, 12}, + {39.4, 80.4, 12}, + {41.8, 79.7, 12}, + {41.3, 79.5, 12}, + {36.2, 79.1, 12}, + {37.6, 78.9, 12}, + {38.7, 78.6, 12}, + {40.7, 78.3, 12}, + {41.1, 78.3, 12}, + {40.2, 77.5, 12}, + {41.9, 81.4, 12}, + {41.2, 80.4, 12}, + {41.1, 80.3, 12}, + {41.6, 80.1, 12}, + {41.3, 79.2, 12}, + {41.7, 78.7, 12}, + {40.3, 78.2, 12}, + {40.8, 77.4, 12}, + {37.8, 87.1, 12}, + {39.1, 85, 12}, + {37.7, 84.9, 12}, + {39.8, 84.2, 12}, + {38.6, 84.1, 12}, + {36.7, 84, 12}, + {38, 83.8, 12}, + {37.4, 83.5, 12}, + {36, 82.7, 12}, + {39, 81.4, 12}, + {40.8, 81.1, 12}, + {41.3, 80.4, 12}, + {37.6, 79, 12}, + {41.1, 78.2, 12}, + }, + [476] = { + {63.5, 58.3, 12}, + {64.7, 56.9, 12}, + {64.5, 56.7, 12}, + {64.5, 56.6, 12}, + {61, 56.5, 12}, + {62.2, 55.7, 12}, + {61.4, 51.7, 12}, + {60.8, 49.6, 12}, + {61.2, 51.6, 12}, + {61.1, 49.3, 12}, + }, + [478] = { + {26.3, 95.2, 12}, + {27.7, 94.5, 12}, + {24.5, 94.1, 12}, + {23.6, 94.1, 12}, + {25.4, 94, 12}, + {24.3, 93.8, 12}, + {28.6, 93, 12}, + {24.8, 92.9, 12}, + {26, 92.7, 12}, + {26.1, 92.1, 12}, + {26, 92.1, 12}, + {26.2, 90.3, 12}, + {25.5, 90.1, 12}, + {27.5, 89.6, 12}, + {25.1, 89.2, 12}, + {25.3, 89.1, 12}, + {27.8, 88.4, 12}, + {28.4, 88.3, 12}, + {25.5, 88.2, 12}, + {28, 88.2, 12}, + {28.6, 87.3, 12}, + {25.1, 87, 12}, + {26.3, 86.8, 12}, + {26.5, 86.8, 12}, + {27.4, 86.2, 12}, + {27.6, 86.1, 12}, + {26.3, 85, 12}, + {27.4, 82.5, 12}, + {26.4, 81.4, 12}, + {28.3, 81.2, 12}, + {27.4, 79.7, 12}, + {68.6, 54.8, 12}, + {68.2, 50.9, 12}, + {68.2, 48.9, 12}, + {67.8, 45.7, 12}, + {68.9, 45.2, 12}, + {68.1, 45.1, 12}, + {67.8, 44.9, 12}, + {66.7, 43.8, 12}, + {68.8, 43.2, 12}, + {67.7, 42.3, 12}, + {65.7, 42.1, 12}, + {66.7, 41.3, 12}, + {66, 41, 12}, + {66.7, 40.9, 12}, + {66.9, 40.6, 12}, + {69.1, 40.4, 12}, + {66.6, 40.3, 12}, + {76.8, 40.2, 12}, + {67.8, 40.2, 12}, + {68, 39.8, 12}, + {69.7, 39.3, 12}, + {67.6, 39.2, 12}, + {68.9, 38.8, 12}, + {68.2, 38.7, 12}, + {69.6, 38.7, 12}, + {69.2, 38.4, 12}, + {72.9, 38.3, 12}, + {65.7, 30.7, 40}, + {66.4, 30.4, 40}, + {67.3, 25.9, 40}, + {67.4, 25.8, 40}, + {67.2, 23.8, 40}, + {27.8, 88.1, 12}, + {26.5, 95.4, 12}, + {25.4, 94.2, 12}, + {23.7, 94.1, 12}, + {27.5, 89.5, 12}, + {25.4, 88.6, 12}, + {28.5, 88.2, 12}, + {27.3, 82.4, 12}, + {28, 81.3, 12}, + {27.5, 79.9, 12}, + {65.9, 30.7, 40}, + }, + [480] = { + {53.4, 25.6, 40}, + {54.4, 25.4, 40}, + {53.9, 25.1, 40}, + {55, 25.1, 40}, + {53.4, 24.9, 40}, + {55.3, 24.6, 40}, + {54.4, 24.4, 40}, + {57.1, 21.2, 40}, + {56.7, 20.9, 40}, + {57.5, 20.7, 40}, + {57.1, 20.1, 40}, + {57.5, 19.8, 40}, + {58.2, 19.1, 40}, + {57.5, 18.1, 40}, + {58.9, 18.1, 40}, + {58.2, 16.9, 40}, + {54.4, 25.6, 40}, + {53.4, 25, 40}, + {56.6, 21, 40}, + {57.5, 20.8, 40}, + {57.4, 19.8, 40}, + {58.2, 19, 40}, + {59.1, 18.2, 40}, + {58.2, 16.6, 40}, + }, + [481] = { + {56.8, 19.8, 40}, + {56.5, 19.3, 40}, + {56.8, 19.2, 40}, + {56.1, 19, 40}, + {56.7, 18.9, 40}, + {56.7, 19.8, 40}, + {56.6, 19.3, 40}, + {56.1, 19.2, 40}, + }, + [524] = { + {45, 89.6, 12}, + {45.6, 88, 12}, + {44.6, 87.2, 12}, + {54.9, 85.9, 12}, + {53.6, 85.4, 12}, + {46.6, 85.1, 12}, + {52.7, 84.4, 12}, + {56.4, 83.9, 12}, + {44.5, 83.8, 12}, + {54.4, 83.7, 12}, + {71.4, 83.6, 12}, + {45.9, 82.9, 12}, + {72.9, 82.7, 12}, + {70.7, 82.6, 12}, + {66.9, 82.5, 12}, + {57.2, 82.4, 12}, + {53.2, 82.4, 12}, + {43, 82, 12}, + {74.4, 81.9, 12}, + {52.6, 81.5, 12}, + {60.5, 81.4, 12}, + {65.7, 81.3, 12}, + {46.6, 81, 12}, + {44, 81, 12}, + {48.3, 80.9, 12}, + {54.7, 80.9, 12}, + {73.5, 80.8, 12}, + {58, 80.8, 12}, + {64, 80.6, 12}, + {46.4, 80.2, 12}, + {74.4, 80.1, 12}, + {53.4, 79.9, 12}, + {66.9, 79.9, 12}, + {54.9, 79.9, 12}, + {62.9, 79.8, 12}, + {60.6, 79.7, 12}, + {72.6, 79.7, 12}, + {50.1, 79.7, 12}, + {61, 79.7, 12}, + {64.6, 79.6, 12}, + {47.2, 79.4, 12}, + {44.1, 79.3, 12}, + {44.5, 79.2, 12}, + {50.1, 78.7, 12}, + {47.9, 78.6, 12}, + {46.6, 78.6, 12}, + {73.8, 78.5, 12}, + {64.1, 78.4, 12}, + {61.7, 78.3, 12}, + {61.6, 78.3, 12}, + {60.3, 78.2, 12}, + {60.2, 78.1, 12}, + {52.1, 78, 12}, + {54.2, 77.9, 12}, + {43.3, 77.9, 12}, + {65.8, 77.7, 12}, + {61, 77.2, 12}, + {67.3, 76.9, 12}, + {65, 76.7, 12}, + {55.5, 76.6, 12}, + {59.1, 76.5, 12}, + {73, 76.5, 12}, + {53.4, 76.5, 12}, + {57.2, 76.4, 12}, + {62.9, 76.2, 12}, + {49, 76.1, 12}, + {48, 76, 12}, + {63.9, 75.8, 12}, + {52.5, 75.8, 12}, + {47.3, 75.8, 12}, + {49.5, 75.7, 12}, + {65.5, 75.6, 12}, + {69.5, 75.5, 12}, + {62.1, 75.4, 12}, + {60.1, 75.4, 12}, + {54.6, 75.2, 12}, + {71.8, 75.2, 12}, + {56.4, 75.2, 12}, + {58.6, 74.8, 12}, + {58.5, 74.7, 12}, + {51.9, 74.7, 12}, + {59.3, 74.5, 12}, + {59.1, 74.3, 12}, + {53.3, 73.9, 12}, + {55.6, 73.8, 12}, + {57.1, 73.8, 12}, + {70.8, 73.7, 12}, + {58.4, 72.8, 12}, + {69.7, 72.6, 12}, + {58.3, 72.4, 12}, + {72.7, 79.6, 12}, + {53.1, 74, 12}, + }, + [583] = { + {34.4, 55.8, 12}, + }, + [732] = { + {87.8, 9.5, 10}, + {82.9, 87.2, 12}, + {81.1, 86.9, 12}, + {84.8, 86.6, 12}, + {76.9, 86.4, 12}, + {76, 86, 12}, + {77.7, 85.9, 12}, + {77.1, 85.8, 12}, + {80, 85.5, 12}, + {79.5, 85.3, 12}, + {77.2, 85, 12}, + {76.4, 84.3, 12}, + {77, 84.2, 12}, + {88.5, 84.2, 12}, + {78.5, 84, 12}, + {75.9, 83.7, 12}, + {76.9, 82.7, 12}, + {89.9, 82.4, 12}, + {91.6, 82.3, 12}, + {78.6, 82.1, 12}, + {76.5, 82, 12}, + {77.5, 80.9, 12}, + {76.5, 79.7, 12}, + {78.5, 60.2, 12}, + {76.6, 58.9, 12}, + {77.8, 58, 12}, + {78.1, 57.7, 12}, + {78.3, 57.5, 12}, + {77.7, 56.9, 12}, + {79.8, 56.8, 12}, + {79.2, 56.4, 12}, + {77.7, 56.3, 12}, + {78.5, 55.8, 12}, + {79.7, 55, 12}, + {80.4, 53.3, 12}, + {79.4, 46.2, 12}, + {78.7, 45.9, 12}, + {78.1, 45.3, 12}, + {78.7, 45.1, 12}, + {78.7, 44.5, 12}, + {77.6, 44.1, 12}, + {79.3, 58.1, 12}, + {78.3, 58, 12}, + {78.3, 56.7, 12}, + {79.3, 55.2, 12}, + {76.3, 84.4, 12}, + {78.2, 57.9, 12}, + {79.4, 55.3, 12}, + }, + [735] = { + {53.1, 70, 12}, + {55.3, 69.6, 12}, + {57.2, 69.5, 12}, + {51.5, 69.5, 12}, + {52, 68.7, 12}, + {52.9, 68, 12}, + {53, 67.9, 12}, + {58.3, 67.9, 12}, + {50.5, 66.3, 12}, + {52, 66.2, 12}, + {58.2, 65.2, 12}, + {51.5, 65.2, 12}, + {52, 64.5, 12}, + {57.3, 63.7, 12}, + {53.3, 63.5, 12}, + {55.5, 63.2, 12}, + {54.2, 62.7, 12}, + {50.5, 62.3, 12}, + {57.1, 62.3, 12}, + {55.2, 69.4, 12}, + {57.7, 67.4, 12}, + {58.3, 65.2, 12}, + {52.8, 65.2, 12}, + {51.6, 65.1, 12}, + {52.5, 65.1, 12}, + {53.6, 63.6, 12}, + {54.2, 62.9, 12}, + }, + [822] = { + {25.5, 90.7, 12}, + {38.3, 89.4, 12}, + {36.3, 89.2, 12}, + {45.9, 85.1, 12}, + {53.5, 84.7, 12}, + {83.5, 84.7, 12}, + {24.5, 83.4, 12}, + {79.8, 83.3, 12}, + {49.2, 82.8, 12}, + {28.8, 81.6, 12}, + {43.3, 81.2, 12}, + {87.8, 81.1, 12}, + {72.9, 80.9, 12}, + {47, 79.7, 12}, + {60.2, 79.5, 12}, + {63.2, 79.2, 12}, + {78.1, 79.2, 12}, + {60.8, 79.1, 12}, + {66.1, 78.8, 12}, + {86.4, 78.5, 12}, + {45, 78.1, 12}, + {88.9, 77.5, 12}, + {81.1, 77.2, 12}, + {63.1, 77.1, 12}, + {48.3, 76.8, 12}, + {77.2, 76.7, 12}, + {56.9, 75.9, 12}, + {49.2, 75.9, 12}, + {46.3, 75.8, 12}, + {22.4, 75.8, 12}, + {53.9, 75.5, 12}, + {70.6, 74.9, 12}, + {87.2, 69.3, 12}, + {75.8, 67.5, 12}, + {59.1, 66.9, 12}, + {74.6, 66.8, 12}, + {68.6, 66.3, 12}, + {62.9, 65.9, 12}, + {88.3, 65.7, 12}, + {86.5, 65.2, 12}, + {86.8, 64.5, 12}, + {68.9, 63.9, 12}, + {86.2, 63.2, 12}, + {64.7, 63, 12}, + {77.9, 61.7, 12}, + {78.7, 61.4, 12}, + {71.3, 61.3, 12}, + {82.2, 59.2, 12}, + {43.4, 81.2, 12}, + {63.3, 77, 12}, + {86.6, 65.2, 12}, + {23, 12.4, 406}, + {23.1, 11.6, 406}, + }, + [834] = { + {19.7, 84, 12}, + {17.6, 78.5, 12}, + {17.5, 75.5, 12}, + {16.8, 74, 12}, + {51.2, 46.1, 40}, + {50.5, 45, 40}, + {62.7, 42.3, 40}, + {35.4, 42.2, 40}, + {55.2, 40.8, 40}, + {61.1, 39.7, 40}, + {30.8, 39.3, 40}, + {53.3, 37.9, 40}, + {60.2, 37.7, 40}, + {62.7, 36.5, 40}, + {41.5, 35, 40}, + {41.9, 32, 40}, + {46.2, 31.1, 40}, + {59.4, 30.5, 40}, + {55.2, 29.3, 40}, + {39.9, 29.3, 40}, + {50.7, 29.2, 40}, + {43.4, 29.1, 40}, + {61.7, 29.1, 40}, + {36, 28.9, 40}, + {58.8, 28.1, 40}, + {40.9, 27.7, 40}, + {54.6, 27.7, 40}, + {36.9, 27.1, 40}, + {56.2, 26.8, 40}, + {47.5, 26.5, 40}, + {41.2, 26.4, 40}, + {61.5, 26.3, 40}, + {36.4, 26, 40}, + {38.1, 23.6, 40}, + {58.8, 22.5, 40}, + {56, 22.4, 40}, + {53.4, 22.1, 40}, + {39.6, 20.8, 40}, + {61.8, 20.8, 40}, + {41.6, 19, 40}, + {57, 18.2, 40}, + {58, 16.7, 40}, + {47.4, 15.3, 40}, + {59.8, 15.3, 40}, + {58.4, 14.7, 40}, + {59.6, 12.3, 40}, + {59, 10.8, 40}, + {61.9, 32.2, 40}, + {51.3, 46.1, 40}, + {50.5, 45.1, 40}, + {55, 40.7, 40}, + {53.3, 38.2, 40}, + {62.7, 36.4, 40}, + {55.4, 30.2, 40}, + {55.1, 29.5, 40}, + {38.2, 23.8, 40}, + {53.3, 22.5, 40}, + }, + [880] = { + {71, 80.6, 12}, + }, + [881] = { + {71, 80.8, 12}, + }, + [1115] = { + {67.9, 60.7, 1}, + {67.9, 60.5, 1}, + {67.5, 60.2, 1}, + {68.9, 59.8, 1}, + {68.7, 59.7, 1}, + {67.1, 59.7, 1}, + {67.4, 59.7, 1}, + {67.5, 59.5, 1}, + {68, 59.4, 1}, + {68.9, 59.3, 1}, + {70.3, 59.3, 1}, + {70.5, 58.9, 1}, + {69.5, 58.8, 1}, + {67.2, 58.6, 1}, + {70.4, 58.5, 1}, + {70.5, 58.4, 1}, + {69.4, 58.3, 1}, + {70, 58.3, 1}, + {68.7, 58.1, 1}, + {69.8, 57.7, 1}, + {70.2, 57.1, 1}, + {70.5, 57.1, 1}, + {69.7, 57.1, 1}, + {70.7, 56.6, 1}, + {70.8, 55.9, 1}, + {71.2, 55.3, 1}, + {71, 55.1, 1}, + {70.8, 54, 1}, + {71.1, 53.9, 1}, + {72.5, 53.5, 1}, + {73.2, 53.3, 1}, + {70.3, 53.3, 1}, + {72.4, 52.9, 1}, + {70.7, 52.8, 1}, + {72.9, 52.7, 1}, + {70.1, 52.7, 1}, + {70.6, 52.7, 1}, + {71.2, 52.6, 1}, + {72.6, 52.4, 1}, + {70.8, 52.3, 1}, + {70.4, 52.3, 1}, + {72.2, 52.3, 1}, + {70.3, 52.2, 1}, + {72, 52.1, 1}, + {73.2, 52, 1}, + {72.6, 51.9, 1}, + {71.6, 51.8, 1}, + {71.4, 51.6, 1}, + {71.5, 51.6, 1}, + {72.6, 51.4, 1}, + {71.6, 51.2, 1}, + {72.3, 51.2, 1}, + {72.5, 51, 1}, + {72.1, 50.8, 1}, + {71.2, 50.6, 1}, + {71.6, 50.3, 1}, + {71.5, 50.3, 1}, + {72, 50, 1}, + {70.5, 58.5, 1}, + {69.8, 58.3, 1}, + {68.6, 58.1, 1}, + {70, 57.8, 1}, + {70.3, 57.1, 1}, + {70.9, 56, 1}, + {71, 55.2, 1}, + {70.8, 53.9, 1}, + {70.7, 52.6, 1}, + {72.2, 52.2, 1}, + {72.7, 51.9, 1}, + {72.4, 51, 1}, + {72.3, 51, 1}, + }, + [1116] = { + {77.3, 59.8, 1}, + {75.8, 58.8, 1}, + {77.2, 58.3, 1}, + {75.7, 57.7, 1}, + {74.9, 56.6, 1}, + {74.7, 56.4, 1}, + {74.5, 56.2, 1}, + {72.6, 55.8, 1}, + {73.4, 55.1, 1}, + {72.1, 55, 1}, + {71.3, 54.3, 1}, + {72.6, 53.9, 1}, + {71, 53.3, 1}, + {70.8, 53, 1}, + {73.5, 52.8, 1}, + {70.3, 52.3, 1}, + {73.9, 51.5, 1}, + {73.4, 51.3, 1}, + {75.7, 61.8, 1}, + {74.8, 61.7, 1}, + {74, 61.2, 1}, + {77.1, 60.9, 1}, + {74.7, 60.8, 1}, + {78.1, 59.6, 1}, + {78, 58.8, 1}, + {79.6, 58.4, 1}, + {76.1, 58.4, 1}, + {80.4, 56.8, 1}, + {75.5, 56.5, 1}, + {79.6, 55.2, 1}, + {79.6, 54.6, 1}, + {79.6, 54.1, 1}, + {70.8, 53.1, 1}, + {0.9, 80.6, 5602}, + {1.6, 79.2, 5602}, + {0.9, 77.7, 5602}, + {0.9, 77.1, 5602}, + {0.9, 76.7, 5602}, + }, + [1117] = { + {75.7, 61.8, 1}, + {77, 60.8, 1}, + {76.2, 60.3, 1}, + {78.4, 60.1, 1}, + {78.1, 59.6, 1}, + {78.9, 59.1, 1}, + {78, 59, 1}, + {79.8, 58, 1}, + {74.7, 56.3, 1}, + {75, 56.2, 1}, + {74.7, 55.9, 1}, + {71.2, 55.5, 1}, + {70.6, 55.3, 1}, + {79.6, 55.1, 1}, + {70.6, 54.6, 1}, + {71, 54.6, 1}, + {79.6, 54.6, 1}, + {79.6, 54, 1}, + {72.8, 54, 1}, + {71, 53.3, 1}, + {70.7, 53.3, 1}, + {70.4, 53.3, 1}, + {70.2, 52.7, 1}, + {71.8, 52.4, 1}, + {71.7, 52.2, 1}, + {73, 52.1, 1}, + {72.4, 51.5, 1}, + {71.9, 50.5, 1}, + {72, 50.2, 1}, + {70.5, 57.1, 1}, + {70.7, 56.6, 1}, + {70.8, 55.9, 1}, + {70.8, 54, 1}, + {72.4, 52.9, 1}, + {70.7, 52.8, 1}, + {70.1, 52.7, 1}, + {70.6, 52.7, 1}, + {71.2, 52.6, 1}, + {70.8, 52.3, 1}, + {70.4, 52.3, 1}, + {70.3, 52.2, 1}, + {72, 52.1, 1}, + {72.6, 51.4, 1}, + {71.6, 51.2, 1}, + {72.3, 51.2, 1}, + {72.1, 50.8, 1}, + {71.6, 50.3, 1}, + {72, 50, 1}, + {71.2, 55.6, 1}, + {72.9, 54.1, 1}, + {71.1, 52.7, 1}, + {70.6, 52.6, 1}, + {70.8, 52.4, 1}, + {70.5, 52.3, 1}, + {70.4, 52.2, 1}, + {72, 52.2, 1}, + {72.9, 52.2, 1}, + {71.7, 51.3, 1}, + {72.2, 50.7, 1}, + {71.9, 50.3, 1}, + {72, 49.9, 1}, + }, + [1120] = { + {25.9, 52.9, 1}, + {26.7, 52.1, 1}, + {26.1, 51.5, 1}, + {26.7, 51.4, 1}, + {26.1, 51.3, 1}, + {25.4, 51.1, 1}, + {26.1, 50, 1}, + {66.6, 41.3, 1}, + {66.5, 41.2, 1}, + {70.6, 39.3, 1}, + {71, 37.9, 1}, + {26.2, 49.9, 1}, + {66.5, 41.1, 1}, + }, + [1121] = { + {22.9, 54.5, 1}, + {23.2, 52, 1}, + {25.4, 52, 1}, + {23.6, 51.4, 1}, + {26.2, 51.3, 1}, + {21.6, 50.8, 1}, + {26.1, 50.6, 1}, + {22.6, 50.5, 1}, + {25.4, 50, 1}, + {41.3, 44.2, 1}, + {66.4, 41.3, 1}, + {66.4, 41.1, 1}, + {70.5, 39.3, 1}, + {71, 38.1, 1}, + {41.6, 36.6, 1}, + {41.9, 35.8, 1}, + {39, 35.2, 1}, + {40.7, 35.1, 1}, + {23.1, 52, 1}, + {25.4, 50.1, 1}, + }, + [1122] = { + {21.9, 55, 1}, + {23.2, 54.8, 1}, + {21.6, 54.8, 1}, + {23.5, 53.9, 1}, + {23.8, 53.6, 1}, + {21.4, 53, 1}, + {21.7, 52.9, 1}, + {24, 52.3, 1}, + {22.8, 52, 1}, + {22, 51.3, 1}, + {21.6, 50.6, 1}, + {21.4, 53.1, 1}, + {23.9, 52.2, 1}, + }, + [1123] = { + {22.6, 54.6, 1}, + {21.3, 54.5, 1}, + {23.9, 53.2, 1}, + {26, 52.2, 1}, + {22.4, 51.8, 1}, + {21.1, 51.7, 1}, + {26, 51.5, 1}, + {26.1, 51.5, 1}, + {23.4, 51.4, 1}, + {21.2, 51, 1}, + {24.6, 50.9, 1}, + {25.4, 50.9, 1}, + {23.1, 50.9, 1}, + {23.6, 50.9, 1}, + {24.3, 50.9, 1}, + {22.1, 50.2, 1}, + {40.9, 43.8, 1}, + {39.3, 35.9, 1}, + {21.1, 51.6, 1}, + {23.2, 50.9, 1}, + {25.5, 50.9, 1}, + {22, 50.1, 1}, + }, + [1124] = { + {22.8, 53.8, 1}, + {21.2, 53.6, 1}, + {23.2, 52.6, 1}, + {22.1, 52.3, 1}, + {21.6, 52.1, 1}, + {26, 51.1, 1}, + {22.4, 50.2, 1}, + {66.5, 41.1, 1}, + {70.6, 39.2, 1}, + {70.9, 37.8, 1}, + {21.1, 53.6, 1}, + {23.3, 52.6, 1}, + {21.6, 52.2, 1}, + }, + [1126] = { + {47.3, 63.9, 1}, + {49.4, 62.6, 1}, + {50.4, 60, 1}, + {49.7, 58.6, 1}, + {48, 56.7, 1}, + {48.2, 56.6, 1}, + {34.3, 55.6, 1}, + {59.8, 55.3, 1}, + {62.4, 55.1, 1}, + {49.2, 54.8, 1}, + {35.6, 54.7, 1}, + {58.7, 54.1, 1}, + {36.3, 54.1, 1}, + {64.2, 53.9, 1}, + {37.3, 52.7, 1}, + {56.8, 52.7, 1}, + {36.3, 52.2, 1}, + {50.5, 52.1, 1}, + {38.1, 51.1, 1}, + {37, 51.1, 1}, + {38.1, 51, 1}, + {67, 50.3, 1}, + {65.3, 50.2, 1}, + {38.7, 48.6, 1}, + {39.1, 48.1, 1}, + {55.9, 47.8, 1}, + {56.1, 47.8, 1}, + {48.5, 47.3, 1}, + {48.1, 47.2, 1}, + {41.4, 47.2, 1}, + {48.1, 47.1, 1}, + {39.6, 47, 1}, + {38.5, 46.6, 1}, + {45.2, 45.3, 1}, + {50.4, 43.2, 1}, + {45.4, 41.6, 1}, + {45.1, 41.6, 1}, + {41.7, 39.5, 1}, + {43.5, 38, 1}, + {43.5, 34.3, 1}, + {41.2, 30.5, 1}, + {43.9, 29.7, 1}, + {50.5, 60, 1}, + {49.5, 58.4, 1}, + {48.4, 56.8, 1}, + {36.4, 54.2, 1}, + {56.2, 47.7, 1}, + {41.2, 47.2, 1}, + {48.1, 47, 1}, + {39.6, 46.9, 1}, + {38.6, 46.7, 1}, + {45.4, 41.7, 1}, + }, + [1127] = { + {70.7, 61.6, 1}, + {74.7, 61.3, 1}, + {72.4, 61, 1}, + {63.9, 60.5, 1}, + {60.9, 60.3, 1}, + {59.6, 60.1, 1}, + {56.6, 60, 1}, + {58.5, 59.7, 1}, + {63.1, 59.2, 1}, + {55.8, 59, 1}, + {58.7, 58.9, 1}, + {59.9, 58.6, 1}, + {61.4, 58.4, 1}, + {53.4, 58.2, 1}, + {59, 57.9, 1}, + {55.5, 57.4, 1}, + {55.6, 57.4, 1}, + {79.2, 57.2, 1}, + {61.3, 56.7, 1}, + {57.3, 56.7, 1}, + {62.8, 56.2, 1}, + {55.7, 55.8, 1}, + {71.5, 55.6, 1}, + {26.6, 55.1, 1}, + {55.3, 54.3, 1}, + {56.7, 54.3, 1}, + {27.4, 54, 1}, + {60.8, 53.9, 1}, + {28.3, 53.3, 1}, + {66.1, 53.3, 1}, + {66.2, 52.7, 1}, + {73.5, 52.1, 1}, + {30.1, 52, 1}, + {70.5, 52, 1}, + {68.7, 51.9, 1}, + {37.7, 51.9, 1}, + {67.4, 51.7, 1}, + {70.8, 51.6, 1}, + {58.2, 51.3, 1}, + {57.9, 50.4, 1}, + {69.7, 50.2, 1}, + {59.2, 50.1, 1}, + {32, 49.8, 1}, + {29.6, 49.7, 1}, + {66.8, 49.3, 1}, + {32.1, 48.8, 1}, + {73.4, 48.7, 1}, + {75.8, 48.2, 1}, + {33.5, 47.9, 1}, + {27.7, 47.4, 1}, + {40.5, 47.2, 1}, + {32.2, 47.1, 1}, + {25.9, 46.4, 1}, + {80.2, 46.3, 1}, + {37.8, 46, 1}, + {27.5, 45.8, 1}, + {26.2, 44.7, 1}, + {28.4, 44.3, 1}, + {79.8, 43.3, 1}, + {26.7, 42.9, 1}, + {28, 42.7, 1}, + {45.3, 42.6, 1}, + {29.6, 42.5, 1}, + {45.2, 42.2, 1}, + {29.2, 40, 1}, + {27.5, 39.3, 1}, + {42.4, 38.9, 1}, + {31.7, 38.7, 1}, + {30.1, 38.4, 1}, + {33.1, 37.5, 1}, + {37.5, 37.4, 1}, + {35.6, 36.8, 1}, + {31, 35.7, 1}, + {37.4, 35.6, 1}, + {81.5, 35.6, 1}, + {34.1, 35.2, 1}, + {35.5, 34.9, 1}, + {37, 34.3, 1}, + {32.4, 34.3, 1}, + {39.5, 34, 1}, + {44.2, 32, 1}, + {34.6, 31.5, 1}, + {36.8, 31.2, 1}, + {33.9, 31.1, 1}, + {40.1, 30.5, 1}, + {42.1, 30, 1}, + {43.6, 29.6, 1}, + {27.5, 53.9, 1}, + {30.1, 51.9, 1}, + {68.7, 51.7, 1}, + {33.5, 48, 1}, + {27.5, 45.7, 1}, + {32.4, 34.4, 1}, + {0.5, 79.6, 5602}, + {1.4, 69.5, 5602}, + {1.1, 66.8, 5602}, + {2.6, 59.8, 5602}, + }, + [1131] = { + {50.7, 52.5, 1}, + {51.5, 49.6, 1}, + {52.1, 48.7, 1}, + {45.8, 47.2, 1}, + {43.2, 47.1, 1}, + {45.9, 47, 1}, + {51.6, 46.4, 1}, + {51, 45, 1}, + {49, 44.6, 1}, + {48.9, 44.6, 1}, + {33.8, 43.7, 1}, + {33.5, 43.6, 1}, + {45.1, 43.3, 1}, + {34, 42.8, 1}, + {44.9, 42.4, 1}, + {45.7, 42.3, 1}, + {34.6, 42.2, 1}, + {34, 42.2, 1}, + {45.1, 41.8, 1}, + {44.9, 41.4, 1}, + {34.6, 41.3, 1}, + {34, 41.3, 1}, + {33.9, 41, 1}, + {46, 47.2, 1}, + {34.5, 41.3, 1}, + }, + [1133] = { + {34.2, 41.9, 1}, + {34.2, 41.8, 1}, + }, + [1135] = { + {41.9, 54, 1}, + {42.5, 53.7, 1}, + {42.2, 53.3, 1}, + {40.2, 53.3, 1}, + {41.9, 52.7, 1}, + {42.2, 52.7, 1}, + {42.4, 52, 1}, + {42.7, 51.8, 1}, + {41.9, 51.4, 1}, + {41.4, 51.1, 1}, + {42.6, 50.4, 1}, + {43, 50, 1}, + {42.8, 49.9, 1}, + {41.1, 49.8, 1}, + {43.6, 49.6, 1}, + {41.2, 49.3, 1}, + {42.6, 49.3, 1}, + {43.3, 49.2, 1}, + {40.9, 49.2, 1}, + {42.4, 49.2, 1}, + {43.2, 48.9, 1}, + {39.1, 48.8, 1}, + {39.7, 48.8, 1}, + {42.2, 48.5, 1}, + {42, 48.4, 1}, + {41.6, 48.4, 1}, + {38.9, 48.3, 1}, + {39.5, 48.1, 1}, + {40.3, 47.8, 1}, + {39.7, 47.7, 1}, + {42.1, 47.6, 1}, + {39.3, 47.4, 1}, + {39.9, 47.2, 1}, + {41.1, 47.2, 1}, + {42, 47, 1}, + {42.6, 47, 1}, + {40.5, 46.9, 1}, + {40.5, 46.6, 1}, + {39.2, 46.4, 1}, + {42.1, 46.3, 1}, + {42.2, 46.1, 1}, + {41.9, 46, 1}, + {41.3, 45.3, 1}, + {41.9, 45.2, 1}, + {42.2, 53.4, 1}, + {42.2, 52.6, 1}, + {42.3, 52, 1}, + {42.7, 51.9, 1}, + {42, 51.5, 1}, + {41.1, 50.6, 1}, + {41.3, 49.4, 1}, + {42.3, 49.2, 1}, + {41, 49.2, 1}, + {42.7, 49.2, 1}, + {41.6, 49, 1}, + {39.7, 48.7, 1}, + {38.9, 48.4, 1}, + {39.4, 48.1, 1}, + {42.1, 47.7, 1}, + {40.9, 47, 1}, + {42.7, 46.8, 1}, + {40.4, 46.5, 1}, + {39.2, 46.3, 1}, + {41.3, 45.4, 1}, + }, + [1138] = { + {44.6, 47.1, 1}, + {45.7, 46.7, 1}, + {43.3, 46.5, 1}, + {45.6, 46.2, 1}, + {45.9, 46.2, 1}, + {45.7, 45.9, 1}, + {51.3, 45.7, 1}, + {51.3, 45.4, 1}, + {44.4, 45.3, 1}, + {49.7, 44.5, 1}, + {45.7, 46, 1}, + }, + [1172] = { + {65.8, 91.6, 11}, + {27.5, 45.1, 38}, + {28.3, 44.8, 38}, + {27.7, 43.6, 38}, + {26.7, 41.5, 38}, + {27.4, 41.5, 38}, + {25.9, 34.7, 38}, + {23.8, 34.2, 38}, + {27.4, 32.4, 38}, + {25, 32.4, 38}, + {26.2, 32, 38}, + {26.1, 31.9, 38}, + {26.2, 31.5, 38}, + {23.8, 30.5, 38}, + {24.8, 30.2, 38}, + {24.7, 30, 38}, + {25.1, 29.8, 38}, + {24.7, 29.7, 38}, + {24.9, 29.6, 38}, + {37, 29.5, 38}, + {22.6, 28.8, 38}, + {25, 28.8, 38}, + {23.3, 28.5, 38}, + {38, 27.4, 38}, + {37, 27.1, 38}, + {26.1, 27, 38}, + {35.8, 26.8, 38}, + {23.7, 26.1, 38}, + {37.4, 25.9, 38}, + {38.4, 25.7, 38}, + {39.5, 25.1, 38}, + {36.4, 25, 38}, + {40.4, 23.3, 38}, + {38.6, 22.9, 38}, + {39.3, 20.9, 38}, + {38.3, 19.6, 38}, + {40.7, 19.5, 38}, + {36.6, 18.3, 38}, + {40, 18, 38}, + {37.1, 17.9, 38}, + {38.3, 17.9, 38}, + {34.5, 17.4, 38}, + {34.5, 17.1, 38}, + {38.4, 16.1, 38}, + {35.5, 15.5, 38}, + {35.8, 14.3, 38}, + {38.3, 14.3, 38}, + {33.4, 12.5, 38}, + {38.3, 12.4, 38}, + {36.6, 11.9, 38}, + {37.1, 10.6, 38}, + {38.9, 10.6, 38}, + {34.6, 10.4, 38}, + {40.5, 10, 38}, + {27.4, 44.6, 38}, + {27.3, 41.7, 38}, + {23.9, 34.4, 38}, + {25.1, 32.7, 38}, + {27.6, 32.1, 38}, + {26, 31.9, 38}, + {26.2, 31.8, 38}, + {24.7, 30.4, 38}, + {23.9, 30.2, 38}, + {25.4, 29.8, 38}, + {24.5, 29.5, 38}, + {25, 28.6, 38}, + {22.4, 28.5, 38}, + {36.9, 26.9, 38}, + {26.4, 26.9, 38}, + {39.8, 25.1, 38}, + {38.3, 19.7, 38}, + {36.9, 18.1, 38}, + {38.2, 17.8, 38}, + {34.6, 17.1, 38}, + {38.3, 16, 38}, + {36, 14.7, 38}, + {38.4, 14.3, 38}, + {33.5, 12.7, 38}, + {38, 12.6, 38}, + {37.3, 11, 38}, + {13, 67.2, 5602}, + {12.6, 67.1, 5602}, + {12.7, 66.6, 5602}, + {12.5, 65.6, 5602}, + {12.2, 65.5, 5602}, + {11.8, 62, 5602}, + {10.8, 61.8, 5602}, + {11.4, 61, 5602}, + {12.7, 60.7, 5602}, + {11.9, 60.6, 5602}, + {12, 60.5, 5602}, + {12, 60.3, 5602}, + {11.2, 59.8, 5602}, + {10.8, 59.7, 5602}, + {11.2, 59.6, 5602}, + {11.6, 59.5, 5602}, + {11.3, 59.4, 5602}, + {17.5, 59.3, 5602}, + {11.1, 59.3, 5602}, + {11.4, 58.9, 5602}, + {10, 58.8, 5602}, + {10.5, 58.8, 5602}, + {18, 58.2, 5602}, + {17.5, 58, 5602}, + {12.1, 58, 5602}, + {16.9, 57.9, 5602}, + {10.7, 57.6, 5602}, + {17.7, 57.5, 5602}, + {18.2, 57.4, 5602}, + {18.9, 57.1, 5602}, + {17.2, 57, 5602}, + {19.2, 56.2, 5602}, + {18.3, 56, 5602}, + {18.6, 54.9, 5602}, + {18.2, 54.3, 5602}, + {19.4, 54.2, 5602}, + {17.3, 53.6, 5602}, + {17.4, 53.5, 5602}, + {19, 53.4, 5602}, + {18.1, 53.3, 5602}, + {16.2, 53.1, 5602}, + {16.2, 52.9, 5602}, + {18.2, 52.4, 5602}, + {16.7, 52.1, 5602}, + {17, 51.7, 5602}, + {18.2, 51.5, 5602}, + {15.7, 50.7, 5602}, + {18, 50.6, 5602}, + {17.3, 50.3, 5602}, + {17.7, 49.8, 5602}, + {18.5, 49.6, 5602}, + {16.3, 49.5, 5602}, + {19.3, 49.3, 5602}, + }, + [1173] = { + {65.8, 91.6, 11}, + {27.5, 45.1, 38}, + {28.3, 44.8, 38}, + {27.7, 43.6, 38}, + {26.7, 41.5, 38}, + {27.4, 41.5, 38}, + {25.9, 34.7, 38}, + {23.8, 34.2, 38}, + {27.4, 32.4, 38}, + {25, 32.4, 38}, + {26.2, 32, 38}, + {26.1, 31.9, 38}, + {26.2, 31.5, 38}, + {23.8, 30.5, 38}, + {24.8, 30.2, 38}, + {24.7, 30, 38}, + {25.1, 29.8, 38}, + {24.7, 29.7, 38}, + {24.9, 29.6, 38}, + {37, 29.5, 38}, + {22.6, 28.8, 38}, + {25, 28.8, 38}, + {23.3, 28.5, 38}, + {38, 27.4, 38}, + {37, 27.1, 38}, + {26.1, 27, 38}, + {35.8, 26.8, 38}, + {23.7, 26.1, 38}, + {37.4, 25.9, 38}, + {38.4, 25.7, 38}, + {39.5, 25.1, 38}, + {36.4, 25, 38}, + {40.4, 23.3, 38}, + {38.6, 22.9, 38}, + {39.3, 20.9, 38}, + {38.3, 19.6, 38}, + {40.7, 19.5, 38}, + {36.6, 18.3, 38}, + {40, 18, 38}, + {37.1, 17.9, 38}, + {38.3, 17.9, 38}, + {34.5, 17.4, 38}, + {34.5, 17.1, 38}, + {38.4, 16.1, 38}, + {35.5, 15.5, 38}, + {35.8, 14.3, 38}, + {38.3, 14.3, 38}, + {33.4, 12.5, 38}, + {38.3, 12.4, 38}, + {36.6, 11.9, 38}, + {37.1, 10.6, 38}, + {38.9, 10.6, 38}, + {34.6, 10.4, 38}, + {40.5, 10, 38}, + {27.4, 44.6, 38}, + {27.3, 41.7, 38}, + {23.9, 34.4, 38}, + {25.1, 32.7, 38}, + {27.6, 32.1, 38}, + {26, 31.9, 38}, + {26.2, 31.8, 38}, + {24.7, 30.4, 38}, + {23.9, 30.2, 38}, + {25.4, 29.8, 38}, + {24.5, 29.5, 38}, + {25, 28.6, 38}, + {22.4, 28.5, 38}, + {36.9, 26.9, 38}, + {26.4, 26.9, 38}, + {39.8, 25.1, 38}, + {38.3, 19.7, 38}, + {36.9, 18.1, 38}, + {38.2, 17.8, 38}, + {34.6, 17.1, 38}, + {38.3, 16, 38}, + {36, 14.7, 38}, + {38.4, 14.3, 38}, + {33.5, 12.7, 38}, + {38, 12.6, 38}, + {37.3, 11, 38}, + {13, 67.2, 5602}, + {12.6, 67.1, 5602}, + {12.7, 66.6, 5602}, + {12.5, 65.6, 5602}, + {12.2, 65.5, 5602}, + {11.8, 62, 5602}, + {10.8, 61.8, 5602}, + {11.4, 61, 5602}, + {12.7, 60.7, 5602}, + {11.9, 60.6, 5602}, + {12, 60.5, 5602}, + {12, 60.3, 5602}, + {11.2, 59.8, 5602}, + {10.8, 59.7, 5602}, + {11.2, 59.6, 5602}, + {11.6, 59.5, 5602}, + {11.3, 59.4, 5602}, + {17.5, 59.3, 5602}, + {11.1, 59.3, 5602}, + {11.4, 58.9, 5602}, + {10, 58.8, 5602}, + {10.5, 58.8, 5602}, + {18, 58.2, 5602}, + {17.5, 58, 5602}, + {12.1, 58, 5602}, + {16.9, 57.9, 5602}, + {10.7, 57.6, 5602}, + {17.7, 57.5, 5602}, + {18.2, 57.4, 5602}, + {18.9, 57.1, 5602}, + {17.2, 57, 5602}, + {19.2, 56.2, 5602}, + {18.3, 56, 5602}, + {18.6, 54.9, 5602}, + {18.2, 54.3, 5602}, + {19.4, 54.2, 5602}, + {17.3, 53.6, 5602}, + {17.4, 53.5, 5602}, + {19, 53.4, 5602}, + {18.1, 53.3, 5602}, + {16.2, 53.1, 5602}, + {16.2, 52.9, 5602}, + {18.2, 52.4, 5602}, + {16.7, 52.1, 5602}, + {17, 51.7, 5602}, + {18.2, 51.5, 5602}, + {15.7, 50.7, 5602}, + {18, 50.6, 5602}, + {17.3, 50.3, 5602}, + {17.7, 49.8, 5602}, + {18.5, 49.6, 5602}, + {16.3, 49.5, 5602}, + {19.3, 49.3, 5602}, + }, + [1190] = { + {29.9, 46.8, 38}, + {37.2, 39.9, 38}, + {27.2, 39.1, 38}, + {37.4, 38.4, 38}, + {38.3, 37.9, 38}, + {38.5, 36.1, 38}, + {37.2, 36.1, 38}, + {34.7, 36.1, 38}, + {29.9, 36, 38}, + {32.9, 35.1, 38}, + {37, 34.5, 38}, + {38.3, 34.3, 38}, + {37.1, 34.3, 38}, + {35.5, 34, 38}, + {33.3, 33.4, 38}, + {29.1, 32.8, 38}, + {33.9, 31.8, 38}, + {34.3, 30.5, 38}, + {39.6, 28.8, 38}, + {27.7, 26.6, 38}, + {31.8, 24.1, 38}, + {28.6, 23.3, 38}, + {24.3, 21.4, 38}, + {30.6, 16.6, 38}, + {30.5, 15.4, 38}, + {25.3, 13.9, 38}, + {38.2, 37.9, 38}, + {29.9, 36.5, 38}, + {34.8, 36.4, 38}, + {37.2, 36.2, 38}, + {37.2, 34.4, 38}, + {38.2, 34.2, 38}, + {39.5, 29.2, 38}, + {28.6, 23.2, 38}, + {13.9, 68.2, 5602}, + {17.6, 64.7, 5602}, + {12.5, 64.3, 5602}, + {17.7, 63.9, 5602}, + {18.1, 63.7, 5602}, + {13.9, 62.9, 5602}, + {16.4, 62.9, 5602}, + {17.6, 62.8, 5602}, + {18.3, 62.7, 5602}, + {15.4, 62.2, 5602}, + {17.5, 61.9, 5602}, + {17.6, 61.8, 5602}, + {18.1, 61.8, 5602}, + {16.7, 61.6, 5602}, + {15.6, 61.3, 5602}, + {13.5, 61, 5602}, + {15.9, 60.5, 5602}, + {16.1, 59.8, 5602}, + {18.8, 59.2, 5602}, + {12.8, 57.9, 5602}, + {14.8, 56.6, 5602}, + {13.2, 56.1, 5602}, + {11, 55.2, 5602}, + {14.2, 52.7, 5602}, + {14.2, 52.1, 5602}, + {11.5, 51.3, 5602}, + }, + [1195] = { + {19.3, 83.5, 38}, + {39.5, 74.7, 38}, + {36.9, 73.9, 38}, + {39.5, 73, 38}, + {21, 71.8, 38}, + {28.6, 57, 38}, + {34.6, 54.2, 38}, + {35.7, 52.8, 38}, + {31, 52.3, 38}, + {28.8, 51.9, 38}, + {29.8, 50.6, 38}, + {28.7, 48.8, 38}, + {29.8, 47, 38}, + {30.5, 45.1, 38}, + {30, 44.6, 38}, + {27.7, 41.9, 38}, + {31.1, 41.6, 38}, + {33.9, 40.9, 38}, + {33.5, 40.5, 38}, + {25.3, 37.8, 38}, + {36, 34.4, 38}, + {32.3, 32.5, 38}, + {38.5, 31.6, 38}, + {36.6, 31.1, 38}, + {36.6, 30.8, 38}, + {31.1, 30.6, 38}, + {28.7, 30.6, 38}, + {32.8, 30.6, 38}, + {29.9, 22.6, 38}, + {27.5, 21.5, 38}, + {30, 19.8, 38}, + {29.7, 18.5, 38}, + {27.5, 18, 38}, + {32.1, 17.1, 38}, + {28.7, 16.8, 38}, + {34.7, 36.1, 38}, + {21.1, 71.6, 38}, + {34.3, 54.2, 38}, + {35.6, 52.8, 38}, + {31.1, 52.2, 38}, + {29.9, 50.7, 38}, + {28.6, 49.1, 38}, + {29.6, 46.8, 38}, + {33.6, 40.5, 38}, + {32.4, 32.6, 38}, + {28.7, 30.8, 38}, + {27.7, 21.3, 38}, + {27.4, 18.3, 38}, + {8.4, 87, 5602}, + {18.7, 82.5, 5602}, + {17.4, 82.1, 5602}, + {18.8, 81.6, 5602}, + {9.4, 80.9, 5602}, + {13.2, 73.4, 5602}, + {16.1, 72, 5602}, + {16.8, 71.3, 5602}, + {14.5, 71, 5602}, + {13.3, 70.8, 5602}, + {13.9, 70.2, 5602}, + {13.2, 69.4, 5602}, + {13.7, 68.2, 5602}, + {14.2, 67.3, 5602}, + {13.9, 67.1, 5602}, + {12.8, 65.7, 5602}, + {14.5, 65.5, 5602}, + {15.9, 65.2, 5602}, + {15.7, 65, 5602}, + {11.5, 63.6, 5602}, + {17, 61.9, 5602}, + {15.1, 60.9, 5602}, + {18.3, 60.4, 5602}, + {17.3, 60.2, 5602}, + {17.3, 60, 5602}, + {13.2, 60, 5602}, + {15.3, 59.9, 5602}, + {14.5, 59.9, 5602}, + {13.9, 55.8, 5602}, + {12.7, 55.1, 5602}, + {13.9, 54.4, 5602}, + {13.8, 53.7, 5602}, + {12.6, 53.6, 5602}, + {15, 52.9, 5602}, + {13.2, 52.8, 5602}, + }, + [1196] = { + {46.3, 63.3, 1}, + {46.7, 63.3, 1}, + {49.3, 62.3, 1}, + {58.7, 61.3, 1}, + {64.9, 59.9, 1}, + {30.5, 59.6, 1}, + {50.2, 59.4, 1}, + {61, 56.9, 1}, + {26.7, 56.2, 1}, + {25.3, 55.8, 1}, + {26, 55.2, 1}, + {28.1, 54.5, 1}, + {50.3, 53.8, 1}, + {28, 52, 1}, + {59.3, 52, 1}, + {29.7, 51.7, 1}, + {66.5, 50.5, 1}, + {32.3, 49.9, 1}, + {80.1, 48.6, 1}, + {74.8, 48.1, 1}, + {28.3, 47.6, 1}, + {27.6, 47.6, 1}, + {32.4, 47.2, 1}, + {35.9, 47.2, 1}, + {26.8, 46.9, 1}, + {25.5, 46.4, 1}, + {37.1, 44, 1}, + {38.5, 43.5, 1}, + {56.3, 43.3, 1}, + {51.2, 43.1, 1}, + {28, 43.1, 1}, + {38.1, 42.9, 1}, + {29.1, 42.7, 1}, + {30.2, 42.3, 1}, + {37.7, 41.6, 1}, + {29.3, 41.4, 1}, + {30.8, 40.8, 1}, + {38.9, 40.3, 1}, + {31.8, 38.6, 1}, + {33.4, 36.8, 1}, + {35.2, 35.9, 1}, + {30.6, 35.1, 1}, + {37.8, 33.9, 1}, + {34.2, 32.1, 1}, + {36.2, 30.5, 1}, + {34.6, 30.3, 1}, + {41.2, 29.2, 1}, + {43.8, 28.8, 1}, + {28.1, 52.2, 1}, + {28.3, 47.5, 1}, + {26.9, 46.9, 1}, + {37.1, 43.9, 1}, + {29.3, 41.5, 1}, + {30.9, 40.8, 1}, + {1.3, 71.7, 5602}, + }, + [1201] = { + {47.3, 63.3, 1}, + {47.9, 63.2, 1}, + {48.5, 61.9, 1}, + {49.5, 61.8, 1}, + {59.5, 61, 1}, + {59.4, 60.8, 1}, + {49.3, 60.6, 1}, + {55.8, 60.4, 1}, + {50.1, 60.4, 1}, + {63.6, 60.2, 1}, + {60.3, 60, 1}, + {58.5, 59.6, 1}, + {61.2, 59.6, 1}, + {62.7, 59.2, 1}, + {48.5, 59.1, 1}, + {55.2, 59, 1}, + {60.1, 58.8, 1}, + {59.1, 58.6, 1}, + {60, 58, 1}, + {56.5, 58, 1}, + {53.8, 57.8, 1}, + {63, 57.3, 1}, + {54.7, 57.2, 1}, + {61.8, 57, 1}, + {62.3, 56.6, 1}, + {53.9, 56.5, 1}, + {48.3, 56.5, 1}, + {57.1, 56.3, 1}, + {59.5, 55.9, 1}, + {57.8, 55.8, 1}, + {27.5, 55, 1}, + {56.5, 54.9, 1}, + {29.1, 54.2, 1}, + {28.3, 53.6, 1}, + {28.6, 53.6, 1}, + {56.1, 52.7, 1}, + {56.3, 52.2, 1}, + {29.5, 50.8, 1}, + {29.1, 49.7, 1}, + {28.2, 48.9, 1}, + {32.5, 48.4, 1}, + {26.8, 47.9, 1}, + {34.5, 47.5, 1}, + {25.7, 46.9, 1}, + {28.1, 44.9, 1}, + {27.6, 42.5, 1}, + {28.2, 40, 1}, + {30.2, 39.8, 1}, + {29.3, 39.6, 1}, + {31.2, 39.6, 1}, + {31.2, 39.4, 1}, + {38.4, 38.4, 1}, + {33.2, 37.8, 1}, + {36.8, 37.7, 1}, + {82.2, 37.6, 1}, + {79.1, 37.2, 1}, + {37.6, 36.8, 1}, + {35.4, 36.4, 1}, + {79, 36.3, 1}, + {80.4, 35.9, 1}, + {34.9, 35.7, 1}, + {79.2, 35.4, 1}, + {34.1, 35.3, 1}, + {35.2, 35, 1}, + {38.3, 33.5, 1}, + {36.7, 31.9, 1}, + {42.7, 31, 1}, + {37.6, 30.9, 1}, + {40.1, 30.8, 1}, + {44, 30.3, 1}, + {49.9, 60.2, 1}, + {27.6, 54.8, 1}, + {32.6, 48.5, 1}, + {26.8, 47.8, 1}, + {34.4, 47.7, 1}, + {28.1, 45, 1}, + {30.1, 40.1, 1}, + {36.7, 37.6, 1}, + {3.3, 61.6, 5602}, + {0.4, 61.2, 5602}, + {0.3, 60.4, 5602}, + {1.6, 60.1, 5602}, + {0.5, 59.5, 5602}, + }, + [1211] = { + {25.6, 45.4, 1}, + {25.4, 44.9, 1}, + {24.8, 44.8, 1}, + {25.2, 44.4, 1}, + {25.4, 43.8, 1}, + {24.8, 43.8, 1}, + {26.8, 43.8, 1}, + {26.1, 43.8, 1}, + {24.4, 43.6, 1}, + {24, 43.3, 1}, + {24.5, 43.2, 1}, + {25.7, 43, 1}, + {24.7, 43, 1}, + {27.2, 43, 1}, + {24.8, 42.9, 1}, + {26.8, 42.8, 1}, + {26.1, 42.8, 1}, + {24.8, 42.8, 1}, + {26.7, 41.8, 1}, + {24.7, 41.8, 1}, + {26.1, 40.7, 1}, + {24.9, 40.1, 1}, + {25.3, 39.8, 1}, + {24.2, 39.8, 1}, + {24.2, 39.6, 1}, + {24.7, 39.4, 1}, + {25.9, 37.9, 1}, + {27.3, 37.3, 1}, + {26.1, 37, 1}, + {27.2, 37, 1}, + {27.7, 37, 1}, + {28, 36.7, 1}, + {26.7, 36.6, 1}, + {27.3, 36.4, 1}, + {27.6, 36.4, 1}, + {26.8, 36.3, 1}, + {28.1, 35.9, 1}, + {27.4, 35.9, 1}, + {26.9, 35.7, 1}, + {26.3, 35.6, 1}, + {25.6, 45.3, 1}, + {25.5, 44.8, 1}, + {24.7, 44.7, 1}, + {25.5, 43.7, 1}, + {26, 43.7, 1}, + {24, 43.4, 1}, + {24.4, 43.1, 1}, + {27.3, 43, 1}, + {25.9, 42.9, 1}, + {26.7, 42.8, 1}, + {25.8, 42.6, 1}, + {26, 42.1, 1}, + {27.5, 42, 1}, + {28.2, 42, 1}, + {26.7, 41.9, 1}, + {24.7, 41.7, 1}, + {28, 41.6, 1}, + {27.8, 41.2, 1}, + {27.7, 41.2, 1}, + {27.7, 41, 1}, + {26, 40.8, 1}, + {28.5, 40.5, 1}, + {27.3, 40.3, 1}, + {24.2, 39.9, 1}, + {27.1, 39.6, 1}, + {28, 38.7, 1}, + {25.8, 38.5, 1}, + {26.5, 38.1, 1}, + {28.3, 37.3, 1}, + {28.7, 36.2, 1}, + {28.2, 35.1, 1}, + {82.7, 99.4, 721}, + {73.5, 98.1, 721}, + {97.9, 97.5, 721}, + {77.4, 95.9, 721}, + {87.1, 88.6, 721}, + {92.8, 84.8, 721}, + {88, 82.8, 721}, + {99.4, 78, 721}, + {89.7, 75.4, 721}, + {99.4, 70.1, 721}, + }, + [1397] = { + {41.1, 45.5, 1}, + {42.1, 45.5, 1}, + {41.5, 45, 1}, + {41.1, 44.9, 1}, + {42.1, 44.6, 1}, + {41.2, 44.5, 1}, + {41.4, 44.4, 1}, + {41.6, 43.8, 1}, + {40.3, 42.9, 1}, + {42.2, 36.6, 1}, + {42.8, 36.6, 1}, + {43, 35.9, 1}, + {41.9, 35.8, 1}, + {42.5, 35.8, 1}, + {41.8, 35.6, 1}, + {41.5, 35.3, 1}, + {41.8, 35.2, 1}, + {40.7, 35, 1}, + {42.3, 34.9, 1}, + {40.8, 34.9, 1}, + {42.1, 34.4, 1}, + {41.3, 34.3, 1}, + {42.6, 33.7, 1}, + {43.1, 35.9, 1}, + {42.6, 33.8, 1}, + }, + [1520] = { + {48.7, 44.2, 85}, + {48.7, 44.1, 85}, + {50.7, 44, 85}, + {46.8, 43.9, 85}, + {49.4, 43.4, 85}, + {46, 43.3, 85}, + {50.1, 43.2, 85}, + {49.3, 42.8, 85}, + {49.3, 42.3, 85}, + {44.6, 41.3, 85}, + {45.8, 40.4, 85}, + {43.9, 39.8, 85}, + {46.1, 39.1, 85}, + {46.1, 38.8, 85}, + {45.4, 38.3, 85}, + {49.1, 38.3, 85}, + {47.7, 37.1, 85}, + {49.5, 36.8, 85}, + {46.1, 36.2, 85}, + {49.1, 36, 85}, + {46.1, 34.3, 85}, + {49, 38.1, 85}, + {46.1, 36.1, 85}, + {49.3, 35.9, 85}, + }, + [1522] = { + {50.2, 43, 85}, + {45.3, 42.9, 85}, + {47.6, 42.8, 85}, + {48.9, 42.8, 85}, + {47, 42, 85}, + {48.3, 41.7, 85}, + {45.4, 41.5, 85}, + {47.4, 40.7, 85}, + {45.4, 40.5, 85}, + {44.6, 40.5, 85}, + {46.8, 39.9, 85}, + {45.4, 39.6, 85}, + {48.3, 39.4, 85}, + {47.4, 38.3, 85}, + {44.4, 38.3, 85}, + {44.4, 37.6, 85}, + {46.7, 37.5, 85}, + {48.3, 37.5, 85}, + {45.1, 37.5, 85}, + {47.7, 37.5, 85}, + {49.9, 36.8, 85}, + {49.2, 36.2, 85}, + {49.8, 36.2, 85}, + {47.5, 36.1, 85}, + {49.6, 36, 85}, + {44.9, 36, 85}, + {49, 35.3, 85}, + {42.7, 35.1, 85}, + {43.9, 35, 85}, + {46.9, 34.9, 85}, + {45.3, 34.9, 85}, + {42.7, 34.3, 85}, + {49, 34.1, 85}, + {48.7, 34.1, 85}, + {47.9, 34.1, 85}, + {51, 34, 85}, + {49.2, 33.8, 85}, + {42.4, 33.6, 85}, + {49.2, 33.5, 85}, + {46.9, 33.4, 85}, + {43.3, 32.5, 85}, + {45.3, 32.4, 85}, + {47.1, 32.4, 85}, + {42.7, 32.3, 85}, + {46.6, 32, 85}, + {46.2, 31.7, 85}, + {50.4, 31.5, 85}, + {47.7, 31.2, 85}, + {47.7, 30.7, 85}, + {43.9, 30.5, 85}, + {45.6, 30.4, 85}, + {48.2, 30.4, 85}, + {47.3, 30.2, 85}, + {46.2, 29.4, 85}, + {47.7, 29.2, 85}, + {43.6, 28.6, 85}, + {49.2, 27.9, 85}, + {48.3, 39.5, 85}, + {42.8, 34.2, 85}, + {49.2, 33.9, 85}, + {47.1, 32.3, 85}, + }, + [1523] = { + {43.7, 35.4, 85}, + {49.6, 34.9, 85}, + {48.3, 34.9, 85}, + {45, 34.6, 85}, + {47.6, 34.4, 85}, + {44.7, 33.8, 85}, + {48.9, 33.7, 85}, + {46.1, 33.5, 85}, + {49.2, 33.3, 85}, + {51.3, 33.2, 85}, + {48.2, 32.9, 85}, + {49.5, 32.9, 85}, + {48.4, 32.8, 85}, + {43.9, 32.8, 85}, + {49.1, 31.6, 85}, + {44.8, 31.5, 85}, + {43.3, 31.3, 85}, + {46, 30.2, 85}, + {47, 30.2, 85}, + {44.8, 29.3, 85}, + {48.8, 29.1, 85}, + {45.4, 28.5, 85}, + {47.1, 28, 85}, + {48.3, 27.9, 85}, + {43.8, 35.4, 85}, + {48.4, 35, 85}, + {44.7, 33.9, 85}, + {48.4, 32.9, 85}, + {43.8, 32.6, 85}, + }, + [1526] = { + {53.6, 60.6, 85}, + {52.1, 59.7, 85}, + {54.4, 59.6, 85}, + {52.7, 59.4, 85}, + {52, 59.3, 85}, + {54.8, 59.2, 85}, + {52, 58.3, 85}, + {53.6, 58.3, 85}, + {51.9, 58.1, 85}, + {51.6, 57.7, 85}, + {53.7, 57.6, 85}, + {55, 57.3, 85}, + {55.8, 57.1, 85}, + {52.7, 57.1, 85}, + {53.4, 57, 85}, + {54.4, 56.9, 85}, + {52.2, 56.5, 85}, + {53, 56.4, 85}, + {53.4, 56.2, 85}, + {49.8, 56, 85}, + {54.2, 56, 85}, + {54, 56, 85}, + {53.6, 55.9, 85}, + {49.1, 55.5, 85}, + {49.3, 55.2, 85}, + {53.2, 54.2, 85}, + {50.6, 54.2, 85}, + {54.7, 54.2, 85}, + {53.3, 54.1, 85}, + {54.5, 54.1, 85}, + {53.5, 54.1, 85}, + {52.9, 54, 85}, + {51.3, 53.8, 85}, + {51.9, 53.8, 85}, + {49.8, 53.8, 85}, + {53, 53.5, 85}, + {52.9, 52.8, 85}, + {51.4, 52.8, 85}, + {52.2, 52.3, 85}, + {52.2, 52.2, 85}, + {53, 51.7, 85}, + {51.3, 51.6, 85}, + {54.2, 51.6, 85}, + {55.4, 51.4, 85}, + {54.1, 51.3, 85}, + {54.9, 50.5, 85}, + {52.6, 50.5, 85}, + {52.1, 50.4, 85}, + {54.5, 50.3, 85}, + {53.3, 50.2, 85}, + {54.4, 50.1, 85}, + {56.1, 49.9, 85}, + {54.6, 49.6, 85}, + {52.8, 49.3, 85}, + {55.5, 48.8, 85}, + {52.1, 48.7, 85}, + {53.6, 48.6, 85}, + {54.2, 48.5, 85}, + {52.7, 48.1, 85}, + {55.2, 48.1, 85}, + {56.1, 47.6, 85}, + {51.9, 47.4, 85}, + {53.6, 47.4, 85}, + {52.7, 47.3, 85}, + {56.4, 46.4, 85}, + {52.1, 46.4, 85}, + {53.5, 46.3, 85}, + {52.7, 45.9, 85}, + {52, 45.4, 85}, + {53.9, 45, 85}, + {52, 59.5, 85}, + {52.8, 59.4, 85}, + {54.3, 55.9, 85}, + {49.2, 55.6, 85}, + {54.5, 54.3, 85}, + {53.6, 54.1, 85}, + {54.2, 51.5, 85}, + }, + [1527] = { + {66.7, 45.1, 85}, + {66.7, 44.8, 85}, + {68, 44, 85}, + {67.5, 43.7, 85}, + {68.7, 43.6, 85}, + {68.1, 43.5, 85}, + {67.6, 43, 85}, + {68.1, 42.5, 85}, + {67.6, 42.5, 85}, + {67.3, 42.3, 85}, + {67.8, 42.2, 85}, + {69.5, 41.8, 85}, + {68.4, 41.5, 85}, + {69.7, 41.1, 85}, + {67.5, 43.8, 85}, + {67.5, 43, 85}, + }, + [1528] = { + {66.7, 45.1, 85}, + {66.7, 44.8, 85}, + {68, 44, 85}, + {67.5, 43.7, 85}, + {68.7, 43.6, 85}, + {68.1, 43.5, 85}, + {67.6, 43, 85}, + {68.1, 42.5, 85}, + {67.6, 42.5, 85}, + {67.3, 42.3, 85}, + {67.8, 42.2, 85}, + {69.5, 41.8, 85}, + {68.4, 41.5, 85}, + {69.7, 41.1, 85}, + {67.5, 43.8, 85}, + {67.5, 43, 85}, + }, + [1529] = { + {19.9, 49.5, 28}, + {19.2, 49.4, 28}, + {19.5, 48.2, 28}, + {20.1, 48, 28}, + {21.7, 47.1, 28}, + {74.2, 62.8, 85}, + {76.8, 62.8, 85}, + {76.2, 62.7, 85}, + {75, 61.8, 85}, + {76.5, 61.5, 85}, + {77, 61.3, 85}, + {76.1, 61.1, 85}, + {78.6, 60.5, 85}, + {76.7, 60.1, 85}, + {76.5, 59.6, 85}, + {75.6, 59.6, 85}, + {74.6, 59.5, 85}, + {77.8, 59.5, 85}, + {20.2, 47.9, 28}, + {21.7, 46.9, 28}, + {74.9, 61.6, 85}, + {77.1, 61.2, 85}, + {78.5, 60.3, 85}, + {74.9, 59.8, 85}, + }, + [1530] = { + {51.6, 33.4, 85}, + {52.1, 32.9, 85}, + {48.6, 32, 85}, + {51.3, 31.5, 85}, + {54.7, 31.3, 85}, + {53.3, 31, 85}, + {52, 30.6, 85}, + {53.4, 30.4, 85}, + {49.8, 30.3, 85}, + {54.2, 29.2, 85}, + {50.6, 29.1, 85}, + {49.7, 28.6, 85}, + {51.3, 28.1, 85}, + {51.8, 27, 85}, + {52.6, 27, 85}, + {52.6, 26.9, 85}, + {52.3, 26.9, 85}, + {52.7, 26.9, 85}, + {52, 26.8, 85}, + {52.4, 26.4, 85}, + {52.3, 26.3, 85}, + {52.7, 26.1, 85}, + {51.9, 25.8, 85}, + {54.4, 29.2, 85}, + {52.4, 26.5, 85}, + {52.2, 26.4, 85}, + {52.7, 26, 85}, + }, + [1531] = { + {44.6, 40.5, 85}, + }, + [1532] = { + {20, 48.6, 28}, + {19.9, 48.3, 28}, + {20.9, 47.9, 28}, + {76.9, 61.9, 85}, + {76.8, 61.5, 85}, + {75.6, 61.5, 85}, + {74.2, 61.5, 85}, + {77.8, 61.2, 85}, + {75, 60.5, 85}, + {76.5, 60.5, 85}, + {78.5, 59.4, 85}, + {75.1, 59.1, 85}, + {76.5, 58.7, 85}, + {77.4, 58.7, 85}, + {75.2, 58.2, 85}, + {21, 48, 28}, + {75.6, 61.4, 85}, + {77.9, 61.3, 85}, + {75.3, 61.1, 85}, + }, + [1533] = { + {45.6, 31.6, 85}, + }, + [1534] = { + {51.9, 31.5, 85}, + {51, 30.5, 85}, + {55.2, 30, 85}, + {54, 29.8, 85}, + {54.7, 29.5, 85}, + {52.7, 29.3, 85}, + {51.4, 29.3, 85}, + {51.9, 28.8, 85}, + {53.5, 28, 85}, + {52.3, 27.5, 85}, + {52.9, 27, 85}, + {52, 26.7, 85}, + {52.6, 26.7, 85}, + {52.1, 26.3, 85}, + {51.8, 26, 85}, + {52, 25.6, 85}, + {52.2, 27.5, 85}, + }, + [1535] = { + {34.1, 51.4, 85}, + {30.5, 51.3, 85}, + {32.6, 51.2, 85}, + {31.8, 51.1, 85}, + {32.8, 50.4, 85}, + {31.4, 50.4, 85}, + {29.9, 50, 85}, + {29.7, 49.5, 85}, + {34.5, 49.4, 85}, + {32.1, 49.3, 85}, + {33.6, 49.2, 85}, + {30.4, 49.1, 85}, + {31.3, 48.4, 85}, + {33.6, 48.4, 85}, + {32.8, 48.3, 85}, + {36.9, 48.2, 85}, + {34.3, 47.9, 85}, + {37.4, 47.7, 85}, + {30.6, 47.6, 85}, + {36.2, 47.5, 85}, + {32.1, 47.4, 85}, + {33.5, 47.4, 85}, + {32.2, 46.7, 85}, + {33, 46.6, 85}, + {30.9, 46.6, 85}, + {31.8, 46.5, 85}, + {31.7, 46.4, 85}, + {31.7, 46.3, 85}, + {31.7, 46.2, 85}, + {30.7, 46.2, 85}, + {31.9, 46, 85}, + {32, 45.9, 85}, + {31.7, 45.9, 85}, + {33.6, 45.3, 85}, + {32.2, 45, 85}, + {37.5, 52.2, 85}, + {33, 50.3, 85}, + {32.8, 48.2, 85}, + {34.2, 47.8, 85}, + {36.2, 47.4, 85}, + {37.5, 47.4, 85}, + }, + [1536] = { + {51.1, 69.5, 85}, + {52, 69.5, 85}, + {54.2, 69.3, 85}, + {52.9, 69, 85}, + {50.1, 68.7, 85}, + {52.2, 68.6, 85}, + {53.5, 68.2, 85}, + {50.6, 68, 85}, + {50.5, 67.7, 85}, + {52.7, 67.6, 85}, + {51.2, 67.6, 85}, + {51.5, 67.6, 85}, + {52.1, 67.1, 85}, + {52.7, 67.1, 85}, + {50.2, 67, 85}, + {53.8, 66.8, 85}, + {48.8, 66.8, 85}, + {51.7, 66.7, 85}, + {48.2, 66.4, 85}, + {48.8, 66.2, 85}, + {51.8, 66.1, 85}, + {53.6, 66, 85}, + {47.6, 66, 85}, + {51.1, 65.6, 85}, + {43.2, 65.2, 85}, + {44.5, 65, 85}, + {50.5, 64.7, 85}, + {46.7, 64, 85}, + {51.1, 69.7, 85}, + {54.3, 69.3, 85}, + {50.2, 68.8, 85}, + {52.2, 68.5, 85}, + {53.4, 68.1, 85}, + {52.8, 67.4, 85}, + {52.7, 67.4, 85}, + {52.2, 67.1, 85}, + {50.2, 66.9, 85}, + {51.7, 66.1, 85}, + {53.7, 66, 85}, + {51.1, 65.7, 85}, + }, + [1537] = { + {31.5, 29.7, 28}, + {31.1, 29.2, 28}, + {30.7, 29, 28}, + {31, 28.7, 28}, + {31.9, 28.3, 28}, + {30.7, 28.1, 28}, + {30.4, 28, 28}, + {29.6, 27.5, 28}, + {30.9, 27.4, 28}, + {30.6, 27.2, 28}, + {80.3, 57.1, 85}, + {81, 56.9, 85}, + {77.2, 56.8, 85}, + {79.9, 56.4, 85}, + {76.9, 56.2, 85}, + {78.4, 56, 85}, + {79.7, 55.9, 85}, + {81, 55.4, 85}, + {76.2, 55.2, 85}, + {79.6, 55.2, 85}, + {77.1, 55.1, 85}, + {81.7, 54.8, 85}, + {76.6, 54.8, 85}, + {87.8, 43.9, 85}, + {85.8, 43.5, 85}, + {87.5, 43.4, 85}, + {86.8, 43.4, 85}, + {87.1, 43.2, 85}, + {87.4, 42.9, 85}, + {85.3, 42.7, 85}, + {88.2, 42.6, 85}, + {87.1, 42.4, 85}, + {86.8, 42.3, 85}, + {86.1, 41.8, 85}, + {87.4, 41.7, 85}, + {87, 41.5, 85}, + {51.1, 69.5, 85}, + {52, 69.5, 85}, + {54.2, 69.3, 85}, + {52.9, 69, 85}, + {50.1, 68.7, 85}, + {52.2, 68.6, 85}, + {53.5, 68.2, 85}, + {50.6, 68, 85}, + {50.5, 67.7, 85}, + {52.7, 67.6, 85}, + {51.2, 67.6, 85}, + {51.5, 67.6, 85}, + {52.1, 67.1, 85}, + {52.7, 67.1, 85}, + {50.2, 67, 85}, + {53.8, 66.8, 85}, + {48.8, 66.8, 85}, + {51.7, 66.7, 85}, + {48.2, 66.4, 85}, + {48.8, 66.2, 85}, + {51.8, 66.1, 85}, + {53.6, 66, 85}, + {47.6, 66, 85}, + {51.1, 65.6, 85}, + {43.2, 65.2, 85}, + {44.5, 65, 85}, + {50.5, 64.7, 85}, + {46.7, 64, 85}, + {51.1, 69.7, 85}, + {54.3, 69.3, 85}, + {50.2, 68.8, 85}, + {52.2, 68.5, 85}, + {53.4, 68.1, 85}, + {52.8, 67.4, 85}, + {52.7, 67.4, 85}, + {52.2, 67.1, 85}, + {50.2, 66.9, 85}, + {51.7, 66.1, 85}, + {53.7, 66, 85}, + {51.1, 65.7, 85}, + }, + [1538] = { + {30.6, 29.6, 28}, + {30.7, 29.2, 28}, + {30.7, 28.5, 28}, + {30.5, 28, 28}, + {81, 57.3, 85}, + {76.1, 57.2, 85}, + {81.1, 56.9, 85}, + {78.3, 56.3, 85}, + {79.4, 56.2, 85}, + {78.9, 56.2, 85}, + {78.5, 56, 85}, + {79.4, 55.5, 85}, + {78.6, 55.4, 85}, + {80.6, 55.1, 85}, + {77.1, 54.9, 85}, + {81.6, 53.1, 85}, + {86.6, 44.9, 85}, + {87, 43.8, 85}, + {87.1, 43.5, 85}, + {86.8, 43.1, 85}, + {87.2, 42.7, 85}, + {86.9, 42.3, 85}, + {78.9, 36.1, 85}, + {79, 31.2, 85}, + {79.3, 26.1, 85}, + {79.5, 26, 85}, + {79.3, 24.6, 85}, + }, + [1539] = { + {78.5, 32.5, 85}, + {79, 28.5, 85}, + {79.1, 27.1, 85}, + {79, 25.6, 85}, + {79.7, 24.7, 85}, + }, + [1540] = { + {78.7, 36.1, 85}, + {77.9, 35.4, 85}, + {77.6, 34.7, 85}, + {78.4, 32.8, 85}, + {77.6, 32.8, 85}, + {78.1, 32.1, 85}, + {79.4, 29.7, 85}, + {79.6, 26.1, 85}, + {79.3, 24.4, 85}, + }, + [1543] = { + {30.3, 45.6, 85}, + {29.4, 45.1, 85}, + {30.6, 45, 85}, + {29.6, 45, 85}, + {34.9, 44.7, 85}, + {30.1, 44.6, 85}, + {30.7, 44.6, 85}, + {34.4, 44.3, 85}, + {30.1, 43.6, 85}, + {30.5, 43.3, 85}, + {36.1, 43, 85}, + {35.9, 43, 85}, + {35.6, 42.2, 85}, + {37, 41.9, 85}, + {34.6, 41.3, 85}, + {34.7, 40.9, 85}, + {36.2, 39.2, 85}, + {62.6, 30.7, 85}, + {61.6, 29.4, 85}, + {59.7, 28.9, 85}, + {58.6, 28.8, 85}, + {58.5, 28.8, 85}, + {60.8, 28.4, 85}, + {57.7, 28.3, 85}, + {58.6, 28.1, 85}, + {63.2, 27.9, 85}, + {61.7, 27.9, 85}, + {63.1, 27.5, 85}, + {60.7, 27.2, 85}, + {58.9, 27.1, 85}, + {61.8, 26.9, 85}, + {58.6, 26.9, 85}, + {58.1, 26.5, 85}, + {57.7, 26.1, 85}, + {34.3, 44.2, 85}, + }, + [1544] = { + {26.1, 47.3, 85}, + {26.2, 46.3, 85}, + {29.2, 45.8, 85}, + {36.8, 45.1, 85}, + {30.8, 44.9, 85}, + {29.8, 44.8, 85}, + {36.4, 44.6, 85}, + {35.6, 44.3, 85}, + {31.4, 43.7, 85}, + {30.7, 43.6, 85}, + {34.6, 43.5, 85}, + {35.8, 43.5, 85}, + {30.5, 43.4, 85}, + {35.6, 43.3, 85}, + {30.7, 43.2, 85}, + {31.1, 43.2, 85}, + {36, 42.9, 85}, + {33.7, 42.8, 85}, + {36.7, 42, 85}, + {34.5, 41.6, 85}, + {34.7, 40.8, 85}, + {37, 40.8, 85}, + {35, 40.8, 85}, + {36.4, 40.6, 85}, + {35.8, 39.5, 85}, + {36.5, 35.9, 85}, + {64, 30.6, 85}, + {61.3, 30, 85}, + {64.5, 29.8, 85}, + {64.9, 29.7, 85}, + {63.1, 29.5, 85}, + {59.4, 29.1, 85}, + {59.8, 28.9, 85}, + {62.9, 28.8, 85}, + {64, 28.7, 85}, + {60.1, 28.4, 85}, + {59.4, 28.3, 85}, + {62.3, 28.3, 85}, + {62.8, 28.3, 85}, + {61.7, 28.2, 85}, + {58.3, 28.1, 85}, + {63.4, 27.8, 85}, + {58.4, 27.6, 85}, + {60.9, 27.6, 85}, + {60.1, 27.3, 85}, + {60.9, 27.2, 85}, + {58.9, 27.1, 85}, + {75.1, 26.9, 85}, + {75.1, 26.5, 85}, + {57.9, 26.3, 85}, + {67.5, 25.8, 85}, + {70.5, 25.7, 85}, + {67.9, 25.4, 85}, + {70.5, 25.3, 85}, + {67.3, 24.9, 85}, + {67.4, 24.5, 85}, + {61.6, 29.4, 85}, + }, + [1545] = { + {25.5, 48.5, 85}, + {26, 48.4, 85}, + {25.3, 47.4, 85}, + {26.8, 46.8, 85}, + {34.8, 45, 85}, + {34.3, 43.6, 85}, + {30.5, 42.8, 85}, + {36.4, 42.6, 85}, + {34.8, 42.4, 85}, + {29.9, 41.5, 85}, + {35.8, 41.4, 85}, + {36.5, 40.6, 85}, + {36.5, 38.4, 85}, + {38, 35.1, 85}, + {40.9, 35, 85}, + {39.5, 34.7, 85}, + {38.6, 30.6, 85}, + {65.2, 30.4, 85}, + {64.7, 30.4, 85}, + {66.2, 29.8, 85}, + {64.4, 29.7, 85}, + {64.6, 29.6, 85}, + {38.1, 29.5, 85}, + {67.4, 29.4, 85}, + {64.5, 29.1, 85}, + {68, 28.6, 85}, + {65.3, 28.6, 85}, + {67.7, 28.5, 85}, + {66.7, 28.1, 85}, + {75, 27.6, 85}, + {75.2, 27.4, 85}, + {66, 27.4, 85}, + {67.6, 27.3, 85}, + {72, 27.2, 85}, + {73.3, 27.2, 85}, + {70.4, 27.1, 85}, + {74.8, 27.1, 85}, + {75.4, 26.9, 85}, + {75.1, 26.8, 85}, + {72.7, 26.4, 85}, + {74.6, 26.4, 85}, + {75.4, 26.4, 85}, + {75.6, 26.4, 85}, + {66.9, 26.3, 85}, + {67, 26.2, 85}, + {74.1, 26.2, 85}, + {71.2, 26.2, 85}, + {75.7, 26.1, 85}, + {72.5, 26.1, 85}, + {68.2, 26.1, 85}, + {69.6, 26, 85}, + {67.8, 25.8, 85}, + {70.2, 25.6, 85}, + {72.3, 25.6, 85}, + {69.1, 25.5, 85}, + {68.7, 25.4, 85}, + {72, 25.2, 85}, + {73.5, 25, 85}, + {70.1, 25, 85}, + {67.5, 29.5, 85}, + }, + [1548] = { + {20.3, 61.2, 28}, + {18.8, 60.9, 28}, + {17.9, 59.9, 28}, + {21, 59.8, 28}, + {23.7, 59.8, 28}, + {24.6, 59.3, 28}, + {20.6, 58.6, 28}, + {19.6, 57.4, 28}, + {17.8, 57, 28}, + {23.1, 56.4, 28}, + {18, 55.8, 28}, + {20.6, 55.6, 28}, + {20, 55.1, 28}, + {23.3, 54.2, 28}, + {21.7, 53.5, 28}, + {22.9, 53, 28}, + {20, 51.7, 28}, + {24.6, 51.6, 28}, + {18.4, 51.4, 28}, + {24.1, 50.8, 28}, + {22.2, 50.8, 28}, + {20.9, 50.2, 28}, + {22.4, 48.3, 28}, + {23.8, 47.3, 28}, + {52.5, 74.6, 85}, + {77.2, 73.9, 85}, + {55.7, 73.6, 85}, + {75.8, 73.6, 85}, + {75, 72.6, 85}, + {77.8, 72.5, 85}, + {80.5, 72.5, 85}, + {52.8, 72.5, 85}, + {52, 72.3, 85}, + {81.4, 72.1, 85}, + {54.9, 71.4, 85}, + {77.5, 71.4, 85}, + {52.4, 71.3, 85}, + {51.2, 70.2, 85}, + {76.5, 70.2, 85}, + {74.8, 69.9, 85}, + {55.3, 69.8, 85}, + {79.9, 69.3, 85}, + {75, 68.8, 85}, + {77.5, 68.5, 85}, + {45.3, 68.5, 85}, + {43.2, 68.1, 85}, + {76.9, 68, 85}, + {46.4, 67.7, 85}, + {42, 67.5, 85}, + {80.1, 67.2, 85}, + {44.7, 67, 85}, + {54.1, 66.9, 85}, + {78.6, 66.5, 85}, + {55.6, 66.1, 85}, + {79.7, 66.1, 85}, + {49.7, 65.8, 85}, + {54, 65.7, 85}, + {55.6, 65.5, 85}, + {41.5, 65.5, 85}, + {53, 65.2, 85}, + {76.9, 64.9, 85}, + {81.4, 64.7, 85}, + {75.4, 64.6, 85}, + {41.5, 64.5, 85}, + {56.6, 64.4, 85}, + {45.8, 64.2, 85}, + {80.9, 64, 85}, + {79, 64, 85}, + {48.5, 63.9, 85}, + {54.3, 63.7, 85}, + {50.9, 63.4, 85}, + {77.7, 63.4, 85}, + {47.7, 63.3, 85}, + {53.4, 63.3, 85}, + {57.5, 63, 85}, + {49.2, 62.9, 85}, + {58.4, 62.7, 85}, + {42.8, 62.4, 85}, + {57.4, 62.4, 85}, + {50.6, 62.3, 85}, + {60.2, 62.2, 85}, + {43.8, 62, 85}, + {44.2, 61.9, 85}, + {71.9, 61.8, 85}, + {41, 61.6, 85}, + {79.2, 61.6, 85}, + {72.8, 61.5, 85}, + {50.1, 60.9, 85}, + {40.6, 60.9, 85}, + {45.1, 60.8, 85}, + {59.4, 60.7, 85}, + {46.7, 60.6, 85}, + {57.9, 60.6, 85}, + {80.6, 60.6, 85}, + {43.9, 60.6, 85}, + {51.4, 60, 85}, + {69, 59.9, 85}, + {58.3, 59.8, 85}, + {57.1, 59.6, 85}, + {71.1, 59.5, 85}, + {78.9, 59.1, 85}, + {48.3, 59, 85}, + {80.2, 58.6, 85}, + {40.6, 58.4, 85}, + {58.3, 58.3, 85}, + {44.3, 58.2, 85}, + {42.8, 58.1, 85}, + {72.9, 56.9, 85}, + {48.6, 56.7, 85}, + {44.8, 54.4, 85}, + {74.6, 54.3, 85}, + {75.8, 54.3, 85}, + {76.2, 52.4, 85}, + {72, 52.1, 85}, + {47.3, 51, 85}, + {46.1, 50.9, 85}, + {46.9, 50.1, 85}, + {48.3, 50, 85}, + {50, 49.9, 85}, + {72.6, 49.2, 85}, + {47.4, 49.1, 85}, + {48, 48.4, 85}, + {45.1, 47.8, 85}, + {45.1, 47.7, 85}, + {72.6, 47.6, 85}, + {71.8, 47.2, 85}, + {42.3, 47, 85}, + {45.9, 46.9, 85}, + {72.7, 46.5, 85}, + {46.3, 46.1, 85}, + {74.9, 46, 85}, + {47, 45.4, 85}, + {75.6, 45.4, 85}, + {43.3, 45.2, 85}, + {44.5, 45.2, 85}, + {41.9, 45, 85}, + {41.3, 44.4, 85}, + {74.6, 44.2, 85}, + {45, 43.9, 85}, + {72.6, 43.7, 85}, + {43.7, 42.9, 85}, + {42.2, 42.8, 85}, + {40.3, 42.5, 85}, + {73, 41.4, 85}, + {44.6, 40.4, 85}, + {72.2, 40.2, 85}, + {73.2, 38.1, 85}, + {71.8, 38, 85}, + {74.5, 37.6, 85}, + {68.1, 37, 85}, + {68.2, 36.2, 85}, + {71.5, 36.1, 85}, + {70.4, 36.1, 85}, + {74.4, 36, 85}, + {69.1, 35.6, 85}, + {74.1, 35.3, 85}, + {66.1, 35, 85}, + {76.7, 34.8, 85}, + {66.7, 34.8, 85}, + {68, 34.6, 85}, + {68.9, 33.9, 85}, + {63.4, 33.7, 85}, + {75.6, 33, 85}, + {65, 32.8, 85}, + {78, 32.7, 85}, + {71.1, 32.6, 85}, + {72.6, 32.6, 85}, + {68.9, 31.7, 85}, + {73.3, 31.7, 85}, + {67.6, 31.6, 85}, + {70.5, 31.5, 85}, + {76.5, 31, 85}, + {68.3, 30.7, 85}, + {69, 29.6, 85}, + {76.4, 29.6, 85}, + {70.4, 29.5, 85}, + {72.1, 29.5, 85}, + {74, 28.7, 85}, + {69.7, 28.5, 85}, + {72.8, 28.5, 85}, + {66.4, 2.7, 130}, + {22, 45, 1497}, + {23.7, 35, 1497}, + {19.8, 34.3, 1497}, + {33.5, 30.1, 1497}, + {21.7, 29.7, 1497}, + {55.7, 65.7, 85}, + {58.4, 60, 85}, + }, + [1549] = { + {32.5, 30.8, 28}, + {33.2, 29.7, 28}, + {31.8, 29.6, 28}, + {23.4, 28.2, 28}, + {33.5, 27.5, 28}, + {31.9, 27.4, 28}, + {34.1, 26.3, 28}, + {29.4, 25.1, 28}, + {30.4, 24.7, 28}, + {29.6, 23.4, 28}, + {83, 50.4, 85}, + {79.6, 49.5, 85}, + {83, 48.8, 85}, + {88.2, 48.5, 85}, + {82.2, 48.3, 85}, + {89.4, 47.9, 85}, + {83.2, 47.4, 85}, + {90.3, 47.1, 85}, + {82, 47, 85}, + {88.9, 46.7, 85}, + {88.2, 46.4, 85}, + {83.6, 46, 85}, + {87.4, 46, 85}, + {85.1, 46, 85}, + {87, 45.3, 85}, + {81.5, 45.2, 85}, + {86, 45.1, 85}, + {88.9, 44.9, 85}, + {83.9, 44.2, 85}, + {85.1, 44, 85}, + {89.5, 43.9, 85}, + {88.2, 43.8, 85}, + {82.1, 43.6, 85}, + {80.2, 42.4, 85}, + {89.8, 41.8, 85}, + {83.3, 41.7, 85}, + {88.3, 41.7, 85}, + {84.4, 41.5, 85}, + {83.9, 41.5, 85}, + {90.4, 40.6, 85}, + {85.9, 39.5, 85}, + {86.8, 39.1, 85}, + {86.1, 37.9, 85}, + }, + [1553] = { + {54.2, 65.9, 85}, + {53.6, 65, 85}, + {53.4, 64.1, 85}, + {55.1, 64, 85}, + {71, 63.8, 85}, + {52.5, 63.5, 85}, + {55.5, 63.4, 85}, + {56.6, 62.9, 85}, + {68.3, 62.9, 85}, + {55, 62.6, 85}, + {66.7, 62.3, 85}, + {54.1, 61.7, 85}, + {59.3, 61.6, 85}, + {63.8, 61.4, 85}, + {55.7, 61, 85}, + {60.3, 60.8, 85}, + {46.7, 60.2, 85}, + {57.4, 59.7, 85}, + {49.2, 59.6, 85}, + {50.9, 59.3, 85}, + {45.5, 58.9, 85}, + {64.7, 58.5, 85}, + {58.8, 58.4, 85}, + {46.6, 58, 85}, + {48.8, 57.7, 85}, + {56.3, 57.5, 85}, + {64.3, 57.5, 85}, + {41.7, 57.4, 85}, + {43, 57.3, 85}, + {45.4, 57.3, 85}, + {58, 57.1, 85}, + {58.4, 57, 85}, + {41.4, 56.9, 85}, + {65.5, 56.6, 85}, + {62.2, 56.6, 85}, + {67, 56.6, 85}, + {45.9, 56.4, 85}, + {61.1, 56.3, 85}, + {47.4, 56.3, 85}, + {43.3, 56.2, 85}, + {58.5, 56.2, 85}, + {59.3, 56.1, 85}, + {66.8, 55.9, 85}, + {43.8, 55.9, 85}, + {56.9, 55.7, 85}, + {40.1, 55.6, 85}, + {47.4, 55.3, 85}, + {58, 55.2, 85}, + {48.9, 55, 85}, + {64, 54.9, 85}, + {45.8, 54.8, 85}, + {45.3, 54.6, 85}, + {65.1, 54.6, 85}, + {66.3, 54.5, 85}, + {45.8, 54.4, 85}, + {57.1, 54.2, 85}, + {68, 54.1, 85}, + {69, 53.9, 85}, + {41.9, 53.7, 85}, + {66.5, 53.6, 85}, + {65.9, 53.2, 85}, + {40.1, 52.7, 85}, + {45.7, 52.5, 85}, + {46.8, 52.5, 85}, + {67.1, 51.8, 85}, + {45.2, 51.2, 85}, + {46.8, 50.8, 85}, + {46.8, 50.3, 85}, + {64.8, 50.1, 85}, + {44.5, 50.1, 85}, + {63.2, 49.7, 85}, + {49.5, 49.7, 85}, + {51.4, 49.5, 85}, + {44.6, 49.4, 85}, + {63.7, 49.2, 85}, + {40.3, 48.6, 85}, + {50, 48.5, 85}, + {51.9, 48.4, 85}, + {39.1, 47.8, 85}, + {42.8, 47.7, 85}, + {63.3, 47.6, 85}, + {41.1, 47.4, 85}, + {36.5, 47.3, 85}, + {63.3, 47.1, 85}, + {47.2, 46.4, 85}, + {43.1, 46.2, 85}, + {37.4, 46.1, 85}, + {64.3, 46, 85}, + {63, 46, 85}, + {47.9, 45.1, 85}, + {42.3, 44.2, 85}, + {64.2, 44.1, 85}, + {62.9, 44, 85}, + {37.9, 43.9, 85}, + {62.3, 43.9, 85}, + {62.5, 43.5, 85}, + {37.3, 43.5, 85}, + {60.4, 43.1, 85}, + {42.5, 42.4, 85}, + {39.3, 42.2, 85}, + {41.1, 42.2, 85}, + {60, 41.8, 85}, + {62.5, 41.8, 85}, + {65.2, 41.7, 85}, + {63.3, 41.7, 85}, + {43.3, 41.5, 85}, + {61.8, 41.4, 85}, + {64.7, 40.4, 85}, + {41.8, 40.3, 85}, + {61.7, 38.8, 85}, + {37.5, 38.8, 85}, + {63.2, 38.7, 85}, + {65.4, 38.6, 85}, + {38.5, 38.6, 85}, + {60.9, 38.5, 85}, + {64.9, 38.2, 85}, + {64.1, 37.8, 85}, + {62.2, 36.8, 85}, + {64.3, 36.6, 85}, + {62.9, 35.8, 85}, + {64.5, 57.3, 85}, + {37.4, 46.4, 85}, + }, + [1554] = { + {25.3, 61.6, 28}, + {21.7, 60.7, 28}, + {23.3, 60.1, 28}, + {18.5, 59.7, 28}, + {19.7, 59.6, 28}, + {21.8, 58.9, 28}, + {17.1, 58.7, 28}, + {20, 58.5, 28}, + {22.5, 57.9, 28}, + {24, 57.7, 28}, + {21.3, 57.4, 28}, + {21.8, 56.9, 28}, + {22.8, 56.3, 28}, + {23.6, 54.8, 28}, + {16.9, 54.3, 28}, + {24.6, 53.5, 28}, + {23.9, 53.4, 28}, + {27.1, 53.3, 28}, + {19.4, 53.1, 28}, + {25.6, 52.9, 28}, + {25.4, 51.5, 28}, + {21.9, 51.5, 28}, + {23.4, 51.1, 28}, + {17.8, 50.9, 28}, + {19.2, 50.5, 28}, + {22, 49.2, 28}, + {23.3, 49.1, 28}, + {34.1, 30.7, 28}, + {32.4, 28.6, 28}, + {34.1, 28.5, 28}, + {30.9, 26.3, 28}, + {28.3, 24.7, 28}, + {30.9, 24, 28}, + {81.9, 74.3, 85}, + {78.6, 73.4, 85}, + {80.1, 72.8, 85}, + {75.5, 72.4, 85}, + {76.6, 72.3, 85}, + {78.6, 71.7, 85}, + {74.2, 71.5, 85}, + {76.9, 71.3, 85}, + {79.3, 70.7, 85}, + {80.7, 70.5, 85}, + {78.2, 70.2, 85}, + {78.7, 69.7, 85}, + {79.6, 69.2, 85}, + {44.9, 69.1, 85}, + {44, 68, 85}, + {47.9, 67.8, 85}, + {80.4, 67.7, 85}, + {74, 67.3, 85}, + {45.9, 67.3, 85}, + {43.1, 67.1, 85}, + {81.3, 66.5, 85}, + {80.7, 66.4, 85}, + {46.9, 66.4, 85}, + {83.7, 66.3, 85}, + {45.3, 66.3, 85}, + {76.4, 66.2, 85}, + {82.2, 66, 85}, + {72.7, 65.3, 85}, + {82, 64.6, 85}, + {78.7, 64.6, 85}, + {80.2, 64.3, 85}, + {74.9, 64, 85}, + {76.1, 63.7, 85}, + {52.9, 63.6, 85}, + {48.6, 63.6, 85}, + {51.9, 63.4, 85}, + {41.6, 63.4, 85}, + {48.4, 63.2, 85}, + {44.9, 63.2, 85}, + {43.2, 63, 85}, + {45.5, 62.8, 85}, + {72.8, 62.6, 85}, + {44.4, 62.6, 85}, + {78.8, 62.4, 85}, + {80.1, 62.3, 85}, + {47.5, 62.2, 85}, + {48.7, 62, 85}, + {51.4, 61.8, 85}, + {50.7, 61.7, 85}, + {46.3, 61.5, 85}, + {70.5, 61.2, 85}, + {41.6, 60.9, 85}, + {69.8, 60.7, 85}, + {44.7, 60.3, 85}, + {40.6, 60, 85}, + {72.1, 59.8, 85}, + {49.1, 59.8, 85}, + {42.1, 59.4, 85}, + {73.4, 59.3, 85}, + {80.5, 59.3, 85}, + {79.2, 59, 85}, + {74.6, 58.7, 85}, + {42.2, 58.6, 85}, + {71, 58.4, 85}, + {72.8, 58.3, 85}, + {73.7, 57.1, 85}, + {75.6, 55.8, 85}, + {70.2, 53.7, 85}, + {75, 53, 85}, + {82.2, 52.8, 85}, + {83.7, 51.6, 85}, + {75.8, 51.3, 85}, + {71.3, 50.1, 85}, + {71.7, 49, 85}, + {79.3, 48.7, 85}, + {83.6, 48.4, 85}, + {76.8, 48.3, 85}, + {71.2, 48.2, 85}, + {81.1, 48.2, 85}, + {75.7, 47.9, 85}, + {74.5, 47.3, 85}, + {75.5, 47.1, 85}, + {72.7, 47.1, 85}, + {71.2, 46.8, 85}, + {82.8, 46.3, 85}, + {80.7, 46.3, 85}, + {75.4, 46.1, 85}, + {77.8, 46.1, 85}, + {79, 45.9, 85}, + {78.6, 44.9, 85}, + {84.4, 44.9, 85}, + {90.4, 44.8, 85}, + {80, 44.5, 85}, + {83.1, 44.3, 85}, + {73.2, 44.3, 85}, + {74.8, 44.2, 85}, + {84.1, 43.1, 85}, + {88.7, 42.8, 85}, + {90.4, 42.8, 85}, + {82.5, 42.8, 85}, + {72.9, 42.6, 85}, + {82.9, 42.6, 85}, + {84.6, 40.8, 85}, + {87.3, 40.6, 85}, + {73.3, 40.6, 85}, + {73, 39.3, 85}, + {84.8, 39.1, 85}, + {87.3, 38.4, 85}, + {72.6, 37.2, 85}, + {71.2, 36.9, 85}, + {64.5, 36.6, 85}, + {73.4, 36.1, 85}, + {75.4, 35.1, 85}, + {65.8, 34.9, 85}, + {69.7, 34.8, 85}, + {71.1, 34.6, 85}, + {67.5, 34, 85}, + {73.7, 33.9, 85}, + {65.5, 33.8, 85}, + {71.9, 33.7, 85}, + {70.4, 33.7, 85}, + {74.9, 33.7, 85}, + {69.7, 32.8, 85}, + {68.2, 32.8, 85}, + {74.2, 32.6, 85}, + {66.2, 31.7, 85}, + {76.8, 31.6, 85}, + {71.8, 31.6, 85}, + {72.8, 30.6, 85}, + {71.2, 30.5, 85}, + {69.7, 30.5, 85}, + {76.4, 30.3, 85}, + {73.3, 29.2, 85}, + {68.6, 28.3, 85}, + {77.7, 46.2, 85}, + }, + [1555] = { + {85.9, 57.3, 85}, + {86.8, 57, 85}, + {87.4, 56.1, 85}, + {83.4, 55.6, 85}, + {83.8, 55.4, 85}, + {85.4, 55.1, 85}, + {88.2, 55, 85}, + {82.4, 54.8, 85}, + {87.5, 53.8, 85}, + {86.6, 53.8, 85}, + {88.9, 53.7, 85}, + {85.9, 53.7, 85}, + {88, 53.7, 85}, + {86.7, 53.2, 85}, + {84.7, 52.8, 85}, + {85.7, 52.5, 85}, + {89.5, 52.5, 85}, + {86, 52.5, 85}, + {83.7, 52.4, 85}, + {89.7, 51.7, 85}, + {87.3, 51.2, 85}, + {84.5, 51, 85}, + {85.1, 51, 85}, + {91.1, 50.5, 85}, + {86.7, 50.5, 85}, + {89.8, 50.4, 85}, + {88.1, 50.4, 85}, + {84.5, 50.1, 85}, + {89.1, 49.8, 85}, + {91.8, 49.5, 85}, + {87.4, 49.3, 85}, + {90.3, 49.3, 85}, + {85.9, 49.3, 85}, + {86.7, 48.4, 85}, + {85.2, 48.3, 85}, + {84.5, 47.2, 85}, + }, + [1653] = { + {30.6, 90.4, 33}, + {33.1, 88.4, 33}, + {33.5, 88, 33}, + {30.6, 90.3, 33}, + {30.7, 90.2, 33}, + {30.7, 89.9, 33}, + {30.6, 89.9, 33}, + {30.5, 89.7, 33}, + {30.6, 89.7, 33}, + {30.6, 89.6, 33}, + {30.7, 89.6, 33}, + {29.4, 89.4, 33}, + {29.4, 89.1, 33}, + {29.3, 89.1, 33}, + {29.3, 89, 33}, + {29.4, 88.9, 33}, + {29.4, 88.8, 33}, + {29.3, 88.6, 33}, + {29.3, 88.5, 33}, + {29.2, 88.4, 33}, + {32.9, 88.3, 33}, + {33.6, 88.2, 33}, + {33.6, 88.1, 33}, + {33.3, 88.1, 33}, + {32.8, 56.5, 33}, + {32.8, 56.3, 33}, + {32, 56.1, 33}, + {32, 55.9, 33}, + {32, 55.8, 33}, + {31.8, 54.7, 33}, + {32.1, 54.6, 33}, + {31.8, 54.5, 33}, + {32, 54.4, 33}, + {31.9, 54.4, 33}, + {32, 54.3, 33}, + {31.5, 54.3, 33}, + {31.8, 54.2, 33}, + {31.9, 53.9, 33}, + {30.7, 90.3, 33}, + {30.7, 89.5, 33}, + {29.4, 89, 33}, + {29.4, 88.6, 33}, + {29.2, 88.3, 33}, + }, + [1654] = { + {46.7, 29.3, 85}, + }, + [1655] = { + {49.7, 36.3, 85}, + }, + [1656] = { + {44, 33.6, 85}, + }, + [1657] = { + {47.3, 40.8, 85}, + }, + [1660] = { + {79.5, 25.3, 85}, + {79.4, 25.1, 85}, + }, + [1662] = { + {51.2, 67.8, 85}, + {51.1, 67.8, 85}, + }, + [1674] = { + {59.4, 44.2, 85}, + {58.7, 43.9, 85}, + {58.8, 42.7, 85}, + {58.7, 42, 85}, + {58.5, 40.6, 85}, + {57.6, 39.4, 85}, + {59.2, 39.3, 85}, + {58.8, 38.5, 85}, + {56.9, 38.2, 85}, + {57.9, 37.2, 85}, + {57.3, 36.1, 85}, + {56.7, 35.4, 85}, + {59.5, 35, 85}, + {60, 34, 85}, + {57.1, 33.8, 85}, + {58.8, 33.7, 85}, + {58.5, 44, 85}, + }, + [1675] = { + {60.4, 38.9, 85}, + {60.2, 38.4, 85}, + {60.9, 37.2, 85}, + {59.4, 37.1, 85}, + {58.6, 36.1, 85}, + {59.9, 36.1, 85}, + {58, 35.2, 85}, + {58.2, 34, 85}, + {56.3, 33.7, 85}, + {59.4, 33.7, 85}, + {60.8, 33.6, 85}, + {59.5, 32.7, 85}, + {57.7, 32.6, 85}, + {57.9, 32.5, 85}, + {58.2, 32.5, 85}, + {60.9, 32.5, 85}, + {59.5, 31.9, 85}, + {58, 31.8, 85}, + {58.4, 31.7, 85}, + {58.5, 31.3, 85}, + {58.7, 31, 85}, + {60.9, 37.4, 85}, + {60.8, 33.7, 85}, + {58.6, 31.7, 85}, + {57.8, 31.5, 85}, + }, + [1689] = { + {70.9, 61.3, 1}, + {74.9, 60.8, 1}, + {73, 59.9, 1}, + {78.7, 59.3, 1}, + {75.7, 57.8, 1}, + {79.7, 57.6, 1}, + {79.4, 55.8, 1}, + {81.6, 55.4, 1}, + {73.5, 55.2, 1}, + {71.2, 54, 1}, + {81.4, 53.1, 1}, + {74, 53, 1}, + {79.6, 51.9, 1}, + {84.9, 50.7, 1}, + {73.5, 50.6, 1}, + {86.7, 50.2, 1}, + {75.7, 49.9, 1}, + {78.9, 49.5, 1}, + {74.1, 49, 1}, + {74.9, 48.9, 1}, + {84.7, 48.3, 1}, + {80.2, 47.5, 1}, + {86.3, 47.5, 1}, + {79.8, 46.4, 1}, + {81, 43.9, 1}, + {80.1, 43.9, 1}, + {83.2, 38.1, 1}, + {82.5, 37.8, 1}, + {82.7, 35.5, 1}, + {80.8, 35.3, 1}, + {80.2, 34.7, 1}, + {83.1, 33.1, 1}, + {17.1, 56.5, 38}, + {0.1, 81.5, 5602}, + {1, 79.9, 5602}, + {0.7, 78.3, 5602}, + {2.7, 77.9, 5602}, + {2.5, 75.8, 5602}, + {0.9, 74.7, 5602}, + {5.7, 73.6, 5602}, + {7.3, 73.2, 5602}, + {0.2, 72.5, 5602}, + {5.5, 71.4, 5602}, + {1.4, 70.7, 5602}, + {7, 70.7, 5602}, + {1, 69.6, 5602}, + {2.2, 67.4, 5602}, + {1.3, 67.3, 5602}, + {4.2, 62.1, 5602}, + {3.5, 61.8, 5602}, + {3.7, 59.7, 5602}, + {2, 59.5, 5602}, + {1.4, 58.9, 5602}, + {4.1, 57.5, 5602}, + }, + [1753] = { + {58.7, 30.8, 85}, + }, + [1765] = { + {51.8, 77.2, 85}, + {51, 77.1, 85}, + {51.9, 77, 85}, + {53.2, 74.7, 85}, + {43.2, 52.4, 130}, + {45.8, 50.2, 130}, + {47.9, 48.1, 130}, + {44.2, 47.9, 130}, + {46.5, 45.9, 130}, + {65, 11.2, 130}, + {63.8, 11.1, 130}, + {66, 10.4, 130}, + {66.4, 9, 130}, + {66.1, 8.8, 130}, + {67.6, 8.4, 130}, + {58, 8.3, 130}, + {64.4, 8, 130}, + {69.1, 7.8, 130}, + {63.7, 7.4, 130}, + {69.3, 7.4, 130}, + {64.8, 7.3, 130}, + {70.2, 7.1, 130}, + {69.8, 6.9, 130}, + {68.3, 6.6, 130}, + {66.6, 6.3, 130}, + {68.3, 6.3, 130}, + {64.8, 5.9, 130}, + {65.7, 5.5, 130}, + {68.9, 5.5, 130}, + {64.8, 5.5, 130}, + {65.8, 5.3, 130}, + {68.8, 4.9, 130}, + {70, 4.7, 130}, + {69.9, 4.3, 130}, + {67.1, 2.9, 130}, + {25.4, 45.6, 1497}, + }, + [1769] = { + {50.7, 47.8, 130}, + {51.8, 46.6, 130}, + {51.8, 44.9, 130}, + {53.9, 43.2, 130}, + {51.4, 43, 130}, + {52, 41.7, 130}, + {54.1, 41.4, 130}, + {48.3, 40.8, 130}, + {49.8, 40.7, 130}, + {55.3, 40.3, 130}, + {49.2, 39.6, 130}, + {50.2, 39.2, 130}, + {52.9, 38, 130}, + {48.9, 36.7, 130}, + {48.4, 35.2, 130}, + {49.4, 34.9, 130}, + {47.8, 34.1, 130}, + {49.1, 34, 130}, + {48.5, 31.7, 130}, + {52.8, 28.5, 130}, + {51.8, 28.1, 130}, + {52.4, 28, 130}, + {52.3, 26.9, 130}, + {54.5, 26.7, 130}, + {53, 26.7, 130}, + {51.4, 26.5, 130}, + {52, 26.3, 130}, + {52.7, 26.2, 130}, + {55.9, 25.4, 130}, + {52.2, 24.8, 130}, + {54.2, 24.6, 130}, + {53.4, 24.4, 130}, + {54.5, 23.8, 130}, + {55.4, 23.1, 130}, + {53.6, 22.5, 130}, + {54.9, 21.7, 130}, + {56, 20.9, 130}, + {54.8, 20.8, 130}, + {55.5, 20.8, 130}, + {55.6, 20.4, 130}, + {55.5, 20, 130}, + {55.1, 20, 130}, + {55.8, 19.9, 130}, + {55.7, 19.8, 130}, + {56.7, 19.5, 130}, + {55.7, 19.1, 130}, + {56.3, 18.6, 130}, + {51.5, 43.1, 130}, + {52.2, 41.8, 130}, + {48.1, 40.6, 130}, + {49.8, 40.5, 130}, + {49.1, 39.6, 130}, + {48.5, 35.2, 130}, + {49.3, 34.2, 130}, + {48.5, 31.8, 130}, + {51.9, 28.2, 130}, + {55.9, 20.7, 130}, + {55.8, 19.2, 130}, + }, + [1922] = { + {60.4, 71.3, 12}, + {62.2, 70.9, 12}, + {61.1, 70.1, 12}, + {69.9, 69.5, 12}, + {59.2, 69.4, 12}, + {63.1, 69, 12}, + {74, 68.9, 12}, + {59.9, 68.6, 12}, + {66.2, 67.8, 12}, + {72.8, 67.6, 12}, + {59.4, 66.9, 12}, + {61.3, 66.8, 12}, + {60.2, 66.7, 12}, + {71.6, 66.5, 12}, + {61.4, 66.4, 12}, + {73.8, 66.2, 12}, + {59.7, 65.9, 12}, + {68.2, 65.8, 12}, + {60.1, 65.8, 12}, + {66.2, 65.6, 12}, + {59.7, 65.4, 12}, + {68.6, 65.1, 12}, + {74.2, 64.9, 12}, + {59.1, 64.7, 12}, + {65.1, 64.4, 12}, + {60.9, 64.3, 12}, + {60.1, 64.3, 12}, + {69.3, 64.1, 12}, + {67.6, 64.1, 12}, + {60.6, 64, 12}, + {58.7, 63.9, 12}, + {73.6, 63.7, 12}, + {60.6, 63.7, 12}, + {63.2, 63.7, 12}, + {65.6, 63.6, 12}, + {70.4, 63.6, 12}, + {60.9, 63.6, 12}, + {61, 63.4, 12}, + {72.8, 63.2, 12}, + {71.8, 63.2, 12}, + {69, 62.9, 12}, + {66.9, 62.8, 12}, + {67.6, 62.2, 12}, + {58.4, 62.1, 12}, + {69.8, 61.8, 12}, + {70.5, 61.8, 12}, + {60.9, 61.5, 12}, + {69.8, 61.1, 12}, + {61.8, 61.1, 12}, + {71.4, 61.1, 12}, + {64.6, 60.3, 12}, + {60.4, 59.7, 12}, + {68.4, 59.5, 12}, + {66.4, 58.2, 12}, + {68.1, 57.9, 12}, + {69, 57.4, 12}, + {59.8, 65.9, 12}, + {73.8, 63.6, 12}, + }, + [1934] = { + {38, 51.9, 85}, + {38.3, 51.8, 85}, + {37.5, 51.6, 85}, + {36.5, 51.6, 85}, + {34.6, 50.7, 85}, + {37.8, 50.6, 85}, + {35.7, 50.5, 85}, + {38.1, 50.4, 85}, + {38, 49.9, 85}, + {36.5, 51.8, 85}, + {38.2, 51.8, 85}, + {35.8, 50.7, 85}, + {37.9, 50.6, 85}, + }, + [1941] = { + {56.6, 46.2, 85}, + {56.5, 46.1, 85}, + {56.8, 45, 85}, + {57.1, 44.9, 85}, + {56.1, 44, 85}, + {53.8, 43.9, 85}, + {55.2, 43.9, 85}, + {56.3, 43.7, 85}, + {56.2, 43.6, 85}, + {58.3, 42.9, 85}, + {54.3, 42.8, 85}, + {55.5, 42.8, 85}, + {55.2, 42.6, 85}, + {54.9, 42.4, 85}, + {55.2, 42.2, 85}, + {57.7, 41.9, 85}, + {55.4, 41.9, 85}, + {53.3, 41.5, 85}, + {55, 41.4, 85}, + {56.4, 41.1, 85}, + {55.4, 41, 85}, + {55.8, 40.5, 85}, + {57.1, 40.3, 85}, + {56.6, 39.7, 85}, + {55.6, 39.2, 85}, + {57.2, 44.7, 85}, + }, + [1993] = { + {44.6, 62.5, 141}, + }, + [1996] = { + {53.1, 77.9, 141}, + {52.5, 77.8, 141}, + {43.8, 77.4, 141}, + {51.9, 77.4, 141}, + {42.9, 77.1, 141}, + {53.7, 77.1, 141}, + {58.9, 77, 141}, + {53.3, 76.6, 141}, + {45.6, 76.5, 141}, + {58.1, 76.2, 141}, + {46.2, 75.5, 141}, + {42.2, 75.3, 141}, + {57.6, 75, 141}, + {58.5, 75, 141}, + {48.2, 74.2, 141}, + {42.4, 72.3, 141}, + {41.9, 71.5, 141}, + {64.3, 69.2, 141}, + {41.1, 68.4, 141}, + {64.2, 67.2, 141}, + {65.9, 67, 141}, + {65.5, 66.5, 141}, + {39.3, 65.3, 141}, + {38.7, 63.4, 141}, + {38.4, 62.5, 141}, + {38.2, 55.6, 141}, + {39.5, 54.5, 141}, + {39.7, 53.7, 141}, + {39.5, 53, 141}, + {42.7, 52.1, 141}, + {38.8, 51.5, 141}, + {39.1, 51.4, 141}, + {37.6, 51.3, 141}, + {43.9, 50.6, 141}, + {41.3, 49.7, 141}, + {42.8, 48.9, 141}, + {98.1, 20.9, 1657}, + }, + [1997] = { + {38.5, 47.8, 141}, + {38.5, 46.2, 141}, + {39.3, 46.1, 141}, + {40.8, 45, 141}, + {45.6, 40.6, 141}, + {44.4, 39.9, 141}, + {46.1, 39.7, 141}, + {38.1, 39.5, 141}, + {38, 38.3, 141}, + {37.7, 37.9, 141}, + {46.2, 31.4, 141}, + {39, 30.7, 141}, + {37.6, 29.3, 141}, + }, + [1999] = { + {45.8, 78.9, 141}, + {45.1, 77.9, 141}, + {44.8, 77.5, 141}, + {54.5, 77.4, 141}, + {54.3, 76.1, 141}, + {59.6, 76.1, 141}, + {49, 76, 141}, + {41.2, 76, 141}, + {48.5, 75.1, 141}, + {61.5, 75, 141}, + {60.2, 75, 141}, + {41.8, 75, 141}, + {42.9, 74.7, 141}, + {44.7, 74.5, 141}, + {54.3, 74.2, 141}, + {61, 73.4, 141}, + {55, 73.2, 141}, + {45.3, 73.1, 141}, + {46.5, 71.6, 141}, + {45.4, 70.2, 141}, + {65.2, 69.1, 141}, + {38.9, 61.7, 141}, + {39.8, 60, 141}, + {38.8, 59.6, 141}, + {43.8, 54.4, 141}, + {42.8, 52.6, 141}, + {40.7, 48.8, 141}, + {40.4, 48.6, 141}, + {38.7, 47.9, 141}, + {37.2, 47.4, 141}, + {96, 2, 1657}, + {43.7, 54.3, 141}, + }, + [2000] = { + {43.7, 47.3, 141}, + {42.4, 46.3, 141}, + {43.2, 45.5, 141}, + {48.6, 44.9, 141}, + {47.8, 44.5, 141}, + {43.8, 44.5, 141}, + {43.1, 43.5, 141}, + {45.8, 43.4, 141}, + {45.1, 35.1, 141}, + {47.4, 34.1, 141}, + {34.6, 32.6, 141}, + {33.7, 32.3, 141}, + {36.3, 32.3, 141}, + {47.9, 32.1, 141}, + {39.9, 31.4, 141}, + {33.3, 30.9, 141}, + {33.6, 32.3, 141}, + {33.3, 31, 141}, + }, + [2001] = { + {41.2, 28.9, 141}, + {38, 27.9, 141}, + {45.7, 27.5, 141}, + {40.2, 25.4, 141}, + }, + [2004] = { + {51.2, 51, 141}, + {52.4, 50.2, 141}, + {52.8, 50, 141}, + {51.7, 50, 141}, + {51.4, 50, 141}, + {52.6, 49.8, 141}, + {53, 49.3, 141}, + {51.7, 48.9, 141}, + }, + [2005] = { + {52.1, 52.1, 141}, + {51.6, 51.6, 141}, + {51.5, 51.4, 141}, + {52.3, 51.1, 141}, + {52.2, 50.7, 141}, + {52, 50.6, 141}, + {51.3, 50.6, 141}, + {51.8, 50.5, 141}, + {52.7, 49.5, 141}, + {51.5, 48.8, 141}, + {52.1, 48.5, 141}, + {52.2, 51.1, 141}, + }, + [2008] = { + {49.7, 68.1, 141}, + {50.1, 66.8, 141}, + {49.7, 66.4, 141}, + {50.3, 66.2, 141}, + {50, 65.6, 141}, + {65.6, 64.9, 141}, + {65.7, 64.8, 141}, + {65.6, 64.7, 141}, + {65.9, 63.7, 141}, + {50.3, 62.1, 141}, + {50.9, 62, 141}, + {49.8, 61.4, 141}, + {50.4, 61.2, 141}, + {67.8, 60.1, 141}, + {67, 59.7, 141}, + {66, 59.6, 141}, + {68, 59.4, 141}, + {66.4, 59.3, 141}, + {66.3, 59, 141}, + {66.3, 58.6, 141}, + {65.8, 58.2, 141}, + {66.6, 58.2, 141}, + {67, 57.6, 141}, + {65.9, 57.4, 141}, + {66.5, 57.2, 141}, + {67.4, 57, 141}, + {69.7, 53.9, 141}, + {68.6, 53.2, 141}, + {70.3, 53.1, 141}, + {68.7, 52.8, 141}, + {70.2, 52.7, 141}, + {69.4, 52.6, 141}, + {68.3, 52.5, 141}, + {68, 52.5, 141}, + {70.5, 52.4, 141}, + {68.3, 52.1, 141}, + {68.5, 51.7, 141}, + {69.3, 52.8, 141}, + {68, 52.6, 141}, + }, + [2009] = { + {48.6, 78.8, 141}, + {47.6, 78.5, 141}, + {48.6, 78.1, 141}, + {45.8, 78.1, 141}, + {49, 78.1, 141}, + {46.8, 77.9, 141}, + {47.9, 77.9, 141}, + {48, 77.8, 141}, + {47.1, 77.7, 141}, + {48.2, 77.7, 141}, + {47.9, 77.6, 141}, + {47.3, 77.4, 141}, + {46.4, 77.3, 141}, + {56.5, 77.2, 141}, + {47.3, 76.9, 141}, + {49, 76.9, 141}, + {56.2, 76.6, 141}, + {56.8, 76.5, 141}, + {57.2, 76, 141}, + {55.6, 75.8, 141}, + {54.9, 75.2, 141}, + {56.9, 75, 141}, + {41.4, 71.9, 141}, + {38.4, 67.9, 141}, + {49.3, 67.4, 141}, + {38.5, 67.2, 141}, + {39.1, 67, 141}, + {37.4, 67, 141}, + {37.9, 66.9, 141}, + {49.4, 66.8, 141}, + {49.5, 66.8, 141}, + {38.2, 66.8, 141}, + {49.4, 66.6, 141}, + {37.4, 66.3, 141}, + {39.3, 66.2, 141}, + {38.5, 66.2, 141}, + {37.9, 66.2, 141}, + {49.4, 66.1, 141}, + {38.3, 65.6, 141}, + {50, 63.2, 141}, + {50.5, 62.9, 141}, + {50, 62.7, 141}, + {49.6, 62.6, 141}, + {50, 62.5, 141}, + {43.3, 61.9, 141}, + {43.8, 61.8, 141}, + {44.3, 61.7, 141}, + {43.9, 61.5, 141}, + {43.8, 61.3, 141}, + {42.9, 61.2, 141}, + {44.4, 61.2, 141}, + {43.9, 61.1, 141}, + {44.1, 61.1, 141}, + {43.3, 60.7, 141}, + {44.3, 60.5, 141}, + {43.7, 60.5, 141}, + {44.5, 60.5, 141}, + {44.4, 60.4, 141}, + {44.2, 60.3, 141}, + {43.9, 60.2, 141}, + {45.1, 60.1, 141}, + {44.5, 60, 141}, + {43.6, 60, 141}, + {43.9, 60, 141}, + {45.5, 59.9, 141}, + {43.1, 59.6, 141}, + {44.1, 59.5, 141}, + {44.2, 59.4, 141}, + {43, 59.1, 141}, + {45.6, 59.1, 141}, + {45.5, 58.9, 141}, + {45.3, 58.8, 141}, + {43.3, 58.8, 141}, + {44.7, 58.7, 141}, + {45.2, 58.7, 141}, + {44.4, 58.5, 141}, + {43.7, 58.5, 141}, + {44.3, 58.5, 141}, + {46.2, 58.4, 141}, + {45, 58.4, 141}, + {44.2, 58.2, 141}, + {45.4, 58.1, 141}, + {43.9, 58, 141}, + {45.2, 57.8, 141}, + {45.6, 57.7, 141}, + {43.9, 57.7, 141}, + {45.1, 57.7, 141}, + {44.6, 57.6, 141}, + {45.1, 57.6, 141}, + {44.5, 57.5, 141}, + {44.3, 57.4, 141}, + {43.9, 57.4, 141}, + {44.9, 57.3, 141}, + {44.5, 57.1, 141}, + {44.1, 57.1, 141}, + {44.4, 57.1, 141}, + {44.7, 56.9, 141}, + {44, 56.6, 141}, + {44.8, 56.1, 141}, + {97.1, 96.4, 1657}, + {99.8, 96.1, 1657}, + {97.4, 93, 1657}, + {99.6, 92.6, 1657}, + {37.8, 67.1, 141}, + {43.8, 61.7, 141}, + {43.9, 61.4, 141}, + {44.3, 60.6, 141}, + {45.6, 59, 141}, + {44.8, 58.7, 141}, + {43.7, 58.6, 141}, + {45, 58.3, 141}, + {45.3, 58.3, 141}, + {44.3, 57.5, 141}, + {44.6, 56.7, 141}, + {99.2, 97.1, 1657}, + }, + [2010] = { + {48.6, 78.8, 141}, + {47.6, 78.5, 141}, + {48.6, 78.1, 141}, + {45.8, 78.1, 141}, + {49, 78.1, 141}, + {46.8, 77.9, 141}, + {47.9, 77.9, 141}, + {48, 77.8, 141}, + {47.1, 77.7, 141}, + {48.2, 77.7, 141}, + {47.9, 77.6, 141}, + {47.3, 77.4, 141}, + {46.4, 77.3, 141}, + {56.5, 77.2, 141}, + {47.3, 76.9, 141}, + {49, 76.9, 141}, + {56.2, 76.6, 141}, + {56.8, 76.5, 141}, + {57.2, 76, 141}, + {55.6, 75.8, 141}, + {54.9, 75.2, 141}, + {56.9, 75, 141}, + {41.4, 71.9, 141}, + {38.4, 67.9, 141}, + {49.3, 67.4, 141}, + {38.5, 67.2, 141}, + {39.1, 67, 141}, + {37.4, 67, 141}, + {37.9, 66.9, 141}, + {49.4, 66.8, 141}, + {49.5, 66.8, 141}, + {38.2, 66.8, 141}, + {49.4, 66.6, 141}, + {37.4, 66.3, 141}, + {39.3, 66.2, 141}, + {38.5, 66.2, 141}, + {37.9, 66.2, 141}, + {49.4, 66.1, 141}, + {38.3, 65.6, 141}, + {50, 63.2, 141}, + {50.5, 62.9, 141}, + {50, 62.7, 141}, + {49.6, 62.6, 141}, + {50, 62.5, 141}, + {43.3, 61.9, 141}, + {43.8, 61.8, 141}, + {44.3, 61.7, 141}, + {43.9, 61.5, 141}, + {43.8, 61.3, 141}, + {42.9, 61.2, 141}, + {44.4, 61.2, 141}, + {43.9, 61.1, 141}, + {44.1, 61.1, 141}, + {43.3, 60.7, 141}, + {44.3, 60.5, 141}, + {43.7, 60.5, 141}, + {44.5, 60.5, 141}, + {44.4, 60.4, 141}, + {44.2, 60.3, 141}, + {43.9, 60.2, 141}, + {45.1, 60.1, 141}, + {44.5, 60, 141}, + {43.6, 60, 141}, + {43.9, 60, 141}, + {45.5, 59.9, 141}, + {43.1, 59.6, 141}, + {44.1, 59.5, 141}, + {44.2, 59.4, 141}, + {43, 59.1, 141}, + {45.6, 59.1, 141}, + {45.5, 58.9, 141}, + {45.3, 58.8, 141}, + {43.3, 58.8, 141}, + {44.7, 58.7, 141}, + {45.2, 58.7, 141}, + {44.4, 58.5, 141}, + {43.7, 58.5, 141}, + {44.3, 58.5, 141}, + {46.2, 58.4, 141}, + {45, 58.4, 141}, + {44.2, 58.2, 141}, + {45.4, 58.1, 141}, + {43.9, 58, 141}, + {45.2, 57.8, 141}, + {45.6, 57.7, 141}, + {43.9, 57.7, 141}, + {45.1, 57.7, 141}, + {44.6, 57.6, 141}, + {45.1, 57.6, 141}, + {44.5, 57.5, 141}, + {44.3, 57.4, 141}, + {43.9, 57.4, 141}, + {44.9, 57.3, 141}, + {44.5, 57.1, 141}, + {44.1, 57.1, 141}, + {44.4, 57.1, 141}, + {44.7, 56.9, 141}, + {44, 56.6, 141}, + {44.8, 56.1, 141}, + {97.1, 96.4, 1657}, + {99.8, 96.1, 1657}, + {97.4, 93, 1657}, + {99.6, 92.6, 1657}, + {37.8, 67.1, 141}, + {43.8, 61.7, 141}, + {43.9, 61.4, 141}, + {44.3, 60.6, 141}, + {45.6, 59, 141}, + {44.8, 58.7, 141}, + {43.7, 58.6, 141}, + {45, 58.3, 141}, + {45.3, 58.3, 141}, + {44.3, 57.5, 141}, + {44.6, 56.7, 141}, + {99.2, 97.1, 1657}, + }, + [2011] = { + {47.5, 77.9, 141}, + {56.4, 75.6, 141}, + {56.6, 75.5, 141}, + {56.3, 75.3, 141}, + {41.4, 73.9, 141}, + {41.5, 73.2, 141}, + {40.8, 72.8, 141}, + {41.3, 72.7, 141}, + {41.8, 72.5, 141}, + {41.3, 72.3, 141}, + {44.5, 62.7, 141}, + {44.5, 62.3, 141}, + {45, 61.5, 141}, + {44.6, 61.3, 141}, + {45, 61.3, 141}, + {44.7, 59, 141}, + {44.8, 58.7, 141}, + {44.9, 58.6, 141}, + {46.2, 58.4, 141}, + {45.6, 58.4, 141}, + {46.1, 58.3, 141}, + {46.2, 58.2, 141}, + {46, 57.2, 141}, + {45.8, 57.1, 141}, + {45.5, 58.9, 141}, + {45.1, 57.7, 141}, + {44.4, 62, 141}, + {44.9, 61.5, 141}, + {45.2, 57.7, 141}, + {45.9, 57, 141}, + }, + [2012] = { + {38.1, 81.3, 141}, + {38.2, 80.4, 141}, + {39.9, 80.3, 141}, + {38.6, 80.3, 141}, + {42, 80.2, 141}, + {40.2, 80.1, 141}, + {38.6, 80.1, 141}, + {41.8, 80, 141}, + {42.1, 80, 141}, + {42.4, 79.9, 141}, + {40, 79.8, 141}, + {42, 79.7, 141}, + {40, 79.7, 141}, + {40.6, 79.6, 141}, + {38.7, 79.5, 141}, + {41.5, 79.4, 141}, + {40.3, 79.4, 141}, + {42.4, 79.2, 141}, + {40.6, 79.1, 141}, + {38.8, 79, 141}, + {41.3, 79, 141}, + {43.3, 79, 141}, + {38.8, 78.4, 141}, + {41.4, 77.9, 141}, + {41.3, 77, 141}, + {40.3, 80.1, 141}, + }, + [2013] = { + {38.1, 81.3, 141}, + {38.2, 80.4, 141}, + {39.9, 80.3, 141}, + {38.6, 80.3, 141}, + {42, 80.2, 141}, + {40.2, 80.1, 141}, + {38.6, 80.1, 141}, + {41.8, 80, 141}, + {42.1, 80, 141}, + {42.4, 79.9, 141}, + {40, 79.8, 141}, + {42, 79.7, 141}, + {40, 79.7, 141}, + {40.6, 79.6, 141}, + {38.7, 79.5, 141}, + {41.5, 79.4, 141}, + {40.3, 79.4, 141}, + {42.4, 79.2, 141}, + {40.6, 79.1, 141}, + {38.8, 79, 141}, + {41.3, 79, 141}, + {43.3, 79, 141}, + {38.8, 78.4, 141}, + {41.4, 77.9, 141}, + {41.3, 77, 141}, + {40.3, 80.1, 141}, + }, + [2014] = { + {37.9, 81.2, 141}, + {38.4, 80.8, 141}, + {39.4, 80.2, 141}, + {39.1, 79.9, 141}, + {38.8, 78.9, 141}, + }, + [2015] = { + {37.9, 44.7, 141}, + {37.3, 43.7, 141}, + {37.9, 43.7, 141}, + {37.3, 43.3, 141}, + {37.9, 43, 141}, + {37.2, 43, 141}, + {38.7, 42.9, 141}, + {36.6, 41.8, 141}, + {37.3, 41.6, 141}, + {37.5, 41.3, 141}, + {37.7, 41.1, 141}, + {38.5, 40.7, 141}, + {37.2, 40.4, 141}, + {36.4, 39.9, 141}, + {35.2, 38.9, 141}, + {36.8, 38.9, 141}, + {36, 38.9, 141}, + {35.6, 38.7, 141}, + {36.4, 38.1, 141}, + {36, 37.8, 141}, + {37.3, 37.8, 141}, + {35.4, 37.7, 141}, + {36.8, 37.7, 141}, + {35.8, 37.3, 141}, + {35.3, 36.8, 141}, + {34.4, 35.5, 141}, + {34.2, 35.4, 141}, + {33.8, 35.4, 141}, + {35.2, 35.1, 141}, + {34.3, 35, 141}, + {35, 35, 141}, + {34.7, 34.9, 141}, + {35.4, 34.9, 141}, + {34.2, 34.8, 141}, + {34.3, 34.4, 141}, + {34.3, 34.1, 141}, + {37.8, 44.7, 141}, + {37.3, 41.7, 141}, + {38.6, 40.5, 141}, + {35.6, 38.5, 141}, + {36.7, 37.7, 141}, + {35.5, 37, 141}, + {34.1, 35.5, 141}, + {34.3, 35.1, 141}, + {34.7, 35, 141}, + {35.5, 34.9, 141}, + {35, 34.9, 141}, + {34.4, 34.4, 141}, + }, + [2017] = { + {37.9, 44.7, 141}, + {37.3, 43.7, 141}, + {37.9, 43.7, 141}, + {37.3, 43.3, 141}, + {37.9, 43, 141}, + {37.2, 43, 141}, + {38.7, 42.9, 141}, + {36.6, 41.8, 141}, + {37.3, 41.6, 141}, + {37.5, 41.3, 141}, + {37.7, 41.1, 141}, + {38.5, 40.7, 141}, + {37.2, 40.4, 141}, + {36.4, 39.9, 141}, + {35.2, 38.9, 141}, + {36.8, 38.9, 141}, + {36, 38.9, 141}, + {35.6, 38.7, 141}, + {36.4, 38.1, 141}, + {36, 37.8, 141}, + {37.3, 37.8, 141}, + {35.4, 37.7, 141}, + {36.8, 37.7, 141}, + {35.8, 37.3, 141}, + {35.3, 36.8, 141}, + {34.4, 35.5, 141}, + {34.2, 35.4, 141}, + {33.8, 35.4, 141}, + {35.2, 35.1, 141}, + {34.3, 35, 141}, + {35, 35, 141}, + {34.7, 34.9, 141}, + {35.4, 34.9, 141}, + {34.2, 34.8, 141}, + {34.3, 34.4, 141}, + {34.3, 34.1, 141}, + {37.8, 44.7, 141}, + {37.3, 41.7, 141}, + {38.6, 40.5, 141}, + {35.6, 38.5, 141}, + {36.7, 37.7, 141}, + {35.5, 37, 141}, + {34.1, 35.5, 141}, + {34.3, 35.1, 141}, + {34.7, 35, 141}, + {35.5, 34.9, 141}, + {35, 34.9, 141}, + {34.4, 34.4, 141}, + }, + [2018] = { + {37.9, 44.7, 141}, + {37.3, 43.7, 141}, + {37.9, 43.7, 141}, + {37.3, 43.3, 141}, + {37.9, 43, 141}, + {37.2, 43, 141}, + {38.7, 42.9, 141}, + {36.6, 41.8, 141}, + {37.3, 41.6, 141}, + {37.5, 41.3, 141}, + {37.7, 41.1, 141}, + {38.5, 40.7, 141}, + {37.2, 40.4, 141}, + {36.4, 39.9, 141}, + {35.2, 38.9, 141}, + {36.8, 38.9, 141}, + {36, 38.9, 141}, + {35.6, 38.7, 141}, + {36.4, 38.1, 141}, + {36, 37.8, 141}, + {37.3, 37.8, 141}, + {35.4, 37.7, 141}, + {36.8, 37.7, 141}, + {35.8, 37.3, 141}, + {35.3, 36.8, 141}, + {34.4, 35.5, 141}, + {34.2, 35.4, 141}, + {33.8, 35.4, 141}, + {35.2, 35.1, 141}, + {34.3, 35, 141}, + {35, 35, 141}, + {34.7, 34.9, 141}, + {35.4, 34.9, 141}, + {34.2, 34.8, 141}, + {34.3, 34.4, 141}, + {34.3, 34.1, 141}, + {37.8, 44.7, 141}, + {37.3, 41.7, 141}, + {38.6, 40.5, 141}, + {35.6, 38.5, 141}, + {36.7, 37.7, 141}, + {35.5, 37, 141}, + {34.1, 35.5, 141}, + {34.3, 35.1, 141}, + {34.7, 35, 141}, + {35.5, 34.9, 141}, + {35, 34.9, 141}, + {34.4, 34.4, 141}, + }, + [2019] = { + {32.2, 32.9, 141}, + {32.6, 32.7, 141}, + {32.2, 32.3, 141}, + {31.8, 32.2, 141}, + {32.7, 32, 141}, + {31.4, 31.6, 141}, + {32.8, 30.5, 141}, + {33.1, 30.1, 141}, + {32.9, 30, 141}, + {34, 30, 141}, + {33.5, 29.8, 141}, + {32.7, 29.6, 141}, + {33.4, 29.3, 141}, + {33.1, 29.2, 141}, + {34.3, 28.9, 141}, + {32.9, 28.8, 141}, + {34.3, 28.3, 141}, + {36.2, 28.3, 141}, + {34.6, 28.1, 141}, + {37.3, 28, 141}, + {34.3, 28, 141}, + {36.7, 27.9, 141}, + {34.1, 27.7, 141}, + {36.4, 27.3, 141}, + {34.2, 27.3, 141}, + {34.8, 27, 141}, + {35.2, 26.3, 141}, + {36.4, 25.9, 141}, + {32.6, 32.6, 141}, + {32.2, 32.4, 141}, + {31.8, 32.3, 141}, + {33.1, 30, 141}, + {33.5, 29.9, 141}, + {34.1, 29.9, 141}, + }, + [2020] = { + {32.2, 32.9, 141}, + {32.6, 32.7, 141}, + {32.2, 32.3, 141}, + {31.8, 32.2, 141}, + {32.7, 32, 141}, + {31.4, 31.6, 141}, + {32.8, 30.5, 141}, + {33.1, 30.1, 141}, + {32.9, 30, 141}, + {34, 30, 141}, + {33.5, 29.8, 141}, + {32.7, 29.6, 141}, + {33.4, 29.3, 141}, + {33.1, 29.2, 141}, + {34.3, 28.9, 141}, + {32.9, 28.8, 141}, + {34.3, 28.3, 141}, + {36.2, 28.3, 141}, + {34.6, 28.1, 141}, + {37.3, 28, 141}, + {34.3, 28, 141}, + {36.7, 27.9, 141}, + {34.1, 27.7, 141}, + {36.4, 27.3, 141}, + {34.2, 27.3, 141}, + {34.8, 27, 141}, + {35.2, 26.3, 141}, + {36.4, 25.9, 141}, + {32.6, 32.6, 141}, + {32.2, 32.4, 141}, + {31.8, 32.3, 141}, + {33.1, 30, 141}, + {33.5, 29.9, 141}, + {34.1, 29.9, 141}, + }, + [2025] = { + {52.4, 74.3, 141}, + {51.2, 73.6, 141}, + {52.5, 73.4, 141}, + {47.7, 72.6, 141}, + {48.7, 72.5, 141}, + {52.6, 72.2, 141}, + {50.1, 72.1, 141}, + {53.4, 70.7, 141}, + {51.6, 70.1, 141}, + {54.5, 69.8, 141}, + {52.5, 69.3, 141}, + {52.9, 68.4, 141}, + }, + [2027] = { + {53.7, 75.3, 141}, + {52.8, 74.7, 141}, + {53.2, 74.6, 141}, + {53, 74.1, 141}, + {42.5, 42.8, 141}, + {43.9, 42.7, 141}, + {41.8, 41.7, 141}, + {43.8, 40.7, 141}, + {42.5, 40.7, 141}, + {43.9, 39.2, 141}, + {42.5, 38.7, 141}, + {41.8, 37.9, 141}, + {53.2, 74.7, 141}, + {42.5, 42.7, 141}, + {41.9, 41.8, 141}, + }, + [2029] = { + {42.5, 36.8, 141}, + {43.4, 35.8, 141}, + {43.9, 34.9, 141}, + {42.5, 34.5, 141}, + {41.9, 33.9, 141}, + {43, 33.7, 141}, + {42.5, 33, 141}, + {43.2, 31.9, 141}, + {43.2, 30.1, 141}, + }, + [2030] = { + {43.9, 28.9, 141}, + {42.5, 28.9, 141}, + {43.2, 27.9, 141}, + {44.4, 27.9, 141}, + {43.9, 27, 141}, + {45, 26.6, 141}, + {43.2, 26, 141}, + {44.5, 26, 141}, + {43.9, 25.9, 141}, + }, + [2033] = { + {46.9, 45.6, 141}, + {46.3, 44.5, 141}, + {39.8, 44.5, 141}, + {45.2, 42.6, 141}, + {45.7, 41, 141}, + {46, 40.9, 141}, + {45.8, 37.8, 141}, + {44.6, 37.7, 141}, + {45.1, 36.7, 141}, + {39.6, 35.4, 141}, + {39.5, 35.2, 141}, + {45.8, 33.8, 141}, + {34.7, 30.9, 141}, + {35.3, 29.8, 141}, + {35.9, 29.2, 141}, + {34.7, 31, 141}, + }, + [2034] = { + {38, 29.3, 141}, + {39.9, 28.7, 141}, + {40.7, 27.4, 141}, + {38.3, 26.8, 141}, + {41.3, 25.1, 141}, + {37.4, 24.8, 141}, + }, + [2038] = { + {51.2, 50.8, 141}, + {52.8, 50.2, 141}, + {51.3, 50.2, 141}, + }, + [2043] = { + {50, 79.2, 141}, + {50.6, 79, 141}, + {50.9, 77.2, 141}, + {49.7, 77.1, 141}, + {50.3, 76.1, 141}, + {45.8, 75.7, 141}, + {47, 75.1, 141}, + {45, 74.6, 141}, + {43.2, 74.3, 141}, + {56.9, 73.9, 141}, + {61.5, 73.3, 141}, + {62.6, 72.8, 141}, + {53.9, 72.5, 141}, + {55.8, 72.3, 141}, + {63.4, 72.2, 141}, + {62.2, 72.1, 141}, + {45.2, 72.1, 141}, + {46.6, 71.3, 141}, + {63.5, 71.3, 141}, + {47.1, 70.9, 141}, + {63.4, 70.2, 141}, + {41.2, 70.2, 141}, + {40.4, 69.9, 141}, + {39.9, 69.3, 141}, + {40.4, 69.1, 141}, + {40.7, 59.5, 141}, + {38.2, 58.6, 141}, + {38.9, 58.1, 141}, + {40.8, 57, 141}, + {38.6, 56.5, 141}, + {40.1, 55.8, 141}, + {40.5, 55.6, 141}, + {41.2, 70.1, 141}, + {38.5, 56.2, 141}, + }, + [2070] = { + {37.5, 93, 148}, + {39.8, 91.8, 148}, + {36.6, 90.8, 148}, + {39.5, 90.5, 148}, + {42.9, 90.3, 148}, + {37, 89.7, 148}, + {35.2, 89.7, 148}, + {45.2, 58.8, 148}, + {44.5, 58.2, 148}, + {45.3, 57.6, 148}, + {45, 57.2, 148}, + {45.2, 56.1, 148}, + {44, 55.7, 148}, + {44.6, 55.7, 148}, + {43.2, 54.6, 148}, + {42, 54.5, 148}, + {46.5, 54.2, 148}, + {46.4, 52.4, 148}, + {38.8, 50.7, 148}, + {40.1, 50.1, 148}, + {38, 50.1, 148}, + {38.9, 48.8, 148}, + {42, 48.2, 148}, + {42.1, 45.3, 148}, + {47.2, 44.5, 148}, + {44.1, 43.8, 148}, + {42.6, 43.2, 148}, + {41, 43, 148}, + {44.8, 42.7, 148}, + {46.4, 42.1, 148}, + {40.5, 41.2, 148}, + {44.8, 41.2, 148}, + {40.3, 41.1, 148}, + {47.2, 40.8, 148}, + {39.7, 40.4, 148}, + {47.9, 40.1, 148}, + {39.4, 39.7, 148}, + {39.4, 39.1, 148}, + {48.3, 38.8, 148}, + {39.6, 37.8, 148}, + {38.1, 36.9, 148}, + {39.2, 34.3, 148}, + {39.6, 33.4, 148}, + {45.5, 33.3, 148}, + {40.1, 33.2, 148}, + {45.7, 31.7, 148}, + {44.9, 29.3, 148}, + {43.6, 29, 148}, + {45.2, 28, 148}, + {44.3, 27.7, 148}, + {43, 26.1, 148}, + {42.7, 25.9, 148}, + {45.1, 24.8, 148}, + {44.7, 24.3, 148}, + {48.7, 24.3, 148}, + {43.5, 24.2, 148}, + {46.3, 24, 148}, + {46.2, 23.7, 148}, + {61, 13, 148}, + {61, 11.9, 148}, + {59.6, 11.8, 148}, + {62.6, 7, 148}, + }, + [2152] = { + {45.8, 54.5, 141}, + {45.2, 54.5, 141}, + {46.8, 54.4, 141}, + {48.4, 54.4, 141}, + {46.3, 54.2, 141}, + {48.3, 53.5, 141}, + {45.8, 53.5, 141}, + {46.5, 53.4, 141}, + {47.9, 53.2, 141}, + {45.5, 52.9, 141}, + {46.3, 52.7, 141}, + {46.7, 52.5, 141}, + {45.9, 52.5, 141}, + {46.3, 52.2, 141}, + {46.3, 51.2, 141}, + {45.2, 54.6, 141}, + {45.9, 54.5, 141}, + {48.5, 54.1, 141}, + {45.8, 53.6, 141}, + {47.9, 53.3, 141}, + {46.2, 51.1, 141}, + }, + [2156] = { + {36.2, 86.3, 148}, + {36.3, 85.8, 148}, + {35.7, 85.6, 148}, + {36.1, 85.6, 148}, + {35.5, 85.1, 148}, + {35.1, 84.9, 148}, + {35.4, 84.4, 148}, + {35.6, 84.1, 148}, + {35.7, 85.7, 148}, + {36, 85.5, 148}, + {35.3, 84.5, 148}, + {35.6, 84.3, 148}, + }, + [2157] = { + {36.2, 86.3, 148}, + {36.3, 85.8, 148}, + {35.7, 85.6, 148}, + {36.1, 85.6, 148}, + {35.5, 85.1, 148}, + {35.1, 84.9, 148}, + {36.2, 86.4, 148}, + {36.2, 85.9, 148}, + {35.5, 85, 148}, + }, + [2162] = { + {46.3, 51, 141}, + }, + [2166] = { + {53.8, 75.1, 141}, + }, + [2176] = { + {41.6, 59.7, 148}, + {41.7, 59.2, 148}, + {43.7, 59.1, 148}, + {41.5, 58.6, 148}, + {42.1, 58.4, 148}, + {43.2, 58.4, 148}, + {43.7, 58.2, 148}, + {42.2, 57.8, 148}, + {41.7, 57.8, 148}, + {42.7, 57.8, 148}, + {43.2, 57.6, 148}, + {41.9, 57.3, 148}, + {42.6, 57.3, 148}, + {41.6, 59.6, 148}, + {43.7, 59, 148}, + {42.2, 57.9, 148}, + {42.5, 57.4, 148}, + }, + [2189] = { + {44.8, 39.8, 148}, + {44.9, 39, 148}, + {44.5, 39, 148}, + {44.7, 38.6, 148}, + {44.1, 38.5, 148}, + {44.4, 38.1, 148}, + {45.1, 37.9, 148}, + {44.7, 37.7, 148}, + {44.4, 37.5, 148}, + {44.7, 37.1, 148}, + {47, 37.1, 148}, + {44.6, 36.6, 148}, + {46.1, 36.5, 148}, + {45.2, 36.3, 148}, + {44.8, 36.2, 148}, + {46.9, 36.2, 148}, + {47.5, 36.1, 148}, + {44.9, 39.7, 148}, + {44.5, 39.1, 148}, + {44, 38.4, 148}, + {44.5, 37.5, 148}, + {44.7, 37.2, 148}, + }, + [2231] = { + {35.4, 51.2, 148}, + {35.6, 50.7, 148}, + {35.7, 50, 148}, + {36.5, 50, 148}, + {36, 49.9, 148}, + {35.4, 49.4, 148}, + {35.1, 49.4, 148}, + {36.5, 49.1, 148}, + {36.4, 49.1, 148}, + {35.1, 48.5, 148}, + {36.7, 48.4, 148}, + {35.9, 48.3, 148}, + {35.6, 48.1, 148}, + {34.7, 48, 148}, + {36.4, 47.8, 148}, + {35.4, 47.5, 148}, + {35.6, 47.1, 148}, + {36, 46.9, 148}, + {35, 46.7, 148}, + {35.3, 46.2, 148}, + {35.9, 45.9, 148}, + {35.5, 44.7, 148}, + {36.2, 44.5, 148}, + {35, 44.4, 148}, + {35.2, 43, 148}, + {36.8, 41.8, 148}, + {35.9, 41.3, 148}, + {35.5, 41.2, 148}, + {37.2, 40.6, 148}, + {36.9, 40.5, 148}, + {36.3, 40.4, 148}, + {37.4, 40.3, 148}, + {36.1, 39.8, 148}, + {37.4, 39.7, 148}, + {36.5, 39.4, 148}, + {36.4, 38.8, 148}, + {37.2, 38.7, 148}, + {36.4, 37.7, 148}, + {36.5, 37.5, 148}, + {37.1, 37.1, 148}, + {36.6, 36.8, 148}, + {37, 36.6, 148}, + {36.7, 36.3, 148}, + {36.8, 35.9, 148}, + {37.7, 35.8, 148}, + {37, 35.5, 148}, + {36, 50.1, 148}, + {36.6, 49.8, 148}, + {36, 47, 148}, + {35.4, 44.6, 148}, + }, + [2234] = { + {35.1, 56.1, 148}, + {35.2, 54.4, 148}, + {34.9, 54.4, 148}, + {34.5, 53.7, 148}, + {35.9, 53.7, 148}, + {35.2, 52.7, 148}, + {36.5, 52.2, 148}, + {37, 34.3, 148}, + {37.8, 33.9, 148}, + {37.4, 32.9, 148}, + {38.1, 32.4, 148}, + {40.6, 31.8, 148}, + {39.5, 31.5, 148}, + {38.5, 31.5, 148}, + {40.2, 31.4, 148}, + {40.9, 31.1, 148}, + {38.9, 30.7, 148}, + }, + [2447] = { + {73.7, 65.3, 36}, + {73.3, 5.6, 267}, + }, + [2749] = { + {41.6, 39.7, 3}, + }, + [2949] = { + {53.7, 74.1, 215}, + {53.5, 73.5, 215}, + {54.5, 73.3, 215}, + {48.3, 72.4, 215}, + {55.1, 72.3, 215}, + {52.5, 72.3, 215}, + {53.8, 72.3, 215}, + {49.3, 72.2, 215}, + {48.3, 71.7, 215}, + {47.3, 71.3, 215}, + {49.2, 71.3, 215}, + {48.7, 71.2, 215}, + {35.6, 63.4, 215}, + {35, 62.7, 215}, + {35.6, 62.4, 215}, + {35.4, 62.3, 215}, + {33.1, 62.2, 215}, + {35.5, 62, 215}, + {32.8, 61.8, 215}, + {35.7, 61.6, 215}, + {33, 61.2, 215}, + {33.4, 61.2, 215}, + {32.7, 61, 215}, + {31.6, 60.4, 215}, + {32, 59.8, 215}, + {54.5, 73.4, 215}, + {53.1, 73.3, 215}, + {48.4, 72.4, 215}, + {53.8, 72.4, 215}, + {52.6, 72.1, 215}, + {55, 72.1, 215}, + {48.4, 71.8, 215}, + {47.3, 71.4, 215}, + {49.2, 71.4, 215}, + }, + [2950] = { + {53.2, 74.1, 215}, + {48.7, 73.3, 215}, + {48, 73.3, 215}, + {52.6, 73.2, 215}, + {53.3, 73, 215}, + {47.3, 72.3, 215}, + {48.2, 72.1, 215}, + {55.7, 71.8, 215}, + {48.6, 71.8, 215}, + {54.4, 71.8, 215}, + {48, 71.2, 215}, + {35.1, 63.4, 215}, + {35.1, 61.8, 215}, + {33, 61.7, 215}, + {32.4, 61.1, 215}, + {30.7, 61, 215}, + {31.7, 60.7, 215}, + {30.6, 60.5, 215}, + {32.2, 60.5, 215}, + {31.6, 60, 215}, + {54.5, 71.6, 215}, + }, + [2951] = { + {53.1, 73.6, 215}, + {55.2, 73.1, 215}, + {54.8, 72.8, 215}, + {53.6, 72.4, 215}, + {53.2, 72.3, 215}, + {54.4, 71.9, 215}, + {54, 71.8, 215}, + {30.7, 62.4, 215}, + {31.2, 62.2, 215}, + {31.3, 61.7, 215}, + {30.9, 61.5, 215}, + {31.2, 61.2, 215}, + {30.7, 60.5, 215}, + {31, 60.4, 215}, + {52.9, 73.4, 215}, + {54.9, 72.9, 215}, + {53.1, 72.5, 215}, + }, + [2956] = { + {37.4, 55.7, 17}, + {34.9, 79.9, 215}, + {35.7, 79.1, 215}, + {36.9, 77.2, 215}, + {34.8, 77, 215}, + {37.8, 75.2, 215}, + {35, 74.4, 215}, + {36.9, 73.2, 215}, + {40.9, 73.2, 215}, + {42.2, 72.3, 215}, + {58.4, 72.3, 215}, + {35.6, 71.4, 215}, + {46, 71.2, 215}, + {45.1, 71.1, 215}, + {39.4, 70.2, 215}, + {40.2, 70.2, 215}, + {34.2, 69.9, 215}, + {37, 69.4, 215}, + {41.1, 69, 215}, + {54.6, 68.6, 215}, + {36.4, 68.4, 215}, + {46.6, 68.4, 215}, + {47.9, 68.3, 215}, + {40.8, 68.3, 215}, + {43.5, 67.5, 215}, + {42.8, 67.4, 215}, + {57.8, 67.4, 215}, + {38, 67.1, 215}, + {50.2, 67, 215}, + {58.1, 66.6, 215}, + {39.1, 66.6, 215}, + {42, 66.4, 215}, + {35.1, 66.4, 215}, + {49.1, 66.4, 215}, + {39.6, 66.3, 215}, + {36.8, 65.8, 215}, + {38.2, 63.5, 215}, + {41.6, 63.4, 215}, + {40.4, 62.6, 215}, + {39.5, 61.9, 215}, + {53.9, 61.6, 215}, + {36.3, 61.6, 215}, + {37.6, 61.3, 215}, + {52.5, 60.6, 215}, + {40.8, 59.7, 215}, + {42.1, 59.7, 215}, + {61.7, 59.6, 215}, + {36.8, 58, 215}, + {55.1, 57.8, 215}, + {56.5, 57.7, 215}, + {59.5, 57.1, 215}, + {40, 56.9, 215}, + {35.9, 56.7, 215}, + {39.3, 56.1, 215}, + {35.6, 55.3, 215}, + {43, 55, 215}, + {62.6, 54.8, 215}, + {38.8, 54.3, 215}, + {36.4, 53.9, 215}, + {39.2, 53, 215}, + {35.8, 52.3, 215}, + {39.5, 52.2, 215}, + {43.7, 51.4, 215}, + {40.5, 51.4, 215}, + {37, 51, 215}, + {36.6, 50, 215}, + {35.3, 48.3, 215}, + {58.4, 46.8, 215}, + {55.7, 46.1, 215}, + {48.8, 45.8, 215}, + {45.4, 45.1, 215}, + {52.5, 45.1, 215}, + {35.7, 44.2, 215}, + {43.7, 43.9, 215}, + {42.2, 43.3, 215}, + {57.2, 43, 215}, + {54.7, 42, 215}, + {36.7, 41.2, 215}, + {56, 40.9, 215}, + {44.9, 40.8, 215}, + {49, 40.1, 215}, + {54.5, 39.2, 215}, + {35.1, 39.2, 215}, + {43.5, 38.9, 215}, + {35.6, 38.1, 215}, + {45.8, 37.2, 215}, + {57.4, 36.9, 215}, + {55.1, 36.9, 215}, + {44.8, 36.3, 215}, + {37.6, 35.3, 215}, + {34.3, 34.1, 215}, + {46.5, 31.5, 215}, + {33.3, 31.1, 215}, + {33.7, 30.7, 215}, + {87.9, 63.1, 405}, + {86.6, 59.7, 405}, + {87.1, 59.2, 405}, + {42.1, 59.6, 215}, + }, + [2957] = { + {39.3, 58.7, 17}, + {40.3, 58.1, 17}, + {34.5, 44.4, 17}, + {35, 43.3, 17}, + {34.6, 42.1, 17}, + {35, 41.6, 17}, + {36.2, 41.4, 17}, + {37.1, 41.2, 17}, + {36.4, 40.9, 17}, + {36.2, 40.4, 17}, + {35.7, 39.5, 17}, + {35.2, 39, 17}, + {35, 38.4, 17}, + {33.6, 37.8, 17}, + {34.6, 37, 17}, + {32.6, 35, 17}, + {33, 34.3, 17}, + {31.7, 33, 17}, + {31.3, 32, 17}, + {61, 64.5, 215}, + {62.2, 64.4, 215}, + {64.2, 63.5, 215}, + {62.4, 62.8, 215}, + {64.2, 61.7, 215}, + {66.2, 60.7, 215}, + {68.2, 59.6, 215}, + {55.9, 56.4, 215}, + {57.7, 53.8, 215}, + {60.5, 53.6, 215}, + {55.7, 50.6, 215}, + {40.8, 50.1, 215}, + {43.3, 47, 215}, + {38.1, 47, 215}, + {50.6, 46.1, 215}, + {56.8, 44.8, 215}, + {46.1, 44.3, 215}, + {53.9, 44.1, 215}, + {37.1, 43, 215}, + {51.2, 42.2, 215}, + {47.4, 41.9, 215}, + {38.2, 39.8, 215}, + {52.5, 39.7, 215}, + {52, 39.4, 215}, + {49.6, 38.1, 215}, + {47.9, 36.5, 215}, + {52.5, 35.2, 215}, + {52.5, 34.4, 215}, + {53.6, 34.1, 215}, + {55.7, 33.7, 215}, + {50.1, 33.5, 215}, + {56.8, 32.5, 215}, + {52.6, 32.5, 215}, + {46.3, 32.2, 215}, + {44.6, 31.5, 215}, + {54, 31.3, 215}, + {57.8, 30.3, 215}, + {49.2, 29.6, 215}, + {56, 29.5, 215}, + {49.8, 29.3, 215}, + {54, 28.9, 215}, + {44.6, 28.4, 215}, + {57, 27.9, 215}, + {51.5, 27.9, 215}, + {47.3, 27, 215}, + {57.8, 27, 215}, + {60.2, 26.6, 215}, + {51.2, 26.6, 215}, + {33.3, 26.5, 215}, + {62, 26.3, 215}, + {60.7, 25.6, 215}, + {49.3, 25.6, 215}, + {55.7, 25.6, 215}, + {47.7, 25.3, 215}, + {52.5, 24.9, 215}, + {60.3, 24.7, 215}, + {54.5, 24.6, 215}, + {51, 23.6, 215}, + {54.3, 23, 215}, + {59.2, 23, 215}, + {52, 22.1, 215}, + {58.2, 21.9, 215}, + {42.9, 21.5, 215}, + {54.8, 21, 215}, + {57.9, 20.6, 215}, + {53.7, 19.7, 215}, + {55.1, 19.6, 215}, + {38.2, 18.9, 215}, + {39.5, 18.8, 215}, + {47.3, 18.6, 215}, + {51.2, 18.6, 215}, + {42.7, 18.6, 215}, + {45.8, 18.5, 215}, + {53.1, 18.4, 215}, + {34.3, 18.1, 215}, + {57, 17.9, 215}, + {44.2, 17.9, 215}, + {51.1, 17.7, 215}, + {38.4, 17.1, 215}, + {46, 17, 215}, + {48.6, 16.1, 215}, + {37.4, 14.8, 215}, + {48.6, 14.7, 215}, + {53.2, 14.1, 215}, + {46.8, 14, 215}, + {50.5, 14, 215}, + {47.6, 13.7, 215}, + {49.7, 13.5, 215}, + {41.1, 13.2, 215}, + {53.9, 12.6, 215}, + {38.5, 12.5, 215}, + {41.6, 11.5, 215}, + {42.2, 11.2, 215}, + {46.1, 10.7, 215}, + {43.7, 10.2, 215}, + {47.3, 10.2, 215}, + {51.3, 10.1, 215}, + {48.1, 10, 215}, + {39.7, 8.8, 215}, + {44, 8.6, 215}, + {50.6, 8.1, 215}, + {47.1, 7.6, 215}, + {86.6, 54.5, 405}, + {87.9, 44.9, 405}, + {57.7, 53.9, 215}, + {69.1, 100, 406}, + {74.8, 99.5, 406}, + {71.8, 99.1, 406}, + }, + [2959] = { + {36.6, 56.3, 17}, + {37.6, 54.9, 17}, + {35.8, 51, 17}, + {57.8, 73, 215}, + {56.5, 71.3, 215}, + {59.1, 71.1, 215}, + {57.7, 70.4, 215}, + {61.8, 69.3, 215}, + {57.9, 68.2, 215}, + {61.1, 67.7, 215}, + {59, 67.2, 215}, + {60.9, 66.5, 215}, + {59.7, 65.9, 215}, + {57.2, 65.6, 215}, + {58.9, 64.6, 215}, + {60.5, 63.3, 215}, + {58.6, 62.5, 215}, + {57, 62.5, 215}, + {60.3, 61.7, 215}, + {60.6, 60.7, 215}, + {56.4, 59.6, 215}, + {60.2, 59.4, 215}, + {60.9, 58.8, 215}, + {56.9, 58.6, 215}, + {57.6, 58, 215}, + {55.8, 57.9, 215}, + {60.9, 56, 215}, + {57.2, 56, 215}, + {56, 55.8, 215}, + {60.3, 55.4, 215}, + {58.8, 55, 215}, + {63, 53.2, 215}, + {42.8, 53, 215}, + {58.8, 52.7, 215}, + {58.8, 52.1, 215}, + {41.7, 52, 215}, + {35.4, 51.7, 215}, + {57.5, 51.6, 215}, + {41.8, 51, 215}, + {35.5, 51, 215}, + {58.2, 50.8, 215}, + {42.8, 50.4, 215}, + {55.2, 50.2, 215}, + {35.4, 50, 215}, + {47.4, 50, 215}, + {40.1, 49.7, 215}, + {41.5, 49.2, 215}, + {45, 49.1, 215}, + {49.3, 49, 215}, + {46.6, 48.9, 215}, + {55.4, 48.2, 215}, + {49.9, 48, 215}, + {51.4, 48, 215}, + {46.5, 47.9, 215}, + {43.2, 47.9, 215}, + {47.9, 47.8, 215}, + {44.6, 47.7, 215}, + {57.4, 47.6, 215}, + {42.7, 47.3, 215}, + {57.6, 47.2, 215}, + {49.3, 47.1, 215}, + {46.8, 47, 215}, + {47.8, 46.9, 215}, + {51, 46.6, 215}, + {43.4, 46.6, 215}, + {57.6, 46.4, 215}, + {51.9, 46.3, 215}, + {57.1, 46.1, 215}, + {42.2, 46, 215}, + {59.4, 45.5, 215}, + {55.9, 45.2, 215}, + {41.4, 45.2, 215}, + {53.8, 45.1, 215}, + {46.7, 45.1, 215}, + {50.8, 45.1, 215}, + {53.5, 45, 215}, + {36.3, 45, 215}, + {52.4, 45, 215}, + {58, 44.6, 215}, + {57.1, 44.5, 215}, + {52.6, 44.4, 215}, + {55, 44.3, 215}, + {54.5, 44.2, 215}, + {51.2, 44.2, 215}, + {55.8, 44, 215}, + {41.1, 44, 215}, + {49.9, 43.2, 215}, + {52.6, 43.2, 215}, + {51.8, 43.2, 215}, + {42.2, 43.2, 215}, + {49, 43.1, 215}, + {35, 43, 215}, + {58.1, 42.8, 215}, + {54.4, 42.6, 215}, + {39.5, 42.4, 215}, + {40.9, 42.2, 215}, + {48, 42.2, 215}, + {43.4, 42.1, 215}, + {42.1, 41.7, 215}, + {42.8, 41.5, 215}, + {54.1, 41.3, 215}, + {42, 41.3, 215}, + {44.1, 41.1, 215}, + {53.8, 40.5, 215}, + {39.5, 40.3, 215}, + {41.7, 40.2, 215}, + {44.8, 40.1, 215}, + {51.9, 40, 215}, + {38.3, 40, 215}, + {45.4, 40, 215}, + {50.5, 39.9, 215}, + {49.5, 39.9, 215}, + {41.6, 39.8, 215}, + {38.9, 39.6, 215}, + {48, 39.2, 215}, + {51.5, 38.9, 215}, + {44.8, 38.3, 215}, + {41.4, 37.9, 215}, + {47, 37.8, 215}, + {48.6, 37.8, 215}, + {54.5, 37.3, 215}, + {50.2, 36.9, 215}, + {35.7, 36.3, 215}, + {49.3, 36.2, 215}, + {37.1, 35.9, 215}, + {34.7, 35.8, 215}, + {45.2, 35.5, 215}, + {37.5, 35.4, 215}, + {46.7, 35.4, 215}, + {37.1, 34.3, 215}, + {45.6, 34.2, 215}, + {51.2, 34.1, 215}, + {46.1, 33.6, 215}, + {35.9, 33.4, 215}, + {45.4, 33.2, 215}, + {47.4, 32.6, 215}, + {35.6, 32.5, 215}, + {48.7, 31.1, 215}, + {34.2, 31.1, 215}, + {33.7, 28.7, 215}, + {34.3, 27.7, 215}, + {35.1, 25.2, 215}, + {35.2, 24.9, 215}, + {88.3, 65.1, 405}, + {89.3, 61.4, 405}, + {87.7, 59.7, 405}, + {87.2, 57, 405}, + {87.9, 55.9, 405}, + {88.7, 53, 405}, + {88.8, 52.6, 405}, + }, + [2960] = { + {38.5, 62.3, 17}, + {38.7, 62.2, 17}, + {38.5, 62, 17}, + {39.5, 61.6, 17}, + {39.2, 61.3, 17}, + {38.9, 61.1, 17}, + {39.6, 60.8, 17}, + {39.2, 60.7, 17}, + {39.5, 60.1, 17}, + {38.8, 59.9, 17}, + {39.6, 58.5, 17}, + {39.1, 58.2, 17}, + {38.2, 58.2, 17}, + {37.5, 58.2, 17}, + {38.4, 57.8, 17}, + {39.2, 57.7, 17}, + {39.5, 57.7, 17}, + {37.7, 57.7, 17}, + {39.6, 57.4, 17}, + {38.9, 56.9, 17}, + {39.3, 56.6, 17}, + {37.1, 56.4, 17}, + {37.8, 56.4, 17}, + {34.8, 43.8, 17}, + {35.6, 42.6, 17}, + {34.7, 42.5, 17}, + {34.5, 42.3, 17}, + {35.2, 42.1, 17}, + {36.6, 42, 17}, + {34.6, 40.9, 17}, + {34.8, 40.5, 17}, + {34.9, 39.3, 17}, + {34.6, 38.4, 17}, + {32.6, 34.5, 17}, + {31.7, 32.5, 17}, + {30.6, 31.8, 17}, + {63.5, 69.2, 215}, + {61.7, 68.3, 215}, + {62.9, 68.1, 215}, + {64.7, 67.8, 215}, + {61.5, 67.8, 215}, + {65.2, 67.7, 215}, + {64.8, 67.2, 215}, + {66.8, 66.5, 215}, + {62.2, 66.3, 215}, + {66.1, 65.8, 215}, + {62.3, 65.6, 215}, + {62.9, 65.5, 215}, + {65.5, 65.4, 215}, + {64.2, 65.4, 215}, + {67, 64.8, 215}, + {66.1, 64.7, 215}, + {66.8, 63.6, 215}, + {65.3, 63.2, 215}, + {61.6, 61.6, 215}, + {62.9, 61.6, 215}, + {64.6, 61, 215}, + {63.5, 60.6, 215}, + {66.9, 60.4, 215}, + {65.9, 59.7, 215}, + {64.2, 59.7, 215}, + {62.8, 59.7, 215}, + {64.5, 59, 215}, + {66.2, 58.8, 215}, + {66.7, 58.8, 215}, + {63.2, 58.7, 215}, + {62.3, 58.7, 215}, + {66.9, 58.1, 215}, + {62.3, 57.7, 215}, + {61.6, 57.7, 215}, + {65.6, 57.3, 215}, + {66.2, 56.7, 215}, + {62.1, 56.2, 215}, + {63.4, 56.2, 215}, + {54.1, 33.9, 215}, + {53.1, 32.5, 215}, + {50.5, 32.2, 215}, + {56, 31.3, 215}, + {57.4, 31.3, 215}, + {49.1, 30.7, 215}, + {44.4, 30.3, 215}, + {44.1, 30.2, 215}, + {50.5, 29.6, 215}, + {53.3, 29.4, 215}, + {45.4, 29.1, 215}, + {59, 29, 215}, + {46.5, 28.8, 215}, + {57.3, 28.7, 215}, + {48, 28.5, 215}, + {56.8, 28.4, 215}, + {45.9, 28, 215}, + {58.2, 27.9, 215}, + {61, 27.8, 215}, + {45.5, 27.3, 215}, + {49.1, 27.2, 215}, + {46.2, 26.6, 215}, + {45.4, 26.6, 215}, + {57.1, 25.6, 215}, + {57.4, 24.8, 215}, + {43.5, 24.7, 215}, + {56.3, 24.6, 215}, + {53.9, 24.1, 215}, + {48.8, 23.7, 215}, + {52.2, 23.2, 215}, + {57.6, 22.4, 215}, + {51, 21.9, 215}, + {54.6, 21.5, 215}, + {56.2, 20.9, 215}, + {57.1, 20.8, 215}, + {52.7, 20.7, 215}, + {32.3, 20.1, 215}, + {52, 19.9, 215}, + {50.5, 19.7, 215}, + {45.3, 18.8, 215}, + {40.7, 18.6, 215}, + {38.9, 17.8, 215}, + {45.3, 17.7, 215}, + {52.6, 17.4, 215}, + {50.9, 16.6, 215}, + {45.3, 15.9, 215}, + {36.3, 15.8, 215}, + {49.4, 15.8, 215}, + {37.2, 15.5, 215}, + {52.3, 15.3, 215}, + {42.9, 14.7, 215}, + {50, 14.2, 215}, + {51.1, 14.2, 215}, + {49.2, 13.9, 215}, + {53.1, 13, 215}, + {50.6, 12.9, 215}, + {49.2, 12.3, 215}, + {49.9, 11.8, 215}, + {40.8, 11.1, 215}, + {42.7, 10.1, 215}, + {40.1, 10, 215}, + {51.3, 9.1, 215}, + {43.4, 9, 215}, + {41.5, 8.1, 215}, + {49.2, 7.7, 215}, + {85.5, 47.2, 405}, + {67.5, 38.8, 1638}, + {38.1, 58.2, 17}, + {62.8, 61.7, 215}, + {61.5, 61.6, 215}, + {63.7, 60.7, 215}, + {64, 59.7, 215}, + {61.5, 57.6, 215}, + {49.3, 14.1, 215}, + {40.2, 10.1, 215}, + {67, 99.5, 406}, + {73.6, 99.2, 406}, + }, + [2962] = { + {39.9, 64.5, 17}, + {39.6, 63.9, 17}, + {39.5, 63.5, 17}, + {39.9, 63.1, 17}, + {39.5, 62.6, 17}, + {63.7, 72.4, 215}, + {67.5, 72.2, 215}, + {62.5, 71.5, 215}, + {61.6, 71.3, 215}, + {62.3, 71.3, 215}, + {67, 71.1, 215}, + {64.9, 71, 215}, + {62.9, 70.5, 215}, + {63.6, 70.4, 215}, + {64.1, 70.4, 215}, + {66.8, 70.3, 215}, + {66.1, 70.2, 215}, + {67.5, 69.5, 215}, + {66.2, 69.4, 215}, + {64.8, 69.4, 215}, + {65.5, 68.4, 215}, + {66.7, 68.4, 215}, + {33.7, 43.2, 215}, + {32.4, 43, 215}, + {31.1, 42.2, 215}, + {31.8, 42.2, 215}, + {32.4, 42.2, 215}, + {34.3, 42.1, 215}, + {32.4, 41.1, 215}, + {31.1, 41.1, 215}, + {33.6, 41.1, 215}, + {31.7, 40.3, 215}, + {87.2, 73.5, 405}, + {85.6, 73.4, 405}, + {84.2, 72.4, 405}, + {84.9, 72.4, 405}, + {85.6, 72.4, 405}, + {87.8, 72.3, 405}, + {85.7, 71.1, 405}, + {84.1, 71.1, 405}, + {87, 71.1, 405}, + {84.9, 70.2, 405}, + {61.6, 71.4, 215}, + {66.2, 69.5, 215}, + {64.8, 69.3, 215}, + {65.6, 68.4, 215}, + {31.7, 42.2, 215}, + {32.3, 42.1, 215}, + {87.1, 73.5, 405}, + {85.5, 72.3, 405}, + }, + [2963] = { + {39.9, 64.1, 17}, + {40.1, 64, 17}, + {39.9, 63.6, 17}, + {39.6, 63.2, 17}, + {40.2, 63.2, 17}, + {61.7, 72.4, 215}, + {67.6, 71.4, 215}, + {63.6, 71.3, 215}, + {68, 71.2, 215}, + {65.5, 70.5, 215}, + {67.5, 70.4, 215}, + {66.9, 69.6, 215}, + {68.1, 69.5, 215}, + {65.3, 69.1, 215}, + {66.1, 68.4, 215}, + {34.4, 44, 215}, + {34.2, 43.1, 215}, + {33.1, 43, 215}, + {31.9, 43, 215}, + {33, 42.2, 215}, + {33.7, 42.2, 215}, + {31.8, 41.3, 215}, + {34.3, 41.2, 215}, + {33.7, 40.3, 215}, + {87.9, 74.5, 405}, + {87.7, 73.5, 405}, + {86.4, 73.3, 405}, + {85, 73.3, 405}, + {86.4, 72.4, 405}, + {87.1, 72.4, 405}, + {85, 71.3, 405}, + {87.8, 71.2, 405}, + {87.2, 70.2, 405}, + {33.1, 42.2, 215}, + {33.6, 42, 215}, + {33.8, 40.3, 215}, + {87.1, 72.2, 405}, + {84.9, 71.4, 405}, + }, + [2964] = { + {33.9, 36.5, 17}, + {33.6, 36.1, 17}, + {34.3, 35.9, 17}, + {34.3, 35.5, 17}, + {34.3, 34.9, 17}, + {33.3, 33.9, 17}, + {33.7, 33.8, 17}, + {33.2, 33.3, 17}, + {31.9, 31.5, 17}, + {31.6, 31.5, 17}, + {32.8, 31, 17}, + {31.9, 30.6, 17}, + {31.1, 28.5, 215}, + {29.7, 27.5, 215}, + {30.5, 26.5, 215}, + {29.7, 26.4, 215}, + {30.4, 25.7, 215}, + {29.2, 25.7, 215}, + {31.1, 25.6, 215}, + {29.7, 24.7, 215}, + {32.4, 24.6, 215}, + {29.8, 23.8, 215}, + {31.8, 23.7, 215}, + {31.1, 23.6, 215}, + {31.6, 22.8, 215}, + {30.4, 22.8, 215}, + {29.2, 22.8, 215}, + {31.1, 21.6, 215}, + {29.7, 21.6, 215}, + {28.4, 21.3, 215}, + {29.2, 21, 215}, + {31.8, 20.8, 215}, + {30.5, 20.7, 215}, + {31.1, 19.6, 215}, + {55.8, 16.9, 215}, + {55, 16.1, 215}, + {56.4, 15.8, 215}, + {56.4, 15, 215}, + {56.5, 13.8, 215}, + {35, 13.7, 215}, + {36.9, 12, 215}, + {35.8, 11.9, 215}, + {54.5, 11.9, 215}, + {55.2, 11.7, 215}, + {36.9, 11, 215}, + {54.4, 10.7, 215}, + {36.3, 9.9, 215}, + {38.8, 9, 215}, + {38.1, 9, 215}, + {37.9, 8.1, 215}, + {39.5, 8, 215}, + {51.8, 7.2, 215}, + {51.1, 7.2, 215}, + {40.2, 7.1, 215}, + {39.5, 6.1, 215}, + {53.4, 6, 215}, + {38.2, 5.9, 215}, + {51.8, 5.4, 215}, + {40.1, 5.3, 215}, + {84.1, 56.7, 405}, + {82.6, 55.6, 405}, + {83.4, 54.5, 405}, + {82.6, 54.3, 405}, + {83.3, 53.6, 405}, + {82, 53.6, 405}, + {84.2, 53.4, 405}, + {82.5, 52.4, 405}, + {85.6, 52.3, 405}, + {82.6, 51.4, 405}, + {84.9, 51.2, 405}, + {84.1, 51.2, 405}, + {84.7, 50.3, 405}, + {83.3, 50.3, 405}, + {81.9, 50.2, 405}, + {84.1, 48.9, 405}, + {82.6, 48.8, 405}, + {81.1, 48.5, 405}, + {82, 48.2, 405}, + {84.9, 48, 405}, + {83.5, 47.8, 405}, + {84.1, 46.6, 405}, + {88.6, 39.8, 405}, + {90, 35.4, 405}, + {34, 36.6, 17}, + {34.2, 35.9, 17}, + {33.5, 35.6, 17}, + {33.3, 34.1, 17}, + {31.1, 28.4, 215}, + {31.1, 25.5, 215}, + {32.5, 24.7, 215}, + {31.8, 23.6, 215}, + {55.8, 17.1, 215}, + {55, 15.2, 215}, + {54.5, 12.1, 215}, + {51.8, 5.3, 215}, + {84.1, 56.6, 405}, + {84.1, 53.3, 405}, + {85.7, 52.4, 405}, + {64, 99.5, 406}, + {65.3, 99.5, 406}, + {75.8, 98.7, 406}, + {75.2, 98.7, 406}, + {65.9, 98.7, 406}, + {65.4, 97.8, 406}, + {77.2, 97.8, 406}, + {64.2, 97.7, 406}, + {65.8, 97.1, 406}, + {75.8, 97.1, 406}, + }, + [2965] = { + {34, 36, 17}, + {34.2, 34.5, 17}, + {33.9, 34.1, 17}, + {33.6, 33.5, 17}, + {33.6, 32.9, 17}, + {33.6, 32.6, 17}, + {32, 31, 17}, + {31.6, 31, 17}, + {32.4, 30.7, 17}, + {31.6, 30.6, 17}, + {31.3, 30.6, 17}, + {31.7, 28.3, 215}, + {31.1, 27.7, 215}, + {30.3, 27.6, 215}, + {32.4, 27.6, 215}, + {31.7, 26.6, 215}, + {31.1, 26.5, 215}, + {29.7, 25.7, 215}, + {32.4, 25.6, 215}, + {30.4, 24.7, 215}, + {33, 24.6, 215}, + {31.8, 24.6, 215}, + {31.1, 24.6, 215}, + {30.4, 23.6, 215}, + {32.4, 23.6, 215}, + {31.1, 22.8, 215}, + {29.8, 22.6, 215}, + {31.8, 21.7, 215}, + {29, 21.7, 215}, + {30.3, 21.7, 215}, + {32.5, 21.6, 215}, + {29.9, 20.8, 215}, + {30.3, 19.7, 215}, + {29.7, 19.6, 215}, + {55.8, 15.9, 215}, + {56.3, 12.9, 215}, + {35.2, 12.8, 215}, + {55.7, 12.3, 215}, + {36.3, 12, 215}, + {55.1, 11.1, 215}, + {36.3, 10.9, 215}, + {37.6, 10.1, 215}, + {55.2, 9.9, 215}, + {36.8, 9.3, 215}, + {55, 9.2, 215}, + {40.1, 8.1, 215}, + {38.2, 7.9, 215}, + {39.3, 7.1, 215}, + {37.9, 7, 215}, + {40.2, 6.2, 215}, + {52, 6.1, 215}, + {51.2, 6, 215}, + {52.7, 5.6, 215}, + {38.8, 5.5, 215}, + {51.2, 5.3, 215}, + {40.8, 5.2, 215}, + {50.6, 5.2, 215}, + {39.4, 5.1, 215}, + {84.8, 56.6, 405}, + {84.1, 55.8, 405}, + {83.3, 55.7, 405}, + {85.6, 55.7, 405}, + {84.9, 54.5, 405}, + {84.2, 54.5, 405}, + {82.6, 53.5, 405}, + {85.7, 53.4, 405}, + {83.4, 52.4, 405}, + {86.4, 52.3, 405}, + {84.9, 52.3, 405}, + {84.1, 52.3, 405}, + {83.3, 51.1, 405}, + {85.7, 51.1, 405}, + {84.1, 50.2, 405}, + {82.7, 50, 405}, + {84.9, 49, 405}, + {81.7, 49, 405}, + {83.3, 49, 405}, + {85.7, 48.8, 405}, + {82.8, 47.9, 405}, + {83.3, 46.6, 405}, + {82.6, 46.6, 405}, + {88.8, 38.8, 405}, + {31.6, 26.6, 215}, + {32.5, 25.4, 215}, + {30.5, 24.8, 215}, + {33.1, 24.7, 215}, + {30.9, 24.5, 215}, + {55.8, 16, 215}, + {84.7, 54.6, 405}, + {85.7, 53.2, 405}, + {83.5, 52.5, 405}, + {86.4, 52.4, 405}, + {84, 52.2, 405}, + {65.8, 99.6, 406}, + {64.2, 99.4, 406}, + {65.2, 98.6, 406}, + {64, 98.6, 406}, + {65.9, 97.9, 406}, + {75.9, 97.8, 406}, + {75.3, 97.8, 406}, + {76.6, 97.4, 406}, + {64.7, 97.3, 406}, + {75.3, 97.1, 406}, + {66.4, 97.1, 406}, + {74.8, 97.1, 406}, + {65.2, 97, 406}, + }, + [2967] = { + {40.3, 57.3, 17}, + {39.6, 56.8, 17}, + {68.2, 58.1, 215}, + {66.8, 57.1, 215}, + {38.6, 60.9, 17}, + {37.7, 56, 17}, + {37.5, 55.8, 17}, + {37, 55.6, 17}, + {36.8, 55.3, 17}, + {37.5, 55.3, 17}, + {37, 55, 17}, + {36.8, 54.9, 17}, + {65, 65.2, 215}, + {62.5, 59.2, 215}, + {63.2, 55.4, 215}, + {62.8, 55.1, 215}, + {61.9, 54.7, 215}, + {61.4, 54.1, 215}, + {62.8, 53.9, 215}, + {61.8, 53.4, 215}, + {61.4, 53.3, 215}, + }, + [2968] = { + {40.3, 57.3, 17}, + {39.6, 56.8, 17}, + {68.2, 58.1, 215}, + {66.8, 57.1, 215}, + {38.7, 61, 17}, + {36.5, 55, 17}, + {37.3, 54.9, 17}, + {65.1, 65.2, 215}, + {62.6, 59.3, 215}, + {60.9, 53.4, 215}, + {62.3, 53.3, 215}, + }, + [2970] = { + {57.7, 68.4, 215}, + {58.9, 64.5, 215}, + {59.8, 59.7, 215}, + {60.2, 54.9, 215}, + {58.3, 52, 215}, + {42.8, 51.9, 215}, + {46.1, 49.7, 215}, + {35.2, 49.5, 215}, + {43.3, 49.2, 215}, + {57.4, 48.6, 215}, + {41.5, 48.6, 215}, + {49, 48, 215}, + {56.7, 46.1, 215}, + {42.7, 45.2, 215}, + {53.8, 45, 215}, + {36.2, 44.9, 215}, + {49.8, 44.4, 215}, + {52.1, 44.1, 215}, + {48.6, 44.1, 215}, + {42.2, 43.5, 215}, + {34.8, 42.8, 215}, + {55.3, 42.3, 215}, + {37.6, 41.2, 215}, + {53.8, 40.7, 215}, + {49.6, 40.7, 215}, + {51.8, 40.3, 215}, + {42.8, 39.8, 215}, + {49.2, 38.4, 215}, + {54.8, 37.4, 215}, + {52.1, 37.2, 215}, + {47.4, 36.5, 215}, + {44.7, 34.9, 215}, + {46.8, 34.8, 215}, + {51.7, 34.8, 215}, + {88.4, 73, 405}, + }, + [2971] = { + {38.6, 58.1, 17}, + {39.5, 57.2, 17}, + {35.1, 42.5, 17}, + {35, 39.3, 17}, + {64.9, 59.6, 215}, + {66.8, 57.7, 215}, + {55.5, 32.4, 215}, + {53.8, 32, 215}, + {52.2, 30.3, 215}, + {50.8, 28.9, 215}, + {58, 28.8, 215}, + {53.9, 26.5, 215}, + {53.2, 23.7, 215}, + {57.8, 22.4, 215}, + {51.7, 20, 215}, + {53.3, 18.9, 215}, + {37.6, 17.3, 215}, + {51.8, 16.4, 215}, + {52.6, 13.8, 215}, + {46.6, 13.4, 215}, + {46.7, 10.1, 215}, + {42.2, 10, 215}, + {48.9, 9.2, 215}, + }, + [2972] = { + {34.8, 43.8, 17}, + {34.8, 43.7, 17}, + {34.8, 43.6, 17}, + {34.8, 43.5, 17}, + {39.4, 66.5, 215}, + {39.7, 66.3, 215}, + {39.6, 66.1, 215}, + {44.5, 52.7, 215}, + {44.1, 52.6, 215}, + {48, 37.9, 215}, + {48.3, 37.8, 215}, + {47.7, 37.5, 215}, + {57.5, 31.3, 215}, + {57.5, 31.2, 215}, + {57.4, 31, 215}, + {57.4, 30.8, 215}, + }, + [2973] = { + {39.7, 66.6, 215}, + {43.4, 53.7, 215}, + {47.7, 37.1, 215}, + }, + [2976] = { + {53.9, 66.6, 215}, + {53.7, 48.9, 215}, + {53.2, 48.9, 215}, + {54.4, 48.7, 215}, + {53.5, 48.4, 215}, + {54.4, 48, 215}, + {53.7, 47.7, 215}, + {53.4, 47.4, 215}, + {53.8, 47.2, 215}, + {44.4, 46, 215}, + {44.1, 45.6, 215}, + {44.8, 45.4, 215}, + {44.2, 44.9, 215}, + {44.5, 44.6, 215}, + }, + [2977] = { + {53.8, 48.4, 215}, + {53, 47.9, 215}, + {54, 47.8, 215}, + {44.7, 46, 215}, + {44.4, 45.4, 215}, + {44.5, 44.9, 215}, + {44.8, 44.9, 215}, + }, + [2978] = { + {36.3, 52.8, 17}, + {36.1, 52.8, 17}, + {36.4, 52.7, 17}, + {36, 52.6, 17}, + {37.1, 52.6, 17}, + {36.2, 52.6, 17}, + {36.4, 52.6, 17}, + {36.7, 52.6, 17}, + {36.1, 52.5, 17}, + {36.3, 52.4, 17}, + {35.9, 52.4, 17}, + {37.2, 52.3, 17}, + {36, 52.3, 17}, + {35.9, 52.2, 17}, + {37.1, 52.2, 17}, + {36.1, 52.1, 17}, + {36.7, 52, 17}, + {36.6, 51.8, 17}, + {37, 51.4, 17}, + {37.2, 51.3, 17}, + {37.5, 51.2, 17}, + {37.1, 51, 17}, + {37.7, 51, 17}, + {37.9, 50.6, 17}, + {38.6, 50.5, 17}, + {37.4, 50.4, 17}, + {37.3, 50.2, 17}, + {37.6, 50.2, 17}, + {37.5, 50.1, 17}, + {37.6, 50, 17}, + {38.7, 49.9, 17}, + {39, 49.9, 17}, + {37.7, 49.9, 17}, + {38.9, 49.9, 17}, + {37.1, 49.8, 17}, + {39, 49.8, 17}, + {36.2, 49.8, 17}, + {36.9, 49.8, 17}, + {38.6, 49.7, 17}, + {36.1, 49.7, 17}, + {38.4, 49.6, 17}, + {38.2, 49.5, 17}, + {37.7, 49.5, 17}, + {36.9, 49.5, 17}, + {36.7, 49.5, 17}, + {38.4, 49.5, 17}, + {37.3, 49.4, 17}, + {37, 49.4, 17}, + {38.2, 49.4, 17}, + {37.7, 49.3, 17}, + {37.4, 49.3, 17}, + {37.6, 49.3, 17}, + {37, 49.2, 17}, + {36.4, 49.2, 17}, + {37.3, 49.1, 17}, + {37.8, 49.1, 17}, + {36.9, 49, 17}, + {37.3, 48.9, 17}, + {37.8, 48.8, 17}, + {37.7, 48.8, 17}, + {38.9, 48.8, 17}, + {38.9, 48.7, 17}, + {37.6, 48.6, 17}, + {37.3, 48.6, 17}, + {38.8, 48.5, 17}, + {39, 48.5, 17}, + {36.6, 48.5, 17}, + {38.1, 48.4, 17}, + {39, 48.4, 17}, + {37.8, 48.2, 17}, + {38.2, 48.2, 17}, + {37.6, 48.2, 17}, + {38.7, 48.1, 17}, + {38.4, 48.1, 17}, + {36.7, 48.1, 17}, + {37.4, 48.1, 17}, + {36.6, 48.1, 17}, + {37.5, 48, 17}, + {39.3, 48, 17}, + {39.5, 47.8, 17}, + {39.2, 47.8, 17}, + {39.1, 47.7, 17}, + {39.4, 47.7, 17}, + {39, 47.6, 17}, + {38.3, 47.6, 17}, + {39.7, 47.6, 17}, + {38.5, 47.6, 17}, + {37.3, 47.6, 17}, + {38.6, 47.6, 17}, + {39.6, 47.6, 17}, + {37.6, 47.5, 17}, + {37.3, 47.5, 17}, + {37.5, 47.5, 17}, + {38.8, 47.3, 17}, + {37.5, 47.2, 17}, + {38.9, 47.2, 17}, + {37.6, 47.1, 17}, + {38.8, 47.1, 17}, + {38.7, 47, 17}, + {37.5, 47, 17}, + {38.8, 46.9, 17}, + {36.6, 46.9, 17}, + {38.7, 46.9, 17}, + {37.5, 46.8, 17}, + {36.7, 46.8, 17}, + {36.8, 46.6, 17}, + {37.1, 46.6, 17}, + {36.7, 46.5, 17}, + {36.2, 46.3, 17}, + {37.2, 46, 17}, + {37, 46, 17}, + {36.7, 45.8, 17}, + {37.2, 45.8, 17}, + {60.4, 49.1, 215}, + {60, 49, 215}, + {60.7, 49, 215}, + {59.7, 48.7, 215}, + {62, 48.7, 215}, + {60.2, 48.7, 215}, + {60.6, 48.7, 215}, + {61.3, 48.6, 215}, + {60, 48.5, 215}, + {60.5, 48.4, 215}, + {59.7, 48.4, 215}, + {62.1, 48.2, 215}, + {59.9, 48, 215}, + {59.6, 48, 215}, + {61.9, 47.8, 215}, + {60, 47.8, 215}, + {61.1, 47.6, 215}, + {61, 47.2, 215}, + {61.9, 46.4, 215}, + {62.2, 46.1, 215}, + {62.7, 45.9, 215}, + {62.1, 45.6, 215}, + {63.1, 45.5, 215}, + {63.5, 44.9, 215}, + {64.9, 44.6, 215}, + {62.5, 44.4, 215}, + {62.3, 44.1, 215}, + {62.9, 44.1, 215}, + {62.7, 43.8, 215}, + {63, 43.5, 215}, + {65.1, 43.4, 215}, + {65.8, 43.4, 215}, + {63.2, 43.4, 215}, + {65.5, 43.4, 215}, + {62, 43.2, 215}, + {65.8, 43.2, 215}, + {60.2, 43.2, 215}, + {61.6, 43.1, 215}, + {65, 43, 215}, + {60, 42.9, 215}, + {64.6, 42.8, 215}, + {64.2, 42.7, 215}, + {63.1, 42.7, 215}, + {61.6, 42.7, 215}, + {61.1, 42.6, 215}, + {64.5, 42.6, 215}, + {62.3, 42.4, 215}, + {61.9, 42.3, 215}, + {64.2, 42.3, 215}, + {63.2, 42.3, 215}, + {62.6, 42.3, 215}, + {63, 42.2, 215}, + {61.9, 42, 215}, + {60.6, 42, 215}, + {62.3, 41.9, 215}, + {63.3, 41.7, 215}, + {61.6, 41.6, 215}, + {62.5, 41.5, 215}, + {63.4, 41.3, 215}, + {63.2, 41.2, 215}, + {65.4, 41.2, 215}, + {65.6, 41.1, 215}, + {62.9, 40.9, 215}, + {62.3, 40.8, 215}, + {65.3, 40.7, 215}, + {65.7, 40.7, 215}, + {61, 40.6, 215}, + {64, 40.4, 215}, + {65.7, 40.3, 215}, + {63.3, 40.1, 215}, + {64.2, 40.1, 215}, + {62.9, 40, 215}, + {65.1, 39.9, 215}, + {64.6, 39.9, 215}, + {61.2, 39.9, 215}, + {62.5, 39.8, 215}, + {61.1, 39.8, 215}, + {62.8, 39.7, 215}, + {66.3, 39.6, 215}, + {66.4, 39.6, 215}, + {66.1, 39.2, 215}, + {66, 39.1, 215}, + {65.7, 38.9, 215}, + {64.3, 38.9, 215}, + {64.7, 38.9, 215}, + {62.4, 38.8, 215}, + {64.9, 38.8, 215}, + {62.9, 38.7, 215}, + {62.4, 38.7, 215}, + {62.9, 38.6, 215}, + {65.2, 38.3, 215}, + {62.7, 38.1, 215}, + {65.5, 38.1, 215}, + {63, 38, 215}, + {65.4, 37.9, 215}, + {65.1, 37.7, 215}, + {62.8, 37.6, 215}, + {65.3, 37.5, 215}, + {61, 37.5, 215}, + {65.2, 37.4, 215}, + {62.7, 37.3, 215}, + {61.2, 37.3, 215}, + {61.4, 36.9, 215}, + {62, 36.8, 215}, + {61.2, 36.7, 215}, + {60.3, 36.3, 215}, + {62.1, 35.7, 215}, + {61.7, 35.6, 215}, + {61.2, 35.4, 215}, + {62.1, 35.2, 215}, + {40.2, 16.9, 215}, + {40.9, 16.8, 215}, + {40.8, 15.9, 215}, + {39.5, 15.9, 215}, + {40.2, 15.9, 215}, + {39.5, 14.9, 215}, + {40.2, 14.9, 215}, + {42.5, 14.4, 215}, + {43, 14.3, 215}, + {42.4, 14.2, 215}, + {42.7, 13.6, 215}, + {43, 13.5, 215}, + {42.5, 13.5, 215}, + {35.9, 52.6, 17}, + {36.8, 52.6, 17}, + {35.9, 52.5, 17}, + {36.1, 52.3, 17}, + {36.6, 52, 17}, + {60.4, 49.2, 215}, + {60.1, 49.1, 215}, + {61.3, 48.7, 215}, + {60, 48, 215}, + {59.6, 47.9, 215}, + {40.3, 16.9, 215}, + {40.8, 16, 215}, + {39.5, 16, 215}, + {39.6, 15, 215}, + {40.1, 14.8, 215}, + {42.6, 13.6, 215}, + {43, 13.6, 215}, + }, + [2979] = { + {36.3, 52.5, 17}, + {36.2, 52.4, 17}, + {36.8, 52, 17}, + {36.9, 51.7, 17}, + {36.9, 51.6, 17}, + {37, 51.3, 17}, + {37.3, 51.2, 17}, + {37.3, 51, 17}, + {37.7, 50.7, 17}, + {38.4, 50.5, 17}, + {38.5, 50.3, 17}, + {38.5, 50.1, 17}, + {38.3, 49.8, 17}, + {36.5, 49.5, 17}, + {36.3, 49.3, 17}, + {36.7, 49.3, 17}, + {37.2, 49.2, 17}, + {37.5, 49.2, 17}, + {36.5, 49.2, 17}, + {36.6, 49, 17}, + {36.8, 48.9, 17}, + {37.6, 48.9, 17}, + {37, 48.8, 17}, + {36.7, 48.8, 17}, + {36.9, 48.7, 17}, + {37.9, 48.7, 17}, + {37.1, 48.6, 17}, + {36.8, 48.6, 17}, + {37.4, 48.5, 17}, + {37.9, 48.3, 17}, + {38.9, 48.1, 17}, + {38.8, 48, 17}, + {38.6, 48, 17}, + {39.1, 48, 17}, + {37.2, 48, 17}, + {37.6, 47.9, 17}, + {36.5, 47.8, 17}, + {38.3, 47.7, 17}, + {38.1, 47.7, 17}, + {38, 47.7, 17}, + {38.7, 47.6, 17}, + {38.9, 47.6, 17}, + {36.5, 47.4, 17}, + {37.2, 47.4, 17}, + {37.7, 47.4, 17}, + {36.4, 47.3, 17}, + {37.3, 47.2, 17}, + {36.5, 47.2, 17}, + {36.6, 47.2, 17}, + {36.6, 46.8, 17}, + {36.4, 46.7, 17}, + {36.6, 46.5, 17}, + {35.6, 46.5, 17}, + {36.3, 46.5, 17}, + {36.5, 46.4, 17}, + {35.6, 46.1, 17}, + {35.6, 45.7, 17}, + {35.5, 45.7, 17}, + {36.4, 45.6, 17}, + {35.8, 45.5, 17}, + {36.2, 45.2, 17}, + {60.4, 48.6, 215}, + {60.2, 48.3, 215}, + {61.4, 47.5, 215}, + {61.5, 46.9, 215}, + {61.7, 46.7, 215}, + {61.8, 46.1, 215}, + {62.3, 45.9, 215}, + {62.3, 45.6, 215}, + {63.2, 45, 215}, + {63.2, 44.9, 215}, + {64.6, 44.5, 215}, + {64.8, 44.1, 215}, + {64.8, 43.7, 215}, + {64.4, 43.1, 215}, + {60.9, 42.5, 215}, + {60.5, 42.3, 215}, + {61.3, 42.1, 215}, + {62.2, 42.1, 215}, + {62.8, 42, 215}, + {60.9, 41.9, 215}, + {61.1, 41.6, 215}, + {61.3, 41.3, 215}, + {62.9, 41.3, 215}, + {61.9, 41.2, 215}, + {61.1, 41.1, 215}, + {61.6, 41, 215}, + {63.5, 40.9, 215}, + {62, 40.9, 215}, + {61.4, 40.8, 215}, + {62.7, 40.7, 215}, + {63.5, 40.2, 215}, + {65.6, 39.9, 215}, + {65.3, 39.7, 215}, + {64.9, 39.6, 215}, + {65.9, 39.6, 215}, + {62.2, 39.6, 215}, + {63.1, 39.5, 215}, + {60.9, 39.3, 215}, + {64.4, 39.1, 215}, + {64, 39, 215}, + {63.7, 39, 215}, + {65.2, 38.9, 215}, + {65.5, 38.8, 215}, + {60.8, 38.5, 215}, + {62.2, 38.5, 215}, + {63.1, 38.4, 215}, + {60.7, 38.2, 215}, + {62.4, 38.1, 215}, + {60.9, 38, 215}, + {61, 38, 215}, + {61, 37.2, 215}, + {60.7, 37, 215}, + {61, 36.8, 215}, + {59.1, 36.6, 215}, + {60.4, 36.6, 215}, + {60.7, 36.5, 215}, + {59.1, 36, 215}, + {59, 35.2, 215}, + {58.9, 35, 215}, + {59.1, 35, 215}, + {60.7, 34.8, 215}, + {59.4, 34.7, 215}, + {60.2, 34.2, 215}, + {40.4, 16.4, 215}, + {40, 15.6, 215}, + {42.6, 14.2, 215}, + {42.9, 13.8, 215}, + }, + [2989] = { + {31.9, 50.5, 215}, + {31.1, 50.2, 215}, + {32.1, 50, 215}, + {30.9, 49.9, 215}, + {33.8, 49.7, 215}, + {32.1, 49.6, 215}, + {30.9, 49.2, 215}, + {32.3, 49.2, 215}, + {32.7, 49.1, 215}, + {33.7, 49, 215}, + {31.7, 48.9, 215}, + {30.9, 48.8, 215}, + {31.5, 48.8, 215}, + {30.9, 48.5, 215}, + {33.7, 48, 215}, + {31, 47.9, 215}, + {34.3, 47.9, 215}, + {31.4, 47.6, 215}, + {31.6, 47.6, 215}, + {32, 47.3, 215}, + {34.4, 47.1, 215}, + {84.1, 81.5, 405}, + {84.1, 78.9, 405}, + {85.2, 78.3, 405}, + {87.9, 78, 405}, + {33.8, 48, 215}, + }, + [2990] = { + {31.4, 49.9, 215}, + {31.8, 49.7, 215}, + {32, 49.7, 215}, + {31.5, 49.4, 215}, + {31.3, 48.8, 215}, + {31.5, 48.8, 215}, + {31.3, 48.7, 215}, + {31.5, 48.5, 215}, + {32.8, 48.3, 215}, + {31.3, 48.3, 215}, + {31.9, 47.9, 215}, + {33, 47.5, 215}, + {33.2, 47.5, 215}, + {84.4, 79.3, 405}, + {86.6, 78.4, 405}, + {31.6, 48.4, 215}, + {32.7, 48.3, 215}, + }, + [3035] = { + {37.5, 54.1, 17}, + {57, 72.2, 215}, + {57.7, 71.9, 215}, + {59.7, 64.8, 215}, + {57.3, 63.8, 215}, + {57.1, 62.8, 215}, + {59.4, 58.4, 215}, + {59, 58.1, 215}, + {59.6, 54.6, 215}, + {59.4, 53.5, 215}, + {58.7, 52.7, 215}, + {43.9, 52.2, 215}, + {35, 51.9, 215}, + {62.7, 51.6, 215}, + {56.2, 51.4, 215}, + {42, 50, 215}, + {57.6, 49.4, 215}, + {57.3, 49.1, 215}, + {44.2, 48.8, 215}, + {36.5, 48.7, 215}, + {36.9, 48.6, 215}, + {56.5, 48.2, 215}, + {45.2, 47.4, 215}, + {54.4, 47, 215}, + {39, 46.9, 215}, + {39.6, 46, 215}, + {54.9, 45.9, 215}, + {38.4, 45.7, 215}, + {43.3, 45.4, 215}, + {49.3, 45.2, 215}, + {48.6, 44.5, 215}, + {36.9, 44, 215}, + {50.4, 43.3, 215}, + {42.8, 43.2, 215}, + {48, 43.1, 215}, + {55.5, 43.1, 215}, + {36.6, 43, 215}, + {34.4, 42.6, 215}, + {46.3, 42.3, 215}, + {35.7, 42.2, 215}, + {50.6, 42, 215}, + {56.1, 41.9, 215}, + {54.1, 41.9, 215}, + {51.7, 41.5, 215}, + {48.6, 41.4, 215}, + {39, 41.2, 215}, + {49.8, 41.1, 215}, + {53.4, 40.7, 215}, + {50.6, 40.6, 215}, + {54.6, 40.4, 215}, + {47.3, 40.2, 215}, + {53.1, 40.2, 215}, + {38.1, 39.9, 215}, + {55.9, 39.5, 215}, + {50.5, 39.3, 215}, + {37.4, 39.3, 215}, + {38.9, 39.1, 215}, + {55.1, 38.4, 215}, + {54, 38.2, 215}, + {40.8, 38.1, 215}, + {45.6, 38.1, 215}, + {56.2, 38.1, 215}, + {52.8, 38, 215}, + {49.4, 37.6, 215}, + {42.1, 37.3, 215}, + {52, 37.3, 215}, + {52.8, 37.3, 215}, + {55.8, 37.2, 215}, + {49.8, 36.8, 215}, + {47.3, 36.4, 215}, + {46.7, 36.4, 215}, + {45.4, 36.4, 215}, + {51.1, 36.4, 215}, + {56.8, 36.3, 215}, + {53.9, 35.9, 215}, + {55.7, 35.6, 215}, + {52.1, 35.5, 215}, + {54.5, 35.3, 215}, + {48.2, 35.2, 215}, + {47.3, 35, 215}, + {53.2, 34.9, 215}, + {50.4, 34.1, 215}, + {35, 33.5, 215}, + {52.1, 33.2, 215}, + {34.5, 32.7, 215}, + {48.1, 32.4, 215}, + {44.7, 31.6, 215}, + {34.8, 31.3, 215}, + {46, 30.6, 215}, + {34.1, 26.4, 215}, + {33.7, 25.6, 215}, + {88, 72.9, 405}, + {88.6, 62.4, 405}, + {88, 61.6, 405}, + {88.4, 60, 405}, + {87.6, 54.4, 405}, + {87.1, 53.4, 405}, + {38.8, 39.3, 215}, + }, + [3068] = { + {50.4, 42.6, 215}, + }, + [3099] = { + {51.8, 78.1, 14}, + {51.6, 72.5, 14}, + {50.5, 71.8, 14}, + {54.1, 70.8, 14}, + {53.5, 68.1, 14}, + {54.6, 67.4, 14}, + {51.9, 66.9, 14}, + {51.7, 65.9, 14}, + {51.6, 65.4, 14}, + {52.7, 65, 14}, + {51.5, 63.7, 14}, + {52.2, 63, 14}, + {53.9, 62.6, 14}, + {51.5, 62.1, 14}, + {52, 61.7, 14}, + {52.2, 60.7, 14}, + {53.8, 59.1, 14}, + {51.7, 59, 14}, + {55.6, 58.7, 14}, + {53.2, 58.6, 14}, + {54.5, 57.4, 14}, + {52.6, 57.1, 14}, + {55.3, 57, 14}, + {51.2, 56.7, 14}, + {52.4, 56.6, 14}, + {55.7, 56.2, 14}, + {53.9, 56.2, 14}, + {50.7, 55.9, 14}, + {50.8, 53.9, 14}, + {51.7, 53.6, 14}, + {54.2, 53.1, 14}, + {51, 51.8, 14}, + {40.7, 50.8, 14}, + {41.7, 50.3, 14}, + {42.9, 50.1, 14}, + {52.1, 49.9, 14}, + {53.8, 49.8, 14}, + {51.2, 49.4, 14}, + {40.2, 49, 14}, + {53.3, 48.8, 14}, + {45.4, 48.6, 14}, + {46, 48.3, 14}, + {52.2, 47.9, 14}, + {40, 47.8, 14}, + {42.8, 47.7, 14}, + {40, 47.4, 14}, + {42, 47.1, 14}, + {38.9, 47.1, 14}, + {57.9, 46.8, 14}, + {43.7, 46.4, 14}, + {47.1, 46.2, 14}, + {53.4, 46.2, 14}, + {51.7, 46, 14}, + {42.7, 46, 14}, + {48.3, 45.9, 14}, + {38.6, 45, 14}, + {41.1, 44.9, 14}, + {40.8, 44.9, 14}, + {46.7, 44.7, 14}, + {45.7, 44.6, 14}, + {47.6, 44.5, 14}, + {46.2, 44.4, 14}, + {57.4, 44.4, 14}, + {42.1, 44.3, 14}, + {55.4, 44, 14}, + {43.1, 44, 14}, + {45, 43.5, 14}, + {47.7, 43.4, 14}, + {43.9, 43.3, 14}, + {42.9, 43.3, 14}, + {47, 42.8, 14}, + {58.4, 41.6, 14}, + {56.2, 41.3, 14}, + {45.2, 41.3, 14}, + {40.7, 40.7, 14}, + {55.2, 40.3, 14}, + {48.2, 40.2, 14}, + {56.2, 39.6, 14}, + {55.4, 39.6, 14}, + {46.2, 39.1, 14}, + {56, 39, 14}, + {54.5, 38.7, 14}, + {47.2, 38.3, 14}, + {45.7, 38.3, 14}, + {54.4, 38, 14}, + {39, 37.8, 14}, + {51.4, 37.6, 14}, + {44.6, 37.1, 14}, + {41.1, 36.8, 14}, + {53.2, 36.8, 14}, + {56.5, 36.7, 14}, + {48.3, 36.7, 14}, + {50.7, 36.7, 14}, + {39.4, 36.5, 14}, + {38.1, 35.9, 14}, + {54.4, 35.9, 14}, + {55.8, 35.4, 14}, + {53.5, 35, 14}, + {46, 34.9, 14}, + {49.5, 34.8, 14}, + {50.9, 34.7, 14}, + {54.6, 34.7, 14}, + {44.4, 34.6, 14}, + {51.5, 34.1, 14}, + {43.8, 34, 14}, + {51.1, 33.1, 14}, + {42.6, 32.9, 14}, + {53.4, 32.8, 14}, + {55.4, 32.8, 14}, + {40.4, 32.8, 14}, + {53.9, 31.6, 14}, + {42, 31.5, 14}, + {53.1, 31.3, 14}, + {54.2, 30.6, 14}, + {54.6, 56.3, 14}, + {52, 50, 14}, + {46.9, 46.3, 14}, + {47, 42.1, 14}, + {46.8, 41.7, 14}, + {47.1, 41.4, 14}, + {48, 41.3, 14}, + {48.8, 40.4, 14}, + {48.7, 39.6, 14}, + {49.1, 39.3, 14}, + {50.2, 39.3, 14}, + {48.8, 38.3, 14}, + {50.6, 38.2, 14}, + {46.8, 38, 14}, + {49.3, 37.7, 14}, + {46, 37.5, 14}, + {48, 37.4, 14}, + {48.8, 37.2, 14}, + {53.7, 36.9, 14}, + {49.3, 36.9, 14}, + {48.7, 36.6, 14}, + {47.9, 36.5, 14}, + {48.6, 36.2, 14}, + {53.3, 36, 14}, + {48.9, 35, 14}, + {49.8, 35, 14}, + }, + [3100] = { + {37.8, 55.8, 14}, + {35.4, 55.7, 14}, + {37.5, 55.6, 14}, + {36.4, 55.5, 14}, + {37.5, 54.6, 14}, + {37.1, 52.4, 14}, + {39.4, 51.7, 14}, + {39.6, 49.5, 14}, + {35.1, 49.3, 14}, + {36.3, 49.2, 14}, + {38.5, 48.9, 14}, + {38, 48.5, 14}, + {36.9, 48.2, 14}, + {35.7, 47, 14}, + {35, 46.5, 14}, + {36.9, 46.1, 14}, + {35.6, 45.6, 14}, + {37.6, 45.2, 14}, + {36.9, 44.9, 14}, + {38.6, 44.3, 14}, + {39.6, 44.3, 14}, + {35, 43.3, 14}, + {40.7, 42.5, 14}, + {38.3, 42.1, 14}, + {37.1, 41.3, 14}, + {35.6, 39.4, 14}, + {36.3, 38.9, 14}, + {36.5, 38.5, 14}, + {36.3, 35.7, 14}, + {35.9, 34.9, 14}, + {40.1, 33.8, 14}, + {38.6, 33.7, 14}, + {36.2, 31.9, 14}, + {37, 31.7, 14}, + {35.7, 31, 14}, + {37.7, 30, 14}, + {57, 29.8, 14}, + {38.3, 28.9, 14}, + {54.6, 28.8, 14}, + {56.9, 28.4, 14}, + {42.8, 28.3, 14}, + {55.2, 28.1, 14}, + {37, 28.1, 14}, + {43.1, 27.9, 14}, + {45.4, 27.3, 14}, + {58.4, 27.3, 14}, + {44.6, 27, 14}, + {37.2, 26.4, 14}, + {43.9, 26.3, 14}, + {36, 26, 14}, + {44.6, 25.2, 14}, + {45.4, 25.1, 14}, + {56.9, 24.6, 14}, + {57.8, 24.5, 14}, + {36.2, 24.3, 14}, + {59, 24.2, 14}, + {56.9, 23.5, 14}, + {44.5, 23.3, 14}, + {42.1, 22.8, 14}, + {57.7, 22.5, 14}, + {43.9, 22.4, 14}, + {42.6, 22.3, 14}, + {44.8, 22.3, 14}, + {46, 22.2, 14}, + {40.2, 21.8, 14}, + {56, 21.7, 14}, + {40.5, 21.6, 14}, + {43.9, 21.5, 14}, + {42.7, 21.5, 14}, + {43.2, 21.4, 14}, + {38.7, 21.2, 14}, + {44.6, 21.2, 14}, + {48.6, 20.7, 14}, + {48, 20.6, 14}, + {55.2, 20.5, 14}, + {57.7, 20.3, 14}, + {47.1, 20.2, 14}, + {39.5, 19.6, 14}, + {44.5, 19.5, 14}, + {46, 19.4, 14}, + {54.8, 19.4, 14}, + {57.1, 19.3, 14}, + {37.2, 19, 14}, + {54, 18.9, 14}, + {39.5, 18.7, 14}, + {45.1, 18.7, 14}, + {55.1, 18.6, 14}, + {57.5, 18.5, 14}, + {42.4, 18.5, 14}, + {47.8, 18.4, 14}, + {43.3, 18.1, 14}, + {46.3, 17.9, 14}, + {57, 17.8, 14}, + {44.7, 17.8, 14}, + {39.4, 17.7, 14}, + {44.9, 17.7, 14}, + {42.7, 17.6, 14}, + {52.9, 17.5, 14}, + {54.3, 17.4, 14}, + {44.2, 17.3, 14}, + {40.1, 17.1, 14}, + {56.1, 17, 14}, + {43.2, 16.9, 14}, + {53.6, 16.8, 14}, + {45.2, 16.8, 14}, + {41.9, 16.7, 14}, + {51.6, 16.7, 14}, + {45.5, 16.6, 14}, + {44.9, 16.2, 14}, + {55.4, 16.1, 14}, + {52.5, 15.9, 14}, + {45.8, 15.8, 14}, + {48.3, 15.8, 14}, + {50.8, 15.8, 14}, + {54.6, 15.8, 14}, + {43.8, 15.5, 14}, + {45.9, 15.5, 14}, + {44, 15.3, 14}, + {50.2, 14.9, 14}, + {52.2, 14.8, 14}, + {52.9, 14.2, 14}, + {55.2, 14, 14}, + {56.5, 13.2, 14}, + {51.6, 13.2, 14}, + {53.4, 13, 14}, + {50.2, 13, 14}, + {55.6, 12.9, 14}, + {47.7, 12.9, 14}, + {51.8, 11.8, 14}, + {54.5, 11, 14}, + {55.1, 11, 14}, + {55.8, 10, 14}, + {53.9, 9.3, 14}, + {65, 26.2, 17}, + {63.7, 26.1, 17}, + {64.8, 26.1, 17}, + {64.3, 26, 17}, + {64.8, 25.6, 17}, + {63.6, 22.8, 17}, + {64.2, 22.8, 17}, + {63.9, 21.6, 17}, + {63.5, 21.3, 17}, + {63.8, 20.9, 17}, + {63.5, 19.7, 17}, + {63.8, 17.6, 17}, + {64.2, 17.4, 17}, + {64.3, 17.2, 17}, + {64.2, 15.7, 17}, + {64, 15.3, 17}, + {64.1, 13.8, 17}, + {63.9, 13.3, 17}, + {64.9, 12.7, 17}, + {65.2, 12.2, 17}, + {64.6, 11.7, 17}, + {64.7, 10.9, 17}, + {64.1, 10.7, 17}, + {64.1, 9.8, 17}, + {64.7, 7, 17}, + {57.4, 98.5, 1637}, + {55.3, 20.4, 14}, + {39.3, 19.6, 14}, + {45.1, 18.6, 14}, + {39.4, 17.6, 14}, + {45.2, 16.7, 14}, + {45.7, 15.7, 14}, + {50.9, 15.6, 14}, + {50.2, 14.8, 14}, + {50.2, 12.9, 14}, + {47.7, 12.7, 14}, + {57.3, 97.6, 1637}, + }, + [3103] = { + {60.9, 96.3, 14}, + {62.2, 92.5, 14}, + {59.1, 91.5, 14}, + {69.9, 88.7, 14}, + {59.1, 87.6, 14}, + {59.7, 86.7, 14}, + {70.3, 85.9, 14}, + {52.7, 84.9, 14}, + {51.5, 84.8, 14}, + {70.5, 82.3, 14}, + {59, 82, 14}, + {61.8, 80.4, 14}, + {57.3, 80, 14}, + {69.8, 77.3, 14}, + {60.9, 77.3, 14}, + {67.3, 77.2, 14}, + {68.7, 77.2, 14}, + {64.6, 77.2, 14}, + {70.8, 75.2, 14}, + {61, 72.7, 14}, + {71.7, 72.6, 14}, + {59.7, 72.4, 14}, + {62.1, 68.9, 14}, + {62.2, 67, 14}, + {62.8, 61.1, 14}, + {63.5, 60.1, 14}, + {63.4, 58.5, 14}, + {63, 56.4, 14}, + {64, 55.4, 14}, + {64.7, 54.8, 14}, + {65.5, 54, 14}, + {65.2, 51.5, 14}, + {61.6, 50.9, 14}, + {63.5, 50.8, 14}, + {65.2, 49.8, 14}, + {64.7, 48.8, 14}, + {62.7, 47, 14}, + {62.8, 45, 14}, + {61.5, 43.2, 14}, + {62.8, 43, 14}, + {62.1, 40.5, 14}, + {77, 47.4, 17}, + {77.7, 45.4, 17}, + {76.1, 44.8, 17}, + {81.7, 43.4, 17}, + {76.1, 42.8, 17}, + {76.4, 42.4, 17}, + {82, 41.9, 17}, + {72.7, 41.4, 17}, + {72.1, 41.4, 17}, + {59.5, 87, 14}, + {51.4, 84.9, 14}, + {59, 82.1, 14}, + {61, 77.3, 14}, + {69.7, 77.3, 14}, + {61.1, 72.5, 14}, + {61.6, 43, 14}, + {76.3, 42.5, 17}, + }, + [3104] = { + {58.2, 85.2, 14}, + {53.9, 84.8, 14}, + {61, 84.6, 14}, + {55.1, 82.8, 14}, + {66.6, 78.1, 14}, + {60.2, 75.7, 14}, + {63.4, 73.8, 14}, + {65.9, 73.6, 14}, + {60.3, 73.5, 14}, + {65.7, 71.7, 14}, + {60.3, 71.6, 14}, + {61.5, 69.7, 14}, + {64, 57.6, 14}, + {63.5, 55.8, 14}, + {61.5, 54.4, 14}, + {64.8, 50.9, 14}, + {62.1, 49.7, 14}, + {61.5, 49, 14}, + {61.4, 44.8, 14}, + {61.1, 42.6, 14}, + {60.9, 40.4, 14}, + {75.6, 41.5, 17}, + {73.4, 41.4, 17}, + {77.1, 41.3, 17}, + {61.4, 69.7, 14}, + {61.5, 48.8, 14}, + }, + [3105] = { + {60.2, 30, 14}, + {59.8, 28.9, 14}, + {60.4, 26.4, 14}, + {60.3, 20.7, 14}, + {60.4, 18.5, 14}, + {60.3, 17.2, 14}, + {58.8, 15.1, 14}, + {58.5, 10.2, 14}, + {57.5, 9.4, 14}, + }, + [3107] = { + {64.6, 98.2, 14}, + {63.3, 98, 14}, + {64.6, 96.5, 14}, + {61.9, 94.9, 14}, + {64.6, 94.2, 14}, + {65.2, 93.3, 14}, + {63.2, 93.2, 14}, + {63.7, 93.2, 14}, + {63.4, 92.8, 14}, + {60.3, 92.3, 14}, + {65.9, 91.5, 14}, + {67, 90.8, 14}, + {62.1, 90.5, 14}, + {65.1, 89.9, 14}, + {63.9, 89, 14}, + {69.5, 87.5, 14}, + {61.5, 87.4, 14}, + {61, 86.7, 14}, + {58.9, 86.2, 14}, + {60.2, 85.8, 14}, + {58.3, 83.5, 14}, + {61.4, 82.1, 14}, + {62.8, 81.3, 14}, + {59.6, 81.3, 14}, + {62.1, 80.9, 14}, + {70.4, 80.1, 14}, + {66.1, 79.4, 14}, + {62.1, 79.3, 14}, + {64.1, 78.9, 14}, + {69.7, 78.8, 14}, + {68, 78.6, 14}, + {69.3, 78.3, 14}, + {63.6, 77, 14}, + {62.1, 76.6, 14}, + {67, 76.4, 14}, + {69, 76.3, 14}, + {62.7, 76.1, 14}, + {70.5, 75.9, 14}, + {65.9, 75.6, 14}, + {67.3, 75.5, 14}, + {65.5, 74.6, 14}, + {63.1, 74, 14}, + {71, 73.9, 14}, + {66.3, 72.9, 14}, + {65.1, 72.6, 14}, + {64.1, 72.2, 14}, + {71.1, 71.5, 14}, + {66.2, 70.8, 14}, + {66.1, 70, 14}, + {70.5, 69, 14}, + {67.9, 68.7, 14}, + {58.8, 28.3, 14}, + {59.9, 27.2, 14}, + {60.8, 25.4, 14}, + {60, 25.3, 14}, + {60.3, 24.7, 14}, + {59.8, 23.6, 14}, + {60.2, 22.8, 14}, + {60.9, 17.7, 14}, + {79, 48.4, 17}, + {78.3, 48.2, 17}, + {79, 47.4, 17}, + {77.5, 46.6, 17}, + {78.9, 46.2, 17}, + {79.3, 45.8, 17}, + {78.2, 45.7, 17}, + {78.5, 45.7, 17}, + {78.3, 45.5, 17}, + {76.7, 45.2, 17}, + {79.7, 44.8, 17}, + {80.2, 44.5, 17}, + {77.6, 44.3, 17}, + {79.2, 44, 17}, + {78.6, 43.6, 17}, + {81.5, 42.8, 17}, + {77.4, 42.7, 17}, + {77.1, 42.4, 17}, + {76, 42.1, 17}, + {76.7, 41.9, 17}, + {75.7, 40.7, 17}, + }, + [3108] = { + {59.1, 16.6, 14}, + {58.4, 15.7, 14}, + {57.9, 15.2, 14}, + {58.3, 13.8, 14}, + {57.7, 13.1, 14}, + {57.2, 12.3, 14}, + {58.1, 12, 14}, + {57.8, 11.4, 14}, + {56.3, 11.1, 14}, + {56.9, 10.2, 14}, + {56.5, 9.1, 14}, + {57.8, 96.6, 14}, + {57.6, 95.6, 14}, + {56.9, 95.2, 14}, + {55.5, 95.1, 14}, + {56.3, 94.8, 14}, + {55.6, 94.6, 14}, + {75, 50.8, 17}, + {75.5, 50.7, 17}, + {75.6, 50.2, 17}, + {73.5, 50, 17}, + {76, 49.6, 17}, + {75.4, 47.5, 17}, + {75.3, 47, 17}, + {74.9, 46.8, 17}, + {74.2, 46.7, 17}, + {74.6, 46.6, 17}, + {74.3, 46.5, 17}, + }, + [3110] = { + {38.5, 71.5, 14}, + {37.8, 69.8, 14}, + {37.6, 68.1, 14}, + {37.4, 66.1, 14}, + {36.9, 62.2, 14}, + {36.4, 61.2, 14}, + {36.5, 61.1, 14}, + {35.7, 59.1, 14}, + {35.7, 57.4, 14}, + {35.6, 57.3, 14}, + {35.3, 56.2, 14}, + {34.4, 55.5, 14}, + {34.8, 54.3, 14}, + {34.5, 53.8, 14}, + {33.8, 52.5, 14}, + {34.6, 52.1, 14}, + {33.7, 51.2, 14}, + {33.5, 50.9, 14}, + {34.1, 49.8, 14}, + {34.3, 48.1, 14}, + {33.9, 47.2, 14}, + {34.1, 46, 14}, + {33.8, 45.6, 14}, + {33.6, 45.2, 14}, + {34.5, 44.3, 14}, + {34.4, 44.2, 14}, + {35.1, 39.2, 14}, + {34.4, 37.1, 14}, + {34.2, 34.9, 14}, + {34.3, 34.7, 14}, + {34.5, 32.3, 14}, + {34.4, 32.3, 14}, + {35.2, 30.1, 14}, + {34.6, 29, 14}, + {35.3, 28.3, 14}, + {34.6, 27.2, 14}, + {34.7, 27.1, 14}, + {35.1, 26.3, 14}, + {34.4, 25.7, 14}, + {35.7, 25.3, 14}, + {35.2, 24.6, 14}, + {35.7, 23.4, 14}, + {36.5, 22.2, 14}, + {36.8, 22.1, 14}, + {35.5, 21.5, 14}, + {36.3, 20.9, 14}, + {36.8, 17.2, 14}, + {37.1, 15.8, 14}, + {65.3, 34.4, 17}, + {65, 33.5, 17}, + {64.9, 32.6, 17}, + {64.8, 31.6, 17}, + {64.5, 29.6, 17}, + {64.3, 29, 17}, + {63.9, 27.9, 17}, + {63.9, 27.1, 17}, + {63.8, 27, 17}, + {63.7, 26.4, 17}, + {63.2, 26, 17}, + {63.4, 25.4, 17}, + {63.3, 25.2, 17}, + {62.9, 24.5, 17}, + {63.3, 24.3, 17}, + {62.8, 23.8, 17}, + {62.7, 23.7, 17}, + {63, 23.1, 17}, + {63.2, 22.2, 17}, + {62.9, 21.7, 17}, + {63.1, 21.1, 17}, + {62.9, 20.9, 17}, + {62.8, 20.7, 17}, + {63.2, 20.2, 17}, + {63.2, 20.1, 17}, + {63.6, 17.6, 17}, + {63.2, 16.5, 17}, + {62.7, 15.5, 17}, + {63.1, 15.3, 17}, + {63.2, 15.2, 17}, + {63.3, 13.9, 17}, + {63.2, 13.9, 17}, + {63.6, 12.8, 17}, + {63.3, 12.2, 17}, + {63.7, 11.9, 17}, + {63.3, 11.3, 17}, + {63.4, 11.2, 17}, + {63.5, 10.8, 17}, + {63.2, 10.5, 17}, + {63.9, 10.3, 17}, + {63.6, 9.9, 17}, + {63.9, 9.3, 17}, + {64.3, 8.7, 17}, + {64.5, 8.6, 17}, + {63.8, 8.3, 17}, + {64.2, 8, 17}, + {64.4, 6.1, 17}, + {64.6, 5.4, 17}, + {35.8, 57.4, 14}, + {35, 26.1, 14}, + {63.9, 27, 17}, + {63.5, 10.7, 17}, + }, + [3111] = { + {50.2, 50.8, 14}, + {47.5, 49.6, 14}, + {47.4, 49.3, 14}, + {50.3, 49.1, 14}, + {46.4, 49, 14}, + {50.7, 48.9, 14}, + {48.8, 48.9, 14}, + {48.9, 48.8, 14}, + {47.2, 48.7, 14}, + {49.1, 48.7, 14}, + {47.5, 48.6, 14}, + {49, 48.3, 14}, + {49.4, 48.2, 14}, + {47, 48, 14}, + {47.6, 48, 14}, + {49.6, 47.6, 14}, + {47.6, 49.6, 14}, + {46.2, 49, 14}, + {47, 48.1, 14}, + }, + [3112] = { + {43.3, 50.8, 14}, + {44.5, 50.6, 14}, + {44.9, 50.4, 14}, + {44, 50.3, 14}, + {43.7, 50.2, 14}, + {44.5, 49.9, 14}, + {47.4, 49.9, 14}, + {50.1, 49.8, 14}, + {44.1, 49.6, 14}, + {47.1, 49.5, 14}, + {44.2, 49.4, 14}, + {50.1, 49.3, 14}, + {49.8, 49.3, 14}, + {49.2, 49.1, 14}, + {49.4, 48.7, 14}, + {47.2, 48, 14}, + {49.6, 47.8, 14}, + {47.5, 49.6, 14}, + {47.4, 49.3, 14}, + {46.4, 49, 14}, + {47, 48, 14}, + {47.6, 48, 14}, + {44.5, 50.7, 14}, + {44.4, 50, 14}, + {44.2, 49.6, 14}, + {49.5, 49.1, 14}, + {47.6, 47.9, 14}, + }, + [3113] = { + {39, 53.7, 14}, + {39.5, 53.2, 14}, + {38.2, 52.9, 14}, + {39.3, 52.9, 14}, + {38.9, 52.8, 14}, + {43.9, 40.4, 14}, + {43.3, 40.4, 14}, + {44, 39.8, 14}, + {43.1, 39.6, 14}, + {42.9, 39.3, 14}, + {44.2, 39.2, 14}, + {43.3, 39, 14}, + {42.6, 38.9, 14}, + {42.9, 38.8, 14}, + {41.7, 38.5, 14}, + {44.1, 38.3, 14}, + {43.6, 38.1, 14}, + {41.3, 37.6, 14}, + {44.1, 37.5, 14}, + {43.4, 37.3, 14}, + {41.8, 37.2, 14}, + {43.9, 40.5, 14}, + {44.2, 38.2, 14}, + }, + [3114] = { + {38.7, 55.2, 14}, + {38.3, 54.7, 14}, + {38.2, 54, 14}, + {39.4, 53.8, 14}, + {38.8, 53.5, 14}, + {39.1, 53, 14}, + {42.9, 41.6, 14}, + {43.9, 41.4, 14}, + {42.6, 40.5, 14}, + {41.9, 40.3, 14}, + {44.4, 39.7, 14}, + {42.3, 39.4, 14}, + {42.6, 39.2, 14}, + {43.1, 38.9, 14}, + {41.9, 38.9, 14}, + {41.7, 38.2, 14}, + {43, 38.2, 14}, + {45.1, 37.6, 14}, + {42.7, 37.4, 14}, + {44.6, 37.3, 14}, + {42.2, 37.2, 14}, + {65.2, 25.6, 17}, + {43.9, 40.4, 14}, + {43.3, 40.4, 14}, + {44, 39.8, 14}, + {43.1, 39.6, 14}, + {42.9, 39.3, 14}, + {44.2, 39.2, 14}, + {42.6, 38.9, 14}, + {43.6, 38.1, 14}, + {43.2, 40.5, 14}, + {42.7, 40.4, 14}, + {44.5, 39.6, 14}, + {44.1, 39.2, 14}, + {42.5, 38.9, 14}, + {43.7, 38.1, 14}, + }, + [3115] = { + {45.8, 33.9, 14}, + {47.7, 33.1, 14}, + {48.2, 33, 14}, + {45, 32.8, 14}, + {50, 32.5, 14}, + {48.7, 32.3, 14}, + {48.2, 32, 14}, + {48.3, 31.6, 14}, + {48.1, 31.5, 14}, + {47.7, 31.1, 14}, + {45.2, 31, 14}, + {47.4, 30.6, 14}, + {46.8, 30.4, 14}, + {47.7, 30.2, 14}, + {46.5, 30.2, 14}, + {47.1, 30.1, 14}, + {45.9, 30.1, 14}, + {47.2, 29.6, 14}, + {45, 29, 14}, + {50, 27.5, 14}, + {51.4, 27.3, 14}, + {50.5, 26.7, 14}, + {50.6, 25.9, 14}, + {50.2, 25.1, 14}, + {49.2, 22.2, 14}, + {49, 21.3, 14}, + {48.3, 20.6, 14}, + {48.9, 20.6, 14}, + {48.9, 19.6, 14}, + {45.9, 33.8, 14}, + {49.9, 32.5, 14}, + {50, 27.6, 14}, + {50.6, 26, 14}, + {49.1, 22.2, 14}, + {48.4, 20.7, 14}, + {48.8, 19.5, 14}, + }, + [3116] = { + {45.8, 33.9, 14}, + {47.7, 33.1, 14}, + {48.2, 33, 14}, + {45, 32.8, 14}, + {50, 32.5, 14}, + {48.7, 32.3, 14}, + {48.2, 32, 14}, + {48.3, 31.6, 14}, + {48.1, 31.5, 14}, + {47.7, 31.1, 14}, + {45.2, 31, 14}, + {47.4, 30.6, 14}, + {46.8, 30.4, 14}, + {47.7, 30.2, 14}, + {46.5, 30.2, 14}, + {47.1, 30.1, 14}, + {45.9, 30.1, 14}, + {47.2, 29.6, 14}, + {45, 29, 14}, + {50, 27.5, 14}, + {51.4, 27.3, 14}, + {50.5, 26.7, 14}, + {50.6, 25.9, 14}, + {50.2, 25.1, 14}, + {49.2, 22.2, 14}, + {49, 21.3, 14}, + {48.3, 20.6, 14}, + {48.9, 20.6, 14}, + {48.9, 19.6, 14}, + {45.9, 33.8, 14}, + {49.9, 32.5, 14}, + {50, 27.6, 14}, + {50.6, 26, 14}, + {49.1, 22.2, 14}, + {48.4, 20.7, 14}, + {48.8, 19.5, 14}, + }, + [3117] = { + {54, 27.8, 14}, + {54, 27.3, 14}, + {54, 26.2, 14}, + {54, 25.3, 14}, + {53.3, 24.8, 14}, + {54, 24.4, 14}, + {52.8, 24.3, 14}, + {52.4, 24, 14}, + {51.8, 23.9, 14}, + {53.3, 23.7, 14}, + {54, 23.4, 14}, + {53.9, 22.7, 14}, + {53.7, 21.9, 14}, + {52.7, 21.3, 14}, + {51.4, 20.9, 14}, + {52.1, 20.8, 14}, + {53.9, 27.8, 14}, + {54.1, 27.3, 14}, + {54, 26.3, 14}, + {53.9, 25, 14}, + {54.1, 24.5, 14}, + {52.4, 23.9, 14}, + {51.8, 23.8, 14}, + {54, 23.6, 14}, + {52.8, 21.3, 14}, + }, + [3118] = { + {53.1, 24.2, 14}, + {53, 23.9, 14}, + {51.3, 23.8, 14}, + {51.9, 23.5, 14}, + {51.5, 23.4, 14}, + {51.2, 23.3, 14}, + {51.4, 23.1, 14}, + {54.1, 22.3, 14}, + {51.6, 21.6, 14}, + {53.3, 21.6, 14}, + {52, 21.4, 14}, + {51.3, 20.2, 14}, + {52, 20.1, 14}, + {51.7, 20, 14}, + {51, 19.6, 14}, + {51.3, 19.2, 14}, + {51.6, 19, 14}, + {53.3, 23.7, 14}, + {53.1, 24.3, 14}, + {53.3, 23.8, 14}, + {51.3, 23.6, 14}, + {54.1, 22.4, 14}, + }, + [3119] = { + {52.1, 82, 14}, + {52.6, 81.2, 14}, + {48, 80.6, 14}, + {46.7, 80.5, 14}, + {49.7, 80.5, 14}, + {52.3, 80.2, 14}, + {49.9, 80, 14}, + {48.2, 80, 14}, + {53, 79.8, 14}, + {46.4, 79.5, 14}, + {49, 79.4, 14}, + {51.3, 79.3, 14}, + {48.1, 79.2, 14}, + {50.2, 79.2, 14}, + {47.7, 78.6, 14}, + {50, 78.5, 14}, + {48.1, 77.7, 14}, + {52.7, 77.2, 14}, + {52, 76.5, 14}, + {48.1, 80.5, 14}, + {49.7, 80.4, 14}, + {49, 79.6, 14}, + {50.1, 79.2, 14}, + {47.6, 78.7, 14}, + {52.7, 77.3, 14}, + }, + [3120] = { + {49.8, 81.4, 14}, + {50.1, 80.5, 14}, + {46.3, 79.3, 14}, + {46.6, 79.2, 14}, + {48.1, 78.4, 14}, + {47.6, 78, 14}, + {47.8, 77.7, 14}, + {69.4, 38.5, 17}, + {47.6, 78.1, 14}, + }, + [3121] = { + {64, 97.3, 14}, + {62.8, 97.1, 14}, + {63.3, 96.2, 14}, + {64.1, 95.4, 14}, + {62.4, 95, 14}, + {60.3, 91.5, 14}, + {61.5, 91.4, 14}, + {59, 90.5, 14}, + {60.9, 90.5, 14}, + {61.5, 89.6, 14}, + {60.9, 88.7, 14}, + {64.6, 88.5, 14}, + {65.3, 87.7, 14}, + {64.6, 85, 14}, + {69.1, 84.8, 14}, + {59.7, 84.8, 14}, + {59.9, 83.4, 14}, + {60.7, 82.3, 14}, + {65.9, 81.1, 14}, + {64.7, 81, 14}, + {65.3, 80.2, 14}, + {69.1, 80.2, 14}, + {60.2, 80, 14}, + {67.6, 73.9, 14}, + {69.2, 73.6, 14}, + {70.3, 73.4, 14}, + {69.8, 72.6, 14}, + {69, 72.3, 14}, + {67.2, 70.7, 14}, + {69.2, 69.7, 14}, + {78.6, 47.9, 17}, + {78, 47.8, 17}, + {78.3, 47.3, 17}, + {78.7, 46.9, 17}, + {77.8, 46.7, 17}, + {76.7, 44.8, 17}, + {77.3, 44.8, 17}, + {76, 44.3, 17}, + {77, 44.3, 17}, + {77.4, 43.9, 17}, + {77, 43.4, 17}, + {79, 43.3, 17}, + {79.3, 42.9, 17}, + {79, 41.4, 17}, + {81.3, 41.4, 17}, + {76.4, 41.3, 17}, + {76.5, 40.6, 17}, + {61.4, 91.3, 14}, + {61.7, 90.4, 14}, + {65.3, 87.8, 14}, + {60.8, 87.5, 14}, + {59.6, 84.7, 14}, + {59.8, 83.3, 14}, + {65.9, 81.3, 14}, + {69.1, 80.3, 14}, + {67.7, 73.8, 14}, + {69.2, 73.7, 14}, + {69.8, 72.8, 14}, + {69, 72.4, 14}, + {77.4, 44.2, 17}, + {77.3, 43.9, 17}, + {77, 42.8, 17}, + }, + [3122] = { + {62, 96.3, 14}, + {63.1, 94.4, 14}, + {59.9, 89.6, 14}, + {66.7, 89, 14}, + {68.2, 88.5, 14}, + {59.9, 88, 14}, + {68.5, 87.1, 14}, + {64, 86.8, 14}, + {59.4, 83.9, 14}, + {68.4, 83.9, 14}, + {63.9, 83.8, 14}, + {60.3, 82.6, 14}, + {64.9, 82.3, 14}, + {69.2, 82.1, 14}, + {68.1, 80.5, 14}, + {67.2, 80.5, 14}, + {60.9, 78.8, 14}, + {61.4, 78.1, 14}, + {69.8, 74.8, 14}, + {68.6, 74.7, 14}, + {63.5, 74.5, 14}, + {64.6, 73.3, 14}, + {68.3, 71.8, 14}, + {67.1, 71.3, 14}, + {68.9, 71.2, 14}, + {70.2, 70.9, 14}, + {69.8, 70.4, 14}, + {67.7, 69.8, 14}, + {39.5, 51.9, 14}, + {41.2, 50.3, 14}, + {42.3, 49.1, 14}, + {40.3, 47.7, 14}, + {39.2, 47, 14}, + {41.6, 47, 14}, + {40.5, 46.6, 14}, + {43.3, 46.5, 14}, + {44.2, 45.4, 14}, + {40.5, 38.2, 14}, + {46.3, 37.6, 14}, + {55.3, 37.3, 14}, + {49.8, 37, 14}, + {48.9, 36.9, 14}, + {47.6, 36.8, 14}, + {45.8, 36.6, 14}, + {50.4, 35.4, 14}, + {40.6, 34.6, 14}, + {54.9, 34.5, 14}, + {42.3, 34.4, 14}, + {44.5, 34, 14}, + {44.2, 33.6, 14}, + {54.4, 32.5, 14}, + {55.3, 30.5, 14}, + {56.1, 29.2, 14}, + {57.6, 28.5, 14}, + {56.9, 26.8, 14}, + {58.2, 26.3, 14}, + {57, 24.5, 14}, + {56.5, 24, 14}, + {58, 23.1, 14}, + {49.4, 19.2, 14}, + {77.6, 47.3, 17}, + {78.2, 46.4, 17}, + {76.5, 43.8, 17}, + {80.1, 43.5, 17}, + {80.8, 43.3, 17}, + {76.5, 43, 17}, + {81, 42.5, 17}, + {78.6, 42.4, 17}, + {76.2, 40.9, 17}, + {81, 40.9, 17}, + {78.6, 40.8, 17}, + {69.8, 74.7, 14}, + {67.2, 71.2, 14}, + {68.9, 71.1, 14}, + }, + [3123] = { + {37.2, 54.9, 14}, + {36.1, 53.2, 14}, + {35.9, 48.9, 14}, + {38.4, 48.9, 14}, + {37.1, 47.3, 14}, + {37, 46.4, 14}, + {37.9, 40, 14}, + {36.6, 39.1, 14}, + {38.5, 37.4, 14}, + {36, 37, 14}, + {37.5, 36.1, 14}, + {36.7, 34.3, 14}, + {37.5, 32.4, 14}, + {36, 31.8, 14}, + {36.9, 29.7, 14}, + {36.3, 27.7, 14}, + {36.8, 27.2, 14}, + {36.8, 25.4, 14}, + {42.1, 21.3, 14}, + {39.2, 19.5, 14}, + {54.9, 19.3, 14}, + {39.2, 19.1, 14}, + {42.9, 18.5, 14}, + {54.4, 18, 14}, + {39.2, 17.3, 14}, + {40.4, 16.1, 14}, + {53.2, 16, 14}, + {55.9, 15.9, 14}, + {56.9, 15, 14}, + {51.8, 14.5, 14}, + {53.9, 13.5, 14}, + {55.2, 13.1, 14}, + {53, 12.4, 14}, + {55.8, 12, 14}, + {54, 11.7, 14}, + {52.1, 10.6, 14}, + {64.7, 25.8, 17}, + {64.1, 24.9, 17}, + {64, 22.6, 17}, + {64.4, 17.5, 17}, + {64.1, 16.4, 17}, + {64.4, 15, 17}, + {64, 13.7, 17}, + {64.5, 12.6, 17}, + {64.2, 11.5, 17}, + {64.4, 11.3, 17}, + {64.4, 10.4, 17}, + {39.3, 17.1, 14}, + {56.8, 14.9, 14}, + }, + [3126] = { + {41.1, 51.7, 14}, + {41.4, 50.7, 14}, + {42.5, 48.6, 14}, + {41.1, 48.4, 14}, + {43.3, 48.1, 14}, + {39.6, 47.8, 14}, + {45.6, 46.7, 14}, + {41, 46.6, 14}, + {37.9, 46.5, 14}, + {42.6, 45.9, 14}, + {44.6, 45.7, 14}, + {43.7, 45.5, 14}, + {40.1, 45.3, 14}, + {44.6, 44.5, 14}, + {37.7, 42.5, 14}, + {39.2, 42.1, 14}, + {41.4, 41.4, 14}, + {36.3, 41.2, 14}, + {38.3, 40.8, 14}, + {40.2, 39.4, 14}, + {40.7, 38.8, 14}, + {48.3, 38.6, 14}, + {35.7, 37.9, 14}, + {51, 37.7, 14}, + {37, 37.2, 14}, + {35.7, 36.9, 14}, + {47.6, 36.9, 14}, + {36.2, 36.9, 14}, + {48.9, 36.8, 14}, + {49.3, 36.6, 14}, + {54.8, 36.6, 14}, + {39, 35.7, 14}, + {47.6, 35.6, 14}, + {46.8, 35.5, 14}, + {42.5, 34.4, 14}, + {37.1, 34.4, 14}, + {38.3, 33.9, 14}, + {37.3, 33.8, 14}, + {54.4, 33.8, 14}, + {44.4, 33.6, 14}, + {43.4, 32.4, 14}, + {55.7, 31.7, 14}, + {45.5, 31.4, 14}, + {57.6, 30.9, 14}, + {48.6, 30.6, 14}, + {57.4, 29.5, 14}, + {58.5, 29.2, 14}, + {56.5, 29, 14}, + {41.9, 28.8, 14}, + {45.2, 28.2, 14}, + {57.2, 27.4, 14}, + {45.2, 26.3, 14}, + {58.4, 25.2, 14}, + {57.2, 24.6, 14}, + {58.4, 23.3, 14}, + {54.9, 23.1, 14}, + {56.1, 22.8, 14}, + {55, 22.1, 14}, + {50.6, 21.6, 14}, + {56.5, 21.4, 14}, + {50.5, 19.5, 14}, + {52.8, 19.4, 14}, + {51.1, 17.8, 14}, + {64.2, 18.6, 17}, + {63.9, 16.9, 17}, + {63.9, 16.3, 17}, + {64.2, 16.3, 17}, + {45.3, 26.5, 14}, + }, + [3127] = { + {35.9, 56.6, 14}, + {36.8, 55.7, 14}, + {36.8, 53.8, 14}, + {35.3, 53.6, 14}, + {36.3, 52.6, 14}, + {35, 50.8, 14}, + {37.5, 50.6, 14}, + {36.7, 50.5, 14}, + {37.9, 49.9, 14}, + {35.8, 49.8, 14}, + {37, 48.9, 14}, + {36.3, 47.2, 14}, + {35.7, 47, 14}, + {35.5, 33.1, 14}, + {37.1, 31, 14}, + {36.5, 30.2, 14}, + {36.2, 28.4, 14}, + {36.8, 27, 14}, + {36.6, 25.1, 14}, + {38.8, 22.6, 14}, + {39.4, 22.1, 14}, + {42.6, 21.1, 14}, + {42, 20.6, 14}, + {44.9, 20.5, 14}, + {38.1, 20.2, 14}, + {41.4, 20.1, 14}, + {42.2, 18.9, 14}, + {43.9, 18.7, 14}, + {39.8, 18.4, 14}, + {43.9, 18.4, 14}, + {56, 18.3, 14}, + {43.6, 18.2, 14}, + {39, 17.7, 14}, + {38.8, 17.7, 14}, + {50.4, 17.6, 14}, + {49.7, 17.5, 14}, + {42, 17, 14}, + {43.9, 17, 14}, + {41, 16.9, 14}, + {44.1, 16.7, 14}, + {53.5, 15.7, 14}, + {55.9, 15.3, 14}, + {55.7, 14.8, 14}, + {56.9, 14.6, 14}, + {53.6, 12.7, 14}, + {55.8, 12.1, 14}, + {53.3, 11.9, 14}, + {54.4, 11.8, 14}, + {64, 26.6, 17}, + {64.4, 26.2, 17}, + {63.7, 25.1, 17}, + {64.2, 24.5, 17}, + {63.5, 23.6, 17}, + {64.4, 23.4, 17}, + {63.9, 23.1, 17}, + {64.2, 21.7, 17}, + {63.9, 21.6, 17}, + {63.8, 14.4, 17}, + {64.6, 13.3, 17}, + {64.3, 12.9, 17}, + {64.1, 11.9, 17}, + {64.5, 11.2, 17}, + {64.4, 10.2, 17}, + {65.2, 7.6, 17}, + {57, 14.3, 14}, + {55.9, 12.1, 14}, + }, + [3129] = { + {60.3, 60.3, 14}, + {60.7, 59.4, 14}, + {59.4, 58.5, 14}, + {59.7, 58.5, 14}, + {58.4, 58.4, 14}, + {59.6, 58.2, 14}, + {59.8, 57.8, 14}, + {59.2, 56.7, 14}, + {59.6, 56.7, 14}, + {59.5, 56.3, 14}, + {58.1, 55.6, 14}, + {59.5, 55.5, 14}, + {59.9, 54.7, 14}, + {59.6, 53.7, 14}, + {56.5, 50.8, 14}, + {59.9, 58.9, 14}, + {59, 55.6, 14}, + {57.5, 55.3, 14}, + {57.7, 53.7, 14}, + {58.4, 53.7, 14}, + {60.2, 60.2, 14}, + {58.4, 58.5, 14}, + {59.1, 55.6, 14}, + {58.1, 55.5, 14}, + {57.8, 53.8, 14}, + {59.5, 53.5, 14}, + }, + [3130] = { + {39.2, 31.5, 14}, + {39.1, 30.8, 14}, + {40.6, 30.1, 14}, + {39.1, 30, 14}, + {40.5, 29.8, 14}, + {39.2, 29.2, 14}, + {40.5, 29, 14}, + {39.2, 28.5, 14}, + {39.6, 28.1, 14}, + {40.2, 28.1, 14}, + {39.2, 26.6, 14}, + {39.1, 31.3, 14}, + {39.1, 30.1, 14}, + {39.1, 29.3, 14}, + }, + [3131] = { + {38.9, 25.3, 14}, + {39.4, 24.9, 14}, + {42.4, 24.4, 14}, + {43.3, 24.4, 14}, + {42, 24.4, 14}, + {41.4, 24.3, 14}, + {40.7, 24.2, 14}, + {43.6, 24.1, 14}, + {40.1, 24.1, 14}, + {42.9, 23.7, 14}, + {39.6, 23.6, 14}, + {40.7, 24.5, 14}, + {40.2, 24.2, 14}, + {39.5, 23.5, 14}, + }, + [3141] = { + {61, 38.7, 14}, + {61.1, 23.4, 14}, + {59.8, 16.1, 14}, + {59, 13, 14}, + }, + [3192] = { + {59.7, 58.3, 14}, + }, + [3195] = { + {52.8, 29.3, 14}, + {52.5, 29.2, 14}, + {53.2, 29.1, 14}, + {52.7, 27.9, 14}, + {53, 27.8, 14}, + {53.4, 26.9, 14}, + }, + [3196] = { + {52.8, 28.5, 14}, + {53.1, 27.2, 14}, + {52.1, 26.9, 14}, + {52.8, 26.8, 14}, + {51.9, 26.3, 14}, + {51.9, 25.1, 14}, + }, + [3197] = { + {42.3, 27.3, 14}, + {42.3, 26.9, 14}, + {42, 26.6, 14}, + {42.3, 26.3, 14}, + {52.7, 11.1, 14}, + {51.9, 10.8, 14}, + {53.4, 10.6, 14}, + {52.5, 10.2, 14}, + {51.6, 10.1, 14}, + {53.7, 9.9, 14}, + {55, 9.8, 14}, + {54.7, 9.3, 14}, + {53.3, 9.2, 14}, + {52.4, 9.2, 14}, + {51.5, 9.2, 14}, + {53, 8.9, 14}, + {54.3, 8.9, 14}, + {53.8, 8.9, 14}, + {51.4, 8.7, 14}, + {52.5, 8.6, 14}, + {52.1, 8.2, 14}, + {53.4, 8.2, 14}, + {52.7, 7.9, 14}, + {52.7, 11, 14}, + {53.4, 10.5, 14}, + {51.6, 10.2, 14}, + {54.7, 9.4, 14}, + {52.5, 9.2, 14}, + {53.3, 8.2, 14}, + }, + [3198] = { + {41.9, 26.9, 14}, + {42.9, 26.4, 14}, + {53.1, 11, 14}, + {52.3, 11, 14}, + {51.6, 10.6, 14}, + {53.3, 10, 14}, + {52.1, 9.8, 14}, + {53.1, 9.4, 14}, + {53.9, 9.4, 14}, + {54.7, 8.8, 14}, + {53.6, 8.5, 14}, + {52.6, 8.2, 14}, + {51.6, 8.1, 14}, + {53.4, 7.7, 14}, + {52.9, 7.6, 14}, + {53.1, 11.1, 14}, + {53.2, 10, 14}, + {53.8, 9.4, 14}, + {53.5, 8.6, 14}, + }, + [3199] = { + {52.4, 26.8, 14}, + {52.6, 26.4, 14}, + {51.9, 25.7, 14}, + {52.7, 25.4, 14}, + {52.6, 25, 14}, + {52.2, 24.9, 14}, + {52.2, 24.5, 14}, + }, + [3203] = { + {42.1, 26.7, 14}, + }, + [3204] = { + {51.8, 10, 14}, + {51.5, 9.7, 14}, + {51.9, 9.6, 14}, + {52.6, 8.8, 14}, + }, + [3205] = { + {67.6, 87.8, 14}, + {80.5, 42.9, 17}, + }, + [3206] = { + {67.1, 87.8, 14}, + {67.2, 87.3, 14}, + {66.9, 87.2, 14}, + {67.6, 86.9, 14}, + {67.1, 86.8, 14}, + {66, 86.8, 14}, + {67.2, 86.5, 14}, + {67.3, 84.9, 14}, + {68.5, 84.3, 14}, + {65.9, 84, 14}, + {68.2, 83.8, 14}, + {68.5, 83.5, 14}, + {68.1, 83.5, 14}, + {68.3, 83.3, 14}, + {65.9, 82.9, 14}, + {67.4, 82.6, 14}, + {68.4, 82.4, 14}, + {67.9, 82, 14}, + {80.3, 42.9, 17}, + {80.3, 42.7, 17}, + {80.2, 42.6, 17}, + {80.5, 42.4, 17}, + {80.2, 42.4, 17}, + {79.7, 42.4, 17}, + {80.3, 42.3, 17}, + {80.3, 41.4, 17}, + {81, 41.1, 17}, + {79.6, 40.9, 17}, + {80.9, 40.8, 17}, + {81, 40.7, 17}, + {80.8, 40.7, 17}, + {80.9, 40.6, 17}, + {65.9, 86.7, 14}, + {67.2, 86.6, 14}, + {68.6, 84.4, 14}, + {65.9, 82.8, 14}, + {67.8, 82.1, 14}, + {80.2, 42.9, 17}, + {80.1, 42.6, 17}, + {79.6, 42.3, 17}, + }, + [3207] = { + {67.3, 88.7, 14}, + {66.6, 87.8, 14}, + {68.5, 86.7, 14}, + {66.5, 86.7, 14}, + {67.9, 85.9, 14}, + {67.2, 85.8, 14}, + {67.8, 83.9, 14}, + {68.8, 83.6, 14}, + {67.7, 83.5, 14}, + {66.6, 83, 14}, + {80.4, 43.4, 17}, + {80, 42.9, 17}, + {81, 42.3, 17}, + {80, 42.3, 17}, + {80.7, 41.9, 17}, + {80.3, 41.9, 17}, + {80.6, 40.9, 17}, + {81.1, 40.7, 17}, + {80.6, 40.7, 17}, + {67.3, 84.9, 14}, + {68.5, 84.3, 14}, + {65.9, 84, 14}, + {68.2, 83.8, 14}, + {68.1, 83.5, 14}, + {68.3, 83.3, 14}, + {65.9, 82.9, 14}, + {67.9, 82, 14}, + {80.3, 41.4, 17}, + {81, 41.1, 17}, + {79.6, 40.9, 17}, + {80.9, 40.8, 17}, + {80.8, 40.7, 17}, + {80.9, 40.6, 17}, + {66.5, 87.8, 14}, + {68.5, 86.8, 14}, + {67.1, 85.9, 14}, + {67.1, 85, 14}, + {67.7, 83.7, 14}, + {68.3, 83.2, 14}, + {81, 42.4, 17}, + {80, 42.4, 17}, + {80.8, 40.8, 17}, + {80.6, 40.8, 17}, + {80.9, 40.5, 17}, + }, + [3225] = { + {36.9, 40.1, 14}, + {43.7, 15.5, 14}, + }, + [3227] = { + {37.6, 56.1, 14}, + {43.7, 31.7, 14}, + {39.8, 19.9, 14}, + {38.8, 18.5, 14}, + {44, 17.6, 14}, + {64.9, 26.4, 17}, + }, + [3228] = { + {60.4, 21.8, 14}, + {56.9, 10.7, 14}, + }, + [3232] = { + {36.8, 39.5, 17}, + {36.7, 39.5, 17}, + {37, 39.4, 17}, + {37.1, 39.3, 17}, + {36.5, 39.3, 17}, + {37.2, 39.1, 17}, + {37, 39.1, 17}, + {36.3, 39, 17}, + {36.8, 39, 17}, + {36.9, 38.9, 17}, + {36.2, 38.9, 17}, + {37.2, 38.8, 17}, + {37, 38.7, 17}, + {36.6, 38.7, 17}, + {36.5, 38.6, 17}, + {37.3, 38.5, 17}, + {36.2, 38.5, 17}, + {36.6, 38.5, 17}, + {36.8, 38.5, 17}, + {37.2, 38.3, 17}, + {36.8, 38.3, 17}, + {36.3, 38.2, 17}, + {37, 38.1, 17}, + {36.2, 38.1, 17}, + {36.5, 38.1, 17}, + {36.6, 37.8, 17}, + {36.3, 37.8, 17}, + {36.9, 37.8, 17}, + {61.5, 22.9, 215}, + {61.2, 22.8, 215}, + {61.8, 22.6, 215}, + {62, 22.5, 215}, + {60.8, 22.4, 215}, + {62.2, 22.1, 215}, + {61.8, 22, 215}, + {60.4, 22, 215}, + {61.3, 21.8, 215}, + {61.6, 21.7, 215}, + {60.2, 21.6, 215}, + {62.2, 21.5, 215}, + {61.7, 21.3, 215}, + {61, 21.3, 215}, + {60.8, 21.1, 215}, + {62.4, 21, 215}, + {60.1, 20.9, 215}, + {61, 20.8, 215}, + {61.3, 20.8, 215}, + {62.1, 20.6, 215}, + {61.3, 20.5, 215}, + {60.4, 20.4, 215}, + {61.9, 20.2, 215}, + {60.1, 20, 215}, + {60.8, 20, 215}, + {60.9, 19.5, 215}, + {60.4, 19.5, 215}, + {61.7, 19.5, 215}, + }, + [3267] = { + {55.7, 27.2, 17}, + {55.9, 27.2, 17}, + {54.7, 27.1, 17}, + {55.3, 27.1, 17}, + {55, 27.1, 17}, + {55.6, 27, 17}, + {55.8, 26.7, 17}, + {55, 26.7, 17}, + {55.8, 26.6, 17}, + {55.4, 26.6, 17}, + {55.1, 26.6, 17}, + {55.7, 26.6, 17}, + {55.7, 26.1, 17}, + {54, 26.1, 17}, + {54.4, 25.9, 17}, + {55.5, 25.7, 17}, + {53.7, 25.6, 17}, + {54.7, 25.6, 17}, + {54.3, 25.5, 17}, + {54, 25.1, 17}, + {53, 25.1, 17}, + {54.4, 25, 17}, + {53.6, 24.5, 17}, + {55.7, 27.3, 17}, + {55.4, 27, 17}, + {55, 27, 17}, + {55.5, 26.9, 17}, + {54.9, 26.7, 17}, + {53.7, 25.7, 17}, + {54.3, 25.6, 17}, + {54, 25.2, 17}, + }, + [3268] = { + {55.8, 27, 17}, + {55.4, 26.9, 17}, + {55.9, 26.3, 17}, + {54.1, 25.5, 17}, + {55, 25.3, 17}, + {54.6, 25.2, 17}, + {54.2, 25.1, 17}, + {54.4, 24.6, 17}, + {54.7, 27.1, 17}, + {55.3, 27.1, 17}, + {55.6, 27, 17}, + {55.8, 26.7, 17}, + {55, 26.7, 17}, + {55.8, 26.6, 17}, + {55.1, 26.6, 17}, + {55.7, 26.6, 17}, + {55.7, 26.1, 17}, + {54.7, 25.6, 17}, + {54, 25.1, 17}, + {55.8, 26.9, 17}, + {55.6, 26.2, 17}, + }, + [3379] = { + {47.4, 19.4, 17}, + {47.5, 19.2, 17}, + {47.7, 19.2, 17}, + {47.8, 19.1, 17}, + {47.2, 19.1, 17}, + {47.4, 19.1, 17}, + {48.5, 19, 17}, + {48.1, 18.9, 17}, + {48.4, 18.8, 17}, + {47.4, 18.7, 17}, + {47.2, 18.7, 17}, + {46.5, 18.2, 17}, + {46.7, 18.2, 17}, + {46.3, 18.1, 17}, + {47.8, 18.1, 17}, + {46.7, 18, 17}, + {48, 18, 17}, + {46.3, 18, 17}, + {46.5, 17.9, 17}, + {47.8, 17.9, 17}, + {47.6, 17.9, 17}, + {47.9, 15.8, 17}, + {47, 15.8, 17}, + {48.2, 15.1, 17}, + {47.6, 15, 17}, + {47.9, 14.9, 17}, + }, + [3566] = { + {37.5, 57.5, 17}, + {35.4, 43, 17}, + {34.7, 42.9, 17}, + {36, 42.4, 17}, + {36.9, 40.5, 17}, + {34.6, 40.4, 17}, + {37.1, 40.3, 17}, + {35.9, 39.9, 17}, + {37.3, 39.8, 17}, + {34.6, 37.6, 17}, + {35, 37.5, 17}, + {34.3, 37.4, 17}, + {33.7, 37.2, 17}, + {34.5, 37, 17}, + {33.3, 36.4, 17}, + {32.7, 36, 17}, + {32.8, 34.2, 17}, + {31.6, 34, 17}, + {32.3, 34, 17}, + {32, 34, 17}, + {32.9, 33.6, 17}, + {32.4, 33.5, 17}, + {32.8, 32.6, 17}, + {32.4, 32.3, 17}, + {62.8, 58.4, 215}, + {56.3, 34.4, 215}, + {55.3, 34.2, 215}, + {45, 33.4, 215}, + {55, 32.5, 215}, + {58.6, 29.8, 215}, + {57.2, 29.6, 215}, + {47.8, 28.8, 215}, + {59.9, 28.6, 215}, + {51.2, 28.4, 215}, + {46.3, 27.8, 215}, + {46.1, 27.5, 215}, + {56.4, 26.3, 215}, + {44.2, 26.1, 215}, + {52.5, 25.7, 215}, + {48.3, 25.7, 215}, + {53.3, 25.5, 215}, + {51.9, 25.2, 215}, + {47.3, 25.1, 215}, + {54.5, 24.8, 215}, + {61.6, 24.8, 215}, + {57, 24.7, 215}, + {55.7, 24.7, 215}, + {62.1, 24.4, 215}, + {42.8, 24.4, 215}, + {51, 24.3, 215}, + {52.8, 23.9, 215}, + {55.9, 23.8, 215}, + {59.7, 23.6, 215}, + {62.3, 23.5, 215}, + {55.3, 23.1, 215}, + {56.3, 23, 215}, + {50.7, 22.5, 215}, + {55, 22.4, 215}, + {52.5, 22.3, 215}, + {56.3, 21.9, 215}, + {53.4, 21.7, 215}, + {53.8, 21.7, 215}, + {55.8, 21.3, 215}, + {53.7, 20.6, 215}, + {51.6, 20.4, 215}, + {48.5, 20.3, 215}, + {46.7, 19.9, 215}, + {42.2, 19.8, 215}, + {54.3, 19.5, 215}, + {42.7, 19.2, 215}, + {57.1, 19.2, 215}, + {53.9, 19, 215}, + {32.3, 19, 215}, + {44.2, 19, 215}, + {57.8, 18.9, 215}, + {56.4, 18.7, 215}, + {43.3, 18.6, 215}, + {48.7, 18.6, 215}, + {55.3, 18.4, 215}, + {43.3, 18.4, 215}, + {49.8, 18.1, 215}, + {37.7, 18, 215}, + {56.9, 17.9, 215}, + {52, 17.6, 215}, + {46.3, 17.5, 215}, + {43.4, 17.4, 215}, + {42, 17.3, 215}, + {37.4, 16.9, 215}, + {48, 16.9, 215}, + {54.4, 16.9, 215}, + {45, 16.8, 215}, + {35.4, 16.2, 215}, + {53.4, 16, 215}, + {44.7, 15.8, 215}, + {50.6, 15.8, 215}, + {44.1, 15.7, 215}, + {38.1, 15.6, 215}, + {47.9, 15.3, 215}, + {44.6, 15.1, 215}, + {44.7, 13.9, 215}, + {48.5, 13.7, 215}, + {45.2, 13.7, 215}, + {47.6, 13, 215}, + {50.3, 13, 215}, + {48.9, 12.8, 215}, + {44.4, 12.4, 215}, + {53.4, 12.4, 215}, + {51.1, 12.1, 215}, + {45.4, 12, 215}, + {52.5, 12, 215}, + {51.9, 12, 215}, + {37.6, 12, 215}, + {46.5, 11.8, 215}, + {48.9, 11.5, 215}, + {43.8, 11.3, 215}, + {53.7, 11.3, 215}, + {44.3, 11.2, 215}, + {42.6, 11.1, 215}, + {39.8, 11.1, 215}, + {52.8, 11, 215}, + {44.2, 11, 215}, + {49.1, 10.1, 215}, + {41.6, 9.8, 215}, + {38.8, 9.2, 215}, + {53.4, 9.2, 215}, + {48.8, 8.9, 215}, + {52.8, 8.7, 215}, + {44.5, 8.7, 215}, + {47.4, 8.6, 215}, + {42.5, 7.3, 215}, + {85.5, 45.9, 405}, + {89, 42.7, 405}, + {64.2, 37, 1638}, + {53.7, 11.2, 215}, + {72.1, 99.9, 406}, + {67.9, 98.9, 406}, + }, + [4279] = { + {76.1, 69.2, 209}, + }, + [4282] = { + {27.9, 22.3, 28}, + {28.2, 21.4, 28}, + {30.4, 21, 28}, + {27.8, 20.5, 28}, + {29.2, 20.2, 28}, + {83, 37.3, 85}, + {82.3, 37.2, 85}, + {84.5, 36.9, 85}, + {83.8, 36.8, 85}, + {82.2, 36.1, 85}, + {84.8, 36, 85}, + {83.8, 35.7, 85}, + {86.9, 35.6, 85}, + {82.3, 35.1, 85}, + {84.4, 35.1, 85}, + {83.7, 35, 85}, + {85.7, 34.8, 85}, + {84.4, 34.6, 85}, + {84.5, 34, 85}, + {82.2, 33.9, 85}, + {83.7, 33.8, 85}, + {81.4, 33.7, 85}, + {82.9, 33.5, 85}, + {81.6, 32.4, 85}, + {82.1, 31.7, 85}, + {82.8, 30.7, 85}, + {83.9, 30.7, 85}, + {81.5, 30.3, 85}, + {84, 30.2, 85}, + {81.5, 29.5, 85}, + {82.2, 29.5, 85}, + {81.1, 29.5, 85}, + {83.7, 29.4, 85}, + {80.9, 29.4, 85}, + {83.1, 29.3, 85}, + {83.7, 28.6, 85}, + {82.9, 28.5, 85}, + {82.3, 37.1, 85}, + {82.2, 35.1, 85}, + {84.3, 35.1, 85}, + {83.6, 34.9, 85}, + {82.9, 30.9, 85}, + {83.7, 28.7, 85}, + {11.3, 97.3, 796}, + {43.2, 95.3, 796}, + {26, 88.2, 796}, + {8.2, 48.6, 796}, + {25.7, 29.4, 796}, + {49.1, 25.9, 796}, + {49.5, 15.1, 796}, + }, + [4283] = { + {84.3, 32.7, 85}, + {84.8, 32.4, 85}, + {84.5, 32.1, 85}, + {84, 31.6, 85}, + {83.9, 31.3, 85}, + {56.6, 70.2, 796}, + {68.7, 62.8, 796}, + {62.4, 57.8, 796}, + {50.5, 46.5, 796}, + {47.7, 38.7, 796}, + {76.6, 70.2, 5136}, + {74.9, 64.5, 5136}, + {72.3, 54, 5136}, + {71.8, 54, 5136}, + {69.6, 45.6, 5136}, + {70.2, 45.3, 5136}, + {74, 45.3, 5136}, + }, + [4285] = { + {28.5, 16.5, 28}, + {83.8, 32.5, 85}, + {83.6, 31.9, 85}, + {85, 31.3, 85}, + {46, 66.4, 796}, + {42.1, 53.2, 796}, + {73.5, 39.3, 796}, + }, + [4286] = { + {63.9, 92.7, 5153}, + {62.7, 92.7, 5153}, + {70.7, 90, 5153}, + {70.3, 89.4, 5153}, + {66.3, 87.9, 5153}, + {63.2, 83.2, 5153}, + {64.8, 82.9, 5153}, + {65, 81, 5153}, + {68.9, 75.9, 5153}, + {66.5, 75.5, 5153}, + {63.5, 73.8, 5153}, + {70.7, 73.6, 5153}, + {70, 73.1, 5153}, + {63.2, 73, 5153}, + {63.6, 73, 5153}, + {70.6, 64, 5153}, + {71.2, 63.2, 5153}, + }, + [4287] = { + {33.3, 71.9, 5135}, + {38.5, 71, 5135}, + {47, 62.2, 5135}, + {39.8, 58.4, 5135}, + {21, 53, 5135}, + {44.7, 52.6, 5135}, + {45.4, 51.5, 5135}, + {39.1, 50.7, 5135}, + {33.3, 48.6, 5135}, + {18.9, 45.3, 5135}, + {46.9, 44.9, 5135}, + {38.6, 41.8, 5135}, + {39.4, 40.6, 5135}, + {21.1, 39.4, 5135}, + {46.2, 36.9, 5135}, + {44.4, 36.7, 5135}, + {21, 31.2, 5135}, + {22.2, 31.1, 5135}, + {22.3, 18.5, 5135}, + {22.3, 16.9, 5135}, + }, + [4288] = { + {32.8, 65.7, 5135}, + {34.8, 59, 5135}, + {30.2, 55.4, 5135}, + {22.1, 49.7, 5135}, + }, + [4290] = { + {74.7, 64.1, 5153}, + {75.4, 57.7, 5153}, + {70.3, 57, 5153}, + {78.5, 50.1, 5153}, + {74.2, 49.6, 5153}, + {63.6, 44.3, 5153}, + {78.2, 38.2, 5153}, + {65, 35.1, 5153}, + {79.7, 20.8, 5153}, + }, + [4291] = { + {87.5, 59.6, 5135}, + {78.5, 58.7, 5135}, + {51.4, 44.8, 5135}, + {80, 42.6, 5135}, + {84.5, 35.4, 5135}, + {82.2, 35.3, 5135}, + {51.1, 35.2, 5135}, + {54, 27.8, 5135}, + {84.5, 25.4, 5135}, + {63.9, 25.3, 5135}, + {53.2, 25, 5135}, + {70.2, 20.7, 5135}, + {44, 18.5, 5135}, + {85.4, 17.5, 5135}, + {77.4, 14, 5135}, + {64.8, 12.9, 5135}, + }, + [4292] = { + {71.3, 64.1, 5153}, + {75.4, 63, 5153}, + {67.2, 62.6, 5153}, + {67.8, 61.8, 5153}, + {74.9, 56.9, 5153}, + {75.3, 56.3, 5153}, + {70.4, 55.6, 5153}, + {78, 51.5, 5153}, + {73.7, 50.7, 5153}, + {61.2, 40.1, 5153}, + {69, 38.9, 5153}, + {60.5, 36.8, 5153}, + {75.2, 32, 5153}, + {71, 25.2, 5153}, + }, + [4293] = { + {68, 66.5, 5136}, + {68.1, 65.6, 5136}, + {72.1, 65.5, 5136}, + {72.5, 59.5, 5136}, + {68, 57.4, 5136}, + {64.1, 53.9, 5136}, + {64.8, 53.8, 5136}, + {72.8, 50.2, 5136}, + {76.4, 44.7, 5136}, + {65.3, 41.1, 5136}, + {65.3, 40.4, 5136}, + {69.7, 38, 5136}, + {72.4, 37.4, 5136}, + {67.9, 36.7, 5136}, + {77, 36.1, 5136}, + }, + [4294] = { + {52.6, 83.5, 5163}, + {44, 83.4, 5163}, + {54.6, 75.5, 5163}, + {54.5, 74.7, 5163}, + {54.5, 68.4, 5163}, + {40.9, 62.6, 5163}, + {43.7, 56.6, 5163}, + {41.3, 50.9, 5163}, + }, + [4295] = { + {63.3, 43.9, 5153}, + {61.7, 40.2, 5153}, + {71.1, 39.5, 5153}, + {74.9, 39.3, 5153}, + {71.1, 38.5, 5153}, + {60.3, 37.6, 5153}, + {74.1, 37.5, 5153}, + {70.6, 33, 5153}, + {67.4, 27.7, 5153}, + {67.9, 27.1, 5153}, + {67.3, 26.6, 5153}, + {74.8, 25.5, 5153}, + {71.6, 24.8, 5153}, + {77.1, 21.7, 5153}, + {72.6, 20.3, 5153}, + {79.6, 19.9, 5153}, + {48.1, 87, 5163}, + {59.9, 86.3, 5163}, + {52, 83.5, 5163}, + {46, 83.5, 5163}, + {43.6, 82.4, 5163}, + {43.6, 74.6, 5163}, + {54.5, 69.3, 5163}, + {43.6, 69, 5163}, + {57, 62.9, 5163}, + {57, 57, 5163}, + {56.8, 56.4, 5163}, + {48.9, 51.2, 5163}, + {56.8, 51.1, 5163}, + {57.1, 50, 5163}, + }, + [4296] = { + {20.1, 53.7, 5135}, + {21.8, 49.8, 5135}, + {20.1, 40.4, 5135}, + {38.7, 39.3, 5135}, + }, + [4298] = { + {61.5, 41, 5153}, + {65.7, 39.8, 5153}, + {78.4, 39.3, 5153}, + {73.8, 39.2, 5153}, + {70.4, 38.6, 5153}, + {65.7, 37.9, 5153}, + {60, 36.9, 5153}, + {57.7, 34.1, 5153}, + {75.3, 33.3, 5153}, + {71.5, 31.6, 5153}, + {70.8, 31.4, 5153}, + {70.7, 28.2, 5153}, + {70, 28, 5153}, + {75.5, 26.8, 5153}, + {80.1, 20.1, 5153}, + {72.1, 20.1, 5153}, + {72.1, 19, 5153}, + {67.5, 18.9, 5153}, + {54.7, 88.7, 5163}, + {48.1, 88.1, 5163}, + {54.7, 86.3, 5163}, + {54.5, 83.4, 5163}, + {45.8, 79.5, 5163}, + {43.6, 68.1, 5163}, + {41.2, 63, 5163}, + {43.6, 61.9, 5163}, + {46.2, 52.4, 5163}, + {54.1, 50.9, 5163}, + }, + [4299] = { + {88.3, 59.6, 5135}, + {53.1, 44.7, 5135}, + {43.6, 31.2, 5135}, + {44.4, 28.3, 5135}, + {75.8, 25.1, 5135}, + {67.7, 19.8, 5135}, + {52.7, 16.3, 5135}, + {66, 13.9, 5135}, + {52.1, 43.2, 5163}, + {46.5, 43.1, 5163}, + {52.2, 43, 5163}, + {41, 30.6, 5163}, + {40.8, 26, 5163}, + }, + [4300] = { + {43.6, 80.5, 5163}, + {52.4, 71.2, 5163}, + {45.6, 70.9, 5163}, + {41.1, 56.2, 5163}, + {54.3, 55.4, 5163}, + {46.4, 42.3, 5163}, + {51.7, 35.9, 5163}, + {52.1, 33.7, 5163}, + {40.5, 31.1, 5163}, + {41.6, 30.9, 5163}, + {57.2, 30.6, 5163}, + }, + [4301] = { + {49, 79.4, 5163}, + {52, 79.3, 5163}, + {52.1, 75.9, 5163}, + {46.9, 67.6, 5163}, + {56.8, 62.3, 5163}, + {54.3, 61.5, 5163}, + {41.1, 56.8, 5163}, + {52, 52.4, 5163}, + {43.8, 50.9, 5163}, + {40.9, 50.2, 5163}, + {51.4, 46.5, 5163}, + {46.6, 46.5, 5163}, + {50.7, 46.3, 5163}, + {47.4, 46.2, 5163}, + {45.2, 42.6, 5163}, + {52.9, 42.6, 5163}, + {45.2, 38.7, 5163}, + {52.8, 38.7, 5163}, + {45.3, 34.7, 5163}, + {52.9, 34.7, 5163}, + {58, 29.9, 5163}, + {40, 29.8, 5163}, + }, + [4302] = { + {49.9, 39.9, 5163}, + {46.2, 37.8, 5163}, + {51.9, 37.2, 5163}, + {48.4, 34.6, 5163}, + {46.2, 33.6, 5163}, + {49.9, 32, 5163}, + {57.6, 31.3, 5163}, + {57, 29.7, 5163}, + {43.5, 25.8, 5163}, + }, + [4303] = { + {48.2, 40, 5163}, + {49.6, 37.3, 5163}, + {48.4, 37.2, 5163}, + {49.6, 34.7, 5163}, + {48.2, 32, 5163}, + }, + [4304] = { + {29.8, 87.3, 5135}, + {30, 86.3, 5135}, + {31, 86, 5135}, + {33.3, 65.2, 5135}, + {35.4, 58.1, 5135}, + {29.9, 56.4, 5135}, + {21.3, 49.8, 5135}, + }, + [4306] = { + {71.7, 67.4, 5136}, + {71.3, 66.8, 5136}, + {68.2, 66.4, 5136}, + {67.9, 65.8, 5136}, + {68.5, 57.3, 5136}, + {72.5, 50.5, 5136}, + {71.2, 49.8, 5136}, + {76.4, 45.7, 5136}, + {65.3, 42.8, 5136}, + {67.9, 41, 5136}, + {78.7, 40.4, 5136}, + {70.3, 37.9, 5136}, + {71.8, 37, 5136}, + {74.8, 36.2, 5136}, + {76.1, 35.9, 5136}, + {67.9, 35.9, 5136}, + {69.6, 35.6, 5136}, + {70.2, 35.5, 5136}, + {74.8, 35.4, 5136}, + {68, 66.5, 5136}, + {68.1, 65.6, 5136}, + {72.1, 65.5, 5136}, + {72.5, 59.5, 5136}, + {68, 57.4, 5136}, + {64.1, 53.9, 5136}, + {64.8, 53.8, 5136}, + }, + [4308] = { + {46, 66.5, 5136}, + {46.7, 66.2, 5136}, + {46.3, 65.3, 5136}, + {41.6, 63.8, 5136}, + {27, 63.6, 5136}, + {25.9, 62.3, 5136}, + {42, 62.2, 5136}, + {26.8, 62.1, 5136}, + {41, 62.1, 5136}, + {27.6, 61.6, 5136}, + {27, 61.3, 5136}, + {49.3, 61.2, 5136}, + {22.3, 61.2, 5136}, + {21.6, 61, 5136}, + {48.4, 60.9, 5136}, + {27.5, 60.4, 5136}, + {22.4, 60.3, 5136}, + {26.1, 60.2, 5136}, + {48.8, 60.1, 5136}, + {26.7, 59.7, 5136}, + {26.1, 59, 5136}, + {51.7, 56.6, 5136}, + {51.8, 56, 5136}, + {51.3, 55.5, 5136}, + {22.1, 54.8, 5136}, + {21.8, 54.7, 5136}, + {21.8, 54.2, 5136}, + {42.8, 52.8, 5136}, + {43.2, 51.9, 5136}, + {43.1, 51, 5136}, + {37, 50.7, 5136}, + {36.4, 50, 5136}, + {37.1, 49.5, 5136}, + {31.3, 45.5, 5136}, + {31.7, 45.1, 5136}, + {26.8, 45, 5136}, + {31.4, 44.7, 5136}, + {26.6, 43.9, 5136}, + {26.4, 43.6, 5136}, + }, + [4540] = { + {82.8, 62.5, 5135}, + {83.9, 62.5, 5135}, + {79.4, 58.7, 5135}, + {87.8, 58.2, 5135}, + {78.7, 57.6, 5135}, + {84.3, 51.6, 5135}, + {75.8, 46.4, 5135}, + {76.5, 45.2, 5135}, + {81.1, 43.1, 5135}, + {55.1, 41.6, 5135}, + {70.1, 40.5, 5135}, + {55.1, 39.4, 5135}, + {66.5, 35.9, 5135}, + {68, 34.4, 5135}, + {44.2, 30.1, 5135}, + {77, 26.2, 5135}, + {62.9, 26, 5135}, + {77.1, 23.7, 5135}, + {43.9, 20.6, 5135}, + {83.3, 19.8, 5135}, + {46.9, 19.8, 5135}, + {70, 19.4, 5135}, + {51.3, 16.4, 5135}, + {76.1, 13.1, 5135}, + {50.2, 40, 5163}, + {47.9, 40, 5163}, + {48.2, 34.6, 5163}, + {47.8, 32.1, 5163}, + {50.2, 32, 5163}, + {40.8, 24.6, 5163}, + {43.6, 24.6, 5163}, + {43.3, 24.6, 5163}, + {57.4, 24.4, 5163}, + }, + [4624] = { + {27.6, 78, 33}, + {28, 77.9, 33}, + {27.2, 77.7, 33}, + {27.2, 77.6, 33}, + {27.5, 77.6, 33}, + {27.8, 77.5, 33}, + {27.4, 77.5, 33}, + {27.2, 77.5, 33}, + {26.9, 77.4, 33}, + {27.3, 77.4, 33}, + {27.7, 77.4, 33}, + {27.1, 77.2, 33}, + {27.7, 77.2, 33}, + {27.6, 77.2, 33}, + {27.8, 77.1, 33}, + {27.7, 77.1, 33}, + {28, 77.1, 33}, + {28.1, 77.1, 33}, + {28.4, 77.1, 33}, + {26.7, 77, 33}, + {28.6, 77, 33}, + {27.7, 77, 33}, + {27.3, 76.9, 33}, + {28, 76.9, 33}, + {27.5, 76.8, 33}, + {28.5, 76.8, 33}, + {28.3, 76.8, 33}, + {26.9, 76.7, 33}, + {27.6, 76.6, 33}, + {26.6, 76.6, 33}, + {28.1, 76.5, 33}, + {26.6, 76.3, 33}, + {28.4, 76.1, 33}, + {28.4, 75.8, 33}, + {28.3, 75.7, 33}, + {28.5, 75.6, 33}, + {28.8, 75.4, 33}, + {28.1, 75.2, 33}, + {28.9, 74.8, 33}, + {27.7, 74.6, 33}, + {27.8, 74, 33}, + {28, 73.7, 33}, + {27.9, 73.7, 33}, + {26.8, 73.7, 33}, + {26.9, 73.6, 33}, + {28, 73.6, 33}, + {26.1, 73.4, 33}, + {26.3, 73.3, 33}, + {26.1, 73.1, 33}, + {29.7, 72.6, 33}, + {29.5, 72.6, 33}, + {29.4, 72.2, 33}, + {27.1, 77.3, 33}, + {27, 77.3, 33}, + {26.8, 76.7, 33}, + {28.3, 76, 33}, + {28.1, 75.9, 33}, + }, + [4848] = { + {25.9, 32.2, 1337}, + {27.7, 31.9, 1337}, + }, + [4849] = { + {26.1, 33, 1337}, + {27.2, 31.8, 1337}, + }, + [4850] = { + {56.6, 91.1, 1337}, + {59.6, 90, 1337}, + {59, 87.6, 1337}, + {57.5, 85.5, 1337}, + {40.2, 79.2, 1337}, + {40.9, 78.9, 1337}, + {42.5, 76.4, 1337}, + {44.7, 75.7, 1337}, + {45.2, 74.9, 1337}, + {50, 73.2, 1337}, + {43.6, 73, 1337}, + {50, 71.8, 1337}, + {44.4, 69.9, 1337}, + {45.1, 69.7, 1337}, + }, + [4852] = { + {60.9, 93.1, 1337}, + {62.6, 91.7, 1337}, + {63.1, 90.5, 1337}, + {61.1, 90.2, 1337}, + {58.9, 90.1, 1337}, + {56.4, 84.1, 1337}, + {53.1, 81.7, 1337}, + {62.4, 81.4, 1337}, + {65.2, 80.3, 1337}, + {56.5, 79.9, 1337}, + {62, 79, 1337}, + {40.8, 77.7, 1337}, + {45.3, 76, 1337}, + {61.4, 75.5, 1337}, + {58.7, 75.2, 1337}, + {42.4, 75.2, 1337}, + {54.2, 73.6, 1337}, + {57.1, 71.9, 1337}, + {61.6, 69.6, 1337}, + {63.7, 63.6, 1337}, + {53.6, 63.6, 1337}, + {56.5, 63.2, 1337}, + {64.4, 62.3, 1337}, + }, + [4853] = { + {35, 31.5, 1337}, + {30.1, 29.2, 1337}, + {23.1, 28, 1337}, + {38.1, 27, 1337}, + {34.1, 26.1, 1337}, + {24.6, 26, 1337}, + {21.5, 25.8, 1337}, + {29.9, 23.8, 1337}, + {27.8, 20, 1337}, + }, + [4855] = { + {35, 32.7, 1337}, + {30.1, 28.2, 1337}, + {35.1, 28.1, 1337}, + {37.4, 27.7, 1337}, + {22.4, 27.6, 1337}, + {31.9, 27.5, 1337}, + {36, 27.2, 1337}, + {33.4, 26, 1337}, + {25.4, 25.9, 1337}, + {37.5, 25.9, 1337}, + {29.8, 25, 1337}, + {25.6, 24.9, 1337}, + {22.1, 24.6, 1337}, + {30.3, 24.6, 1337}, + {31.2, 24.4, 1337}, + {26.9, 21.1, 1337}, + {27, 19.9, 1337}, + }, + [4857] = { + {41.1, 18.9, 1337}, + {43.2, 17.6, 1337}, + {40.2, 15.9, 1337}, + {42.2, 14.5, 1337}, + }, + [4860] = { + {38.4, 49.3, 1337}, + {46.3, 46.7, 1337}, + {41.7, 40.9, 1337}, + {45.6, 24, 1337}, + {48.3, 21.5, 1337}, + {42.8, 21.1, 1337}, + {39, 18.9, 1337}, + {43.9, 15.3, 1337}, + {47.1, 12.9, 1337}, + {39.9, 8.7, 1337}, + {42.9, 6.5, 1337}, + }, + [4861] = { + {40.3, 65.5, 1337}, + {45.1, 55, 1337}, + {32.7, 53.1, 1337}, + {30.4, 47.3, 1337}, + {32.1, 46, 1337}, + {30.6, 43.7, 1337}, + {30.8, 42.5, 1337}, + {32.2, 41.7, 1337}, + }, + [4863] = { + {36.2, 64.3, 1337}, + {37.2, 58, 1337}, + {42.3, 55.4, 1337}, + {21.5, 24.3, 1337}, + }, + [5224] = { + {73.7, 44.8, 8}, + {75.9, 44.6, 8}, + {77, 43.2, 8}, + {73.8, 42.6, 8}, + {72, 41.9, 8}, + }, + [5225] = { + {76.7, 46.2, 8}, + {79.4, 40.4, 8}, + {77.8, 39.6, 8}, + {77.1, 38.7, 8}, + {77.4, 38.6, 8}, + {77.3, 38.2, 8}, + }, + [5226] = { + {56.7, 52.1, 1477}, + {42.7, 51.5, 1477}, + {57, 51.1, 1477}, + {43.4, 50.9, 1477}, + {49.9, 47.6, 1477}, + {50.3, 46.4, 1477}, + {49.9, 34.5, 1477}, + {58.9, 31, 1477}, + {44.5, 15.9, 1477}, + {49.9, 11, 1477}, + {47.3, 7.4, 1477}, + {48.1, 7.3, 1477}, + }, + [5228] = { + {56.2, 51.9, 1477}, + {43.4, 51.6, 1477}, + {49.5, 46.4, 1477}, + {42.9, 40.4, 1477}, + {56.9, 40.2, 1477}, + {56, 40, 1477}, + {57, 39.1, 1477}, + }, + [5235] = { + {78.3, 46.8, 8}, + {75, 42.8, 8}, + {75.1, 42.9, 8}, + }, + [5243] = { + {76.6, 51, 8}, + {74.4, 48.4, 8}, + {75.5, 47.6, 8}, + {73.1, 47.4, 8}, + {80.9, 45.4, 8}, + {79.3, 44.7, 8}, + {78.1, 44, 8}, + }, + [5256] = { + {51.9, 64.9, 1477}, + {54.7, 64.4, 1477}, + {44.9, 63.8, 1477}, + {61, 58.4, 1477}, + {39.9, 58.1, 1477}, + {37.3, 49.2, 1477}, + {35.9, 48.9, 1477}, + {62.9, 40.4, 1477}, + {63.4, 39.2, 1477}, + {38.6, 32.4, 1477}, + {48, 25.8, 1477}, + }, + [5259] = { + {57.1, 75.8, 1477}, + {55.8, 75, 1477}, + {36.3, 68, 1477}, + {55.2, 65.6, 1477}, + {45.2, 65.5, 1477}, + {43.4, 65, 1477}, + {55.6, 64.3, 1477}, + {38.9, 57.8, 1477}, + {63.1, 48.7, 1477}, + {36.5, 47.6, 1477}, + {36.2, 41.3, 1477}, + {37.1, 39.6, 1477}, + {62.3, 38.3, 1477}, + {43.3, 30.7, 1477}, + {59.4, 30.1, 1477}, + {41.8, 30, 1477}, + {40.7, 29.8, 1477}, + {62.9, 26.2, 1477}, + {53.1, 25, 1477}, + {50.6, 24.3, 1477}, + {60.7, 24, 1477}, + {63, 20.5, 1477}, + {42.8, 19.5, 1477}, + {43.3, 16, 1477}, + }, + [5261] = { + {76.9, 49.1, 8}, + {76.8, 48.8, 8}, + {73.6, 48.3, 8}, + {76.9, 47.8, 8}, + {74.8, 47.6, 8}, + {80.6, 47.5, 8}, + {80.6, 47.4, 8}, + {74.3, 47.3, 8}, + {78.5, 45, 8}, + }, + [5263] = { + {80.2, 49.7, 8}, + {80.2, 48.3, 8}, + {80.9, 46.7, 8}, + {80.9, 46.5, 8}, + {80.9, 44.4, 8}, + {78.6, 41.7, 8}, + {78.6, 41.5, 8}, + {77.7, 41.4, 8}, + {78.2, 41.2, 8}, + {76.7, 41.1, 8}, + {77.1, 40.8, 8}, + {77.9, 40.2, 8}, + {78.4, 39.7, 8}, + {77, 39.6, 8}, + {79.1, 38.9, 8}, + {78.2, 38.9, 8}, + {78.2, 38.5, 8}, + {71.6, 51.3, 1477}, + {80.4, 49.2, 1477}, + {77.2, 48.1, 1477}, + {74.7, 45.8, 1477}, + {77.7, 45.6, 1477}, + {71.2, 41.4, 1477}, + }, + [5267] = { + {37.1, 69.8, 1477}, + {38.4, 58.2, 1477}, + {36.7, 40.4, 1477}, + {42.9, 30.6, 1477}, + {43, 27.1, 1477}, + {62.5, 25.6, 1477}, + {51.7, 24.2, 1477}, + {63.2, 23.2, 1477}, + }, + [5269] = { + {76.1, 50.7, 8}, + {75, 48.8, 8}, + {77.6, 48.5, 8}, + {80.4, 48.2, 8}, + {77, 47.7, 8}, + {73, 47.6, 8}, + {81, 44.2, 8}, + {81.3, 42.6, 8}, + {76.9, 41.7, 1477}, + }, + [5270] = { + {60.6, 59.5, 1477}, + {60.3, 58.6, 1477}, + {63.6, 47.5, 1477}, + {59.3, 31.3, 1477}, + {58.5, 30.5, 1477}, + {56.7, 27.1, 1477}, + {62.7, 22.5, 1477}, + }, + [5271] = { + {72.5, 51.1, 1477}, + {80.5, 50.3, 1477}, + {71.5, 50.1, 1477}, + {79.9, 50, 1477}, + {77.8, 48.1, 1477}, + {74.4, 47.8, 1477}, + {75.3, 47.7, 1477}, + {77, 45.7, 1477}, + {74.4, 44.2, 1477}, + {77.8, 43.6, 1477}, + {76.7, 43.6, 1477}, + {74.4, 42.1, 1477}, + {75.1, 42, 1477}, + {71.6, 42, 1477}, + {77.2, 41.8, 1477}, + {80.5, 40.9, 1477}, + {71.4, 40.5, 1477}, + {81, 40, 1477}, + }, + [5273] = { + {77.3, 45.6, 1477}, + {74.9, 44.2, 1477}, + }, + [5277] = { + {49.4, 74.2, 1477}, + {74.2, 62.3, 1477}, + {49.1, 62.2, 1477}, + {50.1, 62, 1477}, + {62.2, 59.3, 1477}, + {45.7, 55.4, 1477}, + {58.2, 53.4, 1477}, + {40.4, 53.3, 1477}, + {40, 39.2, 1477}, + {45.8, 37, 1477}, + {41.2, 36.1, 1477}, + {61.5, 35.7, 1477}, + {52.1, 8.3, 1477}, + }, + [5280] = { + {73.3, 66.1, 1477}, + {61.3, 58.2, 1477}, + {41.2, 55.4, 1477}, + {58.5, 50.1, 1477}, + {40.7, 37.1, 1477}, + {52.6, 9.9, 1477}, + }, + [5283] = { + {50.4, 71.7, 1477}, + {51.2, 62.1, 1477}, + {45.7, 56.3, 1477}, + {46, 54.7, 1477}, + {40.8, 54.4, 1477}, + {40, 52.3, 1477}, + {45, 47.3, 1477}, + {53.6, 44.1, 1477}, + {40.2, 38, 1477}, + {62.4, 38, 1477}, + {54, 36.2, 1477}, + {53, 8.3, 1477}, + }, + [5291] = { + {24.8, 64.8, 1477}, + {34.5, 60.5, 1477}, + {24.4, 50.4, 1477}, + {21, 46.6, 1477}, + {33.6, 31.1, 1477}, + {24.8, 27.1, 1477}, + {27.6, 26.7, 1477}, + }, + [5708] = { + {60.3, 59.5, 1477}, + {51.4, 24.7, 1477}, + }, + [5785] = { + {36.2, 11.3, 215}, + }, + [5807] = { + {53.4, 17, 215}, + }, + [5826] = { + {43.8, 50.5, 14}, + {47.2, 49.9, 14}, + {43.2, 39.3, 14}, + }, + [6123] = { + {78, 62.4, 1}, + {77.4, 62.4, 1}, + {76.8, 62.3, 1}, + {77.8, 62.1, 1}, + {78, 61.9, 1}, + {77.4, 61.3, 1}, + {78.1, 61.2, 1}, + {77, 60.9, 1}, + {77.5, 60.5, 1}, + {78.1, 60, 1}, + {76.9, 60, 1}, + {77.4, 59.2, 1}, + {77.5, 63.1, 1}, + {78.2, 61.2, 1}, + {77.4, 59.3, 1}, + {10.7, 91.1, 38}, + {10.8, 91, 38}, + {3.2, 92.6, 5602}, + {2.9, 92.5, 5602}, + {2.7, 92.5, 5602}, + {4.1, 92.1, 5602}, + {3.1, 91.9, 5602}, + {4.1, 91.6, 5602}, + {4.1, 91.5, 5602}, + {3.9, 91.5, 5602}, + {3.9, 91.3, 5602}, + {2.7, 91.3, 5602}, + {4.1, 91.2, 5602}, + {3.5, 91.1, 5602}, + {2, 91, 5602}, + {4, 90.9, 5602}, + {4.1, 90.9, 5602}, + {3.8, 90.9, 5602}, + {3.2, 90.9, 5602}, + {1.5, 90.9, 5602}, + {2.1, 90.7, 5602}, + {2.9, 90.5, 5602}, + {0.9, 90.1, 5602}, + {2.8, 90.1, 5602}, + {2.9, 89.9, 5602}, + {1.5, 89.6, 5602}, + {0.1, 89.3, 5602}, + {0.8, 89.1, 5602}, + }, + [6206] = { + {52.4, 91.8, 721}, + {49.2, 89.7, 721}, + {47.6, 88, 721}, + {56.3, 85.5, 721}, + {60.7, 81.8, 721}, + {51.4, 80.5, 721}, + {51.2, 77.8, 721}, + {53.9, 76.6, 721}, + {49, 76.1, 721}, + {53.5, 76, 721}, + {53.7, 71.4, 721}, + {43, 68.4, 721}, + {37.2, 67.7, 721}, + {58.1, 66.5, 721}, + {57.1, 65.2, 721}, + {50.2, 63.7, 721}, + {58.3, 62.1, 721}, + {40.7, 53.6, 721}, + {58.7, 52.7, 721}, + {49.5, 47.6, 721}, + {52.9, 36.5, 721}, + {50.7, 36.3, 721}, + }, + [6207] = { + {47.7, 82.9, 721}, + {35.8, 71.2, 721}, + {50.9, 64, 721}, + {59.2, 63.9, 721}, + {53.6, 36.4, 721}, + }, + [6208] = { + {18.1, 39.3, 1}, + {21.7, 34, 1}, + {21.8, 33.1, 1}, + {20.5, 94.8, 721}, + {51.8, 49.7, 721}, + {52.3, 41.8, 721}, + }, + [6210] = { + {18.8, 39.2, 1}, + {20.4, 36.9, 1}, + {21.8, 33.7, 1}, + {20.6, 37, 1}, + {26.2, 94, 721}, + {42.1, 75, 721}, + {52.3, 46.6, 721}, + }, + [6211] = { + {56, 89.6, 721}, + {52.3, 88.7, 721}, + {50.7, 88.2, 721}, + {45.3, 84.2, 721}, + {37.7, 81.4, 721}, + {51.6, 80.5, 721}, + {60.7, 80, 721}, + {59.2, 76.3, 721}, + {33.9, 75.8, 721}, + {48.4, 75.7, 721}, + {61, 73.9, 721}, + {61.5, 72, 721}, + {54.5, 71.1, 721}, + {58.1, 70.4, 721}, + {58.6, 69.3, 721}, + {48.1, 68.9, 721}, + }, + [6213] = { + {19.3, 38.8, 1}, + {19.7, 36.7, 1}, + {20.9, 36, 1}, + {22.1, 34.1, 1}, + {21.8, 32.2, 1}, + {31, 90.9, 721}, + {34.1, 72.8, 721}, + {44.6, 66.8, 721}, + {54.9, 50.5, 721}, + {52.4, 33.7, 721}, + }, + [6218] = { + {46.9, 61.7, 721}, + {49.8, 57.5, 721}, + {51.9, 53.8, 721}, + {46, 48.5, 721}, + {49.9, 44.5, 721}, + {43.2, 44.2, 721}, + {57.2, 43, 721}, + {46.8, 37.5, 721}, + }, + [6219] = { + {53.2, 60.5, 721}, + {57.5, 56.9, 721}, + {51.7, 48.6, 721}, + {54.3, 44.5, 721}, + {55.3, 41.3, 721}, + }, + [6220] = { + {42.4, 57.8, 721}, + {55.1, 56.8, 721}, + {48.5, 53, 721}, + {41.3, 51.5, 721}, + {46.1, 42.3, 721}, + {51.2, 38.9, 721}, + }, + [6221] = { + {23.1, 39.5, 1}, + {22.4, 39.3, 1}, + {22.8, 39.2, 1}, + {21.8, 39.2, 1}, + {22.6, 39.2, 1}, + {22.5, 39.1, 1}, + {22.2, 39.1, 1}, + {22.8, 39.1, 1}, + {22.8, 39, 1}, + {23.5, 38.9, 1}, + {22.5, 38.8, 1}, + {21.9, 38.7, 1}, + {21.9, 38.6, 1}, + {64, 97.1, 721}, + {57.2, 95, 721}, + {61.1, 94.5, 721}, + {52, 94.4, 721}, + {59.7, 94.1, 721}, + {58.6, 93.7, 721}, + {58.6, 93.4, 721}, + {61.2, 93.3, 721}, + {55.8, 93.1, 721}, + {60.9, 92.7, 721}, + {67.5, 91.8, 721}, + {58.1, 91.3, 721}, + {53.1, 89.9, 721}, + {53.5, 89.2, 721}, + }, + [6223] = { + {15.7, 85.1, 721}, + {24.2, 74.2, 721}, + {15.9, 73, 721}, + {27.7, 71.8, 721}, + {29.7, 64.4, 721}, + {28.5, 60.3, 721}, + {29.8, 56, 721}, + {29.1, 50.5, 721}, + {32.9, 48, 721}, + }, + [6225] = { + {3.6, 79.1, 721}, + {5.5, 69.8, 721}, + {1.6, 62.8, 721}, + }, + [6230] = { + {1.3, 68.3, 721}, + {5, 57.3, 721}, + }, + [6231] = { + {19.8, 31.9, 1}, + {35, 31.4, 721}, + }, + [6233] = { + {9.2, 92.1, 721}, + {11.7, 89.4, 721}, + {17.9, 89.4, 721}, + {23.4, 74.1, 721}, + {28.2, 71, 721}, + {25, 69.8, 721}, + {15.7, 69.7, 721}, + {29.8, 65.6, 721}, + {27.4, 65.4, 721}, + {27.3, 57.3, 721}, + {29.7, 57.1, 721}, + {6.8, 56.9, 721}, + {2.2, 55.8, 721}, + {30.4, 53.4, 721}, + {30.8, 52.8, 721}, + {34.5, 51.8, 721}, + {33.6, 51.7, 721}, + {33.3, 51.6, 721}, + {32.9, 49, 721}, + {34.2, 48.1, 721}, + }, + [6329] = { + {53.9, 90, 721}, + {50.7, 89.8, 721}, + {46.4, 88.8, 721}, + {54.4, 88.4, 721}, + {49.1, 88.2, 721}, + {46.7, 87.6, 721}, + {38.8, 83, 721}, + {38.2, 82.1, 721}, + {61.5, 79.3, 721}, + {46.1, 78.2, 721}, + {45.7, 77.3, 721}, + {59.2, 77.2, 721}, + {57.8, 76.9, 721}, + {33.6, 75, 721}, + {41.5, 73.6, 721}, + {51.3, 73.3, 721}, + {41, 73.1, 721}, + {61.3, 73, 721}, + {50.6, 72.9, 721}, + {41.9, 72.8, 721}, + {46, 72, 721}, + {46, 71.2, 721}, + {59.5, 70.1, 721}, + {42.5, 68.4, 721}, + {37.7, 68.2, 721}, + {61.6, 66.9, 721}, + {41.1, 64.6, 721}, + {49.5, 63.7, 721}, + {53.7, 63.6, 721}, + {48.4, 63.5, 721}, + {40.2, 63.5, 721}, + {45, 62.3, 721}, + {55.1, 61.6, 721}, + {43.3, 59.6, 721}, + {58.2, 56.8, 721}, + {40.9, 56, 721}, + {40.6, 55.4, 721}, + {49.5, 55, 721}, + {59, 53.4, 721}, + {48, 49.8, 721}, + {52.9, 49.5, 721}, + {40.5, 48.4, 721}, + {40.7, 47.6, 721}, + {58.8, 44.7, 721}, + {57.4, 42.2, 721}, + {47.5, 36.3, 721}, + }, + [6391] = { + {71, 38, 1}, + {41.8, 71.9, 721}, + {42.2, 70.7, 721}, + {36.9, 68.4, 721}, + {37.7, 67.4, 721}, + {40.9, 64.8, 721}, + {35.1, 62.9, 721}, + {34.9, 62.2, 721}, + }, + [6392] = { + {40.3, 75.1, 721}, + {40, 74.6, 721}, + {40.7, 65.5, 721}, + {35, 62.6, 721}, + }, + [6407] = { + {39.9, 74.9, 721}, + {38.5, 60.7, 721}, + {38.2, 60.5, 721}, + }, + [6426] = { + {26, 69.6, 5136}, + {41.6, 67.9, 5136}, + {32.2, 64.9, 5136}, + {33.8, 62.9, 5136}, + {37.6, 58.8, 5136}, + {24.4, 58.8, 5136}, + {33.1, 56.8, 5136}, + {27.3, 56.3, 5136}, + {40.7, 55.1, 5136}, + {21.8, 52.7, 5136}, + {27, 50.8, 5136}, + {46.5, 47.2, 5136}, + {51.4, 46.6, 5136}, + }, + [6427] = { + {38.4, 68.9, 5136}, + {35.5, 66.9, 5136}, + {51.6, 65.9, 5136}, + {45.9, 56.4, 5136}, + {26.9, 52.9, 5136}, + {24.2, 52.1, 5136}, + {40.8, 46.6, 5136}, + {36.1, 43.9, 5136}, + }, + [6523] = { + {48.6, 17.6, 11}, + {48.8, 16, 11}, + {49, 15.7, 11}, + }, + [6789] = { + {51.4, 38.3, 148}, + {51.6, 38.3, 148}, + {51.5, 38.1, 148}, + {51.6, 37.8, 148}, + {36, 12.4, 361}, + {36.1, 12.3, 361}, + {36.1, 12.1, 361}, + {36.1, 11.8, 361}, + {51.5, 38.2, 148}, + {36, 12.2, 361}, + }, + [6846] = { + {48.1, 87.3, 12}, + }, + [6866] = { + {48.2, 87.6, 12}, + }, + [6906] = { + {59.1, 93.5, 1337}, + }, + [6911] = { + {37.5, 24.3, 141}, + }, + [6927] = { + {48.7, 87.3, 12}, + }, + [7012] = { + {36.1, 51.1, 1337}, + {42.2, 50.3, 1337}, + {46.2, 49.3, 1337}, + {42.8, 49.1, 1337}, + {48.2, 48.6, 1337}, + {35.5, 47.9, 1337}, + {45.6, 47.8, 1337}, + {41.7, 41.7, 1337}, + {42.7, 41.4, 1337}, + {41, 38.3, 1337}, + }, + [7022] = { + {36.5, 60.5, 1337}, + {41.4, 60, 1337}, + {46.5, 56.7, 1337}, + {45.4, 55, 1337}, + }, + [7023] = { + {28.8, 61.8, 1337}, + }, + [7073] = { + {16, 81.1, 267}, + }, + [7234] = { + {69.4, 53.4, 141}, + }, + [7235] = { + {69.7, 55.2, 141}, + {69.4, 53.2, 141}, + {69.7, 52.9, 141}, + {68.9, 52.4, 141}, + {68.6, 52.3, 141}, + {68.6, 52.2, 141}, + {68.7, 52.8, 141}, + {68, 52.5, 141}, + {68.3, 52.1, 141}, + {68.5, 51.7, 141}, + {69.6, 55.3, 141}, + {68.6, 52.9, 141}, + {68.9, 52.3, 141}, + {68.3, 52.2, 141}, + }, + [7290] = { + {28.6, 35.1, 1337}, + {29.3, 33.7, 1337}, + }, + [7318] = { + {45.5, 58.6, 141}, + }, + [7320] = { + {41, 33.1, 1337}, + {40.3, 32.9, 1337}, + {47.1, 32.7, 1337}, + {42.8, 32.6, 1337}, + {44.8, 31.6, 1337}, + {48.2, 30.1, 1337}, + {38.6, 30, 1337}, + {39.4, 29.9, 1337}, + {47.6, 29.5, 1337}, + }, + [7321] = { + {40.6, 33.7, 1337}, + {42.7, 33.7, 1337}, + {45, 33.2, 1337}, + {45.1, 31.6, 1337}, + {48.1, 30.9, 1337}, + {46.8, 29.6, 1337}, + }, + [7328] = { + {57, 34.1, 722}, + {53.8, 32.9, 722}, + {38, 31.5, 722}, + {46.4, 30.4, 722}, + {38.2, 27, 722}, + {54.5, 25.1, 722}, + {34.8, 24.9, 722}, + {44.6, 24.8, 722}, + {41.4, 23.4, 722}, + {60.4, 23.3, 722}, + {47.5, 23, 722}, + }, + [7329] = { + {57.9, 36.6, 722}, + {57.5, 33.3, 722}, + {49.9, 33.3, 722}, + {39, 31.9, 722}, + {54.8, 31.8, 722}, + {38.4, 31.5, 722}, + {47.2, 30.6, 722}, + {48, 30.2, 722}, + {40.5, 29.3, 722}, + {46, 27.5, 722}, + {36.9, 27.3, 722}, + {53.8, 26.1, 722}, + {59.1, 25.2, 722}, + {40.8, 24.5, 722}, + {34.2, 24.5, 722}, + {47.9, 24.3, 722}, + {44.6, 24, 722}, + {46.7, 22.2, 722}, + }, + [7332] = { + {30.3, 22, 722}, + {34, 19.2, 722}, + {36.9, 18.5, 722}, + {28, 17.8, 722}, + {27, 16.3, 722}, + {35.2, 15.8, 722}, + {22.6, 15.1, 722}, + }, + [7335] = { + {28.1, 75.5, 15}, + {28.3, 75.1, 15}, + {28.8, 74.9, 15}, + {27.1, 74.9, 15}, + {27.8, 74.9, 15}, + {28.8, 74.6, 15}, + {28.1, 74.3, 15}, + {50.1, 93.1, 17}, + {50.2, 92.9, 17}, + {50.4, 92.8, 17}, + {49.5, 92.8, 17}, + {49.9, 92.7, 17}, + {50.4, 92.6, 17}, + {50.1, 92.4, 17}, + {34.6, 20.9, 722}, + {30.1, 20.8, 722}, + {37.4, 19.8, 722}, + {31.4, 16.8, 722}, + {25, 14.2, 722}, + }, + [7337] = { + {57.6, 35.4, 722}, + {50.7, 33.4, 722}, + {37.5, 30.9, 722}, + {47.2, 29.7, 722}, + {40.2, 28.6, 722}, + {55, 26.5, 722}, + {37.7, 26.5, 722}, + {44, 24, 722}, + {59.5, 23.6, 722}, + {46.9, 23.1, 722}, + }, + [7341] = { + {35.1, 60.4, 722}, + {38.2, 56.8, 722}, + {27.7, 40.1, 722}, + {28.5, 37.5, 722}, + {43.1, 37, 722}, + {37.6, 34.4, 722}, + }, + [7342] = { + {33, 54.5, 722}, + {32, 51.8, 722}, + {38.8, 48.8, 722}, + {28.8, 46.2, 722}, + {31.4, 44.2, 722}, + {24.7, 20, 722}, + }, + [7345] = { + {32.9, 51.7, 722}, + {38.8, 49.8, 722}, + }, + [7347] = { + {32.2, 55.2, 722}, + {36.4, 54.2, 722}, + {36.9, 54.1, 722}, + {31.1, 49.4, 722}, + {20.2, 48.5, 722}, + {28.8, 48.3, 722}, + {33.1, 43.1, 722}, + {29.2, 42.3, 722}, + {29.4, 41.5, 722}, + {34.4, 39.3, 722}, + {33.3, 38.8, 722}, + {24.4, 36, 722}, + {23, 33.9, 722}, + {21.9, 33.5, 722}, + {24.6, 31.1, 722}, + {25.3, 21.1, 722}, + {23.7, 19.6, 722}, + }, + [7348] = { + {34.3, 61.1, 722}, + {38.6, 57.8, 722}, + {28.3, 56.7, 722}, + {41.9, 51.5, 722}, + {36.8, 41.6, 722}, + {27.2, 40.9, 722}, + {26.6, 38.9, 722}, + {28.6, 37.7, 722}, + {43.6, 37, 722}, + {39.8, 36.1, 722}, + {31.5, 35.6, 722}, + }, + [7352] = { + {28.1, 55.6, 722}, + {40.3, 52.2, 722}, + {37.8, 40.2, 722}, + {43.6, 37.4, 722}, + {43.1, 36.7, 722}, + {40.6, 36.1, 722}, + {37.5, 34.7, 722}, + {32, 34.3, 722}, + }, + [7353] = { + {38.3, 49.3, 722}, + {19.1, 48.5, 722}, + {24.1, 35.5, 722}, + {22.5, 35.3, 722}, + {23.6, 31.8, 722}, + {24.8, 21, 722}, + {24.2, 20.6, 722}, + }, + [8336] = { + {24.8, 64.8, 1477}, + {34.5, 60.5, 1477}, + {24.4, 50.4, 1477}, + {21, 46.6, 1477}, + {33.6, 31.1, 1477}, + {24.8, 27.1, 1477}, + {27.6, 26.7, 1477}, + }, + [9025] = { + {57.6, 70.8, 1584}, + }, + [9045] = { + {61.2, 62.7, 1583}, + {37.9, 50.4, 1583}, + {30.7, 47.4, 1583}, + {33.1, 43.4, 1583}, + {30, 42.6, 1583}, + {64.5, 41, 1583}, + {58.3, 40.9, 1583}, + {40.8, 37.7, 1583}, + }, + [9097] = { + {66.1, 63.3, 1583}, + {60.4, 62.5, 1583}, + {65.7, 62.3, 1583}, + {67.1, 61.6, 1583}, + {67.2, 60.7, 1583}, + {66.8, 58, 1583}, + {66.9, 57.2, 1583}, + {64.3, 56.8, 1583}, + {67.2, 51.7, 1583}, + {60.4, 51.4, 1583}, + {31.8, 50.9, 1583}, + {67.2, 50.9, 1583}, + {30, 50.6, 1583}, + {60.2, 50.5, 1583}, + {38.3, 49.7, 1583}, + {31.9, 48.1, 1583}, + {46.2, 46.9, 1583}, + {46.6, 46.4, 1583}, + {42, 45.8, 1583}, + {42, 45.2, 1583}, + {42, 44.6, 1583}, + {52.5, 44.4, 1583}, + {53.2, 44.4, 1583}, + {31.7, 42.8, 1583}, + {33.6, 42.7, 1583}, + {52.9, 41, 1583}, + {64.9, 40.9, 1583}, + {36.3, 40.5, 1583}, + {32.1, 40.5, 1583}, + {30.4, 40.4, 1583}, + {58.6, 40.3, 1583}, + {33.5, 39.9, 1583}, + {33, 39.5, 1583}, + {33.5, 39.4, 1583}, + {37.2, 38.7, 1583}, + {54, 38.3, 1583}, + {67, 37.7, 1583}, + {66.6, 37.7, 1583}, + {37.2, 37.1, 1583}, + {53.8, 37.1, 1583}, + {30.4, 35.5, 1583}, + {32.2, 35.5, 1583}, + {38.1, 35.3, 1583}, + }, + [9098] = { + {60.6, 63.5, 1583}, + {67.2, 62.3, 1583}, + {66.8, 58.8, 1583}, + {65.1, 56.5, 1583}, + {66.7, 50.9, 1583}, + {59.6, 50.6, 1583}, + {38.7, 50.4, 1583}, + {31.2, 47.6, 1583}, + {46.6, 47.4, 1583}, + {52.5, 43.7, 1583}, + {29.9, 43.4, 1583}, + {32.5, 43.3, 1583}, + {58.6, 41.5, 1583}, + {53.5, 40.9, 1583}, + {67.4, 40, 1583}, + {54.6, 37, 1583}, + {40.8, 36.8, 1583}, + }, + [9196] = { + {35, 55.3, 1583}, + }, + [9197] = { + {42.1, 60.5, 1583}, + {50, 59, 1583}, + {45.8, 55.8, 1583}, + {42.9, 55.8, 1583}, + }, + [9199] = { + {57.5, 58.4, 1583}, + {52.3, 58.1, 1583}, + {57.3, 57.1, 1583}, + {50, 56.7, 1583}, + }, + [9200] = { + {35.8, 60.4, 1583}, + {44.8, 57.8, 1583}, + {38, 53.9, 1583}, + }, + [9201] = { + {38, 60.1, 1583}, + {59.2, 55.7, 1583}, + {38, 55.6, 1583}, + {35.5, 54.7, 1583}, + {40.7, 53.9, 1583}, + }, + [9216] = { + {60.2, 64.9, 1583}, + {42.9, 60.9, 1583}, + {49.4, 59.8, 1583}, + {41.9, 58.4, 1583}, + {43, 57.3, 1583}, + {44.1, 56.8, 1583}, + {39.9, 55.2, 1583}, + {45.3, 55, 1583}, + }, + [9219] = { + {56.2, 57.7, 1583}, + }, + [9236] = { + {55.6, 72, 1583}, + }, + [9237] = { + {53, 54.4, 1583}, + }, + [9239] = { + {54.6, 69.1, 1583}, + {55.7, 67.3, 1583}, + {52, 61.2, 1583}, + {62.3, 58.6, 1583}, + {52, 57.8, 1583}, + {66.2, 56.7, 1583}, + {51.7, 55.2, 1583}, + {51.5, 54.5, 1583}, + {64.1, 52.5, 1583}, + {53.4, 52.2, 1583}, + }, + [9240] = { + {54.5, 72.7, 1583}, + {56.4, 72.7, 1583}, + {54.5, 68.4, 1583}, + {55.1, 67.2, 1583}, + {56.5, 61.2, 1583}, + {52.4, 61.2, 1583}, + {54.4, 61, 1583}, + {56.2, 60.6, 1583}, + {52.4, 60.5, 1583}, + {59.1, 59, 1583}, + {63.1, 57.9, 1583}, + {60, 57.4, 1583}, + {52, 57.1, 1583}, + {54.7, 56.6, 1583}, + {54.7, 55.1, 1583}, + {54.8, 52, 1583}, + {53.2, 51.4, 1583}, + }, + [9257] = { + {66.7, 61.4, 1583}, + {64.8, 55.7, 1583}, + {66.7, 51.8, 1583}, + {59.8, 51.7, 1583}, + {53.2, 43.7, 1583}, + {65, 41.9, 1583}, + {64.4, 41.7, 1583}, + {67.4, 39.4, 1583}, + {54.6, 38.1, 1583}, + }, + [9258] = { + {63, 61.9, 1583}, + {63.7, 59.3, 1583}, + {37.2, 48.4, 1583}, + {57.1, 43.9, 1583}, + {46.6, 43.3, 1583}, + {54.3, 40.3, 1583}, + }, + [9259] = { + {49.8, 72.1, 1583}, + {53.1, 71.5, 1583}, + {52.4, 71.5, 1583}, + {55.2, 71.3, 1583}, + {49.9, 71, 1583}, + {55.8, 70.3, 1583}, + {56.6, 68.6, 1583}, + {48.7, 67.5, 1583}, + {53.4, 66.4, 1583}, + {52.5, 65.6, 1583}, + {52.2, 64.7, 1583}, + {47.1, 63.9, 1583}, + {49.8, 62.1, 1583}, + {47.1, 61.9, 1583}, + {50, 61.6, 1583}, + {46.9, 58.9, 1583}, + {49.2, 57.5, 1583}, + {49.4, 56.1, 1583}, + {47.1, 54.2, 1583}, + {58.1, 47.8, 1583}, + {65.2, 47.5, 1583}, + {66.6, 47.4, 1583}, + {51.4, 47.3, 1583}, + {54.8, 46.6, 1583}, + {56.8, 46.3, 1583}, + }, + [9260] = { + {57.5, 75.6, 1583}, + {58.6, 75.2, 1583}, + {57, 75, 1583}, + {57.3, 48, 1583}, + }, + [9261] = { + {50.3, 71.5, 1583}, + {55.7, 69.5, 1583}, + {49.1, 67.5, 1583}, + {49.5, 65.6, 1583}, + {51.8, 65.2, 1583}, + {47.2, 61, 1583}, + {49, 58.7, 1583}, + {47.3, 57.8, 1583}, + {47.2, 55.6, 1583}, + {51, 48.3, 1583}, + {66.8, 48.3, 1583}, + {55, 47.2, 1583}, + {62.9, 45.7, 1583}, + }, + [9262] = { + {49.5, 71.5, 1583}, + {52.8, 71, 1583}, + {55.6, 71, 1583}, + {52, 65.9, 1583}, + {49, 65.6, 1583}, + {47.8, 61.8, 1583}, + {48.6, 58.6, 1583}, + {47.7, 56, 1583}, + {54.4, 46.9, 1583}, + {63.3, 46.1, 1583}, + {56.6, 45.6, 1583}, + }, + [9264] = { + {57.5, 78.2, 1583}, + {57.3, 76.4, 1583}, + {58.6, 75.9, 1583}, + {49.3, 61.8, 1583}, + }, + [9265] = { + {56.6, 69, 1583}, + {65.9, 63.1, 1583}, + {60, 59, 1583}, + {52.3, 57.8, 1583}, + {59, 57.4, 1583}, + {55, 57.3, 1583}, + }, + [9268] = { + {59.5, 64.6, 1583}, + {59.1, 64.6, 1583}, + {60, 64.5, 1583}, + {56.5, 62.5, 1583}, + {61.9, 62.5, 1583}, + {59.5, 62.5, 1583}, + {56, 62.2, 1583}, + {59.2, 59.9, 1583}, + {51.8, 58.5, 1583}, + {64.1, 54.7, 1583}, + {59.6, 52.3, 1583}, + {55.5, 51.6, 1583}, + }, + [9583] = { + {55.2, 85.8, 1583}, + {48.2, 79.1, 1583}, + {40, 77.7, 1583}, + {42.8, 75, 1583}, + {34.9, 74.5, 1583}, + {38.4, 72, 1583}, + {34.2, 70.3, 1583}, + {40.2, 66.5, 1583}, + {36.8, 65.7, 1583}, + {37.2, 63.7, 1583}, + {37.5, 63.7, 1583}, + {55.4, 61.4, 1583}, + {49.4, 61.1, 1583}, + {40.1, 60.9, 1583}, + {51.1, 60.6, 1583}, + {59.3, 58.9, 1583}, + }, + [9676] = { + {38.5, 73.8, 721}, + }, + [9692] = { + {52.8, 79.4, 1583}, + {40, 76.5, 1583}, + {37.1, 75, 1583}, + {42.8, 73, 1583}, + {40.7, 71.8, 1583}, + {34, 66.7, 1583}, + {55.6, 59.9, 1583}, + {60, 57.8, 1583}, + }, + [9693] = { + {47.7, 79.8, 1583}, + {37.4, 79.1, 1583}, + {35.1, 75.8, 1583}, + {38.4, 71.5, 1583}, + {36.5, 66.2, 1583}, + {40.7, 65.9, 1583}, + {59.9, 63.7, 1583}, + {56.1, 61.2, 1583}, + {36.5, 60.7, 1583}, + {59.9, 59.6, 1583}, + {34.9, 58.8, 1583}, + }, + [9716] = { + {53.3, 86.4, 1583}, + {55.5, 86.3, 1583}, + {53, 85.8, 1583}, + {48.3, 80.5, 1583}, + {37.4, 80.1, 1583}, + {37.8, 79.6, 1583}, + {34.4, 73.2, 1583}, + {33.8, 72.7, 1583}, + {38.1, 71.8, 1583}, + {40.2, 65.9, 1583}, + {36.8, 60.2, 1583}, + {40, 59.9, 1583}, + }, + [9717] = { + {52.5, 86.3, 1583}, + {54.8, 86.2, 1583}, + {48.6, 79.8, 1583}, + {34.5, 72.4, 1583}, + {34.2, 69.7, 1583}, + {40.6, 66.5, 1583}, + {36.2, 65.6, 1583}, + {37.1, 60.7, 1583}, + {34.8, 60, 1583}, + }, + [10997] = { + {25.1, 74.7, 2017}, + }, + [14428] = { + {67.1, 58.6, 141}, + }, + [14431] = { + {34.8, 34.9, 141}, + }, + [15318] = { + {64.2, 81.5, 3429}, + {61.7, 81.4, 3429}, + {63.8, 81.4, 3429}, + {61.1, 79.9, 3429}, + {65.8, 76.9, 3429}, + {66, 75.3, 3429}, + }, + [15320] = { + {64.9, 68.6, 3429}, + {73.3, 66, 3429}, + {73.6, 65.4, 3429}, + {65.3, 64.6, 3429}, + {65.7, 64.4, 3429}, + {65.2, 59.3, 3429}, + {63.1, 57.1, 3429}, + {69.4, 54.3, 3429}, + }, + [15323] = { + {65.4, 68.6, 3429}, + {65.6, 63.9, 3429}, + {64.9, 58.9, 3429}, + {65.2, 58.5, 3429}, + {62.6, 56.9, 3429}, + {62.8, 56.3, 3429}, + {69.6, 54.9, 3429}, + {68.9, 54.9, 3429}, + }, + [15324] = { + {60.3, 55.4, 3429}, + {60.7, 54.6, 3429}, + {54.3, 49.8, 3429}, + {54.6, 49.1, 3429}, + {59.3, 28.3, 3429}, + {58.4, 27, 3429}, + {60.4, 17.9, 3429}, + {60.8, 16.7, 3429}, + }, + [15325] = { + {61.2, 88.3, 3429}, + {61.4, 88, 3429}, + {59.2, 83.4, 3429}, + {59.3, 83.1, 3429}, + {59.2, 82.9, 3429}, + {58, 81.3, 3429}, + {57.7, 81.2, 3429}, + {57.8, 81.1, 3429}, + {57.9, 80.5, 3429}, + {58, 80.3, 3429}, + {57.9, 80.1, 3429}, + {55.4, 38.6, 3429}, + {55.4, 38, 3429}, + {55.6, 32.6, 3429}, + {55.9, 32.2, 3429}, + {55.7, 30.3, 3429}, + {57.9, 30, 3429}, + {55.7, 29.9, 3429}, + {58.2, 29.6, 3429}, + {61.9, 24, 3429}, + {61.6, 23.8, 3429}, + {60.1, 23.7, 3429}, + {57.7, 23.5, 3429}, + {57.3, 23.4, 3429}, + {60, 23.3, 3429}, + {61.9, 21.6, 3429}, + {62.1, 21.3, 3429}, + {66, 19.6, 3429}, + {66.4, 19.5, 3429}, + {61.9, 19, 3429}, + {62.2, 18.7, 3429}, + }, + [15327] = { + {61.3, 88.2, 3429}, + {61.1, 88.1, 3429}, + {61.2, 87.9, 3429}, + {59.1, 83.2, 3429}, + {59, 83, 3429}, + {57.8, 81.6, 3429}, + {57.7, 81.4, 3429}, + {57.8, 80.4, 3429}, + {57.8, 80.1, 3429}, + {55.6, 38.3, 3429}, + {55.9, 32.7, 3429}, + {55.8, 30.2, 3429}, + {57.9, 29.5, 3429}, + {61.9, 23.7, 3429}, + {60.3, 23.1, 3429}, + {57.5, 23, 3429}, + {62.3, 21.7, 3429}, + {66.2, 19.8, 3429}, + {62.3, 19.1, 3429}, + }, + [15333] = { + {60.7, 90.3, 3429}, + {61.5, 89.9, 3429}, + {61.9, 89.3, 3429}, + {59.8, 86.7, 3429}, + {57.4, 86.5, 3429}, + {55.6, 86.4, 3429}, + {58.2, 85.8, 3429}, + {54.5, 85, 3429}, + {58.2, 84.7, 3429}, + {55.1, 84.1, 3429}, + {57, 83.8, 3429}, + {56, 83.7, 3429}, + {58.8, 83, 3429}, + {64.4, 81.2, 3429}, + {61.8, 81.1, 3429}, + {56.9, 81, 3429}, + {58, 81, 3429}, + {62.5, 80.8, 3429}, + {64.9, 80.5, 3429}, + {63.8, 80.3, 3429}, + {60.8, 80.3, 3429}, + {61.5, 79.9, 3429}, + {60, 79.5, 3429}, + {60.9, 79.5, 3429}, + {65.6, 79, 3429}, + {65, 78, 3429}, + {65.5, 77.7, 3429}, + {61.8, 76.1, 3429}, + {66.9, 75.9, 3429}, + {62.9, 75.8, 3429}, + {66.7, 75.1, 3429}, + {61.9, 75, 3429}, + {67.6, 74.8, 3429}, + {66, 74.6, 3429}, + {63.2, 74.3, 3429}, + {63.5, 73, 3429}, + {62.2, 72.9, 3429}, + }, + [15335] = { + {66.1, 69.2, 3429}, + {69.6, 65.1, 3429}, + {65.7, 64.3, 3429}, + {41.2, 42.5, 3429}, + {38, 42.5, 3429}, + }, + [15336] = { + {61.5, 80.9, 3429}, + {65, 80.8, 3429}, + {61.4, 80.2, 3429}, + {65.5, 80, 3429}, + {66.1, 76.1, 3429}, + {66.2, 75.6, 3429}, + }, + [15338] = { + {31.9, 52.5, 3429}, + {49.2, 51.2, 3429}, + {49.8, 51.2, 3429}, + {43.2, 45, 3429}, + {43.9, 44.8, 3429}, + {40.3, 44.6, 3429}, + {52.5, 42.4, 3429}, + {33.4, 35.4, 3429}, + {40.6, 33.1, 3429}, + }, + [15343] = { + {60.4, 29.1, 3429}, + {58.5, 27.6, 3429}, + {64.8, 25.5, 3429}, + {63.2, 22.9, 3429}, + {61.7, 21.7, 3429}, + }, + [15355] = { + {47.1, 76.1, 3429}, + {50.3, 71.2, 3429}, + {47, 69.4, 3429}, + {40.8, 66.8, 3429}, + {52, 66.6, 3429}, + {45.8, 61.1, 3429}, + {41, 60.8, 3429}, + {45.8, 59.3, 3429}, + }, + [15386] = { + {56.2, 50.8, 3429}, + }, + [15387] = { + {59.3, 53.7, 3429}, + {59.1, 53.1, 3429}, + {59.4, 53, 3429}, + {59, 52.6, 3429}, + {58.1, 52.4, 3429}, + {57.8, 52.1, 3429}, + {57.5, 51.7, 3429}, + {56.6, 50.5, 3429}, + {60.7, 50.5, 3429}, + {55.9, 50.5, 3429}, + {60.4, 50, 3429}, + {60.1, 49.6, 3429}, + {61.2, 48.3, 3429}, + {60.9, 47.9, 3429}, + {60.6, 47.4, 3429}, + {57.3, 47.1, 3429}, + {59.5, 46.6, 3429}, + {59.2, 46, 3429}, + {59.5, 45.7, 3429}, + {58.9, 45.5, 3429}, + }, + [15389] = { + {57.2, 47.7, 3429}, + }, + [15390] = { + {59.8, 50.8, 3429}, + }, + [15391] = { + {58.9, 46.7, 3429}, + }, + [15392] = { + {60.5, 48.1, 3429}, + }, + [15462] = { + {32.4, 52.8, 3429}, + {33.8, 52.1, 3429}, + {32.7, 51.9, 3429}, + {32.7, 50.8, 3429}, + {33.7, 50.7, 3429}, + {45.7, 48.7, 3429}, + {45.4, 48.1, 3429}, + {47.9, 47.4, 3429}, + {46.9, 46.9, 3429}, + {46.4, 46.7, 3429}, + {41, 45.1, 3429}, + {41.3, 45, 3429}, + {40, 43.9, 3429}, + {41.5, 43.3, 3429}, + {40.9, 43, 3429}, + {51.9, 42.2, 3429}, + {50.6, 41.2, 3429}, + {51.6, 40.8, 3429}, + {49.6, 39.9, 3429}, + {50.6, 39.7, 3429}, + {49.3, 37, 3429}, + {43.8, 34.3, 3429}, + {43.7, 33.4, 3429}, + {44.3, 33, 3429}, + {43.6, 32.6, 3429}, + {43.2, 32.4, 3429}, + }, + [49007] = { + {64.1, 73.4, 28}, + }, + [60708] = { + {43.3, 39, 14}, + }, + [60736] = { + {58.3, 45.3, 5103}, + }, + [60893] = { + {62.5, 97.7, 14}, + {62.6, 97, 14}, + {62.4, 96.9, 14}, + {62.3, 96.8, 14}, + {61.9, 95.7, 14}, + {63.2, 95.3, 14}, + {62.4, 95.2, 14}, + {58.9, 90.8, 14}, + {59.7, 90.7, 14}, + {60.8, 90.5, 14}, + {59, 90.2, 14}, + {59.3, 89.7, 14}, + {60.3, 88.8, 14}, + {77.9, 48.1, 17}, + {77.9, 47.7, 17}, + {77.8, 47.7, 17}, + {77.8, 47.6, 17}, + {77.5, 47, 17}, + {78.2, 46.8, 17}, + {77.8, 46.8, 17}, + {76, 44.5, 17}, + {76.4, 44.4, 17}, + {77, 44.3, 17}, + {76.1, 44.1, 17}, + {76.2, 43.9, 17}, + {76.7, 43.4, 17}, + }, + [60898] = { + {63.2, 97.5, 14}, + {61.8, 97.3, 14}, + {62.1, 96.8, 14}, + {61.6, 96.5, 14}, + {62.9, 96.4, 14}, + {64, 96.3, 14}, + {62.5, 96.3, 14}, + {62.3, 95.1, 14}, + {60.8, 91.4, 14}, + {60.6, 90.8, 14}, + {59.1, 90.6, 14}, + {59, 90.6, 14}, + {58.7, 90.4, 14}, + {60.5, 90.2, 14}, + {59.6, 88.6, 14}, + {53.3, 87.5, 14}, + {53.2, 86.8, 14}, + {78.2, 48, 17}, + {77.5, 47.9, 17}, + {77.6, 47.6, 17}, + {77.4, 47.4, 17}, + {78.1, 47.4, 17}, + {78.7, 47.3, 17}, + {77.8, 47.3, 17}, + {77.8, 46.7, 17}, + {77, 44.8, 17}, + {76.8, 44.5, 17}, + {76.1, 44.4, 17}, + {76, 44.4, 17}, + {75.9, 44.3, 17}, + {76.8, 44.2, 17}, + {76.4, 43.3, 17}, + {73, 42.7, 17}, + {73, 42.4, 17}, + }, + [60906] = { + {40.7, 96.5, 5602}, + }, + [60980] = { + {35.8, 19.9, 17}, + {30.6, 19.8, 17}, + {35.1, 19.6, 17}, + {32.1, 19.5, 17}, + {33.6, 19.3, 17}, + {34.6, 19.2, 17}, + {31.6, 18.5, 17}, + {33.4, 17.9, 17}, + {32.5, 17.8, 17}, + {33.4, 17.2, 17}, + {31.4, 16.5, 17}, + {82.3, 79.1, 406}, + {73.6, 79, 406}, + {81.1, 78.6, 406}, + {76.1, 78.4, 406}, + {78.6, 78.1, 406}, + {80.3, 77.9, 406}, + {73, 77.1, 406}, + {75.3, 76.7, 406}, + {78.4, 75.7, 406}, + {76.7, 75.7, 406}, + {78.3, 74.6, 406}, + {74.9, 73.4, 406}, + }, + [61191] = { + {49.3, 59.4, 3457}, + {66.2, 38.1, 3457}, + {51.5, 32.9, 3457}, + }, + [61192] = { + {46.7, 59.9, 3457}, + {53.5, 57.8, 3457}, + {44.7, 56.8, 3457}, + {68.1, 55, 3457}, + {65.8, 54.6, 3457}, + {54.8, 52.6, 3457}, + {71.2, 50.6, 3457}, + {67.9, 49, 3457}, + {71.5, 47.6, 3457}, + {67.7, 47.3, 3457}, + {60.2, 47.3, 3457}, + {61.2, 46.8, 3457}, + {53.4, 44.1, 3457}, + {48.6, 43.5, 3457}, + {46.6, 42.9, 3457}, + {54.1, 41.4, 3457}, + {63.1, 40.2, 3457}, + {54.8, 40, 3457}, + {62.2, 39.8, 3457}, + {50.9, 32.9, 3457}, + {27, 32.3, 3457}, + {16.8, 17.5, 3457}, + }, + [61193] = { + {44.7, 59.7, 3457}, + {44, 58.9, 3457}, + {52.6, 57.9, 3457}, + {66.9, 55.2, 3457}, + {66.9, 47.4, 3457}, + {71.1, 46.6, 3457}, + {61.3, 45.5, 3457}, + {46.6, 45.3, 3457}, + {53.9, 40.3, 3457}, + {46.9, 39.3, 3457}, + {62.4, 38.4, 3457}, + {26.5, 30.8, 3457}, + }, + [61194] = { + {44.6, 58.5, 3457}, + {53, 56.5, 3457}, + {65.6, 55.3, 3457}, + {48.4, 51.5, 3457}, + {66.6, 49, 3457}, + {71, 49, 3457}, + {67.5, 48.4, 3457}, + {61.6, 48, 3457}, + {53.9, 47.1, 3457}, + {62.5, 46.9, 3457}, + {53.3, 45.8, 3457}, + {54.4, 45.6, 3457}, + {45.8, 43.8, 3457}, + {47.5, 40.4, 3457}, + {64.1, 38.7, 3457}, + {47.7, 38.4, 3457}, + {63.2, 38.3, 3457}, + {51.2, 34.1, 3457}, + {51.3, 31.4, 3457}, + {30.5, 24.3, 3457}, + {17.1, 15.7, 3457}, + }, + [61211] = { + {53.4, 80, 3457}, + {54.1, 76.6, 3457}, + {55.2, 72, 3457}, + {59.9, 70.5, 3457}, + {64.7, 68.7, 3457}, + {52.7, 68.1, 3457}, + {52, 67.7, 3457}, + {53, 67.2, 3457}, + {52.2, 65.7, 3457}, + }, + [61223] = { + {59.7, 60.6, 3457}, + }, + [61364] = { + {73, 84.7, 5208}, + {71.7, 82.5, 5208}, + {70.1, 81.6, 5208}, + {80.5, 81.3, 5208}, + {80.7, 81.1, 5208}, + {70.5, 80.9, 5208}, + {78.5, 80.2, 5208}, + {78.3, 75, 5208}, + {77.2, 71.9, 5208}, + {82.1, 71.6, 5208}, + {79.8, 71.3, 5208}, + {66.2, 71.1, 5208}, + {63.4, 70.7, 5208}, + {73.6, 69.9, 5208}, + {75.9, 68.1, 5208}, + {75.3, 67.9, 5208}, + {60.5, 67.7, 5208}, + {57.1, 61.2, 5208}, + {72, 46.6, 5208}, + {71.9, 43.6, 5208}, + {74.4, 42, 5208}, + {74.2, 41.9, 5208}, + {73.3, 40.4, 5208}, + }, + [61419] = { + {76.6, 40.6, 5208}, + }, + [61464] = { + {28.6, 30, 1}, + {27.5, 29.7, 1}, + {29.4, 29.6, 1}, + {27.8, 29.2, 1}, + {32.7, 28.6, 1}, + {29.9, 28.2, 1}, + {28, 28.1, 1}, + {32.3, 28.1, 1}, + {29.9, 27.6, 1}, + {31.9, 27.5, 1}, + {30, 27.5, 1}, + {27.6, 27.3, 1}, + {33.3, 27.2, 1}, + {30.8, 27.1, 1}, + {28.8, 27, 1}, + {32.7, 26.7, 1}, + {29.8, 26.4, 1}, + {30.9, 26.2, 1}, + {33.3, 25.9, 1}, + {33, 25.9, 1}, + {32.7, 25.4, 1}, + {33.1, 25.3, 1}, + {30.6, 25.2, 1}, + {31.9, 24.9, 1}, + {30.9, 24.2, 1}, + {32.2, 24.1, 1}, + {32, 23.6, 1}, + {32.3, 23.5, 1}, + {30.2, 23.4, 1}, + {31.1, 22.8, 1}, + }, + [61656] = { + {99.9, 53.5, 14}, + {99.6, 51.3, 14}, + {44.2, 61.6, 5536}, + {52.2, 61.5, 5536}, + {51.6, 58.5, 5536}, + {55, 57.9, 5536}, + {56.5, 56.1, 5536}, + {50.3, 55.3, 5536}, + {39.4, 52.6, 5536}, + {42.8, 52.4, 5536}, + {42.2, 50.3, 5536}, + {48.7, 50.1, 5536}, + {38.6, 47.9, 5536}, + {53.6, 47.2, 5536}, + {51.5, 45.8, 5536}, + {40.5, 44.3, 5536}, + {45.5, 41.8, 5536}, + {49.8, 39.8, 5536}, + {48.9, 39.1, 5536}, + {50.6, 38.1, 5536}, + {45, 37.3, 5536}, + {48.3, 33.8, 5536}, + {44.7, 31.2, 5536}, + }, + [61657] = { + {57, 72.7, 5536}, + {58.1, 72.7, 5536}, + {56.2, 72.1, 5536}, + {57.4, 71, 5536}, + {55.4, 67.5, 5536}, + {57.7, 64.8, 5536}, + {55.6, 62.6, 5536}, + {59, 55.5, 5536}, + {59.1, 55.1, 5536}, + {57, 53, 5536}, + {60.3, 51.7, 5536}, + {60.9, 50.3, 5536}, + {59.8, 49.5, 5536}, + {59.3, 48.8, 5536}, + {60.6, 48.5, 5536}, + {61.6, 47.9, 5536}, + {60.7, 47.7, 5536}, + {55.6, 44.4, 5536}, + {55.6, 43.5, 5536}, + {54.6, 43.4, 5536}, + {58.6, 41.8, 5536}, + {63.6, 39.8, 5536}, + {58.5, 39.2, 5536}, + {61.6, 38.8, 5536}, + {63.6, 37.3, 5536}, + {63.9, 34.8, 5536}, + {63, 32.8, 5536}, + {61.9, 31.4, 5536}, + {63, 31, 5536}, + {51.7, 26.8, 5536}, + {57.1, 26.7, 5536}, + {54.2, 24, 5536}, + }, + [61658] = { + {45.1, 46.4, 5536}, + {52.8, 44.5, 5536}, + {51.5, 42.8, 5536}, + {55, 42.5, 5536}, + {53.6, 42.2, 5536}, + {42.5, 40.9, 5536}, + {47.5, 40.8, 5536}, + {45.9, 39.2, 5536}, + {52.2, 39.1, 5536}, + {41.6, 38.6, 5536}, + {43.2, 37.5, 5536}, + {48.2, 36.8, 5536}, + {46.1, 36.2, 5536}, + {45, 34.5, 5536}, + {43.9, 33.9, 5536}, + }, + [61659] = { + {59.8, 52.4, 5536}, + {63, 51.4, 5536}, + {58.9, 48.1, 5536}, + {63.2, 47.1, 5536}, + {57.6, 47.1, 5536}, + {59.1, 44.6, 5536}, + {57.2, 43.4, 5536}, + {46.2, 31.5, 5536}, + {52, 23.5, 5536}, + {53.7, 21.6, 5536}, + {55.4, 21.4, 5536}, + }, + [61660] = { + {99.4, 53, 14}, + {99.9, 52.3, 14}, + {99.7, 50.1, 14}, + {43.6, 75, 5536}, + {50, 58.2, 5536}, + {52.2, 57.5, 5536}, + {49.2, 57.3, 5536}, + {54.2, 56.3, 5536}, + {48.2, 54, 5536}, + {47.3, 52, 5536}, + {38.2, 51.6, 5536}, + {43.6, 50.4, 5536}, + {39.3, 50.2, 5536}, + {41.4, 49.5, 5536}, + {50.4, 48.3, 5536}, + {43.3, 48.2, 5536}, + {48.3, 47.8, 5536}, + {40.8, 47.4, 5536}, + {38.9, 45.4, 5536}, + {43.7, 43.8, 5536}, + {39.5, 42.3, 5536}, + }, + [61661] = { + {46.9, 76.6, 5536}, + {45.5, 74.7, 5536}, + {48.6, 73.5, 5536}, + {56.3, 51.7, 5536}, + }, + [61662] = { + {55.9, 75.6, 5536}, + {51.3, 73.3, 5536}, + {53.2, 72.2, 5536}, + {58.2, 71.3, 5536}, + {56.7, 70.4, 5536}, + {56.4, 68.7, 5536}, + {59.6, 68.1, 5536}, + {56.5, 65.5, 5536}, + {54.9, 64.7, 5536}, + {54.9, 60.8, 5536}, + {53.2, 60.1, 5536}, + {56.4, 58.2, 5536}, + }, + [61663] = { + {66.1, 54.4, 5536}, + {67.7, 52.8, 5536}, + {66.9, 50.7, 5536}, + {68.2, 50.5, 5536}, + {68.3, 50.2, 5536}, + {68.2, 49, 5536}, + {66.9, 48.7, 5536}, + {68.3, 43.9, 5536}, + {66.3, 42.9, 5536}, + {67.9, 41.6, 5536}, + {66, 38.9, 5536}, + {68.3, 38.5, 5536}, + {66.1, 37.6, 5536}, + {69.5, 37, 5536}, + {67.7, 34.9, 5536}, + {67.7, 31.5, 5536}, + {68.1, 28.2, 5536}, + {64.7, 27.9, 5536}, + {63.3, 26.6, 5536}, + {65.4, 26.4, 5536}, + {66.2, 23.6, 5536}, + }, + [61664] = { + {64.1, 54.7, 5536}, + {66.9, 52.6, 5536}, + {65.7, 51.8, 5536}, + {68.8, 51.8, 5536}, + {67.7, 51.3, 5536}, + {66.5, 50.6, 5536}, + {68.5, 50, 5536}, + {49.7, 49.7, 5536}, + {52.2, 48.9, 5536}, + {65.9, 48.8, 5536}, + {67, 48.5, 5536}, + {67.1, 46.8, 5536}, + {55.9, 46.6, 5536}, + }, + [61665] = { + {57.9, 41.3, 5536}, + {59.3, 39.7, 5536}, + {61.4, 38.8, 5536}, + {58.4, 38.6, 5536}, + {57.6, 37.7, 5536}, + {59.4, 36.9, 5536}, + {57.1, 35.5, 5536}, + {62.3, 34.6, 5536}, + {64, 32.9, 5536}, + {65.6, 29.8, 5536}, + {66.5, 28.5, 5536}, + {69.4, 28.4, 5536}, + {65.3, 26, 5536}, + {63.3, 24.8, 5536}, + {64.5, 22.3, 5536}, + }, + [61666] = { + {57.1, 40.1, 5536}, + {60, 38.6, 5536}, + {62.1, 37.9, 5536}, + {67, 37.8, 5536}, + {58.3, 37, 5536}, + {62.6, 36.3, 5536}, + {63.3, 34.5, 5536}, + {57.8, 33.8, 5536}, + {66, 33, 5536}, + {60.4, 32, 5536}, + {64.9, 31.8, 5536}, + {62, 27.8, 5536}, + {67.8, 27.7, 5536}, + {62.9, 26.4, 5536}, + {65.6, 26.1, 5536}, + }, + [61667] = { + {65.5, 74.5, 5536}, + {64.4, 70.3, 5536}, + {61.7, 69.7, 5536}, + {68.5, 61.5, 5536}, + {69.5, 55.4, 5536}, + {68.4, 54.9, 5536}, + {69.7, 54, 5536}, + {70.8, 48.7, 5536}, + {72.3, 48.3, 5536}, + {73.3, 44.5, 5536}, + {72.7, 33.9, 5536}, + {74.2, 30.9, 5536}, + {72.5, 28.9, 5536}, + {70.4, 24.7, 5536}, + {67.7, 20.7, 5536}, + {64.4, 19.5, 5536}, + {65.2, 17.1, 5536}, + }, + [61668] = { + {55.9, 60.2, 5536}, + {57.4, 60.1, 5536}, + {59, 59.8, 5536}, + {60.6, 59.7, 5536}, + {56.8, 59.6, 5536}, + {61.3, 59.2, 5536}, + {59.3, 58.8, 5536}, + {60.1, 58.1, 5536}, + {59, 57.6, 5536}, + {61.3, 57, 5536}, + {56.8, 56.1, 5536}, + {52.9, 54.7, 5536}, + {55, 54.5, 5536}, + {51.4, 53.5, 5536}, + {50.2, 53.1, 5536}, + {58.3, 52.2, 5536}, + {55, 51.6, 5536}, + {49.5, 51.5, 5536}, + {50.9, 51.5, 5536}, + {57.1, 50.4, 5536}, + {54.5, 48.5, 5536}, + {56.1, 48.4, 5536}, + }, + [61669] = { + {56, 60, 5536}, + {56.3, 59.1, 5536}, + {61.1, 58.3, 5536}, + {55.6, 55.7, 5536}, + {53.7, 53.8, 5536}, + {51.8, 51.9, 5536}, + {53.7, 50.5, 5536}, + }, + [61670] = { + {53.2, 29.3, 5536}, + }, + [61672] = { + {56.7, 40.8, 5536}, + {54.1, 37.8, 5536}, + {53.6, 35.6, 5536}, + {62.6, 35.5, 5536}, + {53.5, 32.8, 5536}, + {61.8, 32.2, 5536}, + {55.6, 31.8, 5536}, + {55.3, 30, 5536}, + {54.1, 29.8, 5536}, + }, + [61673] = { + {52.5, 36.4, 5536}, + {59.5, 36, 5536}, + {53.3, 35.1, 5536}, + {51.8, 34.1, 5536}, + {53.2, 32.5, 5536}, + {55.6, 32.3, 5536}, + {55.9, 29.7, 5536}, + {52.9, 29.6, 5536}, + {56.3, 29.3, 5536}, + {53.1, 29.3, 5536}, + }, + [61674] = { + {62.7, 43, 5536}, + {57.5, 38.4, 5536}, + {64.8, 35, 5536}, + {53.7, 34.8, 5536}, + {55.2, 33.9, 5536}, + {55.2, 33.6, 5536}, + {52.6, 32.8, 5536}, + {54.8, 32.3, 5536}, + {53.5, 32.1, 5536}, + {55.9, 29.3, 5536}, + {54.2, 28.7, 5536}, + {52.7, 28.5, 5536}, + }, + [61675] = { + {59.6, 65.9, 5536}, + {64.1, 65.3, 5536}, + {63.3, 64.8, 5536}, + {61.8, 64.4, 5536}, + {61.8, 62.5, 5536}, + {60.3, 61.7, 5536}, + {63.8, 61.4, 5536}, + {61.2, 60.9, 5536}, + {61.7, 59.7, 5536}, + {58.5, 59.5, 5536}, + {62, 59.5, 5536}, + {60.7, 59.4, 5536}, + {62.7, 59.3, 5536}, + {61.9, 59, 5536}, + {61.8, 58.7, 5536}, + {60.6, 58.6, 5536}, + {62.9, 57.7, 5536}, + {59.5, 56.9, 5536}, + {58.7, 56.2, 5536}, + {63.8, 56, 5536}, + {60.6, 53.4, 5536}, + }, + [61677] = { + {59.9, 64.1, 5536}, + {59, 62, 5536}, + {59.2, 60.9, 5536}, + {60.4, 60.4, 5536}, + {61.3, 59.5, 5536}, + {63.8, 58.2, 5536}, + {62.3, 54, 5536}, + {44, 45.8, 5536}, + {43.6, 45.1, 5536}, + {55.2, 36.7, 5536}, + {55.4, 32.5, 5536}, + {52.8, 31.2, 5536}, + {53.3, 29.5, 5536}, + {55.1, 28.3, 5536}, + }, + [61678] = { + {96.2, 71.3, 14}, + {97.6, 71.1, 14}, + {99.8, 70.4, 14}, + {95.4, 69.7, 14}, + {98.7, 69.1, 14}, + {95.3, 68.8, 14}, + {99.2, 68.3, 14}, + {97.4, 68, 14}, + {98.3, 67.8, 14}, + {96.2, 67.7, 14}, + {95.4, 67.6, 14}, + {96.2, 66.9, 14}, + {97.1, 66.9, 14}, + {98.2, 66.8, 14}, + {99.4, 66.6, 14}, + {97.5, 66.1, 14}, + {94.8, 66, 14}, + {95, 65.7, 14}, + {96.6, 65.6, 14}, + {96.3, 64.8, 14}, + {95.5, 64.5, 14}, + {95, 63.8, 14}, + {91.2, 63.2, 14}, + {92.7, 62.8, 14}, + {94.3, 62.7, 14}, + {96.4, 62.4, 14}, + {91.2, 61.6, 14}, + {94.8, 61.4, 14}, + {92.6, 61.2, 14}, + {95.6, 60.8, 14}, + {96.3, 59.9, 14}, + {95.1, 58.1, 14}, + {95.9, 56.7, 14}, + {94.3, 55.6, 14}, + {93.5, 55.4, 14}, + {97.3, 53.3, 14}, + {96.1, 53.2, 14}, + {93.3, 53, 14}, + {31.4, 90.2, 5536}, + {34.3, 90, 5536}, + {39.2, 88.3, 5536}, + {40.6, 88.1, 5536}, + {29.5, 87, 5536}, + {41.7, 86.8, 5536}, + {36.7, 85.7, 5536}, + {29.5, 85, 5536}, + {37.7, 84, 5536}, + {33.9, 83.3, 5536}, + {39.9, 83, 5536}, + {35.8, 82.8, 5536}, + {31.4, 82.7, 5536}, + {29.7, 82.4, 5536}, + {31.4, 81.1, 5536}, + {33.3, 81.1, 5536}, + {35.6, 80.9, 5536}, + {38.3, 80.4, 5536}, + {40.4, 80.3, 5536}, + {34, 79.3, 5536}, + {28.4, 79.2, 5536}, + {28.7, 78.4, 5536}, + {32.1, 78.3, 5536}, + {31.5, 76.6, 5536}, + {29.9, 76, 5536}, + {28.8, 74.4, 5536}, + {20.6, 73.2, 5536}, + {23.9, 72.4, 5536}, + {27.3, 72.1, 5536}, + {31.7, 71.6, 5536}, + {20.8, 69.8, 5536}, + {28.4, 69.5, 5536}, + {23.7, 69, 5536}, + {30.1, 68.1, 5536}, + {31.6, 66.1, 5536}, + {28.9, 62.5, 5536}, + {30.7, 59.3, 5536}, + {27.4, 57.1, 5536}, + {25.6, 56.7, 5536}, + {33.6, 52.2, 5536}, + {31.2, 52, 5536}, + {25.2, 51.5, 5536}, + }, + [61679] = { + {95.6, 72.2, 14}, + {93.8, 71.7, 14}, + {97.3, 71.7, 14}, + {96.5, 71.6, 14}, + {94.4, 71.3, 14}, + {95.7, 71.3, 14}, + {97.5, 70.8, 14}, + {96.9, 70.7, 14}, + {96.8, 70.6, 14}, + {94.7, 70.5, 14}, + {98.3, 70.4, 14}, + {99.3, 70.4, 14}, + {96.8, 70.4, 14}, + {93.1, 70.3, 14}, + {93.8, 70.2, 14}, + {95.8, 70.2, 14}, + {97.9, 70, 14}, + {93.4, 69.7, 14}, + {96.4, 69.6, 14}, + {93.7, 69.5, 14}, + {97.4, 69.5, 14}, + {94.7, 69.5, 14}, + {92, 68.8, 14}, + {92.8, 68.8, 14}, + {93.9, 68.7, 14}, + {91.6, 67.8, 14}, + {93.4, 67.7, 14}, + {92.2, 67.1, 14}, + {30, 92.3, 5536}, + {26.3, 91.3, 5536}, + {33.8, 91.1, 5536}, + {31.9, 91, 5536}, + {27.5, 90.4, 5536}, + {30.2, 90.3, 5536}, + {34.1, 89.4, 5536}, + {32.8, 89, 5536}, + {32.6, 88.8, 5536}, + {28.1, 88.6, 5536}, + {35.8, 88.5, 5536}, + {38, 88.5, 5536}, + {32.6, 88.5, 5536}, + {24.8, 88.1, 5536}, + {26.1, 88.1, 5536}, + {30.4, 88.1, 5536}, + {35, 87.7, 5536}, + {25.3, 86.9, 5536}, + {31.8, 86.8, 5536}, + {26, 86.6, 5536}, + {34, 86.5, 5536}, + {28.2, 86.4, 5536}, + {40.3, 85.7, 5536}, + {22.4, 85.1, 5536}, + {24.1, 85, 5536}, + {26.5, 84.9, 5536}, + {21.6, 83, 5536}, + {25.3, 82.7, 5536}, + {22.9, 81.5, 5536}, + }, + [61680] = { + {98.8, 37.9, 14}, + {54.6, 91.8, 5536}, + {57.7, 90.9, 5536}, + {50.6, 90.1, 5536}, + {47.5, 87.5, 5536}, + {44.9, 82.9, 5536}, + {49.8, 82.6, 5536}, + {53.1, 81.2, 5536}, + {46, 80, 5536}, + {56.3, 79.5, 5536}, + {63.9, 75.7, 5536}, + {61, 73.4, 5536}, + {63.1, 69.3, 5536}, + {60.9, 69.1, 5536}, + {67.1, 68.9, 5536}, + {71.2, 65.9, 5536}, + {66.5, 65.2, 5536}, + {72.5, 65, 5536}, + {70, 62.9, 5536}, + {70.4, 57.4, 5536}, + {68.4, 56, 5536}, + {77.2, 54, 5536}, + {72.1, 51.5, 5536}, + {75.8, 48.1, 5536}, + {78.2, 46.5, 5536}, + {69, 46.1, 5536}, + {71.9, 43.1, 5536}, + {79.1, 41.7, 5536}, + {69.5, 40.9, 5536}, + {72.8, 39.9, 5536}, + {78.9, 37.5, 5536}, + {73.5, 35.6, 5536}, + {71.3, 34.5, 5536}, + {76.1, 33.9, 5536}, + {69.2, 33.6, 5536}, + {71.4, 31.4, 5536}, + {78.5, 30.5, 5536}, + {46.6, 29, 5536}, + {73.9, 26.6, 5536}, + {78, 25.5, 5536}, + {41.6, 25.1, 5536}, + {45.5, 24, 5536}, + {69, 23.6, 5536}, + {43.2, 22.6, 5536}, + {48.1, 21.8, 5536}, + {69.8, 20.9, 5536}, + {76.5, 20.8, 5536}, + {37, 19.6, 5536}, + {63, 19.5, 5536}, + {48, 19.3, 5536}, + {53.1, 17.7, 5536}, + {67.5, 17.4, 5536}, + {70.6, 16.8, 5536}, + {55.1, 16.8, 5536}, + {44.2, 16.8, 5536}, + {62.2, 16.2, 5536}, + {46, 16, 5536}, + {51.7, 15.3, 5536}, + {48.8, 15.1, 5536}, + {45.4, 13.4, 5536}, + {55.3, 13.1, 5536}, + {40.8, 12.8, 5536}, + {69.8, 12, 5536}, + {44.2, 11.7, 5536}, + {60.4, 11.6, 5536}, + {46.6, 10.5, 5536}, + {63, 10.4, 5536}, + {51.8, 10.3, 5536}, + {49.5, 8.7, 5536}, + {59, 7.9, 5536}, + }, + [61681] = { + {95.8, 67.9, 14}, + {96.8, 67.6, 14}, + {99.7, 67.5, 14}, + {98.3, 67.3, 14}, + {95.2, 66.9, 14}, + {97.3, 66.8, 14}, + {97, 64.7, 14}, + {95.4, 63.5, 14}, + {94.6, 61.8, 14}, + {95.2, 60.1, 14}, + {93.5, 60, 14}, + {94.2, 59.3, 14}, + {94.9, 58.7, 14}, + {93.7, 58.5, 14}, + {95.5, 57.5, 14}, + {94, 56.9, 14}, + {96.3, 56.3, 14}, + {95.2, 55.2, 14}, + {94.2, 55, 14}, + {94, 53.8, 14}, + {94.6, 52.5, 14}, + {96.7, 51.8, 14}, + {98.2, 48.8, 14}, + {95.7, 48.7, 14}, + {99.9, 46.1, 14}, + {96.5, 45.5, 14}, + {98.7, 45.4, 14}, + {98, 44.1, 14}, + {97.4, 42.2, 14}, + {97.2, 40.3, 14}, + {99.7, 37.8, 14}, + {98.2, 36.4, 14}, + {99.6, 35.3, 14}, + {30.6, 83.1, 5536}, + {32.7, 82.6, 5536}, + {38.8, 82.2, 5536}, + {35.9, 82, 5536}, + {29.2, 81, 5536}, + {33.6, 80.8, 5536}, + {39.7, 79.5, 5536}, + {33, 76.4, 5536}, + {29.7, 73.9, 5536}, + {28, 70.2, 5536}, + {29.2, 66.7, 5536}, + {25.7, 66.3, 5536}, + {27.1, 65, 5536}, + {28.6, 63.7, 5536}, + {26, 63.2, 5536}, + {30, 61.2, 5536}, + {26.6, 59.8, 5536}, + {31.5, 58.6, 5536}, + {29.3, 56.2, 5536}, + {27.1, 55.9, 5536}, + {26.6, 53.2, 5536}, + {27.9, 50.5, 5536}, + {32.5, 49, 5536}, + {35.6, 42.8, 5536}, + {30.3, 42.4, 5536}, + {39.2, 37, 5536}, + {41.8, 35.9, 5536}, + {31.9, 35.6, 5536}, + {36.7, 35.5, 5536}, + {35.2, 32.8, 5536}, + {40.6, 32, 5536}, + {41.6, 31.5, 5536}, + {33.9, 28.7, 5536}, + {33.4, 24.6, 5536}, + {40, 23.4, 5536}, + {38.8, 19.5, 5536}, + {35.6, 16.4, 5536}, + {38.7, 14.1, 5536}, + }, + [61682] = { + {96.6, 52.8, 14}, + {98, 51.1, 14}, + {96.8, 50.5, 14}, + {96.7, 49.9, 14}, + {97.7, 49.6, 14}, + {94.9, 48.8, 14}, + {98.5, 48.2, 14}, + {96.2, 47.6, 14}, + {96.6, 47.5, 14}, + {98, 47.3, 14}, + {99.4, 47.3, 14}, + {97.8, 47.3, 14}, + {98.4, 46.5, 14}, + {99.5, 46.3, 14}, + {98.8, 46.3, 14}, + {99.1, 46.1, 14}, + {98.1, 45.9, 14}, + {99.2, 45.6, 14}, + {98.6, 45.4, 14}, + {98.9, 44.8, 14}, + {97.9, 43.8, 14}, + {98.7, 42.8, 14}, + {99.8, 42.7, 14}, + {99.1, 40.8, 14}, + {97.3, 38.6, 14}, + {62.2, 94, 5536}, + {56.2, 91.9, 5536}, + {52.2, 90.2, 5536}, + {67.7, 89.3, 5536}, + {46, 88.9, 5536}, + {59.2, 86.4, 5536}, + {43.5, 86.2, 5536}, + {52.7, 85.4, 5536}, + {69, 84.9, 5536}, + {52.8, 84.6, 5536}, + {50.8, 84, 5536}, + {45.6, 83.7, 5536}, + {54.4, 83.7, 5536}, + {51.7, 83.5, 5536}, + {55.6, 83.4, 5536}, + {41.3, 83.4, 5536}, + {58.7, 83.2, 5536}, + {57.3, 83.2, 5536}, + {48.3, 82.6, 5536}, + {57.1, 82.2, 5536}, + {50.8, 82.1, 5536}, + {40.6, 79, 5536}, + {54.4, 78.9, 5536}, + {54.9, 78, 5536}, + {73.4, 68.8, 5536}, + {75.9, 67.2, 5536}, + {74.7, 65.9, 5536}, + {76.1, 65.3, 5536}, + {76.1, 64, 5536}, + {74.9, 62.8, 5536}, + {72.6, 62.3, 5536}, + {76.3, 59.6, 5536}, + {32.2, 51.2, 5536}, + {35.2, 47.5, 5536}, + {32.6, 46.4, 5536}, + {32.3, 45.1, 5536}, + {34.5, 44.3, 5536}, + {28.6, 42.7, 5536}, + {74.1, 42.4, 5536}, + {73.3, 42.2, 5536}, + {36.3, 41.5, 5536}, + {31.3, 40.1, 5536}, + {32.2, 39.9, 5536}, + {35.1, 39.6, 5536}, + {38.1, 39.5, 5536}, + {34.8, 39.4, 5536}, + {36.1, 37.9, 5536}, + {38.3, 37.5, 5536}, + {36.8, 37.3, 5536}, + {37.6, 37, 5536}, + {35.4, 36.6, 5536}, + {37.8, 36, 5536}, + {36.4, 35.6, 5536}, + {40.5, 35.3, 5536}, + {37.1, 34.3, 5536}, + {35.1, 32.1, 5536}, + {43.4, 31, 5536}, + {36.8, 30.1, 5536}, + {39.1, 29.8, 5536}, + {46.4, 26.3, 5536}, + {37.5, 25.8, 5536}, + {33.7, 21, 5536}, + {48.5, 20.5, 5536}, + {52.2, 20.3, 5536}, + {62.2, 20.2, 5536}, + {41.9, 19.9, 5536}, + {58.8, 18.7, 5536}, + {57.4, 18.4, 5536}, + {54.9, 18.1, 5536}, + {39.8, 18, 5536}, + {56.2, 17.7, 5536}, + {59.9, 17.2, 5536}, + {57.9, 17.1, 5536}, + {53, 16.8, 5536}, + {57.6, 13, 5536}, + {66.4, 9.1, 5536}, + }, + [61683] = { + {98.9, 47.9, 14}, + {98.2, 46.1, 14}, + {99.4, 46.1, 14}, + {99.5, 45.6, 14}, + {99.4, 38.9, 14}, + {64.8, 91, 5536}, + {48.8, 89.4, 5536}, + {54.6, 83.6, 5536}, + {57.5, 83.1, 5536}, + {52.9, 83, 5536}, + {53.5, 81.5, 5536}, + {55, 81.3, 5536}, + {70.9, 80.8, 5536}, + {43.3, 80.8, 5536}, + {48.9, 80.4, 5536}, + {57.9, 77.5, 5536}, + {74.9, 62.6, 5536}, + {76.4, 62.3, 5536}, + {74, 60.5, 5536}, + {37.2, 40.7, 5536}, + {35.6, 37, 5536}, + {38.1, 37, 5536}, + {38.4, 36, 5536}, + {43.4, 26.9, 5536}, + {38.3, 21.7, 5536}, + {57.3, 20, 5536}, + {51.2, 13.3, 5536}, + }, + [61684] = { + {96.5, 46.2, 14}, + {56.9, 80.6, 5536}, + {32.1, 37.1, 5536}, + {39.6, 34.8, 5536}, + {41.6, 31.9, 5536}, + }, + [61685] = { + {99, 47.9, 14}, + {98.4, 46.9, 14}, + {99, 46.8, 14}, + {95.9, 46.5, 14}, + {99.3, 46.4, 14}, + {98.3, 45.9, 14}, + {97.2, 44.2, 14}, + {99.8, 40.6, 14}, + {98.8, 35.7, 14}, + {59.5, 92.2, 5536}, + {52.9, 85.2, 5536}, + {57.4, 84.5, 5536}, + {50.6, 84.3, 5536}, + {52.4, 80.8, 5536}, + {75.8, 65.2, 5536}, + {74, 62.4, 5536}, + {74.8, 58.9, 5536}, + {73.9, 41, 5536}, + {37.4, 40.9, 5536}, + {39.9, 38.7, 5536}, + {36.1, 38.7, 5536}, + {37.4, 38.5, 5536}, + {30.8, 37.8, 5536}, + {38, 37.7, 5536}, + {35.9, 36.7, 5536}, + {78.4, 33.5, 5536}, + {33.5, 32.9, 5536}, + {39.5, 32.5, 5536}, + {41.7, 29.8, 5536}, + {39, 25.4, 5536}, + {46, 20.7, 5536}, + {50.4, 19.3, 5536}, + {56.4, 19, 5536}, + {60, 18.8, 5536}, + {36.9, 15, 5536}, + {59.7, 14.5, 5536}, + {52.7, 14, 5536}, + {43.2, 13.2, 5536}, + {47.7, 12.2, 5536}, + {55.9, 9.4, 5536}, + }, + [61686] = { + {99.4, 65.2, 14}, + {98, 65.2, 14}, + {98.4, 63.1, 14}, + {96.8, 63.1, 14}, + {99.1, 63, 14}, + {100, 62.8, 14}, + {99, 62.2, 14}, + {98, 62.2, 14}, + {99.9, 61.5, 14}, + {98.1, 61, 14}, + {96.5, 59.3, 14}, + {97.3, 59, 14}, + {96.8, 57.5, 14}, + {97.8, 56, 14}, + {96.9, 55.6, 14}, + {99.9, 55.5, 14}, + {99.7, 54.8, 14}, + {38.2, 77.5, 5536}, + {35.2, 77.3, 5536}, + {36.1, 73.1, 5536}, + {32.7, 73, 5536}, + {37.6, 72.8, 5536}, + {39.5, 72.3, 5536}, + {41.8, 72.2, 5536}, + {37.2, 71, 5536}, + {35.1, 71, 5536}, + {40.7, 70.3, 5536}, + {39.2, 69.6, 5536}, + {35.5, 68.6, 5536}, + {31.9, 65, 5536}, + {33.7, 64.3, 5536}, + {32.6, 61.2, 5536}, + {41.5, 58.6, 5536}, + {34.8, 57.8, 5536}, + {40.2, 57.2, 5536}, + {32.9, 57, 5536}, + {39.2, 56.9, 5536}, + {38.8, 55.3, 5536}, + }, + [61687] = { + {97.6, 63.5, 14}, + {99.4, 61.2, 14}, + {99.7, 59.8, 14}, + {34.3, 73.8, 5536}, + {38.1, 69, 5536}, + {41.8, 67.5, 5536}, + {38.9, 66, 5536}, + {42.7, 62.3, 5536}, + {56.4, 61.8, 5536}, + {55.7, 59.8, 5536}, + {43.3, 59.6, 5536}, + {59.9, 59.5, 5536}, + {55.9, 59.2, 5536}, + {59.8, 58.5, 5536}, + {56.9, 58.3, 5536}, + {58.1, 58.2, 5536}, + {48.2, 57.9, 5536}, + {60.1, 57.4, 5536}, + {56.5, 56.7, 5536}, + {44.5, 55.9, 5536}, + {46.4, 55.3, 5536}, + {54, 52.7, 5536}, + {50.2, 52.2, 5536}, + {52.9, 52, 5536}, + {57.9, 50.5, 5536}, + {46.4, 50.4, 5536}, + {54.5, 50.3, 5536}, + {52.7, 50.2, 5536}, + {40.3, 50.1, 5536}, + {51.3, 49.7, 5536}, + {55.5, 49, 5536}, + {42.1, 45.5, 5536}, + {55.1, 45.2, 5536}, + {40.8, 41.7, 5536}, + {50.8, 36.6, 5536}, + }, + [61694] = { + {17.4, 11.9, 139}, + {14.5, 10.5, 139}, + {13.8, 97, 2040}, + {39.4, 74.4, 5225}, + {39.4, 73.4, 5225}, + {60.1, 70.8, 5225}, + {38.9, 69.2, 5225}, + {56.5, 69, 5225}, + {58.3, 68.8, 5225}, + {58.9, 67.7, 5225}, + {40.8, 67.5, 5225}, + {39, 67, 5225}, + {57.7, 66.3, 5225}, + {41.4, 66.2, 5225}, + {47.7, 65.2, 5225}, + {41.4, 65, 5225}, + {53.5, 64.7, 5225}, + {47, 64.5, 5225}, + {51.1, 64.1, 5225}, + {47.4, 62.3, 5225}, + {47, 62.3, 5225}, + {43.7, 62.1, 5225}, + {45.9, 61.7, 5225}, + {51.1, 61.5, 5225}, + {52.1, 60.8, 5225}, + {48.3, 59.3, 5225}, + {49.4, 58.8, 5225}, + {51.6, 57.6, 5225}, + {46.9, 57.1, 5225}, + {49.9, 56, 5225}, + {48.3, 55.4, 5225}, + {47.4, 55.4, 5225}, + {45.5, 54.2, 5225}, + {51, 53.6, 5225}, + {48.8, 52.9, 5225}, + {53.8, 51.6, 5225}, + {44.3, 48.1, 5225}, + }, + [61695] = { + {16.9, 12.5, 139}, + {18.8, 11.4, 139}, + {20.5, 9.7, 139}, + {19.9, 7.5, 139}, + {16.8, 94.9, 2040}, + {31.5, 94.4, 2040}, + {28.7, 94, 2040}, + {20.8, 92.3, 2040}, + {12, 91.8, 2040}, + {28.9, 91.5, 2040}, + {39.9, 90.7, 2040}, + {23.6, 89.9, 2040}, + {14.4, 89, 2040}, + {22.9, 88.7, 2040}, + {28, 85.6, 2040}, + {28.8, 82.4, 2040}, + {39.2, 71.6, 5225}, + {59.5, 71.6, 5225}, + {61.9, 70.2, 5225}, + {54.9, 69.8, 5225}, + {53.3, 68.6, 5225}, + {64, 68.1, 5225}, + {51.7, 68, 5225}, + {56, 66.4, 5225}, + {51.5, 66.2, 5225}, + {60.4, 65.9, 5225}, + {48.2, 65.8, 5225}, + {49.2, 65.8, 5225}, + {63.3, 65.3, 5225}, + {42.2, 64.8, 5225}, + {57.6, 64.7, 5225}, + {45.2, 64, 5225}, + {46, 63.7, 5225}, + {49.6, 62.8, 5225}, + {51.4, 62.5, 5225}, + {45.5, 61, 5225}, + {50.1, 59.7, 5225}, + {43.6, 59.2, 5225}, + {47.4, 57.1, 5225}, + {52.3, 56, 5225}, + {43.8, 55.6, 5225}, + {46.8, 55.2, 5225}, + {48.6, 54.9, 5225}, + {51.7, 53.3, 5225}, + {45.4, 53.1, 5225}, + {43.3, 50, 5225}, + {50.6, 50, 5225}, + {45.8, 47.1, 5225}, + {52.8, 46.9, 5225}, + {51.5, 46.7, 5225}, + {47.7, 45.9, 5225}, + {43.5, 45.6, 5225}, + {51.5, 45.5, 5225}, + {56.8, 45.1, 5225}, + {49, 44.8, 5225}, + {44.6, 44.3, 5225}, + {48.7, 44.2, 5225}, + {51.1, 42.7, 5225}, + {51.5, 41.2, 5225}, + }, + [61699] = { + {20.6, 8.9, 139}, + {64.2, 67, 5225}, + }, + [61700] = { + {35.2, 98.1, 2040}, + {24.7, 94.6, 2040}, + {21.6, 94.5, 2040}, + {0.3, 94.3, 2040}, + {27.5, 93.5, 2040}, + {17.8, 92.3, 2040}, + {5, 90.3, 2040}, + {37.3, 88.7, 2040}, + {39.6, 87.8, 2040}, + {26.1, 87.2, 2040}, + {9.7, 85.8, 2040}, + {26.7, 60.3, 5225}, + {30.1, 55.5, 5225}, + {33, 52.4, 5225}, + {29.3, 52.2, 5225}, + {53.4, 52.1, 5225}, + {34.1, 51.1, 5225}, + {37, 50.3, 5225}, + {53.2, 50.2, 5225}, + {54.5, 48.6, 5225}, + {36.8, 48.5, 5225}, + {30.1, 48.5, 5225}, + {49.5, 47, 5225}, + {48.1, 47, 5225}, + {37.9, 46.8, 5225}, + {33.8, 46.6, 5225}, + {50.9, 46.5, 5225}, + {46.2, 45.9, 5225}, + {40.1, 44.9, 5225}, + {55.6, 44.2, 5225}, + {56.6, 43.8, 5225}, + {50.2, 43.5, 5225}, + {42.4, 42.8, 5225}, + }, + [61701] = { + {32.7, 99.8, 2040}, + {21.6, 98.3, 2040}, + {39.2, 97.1, 2040}, + {38, 96.7, 2040}, + {6.8, 95.5, 2040}, + {34.5, 95.3, 2040}, + {23.3, 95.2, 2040}, + {30.3, 94.7, 2040}, + {24.6, 94.1, 2040}, + {39.8, 93.5, 2040}, + {18.2, 92.8, 2040}, + {29.7, 92.5, 2040}, + {36.5, 91.2, 2040}, + {24, 91.1, 2040}, + {31.3, 91.1, 2040}, + {33, 90.8, 2040}, + {41.4, 90.6, 2040}, + {36.7, 89.9, 2040}, + {31.5, 89.4, 2040}, + {38.3, 89.3, 2040}, + {28, 88.3, 2040}, + {24.8, 87.6, 2040}, + {32.6, 86.9, 2040}, + {37.2, 86.3, 2040}, + {28.9, 85.9, 2040}, + {34.1, 85, 2040}, + {27.5, 82.7, 2040}, + {49.5, 55.3, 5225}, + {54.4, 50.7, 5225}, + {33.5, 50.1, 5225}, + {55.4, 49.8, 5225}, + {47.3, 49.7, 5225}, + {53.3, 49.5, 5225}, + {48, 48.7, 5225}, + {56.5, 48.2, 5225}, + {55.9, 48, 5225}, + {41, 47.4, 5225}, + {54.2, 47.3, 5225}, + {48.9, 47.3, 5225}, + {52.2, 47, 5225}, + {49.5, 46.7, 5225}, + {36.9, 46.6, 5225}, + {56.7, 46.5, 5225}, + {46.4, 46.1, 5225}, + {51.9, 46, 5225}, + {55.2, 45.4, 5225}, + {49.2, 45.3, 5225}, + {52.7, 45.3, 5225}, + {53.5, 45.2, 5225}, + {57.5, 45.1, 5225}, + {55.2, 44.8, 5225}, + {52.8, 44.5, 5225}, + {56, 44.5, 5225}, + {51.1, 44, 5225}, + {49.6, 43.7, 5225}, + {53.3, 43.4, 5225}, + {55.5, 43, 5225}, + {51.5, 42.9, 5225}, + {54, 42.4, 5225}, + {50.9, 41.4, 5225}, + }, + [61702] = { + {11.7, 83, 2040}, + {2.1, 82.7, 2040}, + {1.9, 82.6, 2040}, + {6.3, 79.4, 2040}, + {6.1, 66.3, 2040}, + {6.6, 63.5, 2040}, + {7.4, 59.1, 2040}, + {28.7, 50.4, 5225}, + {28.9, 48.3, 5225}, + {30.1, 46.5, 5225}, + {32.4, 45.2, 5225}, + {34.5, 43.3, 5225}, + {36.8, 42.3, 5225}, + {32.5, 41.6, 5225}, + {43.3, 41.5, 5225}, + {38.8, 41.3, 5225}, + {38.7, 41.3, 5225}, + {33.9, 40.6, 5225}, + {40.8, 39.8, 5225}, + {34.6, 39.5, 5225}, + {36.3, 37.4, 5225}, + {36.6, 36.9, 5225}, + {40.7, 33.6, 5225}, + {40.9, 32.2, 5225}, + {41.3, 30.2, 5225}, + }, + [61703] = { + {18.6, 87.3, 2040}, + {14.8, 85.4, 2040}, + {1, 84, 2040}, + {5.1, 82, 2040}, + {3.4, 82, 2040}, + {10.8, 81.2, 2040}, + {7.9, 81.2, 2040}, + {28.6, 48.6, 5225}, + {30, 47.7, 5225}, + {31.1, 46.4, 5225}, + {32.6, 44.7, 5225}, + {33.2, 44.6, 5225}, + {32.8, 44.3, 5225}, + {46.6, 43.5, 5225}, + {35.2, 42.9, 5225}, + {34.2, 42.8, 5225}, + {36, 42.8, 5225}, + {44.8, 42.6, 5225}, + {37.6, 42.1, 5225}, + {37.1, 42, 5225}, + {38.2, 42, 5225}, + {33.5, 41.7, 5225}, + {40.2, 41, 5225}, + {39.4, 41, 5225}, + {42.9, 40.6, 5225}, + {41.5, 40.6, 5225}, + {35.7, 38.9, 5225}, + }, + [61704] = { + {17.7, 86.9, 2040}, + {13.5, 84.2, 2040}, + {11.6, 83.2, 2040}, + {5.2, 82.4, 2040}, + {0.9, 82, 2040}, + {9.1, 80, 2040}, + {2.1, 68.7, 2040}, + {6.4, 64.9, 2040}, + {7.3, 60.5, 2040}, + {28, 50.2, 5225}, + {28.1, 48.9, 5225}, + {30.7, 47.2, 5225}, + {29.1, 46.7, 5225}, + {31, 45.8, 5225}, + {31.6, 45.8, 5225}, + {31.9, 45.1, 5225}, + {33.7, 44.2, 5225}, + {46.2, 43.3, 5225}, + {35.2, 43.3, 5225}, + {32.7, 43.2, 5225}, + {34.3, 43.2, 5225}, + {36.1, 42.3, 5225}, + {44.2, 42, 5225}, + {43.3, 41.6, 5225}, + {32.9, 41.4, 5225}, + {32.1, 41.4, 5225}, + {31.5, 41.3, 5225}, + {40.3, 41.2, 5225}, + {38.2, 41, 5225}, + {35.6, 40.5, 5225}, + {42.1, 40.1, 5225}, + {30.5, 39.9, 5225}, + {30.2, 39.7, 5225}, + {37.2, 39.5, 5225}, + {34.9, 39.2, 5225}, + {29.7, 39, 5225}, + {28.7, 38.1, 5225}, + {27.9, 37.9, 5225}, + {35.8, 37.2, 5225}, + {27.1, 37, 5225}, + {37.1, 36.2, 5225}, + {38.8, 34.7, 5225}, + {40.8, 32.9, 5225}, + {41.2, 30.8, 5225}, + }, + [61705] = { + {33.5, 67.2, 5225}, + {35.2, 66.1, 5225}, + {33.5, 65.7, 5225}, + {36.4, 65.6, 5225}, + {38.2, 63.8, 5225}, + {38.5, 61.2, 5225}, + {32.9, 60.8, 5225}, + {35.7, 60.2, 5225}, + {38.4, 59.6, 5225}, + {33.1, 57.4, 5225}, + }, + [61706] = { + {34.3, 65.6, 5225}, + {36.1, 65.2, 5225}, + {34.1, 63.7, 5225}, + {36.2, 63.6, 5225}, + {37.2, 63.5, 5225}, + {35.6, 62.5, 5225}, + {36.8, 62.2, 5225}, + {39, 62.1, 5225}, + {35.1, 61.7, 5225}, + {34.3, 61.6, 5225}, + {38.2, 60.7, 5225}, + {36.9, 60.6, 5225}, + {34, 60.5, 5225}, + {38.7, 60.4, 5225}, + {34.7, 60.3, 5225}, + {36.6, 59.8, 5225}, + {34.3, 59.7, 5225}, + {33.3, 58.5, 5225}, + {37.8, 58.4, 5225}, + {32.6, 55.1, 5225}, + {31.4, 54.6, 5225}, + {31.9, 54.1, 5225}, + }, + [61707] = { + {34.6, 66.6, 5225}, + {34, 66.4, 5225}, + {35.9, 65.7, 5225}, + {33.7, 64.6, 5225}, + {37.4, 63.8, 5225}, + {39.1, 63.1, 5225}, + {38.7, 63.1, 5225}, + {38.1, 62.8, 5225}, + {38.3, 62.2, 5225}, + {33.9, 62.1, 5225}, + {32.9, 61.4, 5225}, + {37.5, 61.4, 5225}, + }, + [61708] = { + {39, 62.3, 5225}, + }, + [61709] = { + {23.5, 58.9, 5225}, + {25.9, 55.7, 5225}, + {23.1, 54.3, 5225}, + {27.7, 53.5, 5225}, + {22.2, 52.6, 5225}, + {19.1, 52.5, 5225}, + {28.3, 51.4, 5225}, + {21.7, 50.5, 5225}, + {24.9, 50.1, 5225}, + {16, 50.1, 5225}, + {20.3, 49.8, 5225}, + {17.8, 49.2, 5225}, + {19.2, 48.8, 5225}, + {16.3, 48.7, 5225}, + {17.8, 47.6, 5225}, + {29.2, 46.2, 5225}, + {17.2, 45.9, 5225}, + {28, 45.6, 5225}, + {22.6, 45.1, 5225}, + {26.2, 43.7, 5225}, + {18.8, 43.7, 5225}, + {16.6, 42.8, 5225}, + {19.2, 41.5, 5225}, + {16.9, 41.1, 5225}, + {16, 40.2, 5225}, + {21.6, 39.5, 5225}, + {14.7, 38.8, 5225}, + {19.8, 38.2, 5225}, + }, + [61710] = { + {25.3, 57.5, 5225}, + {24.2, 57, 5225}, + {26.5, 55.3, 5225}, + {26, 55.1, 5225}, + {25.6, 54.5, 5225}, + {20.4, 54.1, 5225}, + {26.5, 53.4, 5225}, + {26.9, 53.4, 5225}, + {14.2, 52.6, 5225}, + {18.2, 51.8, 5225}, + {13.4, 50.4, 5225}, + {27.9, 50.4, 5225}, + {16.9, 50.3, 5225}, + {14.3, 50.2, 5225}, + {14.1, 49.9, 5225}, + {29.1, 48.9, 5225}, + {20.2, 48.6, 5225}, + {21.2, 47.9, 5225}, + {16.8, 46.9, 5225}, + {22.8, 46.6, 5225}, + {23.9, 46.4, 5225}, + {17.5, 46.3, 5225}, + {27.4, 46.1, 5225}, + {25.8, 45.3, 5225}, + {21.2, 45.3, 5225}, + {17.3, 44.1, 5225}, + {21.8, 42.5, 5225}, + {14.9, 41.9, 5225}, + {18, 41.8, 5225}, + {14.7, 41, 5225}, + {21.1, 40.5, 5225}, + {19.5, 40.1, 5225}, + }, + [61712] = { + {13.7, 50.8, 5225}, + }, + [61713] = { + {16.8, 42.1, 5225}, + }, + [61714] = { + {3.6, 59.2, 2040}, + {1.2, 57.7, 2040}, + {3.2, 53.1, 2040}, + {5.4, 49.5, 2040}, + {1.3, 47.3, 2040}, + {5.4, 46.2, 2040}, + {2.7, 43.1, 2040}, + {6.4, 43.1, 2040}, + {32.4, 37.4, 5225}, + {32.4, 35.5, 5225}, + {27.5, 35.1, 5225}, + {35.5, 34.1, 5225}, + {28.4, 33.6, 5225}, + {36.7, 33.3, 5225}, + {27.4, 32.9, 5225}, + {30.3, 32.8, 5225}, + {37, 30.8, 5225}, + {26, 30.4, 5225}, + {39.5, 30.2, 5225}, + {38.3, 29.5, 5225}, + {29.1, 29.4, 5225}, + {37.1, 28.4, 5225}, + {34.1, 27.6, 5225}, + {37, 27.6, 5225}, + {39.3, 27.3, 5225}, + {36.5, 27, 5225}, + {30.3, 26.3, 5225}, + {40.3, 25.6, 5225}, + {38.4, 24.6, 5225}, + {37, 24.1, 5225}, + {40.3, 24.1, 5225}, + {27.4, 23.8, 5225}, + {35.3, 23.8, 5225}, + {29, 22.7, 5225}, + {33.1, 22.7, 5225}, + {39.1, 22.6, 5225}, + {40.8, 22.6, 5225}, + }, + [61715] = { + {3, 98.1, 2040}, + {10.6, 93.3, 2040}, + {2.3, 93.1, 2040}, + {3.4, 87.8, 2040}, + {0.1, 60.1, 2040}, + {5.6, 58.1, 2040}, + {4, 48.1, 2040}, + {26, 58.9, 5225}, + {27.9, 58.1, 5225}, + {27.3, 57.3, 5225}, + {28.5, 55.6, 5225}, + {30.5, 54.8, 5225}, + {27.8, 54.3, 5225}, + {29.8, 54.1, 5225}, + {29.7, 52.9, 5225}, + {35.7, 51.4, 5225}, + {30.4, 50.9, 5225}, + {33.7, 50.4, 5225}, + {34.8, 49.7, 5225}, + {30.4, 49.5, 5225}, + {39.2, 48.6, 5225}, + {31.5, 47.9, 5225}, + {42.8, 46.4, 5225}, + {38.9, 46.3, 5225}, + {33.8, 45.9, 5225}, + {39.4, 43.7, 5225}, + {31.9, 39.6, 5225}, + {32.6, 38.7, 5225}, + {33.3, 37.4, 5225}, + {34, 36.6, 5225}, + {30.6, 36.3, 5225}, + {29.3, 35.6, 5225}, + {36, 35, 5225}, + {34.4, 35, 5225}, + {28.8, 34.9, 5225}, + {27.2, 34, 5225}, + {31, 33.2, 5225}, + {36.6, 32.3, 5225}, + {26.1, 32, 5225}, + {26.5, 31.5, 5225}, + {37.8, 30.6, 5225}, + {30.8, 30, 5225}, + {40.4, 29.7, 5225}, + {27.2, 29.6, 5225}, + {28.6, 28.5, 5225}, + {30, 27.5, 5225}, + {36.9, 25.8, 5225}, + {28.6, 25.6, 5225}, + {29.4, 25.4, 5225}, + {39.7, 25, 5225}, + {31.9, 24.2, 5225}, + {34, 22.8, 5225}, + {26.8, 22.5, 5225}, + {33.5, 21.5, 5225}, + {29, 21.1, 5225}, + {31.1, 20.9, 5225}, + }, + [61716] = { + {0.5, 88.8, 2040}, + {6.4, 86.5, 2040}, + {2.2, 85.7, 2040}, + {0.8, 61.7, 2040}, + {4.7, 60.2, 2040}, + {5.3, 53.7, 2040}, + {0.9, 45.7, 2040}, + {3.1, 41.1, 2040}, + {29.4, 58.9, 5225}, + {27.2, 56.2, 5225}, + {32.4, 51.5, 5225}, + {34.8, 51.4, 5225}, + {29.5, 50.8, 5225}, + {31, 50.1, 5225}, + {37.7, 49.2, 5225}, + {31.8, 48.4, 5225}, + {33.7, 47.9, 5225}, + {35.1, 45.5, 5225}, + {38, 44.2, 5225}, + {36.7, 43.5, 5225}, + {40.8, 43.1, 5225}, + {38.8, 42.8, 5225}, + {29.2, 37, 5225}, + {31.1, 34.6, 5225}, + {34.4, 34.1, 5225}, + {33.5, 33.9, 5225}, + {29.3, 32.8, 5225}, + {38.1, 31.4, 5225}, + {32.8, 30.8, 5225}, + {40, 30.7, 5225}, + {26.8, 30.2, 5225}, + {25.5, 29.3, 5225}, + {40.3, 27.6, 5225}, + {32, 27.4, 5225}, + {28.8, 27.4, 5225}, + {35, 27.3, 5225}, + {32, 25.5, 5225}, + {31.3, 24.6, 5225}, + {38.2, 23.8, 5225}, + {32.4, 22.5, 5225}, + {39.2, 21.6, 5225}, + }, + [61717] = { + {0.2, 64.3, 2040}, + {0.7, 56.6, 2040}, + {4.4, 50.6, 2040}, + {0.4, 48.2, 2040}, + {5.1, 41.9, 2040}, + {34.7, 36.7, 5225}, + {32.8, 36.5, 5225}, + {37.5, 33.6, 5225}, + {37.9, 32.7, 5225}, + {27.7, 31.3, 5225}, + {29.9, 30.8, 5225}, + {29.1, 30.6, 5225}, + {38.1, 29, 5225}, + {27.8, 28.4, 5225}, + {39.8, 26.2, 5225}, + {37.9, 25, 5225}, + {36.1, 25, 5225}, + {28.9, 23.9, 5225}, + {31.4, 22.3, 5225}, + {40.2, 22, 5225}, + {28.3, 21.4, 5225}, + }, + [61718] = { + {1.5, 53.3, 2040}, + {1.1, 50.9, 2040}, + {28.9, 36.2, 5225}, + {29.2, 32.8, 5225}, + {31.2, 32.1, 5225}, + {32.9, 31.8, 5225}, + {30.3, 31.8, 5225}, + {31, 31.7, 5225}, + {28.8, 31.7, 5225}, + {31.9, 31.3, 5225}, + {32.3, 29.9, 5225}, + {30.2, 29.9, 5225}, + {29.6, 29.6, 5225}, + {33.3, 29, 5225}, + {32.6, 29, 5225}, + {30.1, 28.9, 5225}, + {30.7, 28.6, 5225}, + {31.4, 27.9, 5225}, + {38.5, 27.4, 5225}, + {38.3, 26.3, 5225}, + {29.8, 24.8, 5225}, + {30.3, 24.3, 5225}, + {29.4, 23.5, 5225}, + {30.5, 23.5, 5225}, + }, + [61719] = { + {30.9, 29.3, 5225}, + }, + [61759] = { + {42.7, 47.5, 5536}, + }, + [61783] = { + {29.3, 80.8, 5225}, + {28.8, 74.9, 5225}, + {29.5, 74, 5225}, + {31.7, 72.4, 5225}, + }, + [61789] = { + {26.5, 58.4, 5225}, + {35.3, 48.2, 5225}, + }, + [61790] = { + {28.1, 21.1, 5225}, + }, + [61792] = { + {92.8, 68.1, 14}, + {24.1, 83.6, 5536}, + }, + [61794] = { + {54.8, 28.8, 5536}, + }, + [61863] = { + {58.6, 37.7, 5536}, + }, + [61895] = { + {6.9, 40.7, 2040}, + {41.1, 21.5, 5225}, + }, + [61951] = { + {63.2, 78.6, 3457}, + }, + [61963] = { + {37.6, 23.9, 5138}, + }, + [61971] = { + {72.1, 49.8, 5136}, + {72.5, 47.6, 5136}, + {71.6, 47.6, 5136}, + {79, 44.8, 5136}, + {77.8, 43.4, 5136}, + {72.1, 40.6, 5136}, + {74.9, 39, 5136}, + {66.8, 38.5, 5136}, + {77, 38.2, 5136}, + {77.9, 38.1, 5136}, + {66.3, 37.9, 5136}, + {73.8, 37.8, 5136}, + {78.3, 36.3, 5136}, + {73.4, 35.1, 5136}, + }, + [62070] = { + {54.4, 65.9, 5601}, + }, + [62537] = { + {42.5, 52.1, 5628}, + {37.6, 48.2, 5628}, + {41.8, 47.9, 5628}, + {42.6, 47.9, 5628}, + {44.6, 47.8, 5628}, + {40.7, 47.7, 5628}, + {33.1, 47.7, 5628}, + {47.9, 47.7, 5628}, + {41.6, 47.7, 5628}, + {43, 47.7, 5628}, + {42.6, 47.5, 5628}, + {37.6, 47.2, 5628}, + {45.9, 46.5, 5628}, + {46.8, 45.9, 5628}, + {46.4, 45.9, 5628}, + {39.3, 44.9, 5628}, + {41.9, 44.1, 5628}, + {42.5, 43.6, 5628}, + {42.7, 43.6, 5628}, + {48, 40.9, 5628}, + {47.7, 40.7, 5628}, + {48, 40.5, 5628}, + {45.2, 33.2, 5628}, + {45, 32.8, 5628}, + {44.9, 32.6, 5628}, + }, + [62555] = { + {81.9, 34.9, 5581}, + {76.7, 33.9, 5581}, + }, + [62645] = { + {29.5, 91.1, 5628}, + {34.4, 90.6, 5628}, + {10, 77.3, 5628}, + {9.8, 76.2, 5628}, + {8.5, 70.7, 5628}, + {13.9, 64.6, 5628}, + {26, 54.6, 5628}, + {28.4, 52.4, 5628}, + {28.5, 52.2, 5628}, + {21.6, 50.9, 5628}, + {21.2, 43.7, 5628}, + {21, 41.6, 5628}, + {21.1, 38, 5628}, + {18.9, 31.3, 5628}, + {19, 30.8, 5628}, + {30.2, 29, 5628}, + {27.1, 27.1, 5628}, + {27.2, 26.8, 5628}, + }, + [62647] = { + {28, 28.5, 5628}, + {30.3, 28, 5628}, + {31.6, 22, 5628}, + {29.4, 19.9, 5628}, + {29.3, 15.8, 5628}, + {31.6, 15.8, 5628}, + {30.6, 14.8, 5628}, + {30.4, 14.8, 5628}, + {31.8, 12.8, 5628}, + {30.5, 10.8, 5628}, + }, + [62651] = { + {33.3, 92.3, 5628}, + {32, 92.3, 5628}, + {33.2, 92.2, 5628}, + {31.7, 92.1, 5628}, + {33.4, 92, 5628}, + {31.9, 91.9, 5628}, + {29.5, 91.6, 5628}, + {29.2, 91.2, 5628}, + {24.9, 91.2, 5628}, + {24.7, 91.2, 5628}, + {34.5, 90.9, 5628}, + {34.6, 90.6, 5628}, + {21.1, 88.6, 5628}, + {20.9, 87.2, 5628}, + {36.6, 86.9, 5628}, + {36.3, 86.6, 5628}, + {36.7, 86.5, 5628}, + {24.8, 84.7, 5628}, + {24.5, 84.5, 5628}, + {29.9, 84.4, 5628}, + }, + [62659] = { + {30.7, 29.9, 5628}, + {30.3, 29.9, 5628}, + {31.1, 28.2, 5628}, + {28.1, 28.1, 5628}, + {30.5, 28, 5628}, + {24.4, 27.9, 5628}, + {28.1, 27.7, 5628}, + {31.1, 27.6, 5628}, + {29.2, 12.8, 5628}, + {29.1, 12.6, 5628}, + {31.9, 12.5, 5628}, + {28.9, 10.4, 5628}, + {32.1, 10.4, 5628}, + {32.1, 10, 5628}, + {28.9, 9.9, 5628}, + {30.3, 8.6, 5628}, + {30.7, 8.6, 5628}, + }, + [62663] = { + {31.3, 22.5, 5628}, + {29.7, 20.2, 5628}, + {29.8, 17.2, 5628}, + {31.3, 16.4, 5628}, + }, + [62665] = { + {30.5, 24.1, 5628}, + }, + [62666] = { + {30.7, 14.9, 5628}, + {30.3, 14.9, 5628}, + {31.9, 12.9, 5628}, + {29.1, 12.9, 5628}, + {31.7, 12.6, 5628}, + {31.6, 11.3, 5628}, + {31.9, 9, 5628}, + {31.8, 7.3, 5628}, + {29.2, 7.2, 5628}, + }, + [62669] = { + {27.3, 29.2, 5628}, + {30.3, 27.6, 5628}, + {27.9, 26.9, 5628}, + {29.3, 24.1, 5628}, + {31.6, 24.1, 5628}, + {29.3, 17.9, 5628}, + {31.7, 12.5, 5628}, + {29.3, 12.5, 5628}, + }, + [62711] = { + {34.7, 79.5, 141}, + {33.8, 78.9, 141}, + {34.9, 77.6, 141}, + {33.2, 77.3, 141}, + {35.7, 75.6, 141}, + {33.8, 75.5, 141}, + {36.9, 75.2, 141}, + {36, 74.9, 141}, + {31.3, 74.6, 141}, + {30.8, 74.2, 141}, + {32.9, 73.7, 141}, + {33.7, 73.6, 141}, + {34.4, 73.1, 141}, + {36.4, 72.9, 141}, + {33.7, 72.8, 141}, + {35.2, 72.7, 141}, + {36.8, 71.1, 141}, + {36, 71, 141}, + {37.8, 68.8, 141}, + {37.4, 68.2, 141}, + }, + [62712] = { + {35.4, 77.4, 141}, + {33.3, 76.1, 141}, + {31.6, 75.6, 141}, + {33.6, 75.4, 141}, + {36.7, 74.4, 141}, + {37, 73.9, 141}, + {29.5, 73.7, 141}, + {30.8, 73.4, 141}, + {33.3, 73.3, 141}, + {33.9, 72.8, 141}, + {35.2, 71.4, 141}, + {35.5, 71.3, 141}, + }, + [62713] = { + {33.7, 79.6, 141}, + {31.9, 76.4, 141}, + {33.8, 76.4, 141}, + {31.4, 75.1, 141}, + {36.7, 74.2, 141}, + {36.1, 74.2, 141}, + {36.9, 74.1, 141}, + {34.8, 73.6, 141}, + {35.7, 73.5, 141}, + {33.4, 73.2, 141}, + {33.7, 73.1, 141}, + {34.7, 72.5, 141}, + {35.9, 72.4, 141}, + {35.4, 72, 141}, + {36.6, 70.5, 141}, + {37.2, 70.4, 141}, + {37.5, 69.3, 141}, + }, + [62714] = { + {35.1, 78.5, 141}, + {34, 76.4, 141}, + {31.7, 76, 141}, + {31.9, 75.4, 141}, + {33.7, 74.8, 141}, + {31.6, 74.4, 141}, + {36.4, 73.9, 141}, + {32, 73.6, 141}, + {33.4, 73.6, 141}, + {36.7, 73.3, 141}, + {34, 72.9, 141}, + {34.1, 72.5, 141}, + {35.3, 71.8, 141}, + {35, 71.8, 141}, + {35.7, 71.5, 141}, + }, + [62715] = { + {33.8, 75.6, 141}, + }, + [62717] = { + {29.9, 74.3, 141}, + {29.1, 73.7, 141}, + {31, 72.8, 141}, + {30.4, 72.5, 141}, + {29.4, 72.3, 141}, + {29.9, 72, 141}, + }, + [62725] = { + {49.4, 78.2, 5602}, + }, + [62753] = { + {2.8, 89.9, 5602}, + }, + [65100] = { + {89.4, 41.1, 2366}, + {88.2, 40.4, 2366}, + {90.6, 32.8, 2366}, + {87.2, 25.9, 2366}, + {90.4, 25.3, 2366}, + {87.9, 25.1, 2366}, + {91.2, 25, 2366}, + {84.8, 19.6, 2366}, + {85, 18.7, 2366}, + {85.5, 17.8, 2366}, + {86.2, 17.7, 2366}, + {79.9, 14.8, 2366}, + {80.3, 13.9, 2366}, + {82.2, 12.4, 2366}, + {82.7, 11.5, 2366}, + {77.4, 11.1, 2366}, + {78.6, 8.9, 2366}, + {51.7, 65.3, 5204}, + {50.7, 64.6, 5204}, + {52.7, 58.1, 5204}, + {49.8, 52.1, 5204}, + {52.5, 51.7, 5204}, + {50.4, 51.5, 5204}, + {53.2, 51.4, 5204}, + {47.8, 46.8, 5204}, + {47.9, 45.9, 5204}, + {48.3, 45.2, 5204}, + {48.9, 45.1, 5204}, + {43.6, 42.7, 5204}, + {43.9, 41.8, 5204}, + {45.6, 40.6, 5204}, + {46, 39.8, 5204}, + {41.5, 39.5, 5204}, + {42.4, 37.5, 5204}, + {32.6, 28.7, 5204}, + {32.5, 27.9, 5204}, + {26.4, 27.1, 5204}, + {29, 24.2, 5204}, + }, + [65101] = { + {84.8, 17.8, 2366}, + {80.5, 14.6, 2366}, + {77.5, 8.6, 2366}, + {47.7, 45.2, 5204}, + {44.1, 42.5, 5204}, + {41.5, 37.3, 5204}, + {29.4, 23.5, 5204}, + {28.8, 23.3, 5204}, + {22.8, 18.4, 5204}, + {28.8, 12.3, 5204}, + {28.9, 11.3, 5204}, + }, + [65102] = { + {82.2, 11.7, 2366}, + {77, 9.6, 2366}, + {45.5, 40, 5204}, + {41.1, 38.1, 5204}, + {33, 28, 5204}, + {21.3, 23.1, 5204}, + {23.8, 20.1, 5204}, + }, + [65105] = { + {70.6, 1.8, 2366}, + {35.6, 31.4, 5204}, + {19.9, 17.4, 5204}, + }, + [80257] = { + {19.7, 83.7, 38}, + {22.3, 75.6, 38}, + {20.5, 71.3, 38}, + {28.6, 66.4, 38}, + {29.6, 58.5, 38}, + {42.7, 57.3, 38}, + {28.3, 56.2, 38}, + {32.7, 55.2, 38}, + {35.4, 52.9, 38}, + {41, 50.2, 38}, + {27.9, 49.9, 38}, + {30.2, 42.9, 38}, + {26.9, 41.7, 38}, + {35.4, 37.9, 38}, + {36.3, 37.5, 38}, + {33.7, 31.1, 38}, + {26.8, 31, 38}, + {38.6, 30.5, 38}, + {36, 29.4, 38}, + {32.5, 29, 38}, + {35.5, 28.1, 38}, + {8.6, 87.2, 5602}, + {10, 83, 5602}, + {9.1, 80.8, 5602}, + {13.2, 78.3, 5602}, + {13.7, 74.2, 5602}, + {20.4, 73.6, 5602}, + {13, 73, 5602}, + {15.3, 72.5, 5602}, + {16.7, 71.3, 5602}, + {19.5, 70, 5602}, + {12.8, 69.8, 5602}, + {14, 66.2, 5602}, + {12.3, 65.6, 5602}, + {16.7, 63.6, 5602}, + {17.1, 63.4, 5602}, + {15.8, 60.1, 5602}, + {12.3, 60.1, 5602}, + {18.3, 59.8, 5602}, + {17, 59.3, 5602}, + {15.2, 59.1, 5602}, + {16.7, 58.6, 5602}, + }, + [80269] = { + {26.8, 55.3, 33}, + }, + [91916] = { + {89.4, 69.4, 5086}, + }, + [92109] = { + {43.1, 43.4, 5077}, + }, + [93333] = { + {52.1, 68.2, 5557}, + }, +} + +MTH_DS_CreatureNames = { + [40] = "Kobold Miner", + [43] = "Mine Spider", + [46] = "Murloc Forager", + [60] = "Ruklar the Trapper", + [68] = "Stormwind City Guard", + [79] = "Narg the Taskmaster", + [97] = "Riverpaw Runt", + [99] = "Morgaine the Sly", + [116] = "Defias Bandit", + [118] = "Prowler", + [199] = "Young Fleshripper", + [285] = "Murloc", + [327] = "Goldtooth", + [330] = "Princess", + [390] = "Porcine Entourage", + [471] = "Mother Fang", + [473] = "Morgan the Collector", + [474] = "Defias Rogue Wizard", + [475] = "Kobold Tunneler", + [476] = "Kobold Geomancer", + [478] = "Riverpaw Outrunner", + [480] = "Rusty Harvest Golem", + [481] = "Defias Footpad", + [524] = "Rockhide Boar", + [583] = "Defias Ambusher", + [732] = "Murloc Lurker", + [735] = "Murloc Streamrunner", + [822] = "Young Forest Bear", + [834] = "Coyote", + [880] = "Erlan Drudgemoor", + [881] = "Surena Caledon", + [1115] = "Rockjaw Skullthumper", + [1116] = "Rockjaw Ambusher", + [1117] = "Rockjaw Bonesnapper", + [1120] = "Frostmane Troll", + [1121] = "Frostmane Snowstrider", + [1122] = "Frostmane Hideskinner", + [1123] = "Frostmane Headhunter", + [1124] = "Frostmane Shadowcaster", + [1126] = "Large Crag Boar", + [1127] = "Elder Crag Boar", + [1131] = "Winter Wolf", + [1133] = "Starving Winter Wolf", + [1135] = "Wendigo", + [1138] = "Snow Tracker Wolf", + [1172] = "Tunnel Rat Vermin", + [1173] = "Tunnel Rat Scout", + [1190] = "Mountain Boar", + [1195] = "Forest Lurker", + [1196] = "Ice Claw Bear", + [1201] = "Snow Leopard", + [1211] = "Leper Gnome", + [1397] = "Frostmane Seer", + [1520] = "Rattlecage Soldier", + [1522] = "Darkeye Bonecaster", + [1523] = "Cracked Skull Soldier", + [1526] = "Ravaged Corpse", + [1527] = "Hungering Dead", + [1528] = "Shambling Horror", + [1529] = "Bleeding Horror", + [1530] = "Rotting Ancestor", + [1531] = "Lost Soul", + [1532] = "Wandering Spirit", + [1533] = "Tormented Spirit", + [1534] = "Wailing Ancestor", + [1535] = "Scarlet Warrior", + [1536] = "Scarlet Missionary", + [1537] = "Scarlet Zealot", + [1538] = "Scarlet Friar", + [1539] = "Scarlet Neophyte", + [1540] = "Scarlet Vanguard", + [1543] = "Vile Fin Puddlejumper", + [1544] = "Vile Fin Minor Oracle", + [1545] = "Vile Fin Muckdweller", + [1548] = "Cursed Darkhound", + [1549] = "Ravenous Darkhound", + [1553] = "Greater Duskbat", + [1554] = "Vampiric Duskbat", + [1555] = "Vicious Night Web Spider", + [1653] = "Bloodsail Elder Magus", + [1654] = "Gregor Agamand", + [1655] = "Nissa Agamand", + [1656] = "Thurman Agamand", + [1657] = "Devlin Agamand", + [1660] = "Scarlet Bodyguard", + [1662] = "Captain Perrine", + [1674] = "Rot Hide Gnoll", + [1675] = "Rot Hide Mongrel", + [1689] = "Scarred Crag Boar", + [1753] = "Maggot Eye", + [1765] = "Worg", + [1769] = "Moonrage Whitescalp", + [1922] = "Gray Forest Wolf", + [1934] = "Tirisfal Farmer", + [1941] = "Rot Hide Graverobber", + [1993] = "Greenpaw", + [1996] = "Strigid Screecher", + [1997] = "Strigid Hunter", + [1999] = "Webwood Venomfang", + [2000] = "Webwood Silkspinner", + [2001] = "Giant Webwood Spider", + [2004] = "Dark Sprite", + [2005] = "Vicious Grell", + [2008] = "Gnarlpine Warrior", + [2009] = "Gnarlpine Shaman", + [2010] = "Gnarlpine Defender", + [2011] = "Gnarlpine Augur", + [2012] = "Gnarlpine Pathfinder", + [2013] = "Gnarlpine Avenger", + [2014] = "Gnarlpine Totemic", + [2015] = "Bloodfeather Harpy", + [2017] = "Bloodfeather Rogue", + [2018] = "Bloodfeather Sorceress", + [2019] = "Bloodfeather Fury", + [2020] = "Bloodfeather Wind Witch", + [2025] = "Timberling Bark Ripper", + [2027] = "Timberling Trampler", + [2029] = "Timberling Mire Beast", + [2030] = "Elder Timberling", + [2033] = "Elder Nightsaber", + [2034] = "Feral Nightsaber", + [2038] = "Lord Melenas", + [2043] = "Nightsaber Stalker", + [2070] = "Moonstalker Runt", + [2152] = "Gnarlpine Ambusher", + [2156] = "Cracked Golem", + [2157] = "Stone Behemoth", + [2162] = "Agal", + [2166] = "Oakenscowl", + [2176] = "Cursed Highborne", + [2189] = "Vile Sprite", + [2231] = "Pygmy Tide Crawler", + [2234] = "Young Reef Crawler", + [2447] = "Narillasanz", + [2749] = "Siege Golem", + [2949] = "Palemane Tanner", + [2950] = "Palemane Skinner", + [2951] = "Palemane Poacher", + [2956] = "Adult Plainstrider", + [2957] = "Elder Plainstrider", + [2959] = "Prairie Stalker", + [2960] = "Prairie Wolf Alpha", + [2962] = "Windfury Harpy", + [2963] = "Windfury Wind Witch", + [2964] = "Windfury Sorceress", + [2965] = "Windfury Matriarch", + [2967] = "Galak Centaur", + [2968] = "Galak Outrunner", + [2970] = "Swoop", + [2971] = "Taloned Swoop", + [2972] = "Kodo Calf", + [2973] = "Kodo Bull", + [2976] = "Venture Co. Laborer", + [2977] = "Venture Co. Taskmaster", + [2978] = "Venture Co. Worker", + [2979] = "Venture Co. Supervisor", + [2989] = "Bael'dun Digger", + [2990] = "Bael'dun Appraiser", + [3035] = "Flatland Cougar", + [3068] = "Mazzranache", + [3099] = "Dire Mottled Boar", + [3100] = "Elder Mottled Boar", + [3103] = "Makrura Clacker", + [3104] = "Makrura Shellhide", + [3105] = "Makrura Snapclaw", + [3107] = "Surf Crawler", + [3108] = "Encrusted Surf Crawler", + [3110] = "Dreadmaw Crocolisk", + [3111] = "Razormane Quilboar", + [3112] = "Razormane Scout", + [3113] = "Razormane Dustrunner", + [3114] = "Razormane Battleguard", + [3115] = "Dustwind Harpy", + [3116] = "Dustwind Pillager", + [3117] = "Dustwind Savage", + [3118] = "Dustwind Storm Witch", + [3119] = "Kolkar Drudge", + [3120] = "Kolkar Outrunner", + [3121] = "Durotar Tiger", + [3122] = "Bloodtalon Taillasher", + [3123] = "Bloodtalon Scythemaw", + [3126] = "Armored Scorpid", + [3127] = "Venomtail Scorpid", + [3129] = "Kul Tiras Marine", + [3130] = "Thunder Lizard", + [3131] = "Lightning Hide", + [3141] = "Makrura Elder", + [3192] = "Lieutenant Benedict", + [3195] = "Burning Blade Thug", + [3196] = "Burning Blade Neophyte", + [3197] = "Burning Blade Fanatic", + [3198] = "Burning Blade Apprentice", + [3199] = "Burning Blade Cultist", + [3203] = "Fizzle Darkstorm", + [3204] = "Gazz'uz", + [3205] = "Zalazane", + [3206] = "Voodoo Troll", + [3207] = "Hexed Troll", + [3225] = "Corrupted Mottled Boar", + [3227] = "Corrupted Bloodtalon Scythemaw", + [3228] = "Corrupted Surf Crawler", + [3232] = "Bristleback Interloper", + [3267] = "Razormane Water Seeker", + [3268] = "Razormane Thornweaver", + [3379] = "Burning Blade Bruiser", + [3566] = "Flatland Prowler", + [4279] = "Odo the Blindwatcher", + [4282] = "Scarlet Magician", + [4283] = "Scarlet Sentry", + [4285] = "Scarlet Disciple", + [4286] = "Scarlet Soldier", + [4287] = "Scarlet Gallant", + [4288] = "Scarlet Beastmaster", + [4290] = "Scarlet Guardsman", + [4291] = "Scarlet Diviner", + [4292] = "Scarlet Protector", + [4293] = "Scarlet Scryer", + [4294] = "Scarlet Sorcerer", + [4295] = "Scarlet Myrmidon", + [4296] = "Scarlet Adept", + [4298] = "Scarlet Defender", + [4299] = "Scarlet Chaplain", + [4300] = "Scarlet Wizard", + [4301] = "Scarlet Centurion", + [4302] = "Scarlet Champion", + [4303] = "Scarlet Abbot", + [4304] = "Scarlet Tracking Hound", + [4306] = "Scarlet Torturer", + [4308] = "Unfettered Spirit", + [4540] = "Scarlet Monk", + [4624] = "Booty Bay Bruiser", + [4848] = "Shadowforge Darkcaster", + [4849] = "Shadowforge Archaeologist", + [4850] = "Stonevault Cave Lurker", + [4852] = "Stonevault Oracle", + [4853] = "Stonevault Geomancer", + [4855] = "Stonevault Brawler", + [4857] = "Stone Keeper", + [4860] = "Stone Steward", + [4861] = "Shrike Bat", + [4863] = "Jadespine Basilisk", + [5224] = "Murk Slitherer", + [5225] = "Murk Spitter", + [5226] = "Murk Worm", + [5228] = "Saturated Ooze", + [5235] = "Fungal Ooze", + [5243] = "Cursed Atal'ai", + [5256] = "Atal'ai Warrior", + [5259] = "Atal'ai Witch Doctor", + [5261] = "Enthralled Atal'ai", + [5263] = "Mummified Atal'ai", + [5267] = "Unliving Atal'ai", + [5269] = "Atal'ai Priest", + [5270] = "Atal'ai Corpse Eater", + [5271] = "Atal'ai Deathwalker", + [5273] = "Atal'ai High Priest", + [5277] = "Nightmare Scalebane", + [5280] = "Nightmare Wyrmkin", + [5283] = "Nightmare Wanderer", + [5291] = "Hakkari Frostwing", + [5708] = "Spawn of Hakkar", + [5785] = "Sister Hatelash", + [5807] = "The Rake", + [5826] = "Geolord Mottle", + [6123] = "Dark Iron Spy", + [6206] = "Caverndeep Burrower", + [6207] = "Caverndeep Ambusher", + [6208] = "Caverndeep Invader", + [6210] = "Caverndeep Pillager", + [6211] = "Caverndeep Reaver", + [6213] = "Irradiated Invader", + [6218] = "Irradiated Slime", + [6219] = "Corrosive Lurker", + [6220] = "Irradiated Horror", + [6221] = "Addled Leper", + [6223] = "Leprous Defender", + [6225] = "Mechano-Tank", + [6230] = "Peacekeeper Security Suit", + [6231] = "Techbot", + [6233] = "Mechanized Sentry", + [6329] = "Irradiated Pillager", + [6391] = "Holdout Warrior", + [6392] = "Holdout Medic", + [6407] = "Holdout Technician", + [6426] = "Anguished Dead", + [6427] = "Haunting Phantasm", + [6523] = "Dark Iron Rifleman", + [6789] = "Thistle Cub", + [6846] = "Defias Dockmaster", + [6866] = "Defias Bodyguard", + [6906] = "Baelog", + [6911] = "Minion of Sethir", + [6927] = "Defias Dockworker", + [7012] = "Earthen Sculptor", + [7022] = "Venomlash Scorpid", + [7023] = "Obsidian Sentinel", + [7073] = "Arados the Damned", + [7234] = "Ferocitas the Dream Eater", + [7235] = "Gnarlpine Mystic", + [7290] = "Shadowforge Sharpshooter", + [7318] = "Rageclaw", + [7320] = "Stonevault Mauler", + [7321] = "Stonevault Flameweaver", + [7328] = "Withered Reaver", + [7329] = "Withered Quilguard", + [7332] = "Withered Spearhide", + [7335] = "Death's Head Geomancer", + [7337] = "Death's Head Necromancer", + [7341] = "Skeletal Frostweaver", + [7342] = "Skeletal Summoner", + [7345] = "Splinterbone Captain", + [7347] = "Boneflayer Ghoul", + [7348] = "Thorn Eater Ghoul", + [7352] = "Frozen Soul", + [7353] = "Freezing Spirit", + [8336] = "Hakkari Sapper", + [9025] = "Lord Roccor", + [9045] = "Scarshield Acolyte", + [9097] = "Scarshield Legionnaire", + [9098] = "Scarshield Spellbinder", + [9196] = "Highlord Omokk", + [9197] = "Spirestone Battle Mage", + [9199] = "Spirestone Enforcer", + [9200] = "Spirestone Reaver", + [9201] = "Spirestone Ogre Magus", + [9216] = "Spirestone Warlord", + [9219] = "Spirestone Butcher", + [9236] = "Shadow Hunter Vosh'gajin", + [9237] = "War Master Voone", + [9239] = "Smolderthorn Mystic", + [9240] = "Smolderthorn Shadow Priest", + [9257] = "Scarshield Warlock", + [9258] = "Scarshield Raider", + [9259] = "Firebrand Grunt", + [9260] = "Firebrand Legionnaire", + [9261] = "Firebrand Darkweaver", + [9262] = "Firebrand Invoker", + [9264] = "Firebrand Pyromancer", + [9265] = "Smolderthorn Shadow Hunter", + [9268] = "Smolderthorn Berserker", + [9583] = "Bloodaxe Veteran", + [9676] = "Tink Sprocketwhistle", + [9692] = "Bloodaxe Raider", + [9693] = "Bloodaxe Evoker", + [9716] = "Bloodaxe Warmonger", + [9717] = "Bloodaxe Summoner", + [10997] = "Cannon Master Willey", + [14428] = "Uruson", + [14431] = "Fury Shelda", + [15318] = "Hive'Zara Drone", + [15320] = "Hive'Zara Soldier", + [15323] = "Hive'Zara Sandstalker", + [15324] = "Qiraji Gladiator", + [15325] = "Hive'Zara Wasp", + [15327] = "Hive'Zara Stinger", + [15333] = "Silicate Feeder", + [15335] = "Flesh Hunter", + [15336] = "Hive'Zara Tail Lasher", + [15338] = "Obsidian Destroyer", + [15343] = "Qiraji Swarmguard", + [15355] = "Anubisath Guardian", + [15386] = "Major Yeggeth", + [15387] = "Qiraji Warrior", + [15389] = "Captain Drenn", + [15390] = "Captain Xurrem", + [15391] = "Captain Qeez", + [15392] = "Captain Tuubid", + [15462] = "Spitting Scarab", + [49007] = "The Wandering Knight", + [60708] = "Death's Head Cultist", + [60736] = "Engineer Figgles", + [60893] = "Expedition Commando", + [60898] = "Expedition Scout", + [60906] = "Kegdesh Foulmantle", + [60980] = "Bramblethorn Boar", + [61191] = "Shadowbane Alpha", + [61192] = "Shadowbane Darkcaster", + [61193] = "Shadowbane Ambusher", + [61194] = "Shadowbane Ragefang", + [61211] = "Shadowbane Glutton", + [61223] = "Clawlord Howlfang", + [61364] = "Greymane Arbalest", + [61419] = "Matthias Holtz", + [61464] = "Malfunctioning Servitor", + [61656] = "Ashfeather Buzzard", + [61657] = "Ashfeather Swooper", + [61658] = "Ashpaw Hyena", + [61659] = "Ashpaw Hunter", + [61660] = "Stonetail Scorpid", + [61661] = "Stonetail Lasher", + [61662] = "Blackvenom Scorpid", + [61663] = "Mudpaw Gnoll", + [61664] = "Mudpaw Scavenger", + [61665] = "Mudpaw Brute", + [61666] = "Mudpaw Mystic", + [61667] = "Mudpaw Shell Collector", + [61668] = "Mudpaw Miner", + [61669] = "Mudpaw Overseer", + [61670] = "Gelwex Goldfingers", + [61672] = "Venture Co. Instigator", + [61673] = "Venture Co. Thug", + [61674] = "Venture Co. Drifter", + [61675] = "Venture Co. Lumber Worker", + [61677] = "Venture Co. Cartographer", + [61678] = "Stoneshell Makrura", + [61679] = "Stoneshell Scrabbler", + [61680] = "Muckreef Crawler", + [61681] = "Young Muckreef Crawler", + [61682] = "Muckfin Murloc", + [61683] = "Muckfin Trapper", + [61684] = "Muckfin Lurker", + [61685] = "Muckfin Tidecaller", + [61686] = "Ashfeather Scavenger", + [61687] = "Malfunctioning Mine-Bot", + [61694] = "Thalassian Stag", + [61695] = "Ivory Hawkstrider", + [61699] = "Shar'lan", + [61700] = "Elder Crimson Lynx", + [61701] = "Thalassian Treant", + [61702] = "Farstride Netter", + [61703] = "Farstride Coastrunner", + [61704] = "Farstride Crawler", + [61705] = "Deepmurk Oracle", + [61706] = "Deepmurk Warrior", + [61707] = "Deepmurk Darkhunter", + [61708] = "Murkblood", + [61709] = "Reefscale Sorceress", + [61710] = "Reefscale Warrior", + [61712] = "Lord Vash'arj", + [61713] = "Lady Renirja", + [61714] = "Autumnal Treant", + [61715] = "Stallhorn Stag", + [61716] = "Crimson Hawkstrider", + [61717] = "Thalassian Fox", + [61718] = "Highborne Soulwraith", + [61719] = "Vanthalir'il Theron", + [61759] = "DV-500", + [61783] = "Illidari Felhunter", + [61789] = "Moonfeather", + [61790] = "Arcandir", + [61792] = "The Snipper", + [61794] = "Vanessa Moneybutton", + [61863] = "Grabb Mudhide", + [61895] = "Aeltalor Flameshard", + [61951] = "Anomalus", + [61963] = "Masterpiece Harvester", + [61971] = "Scarlet Jailer", + [62070] = "Overlord Blackheart", + [62537] = "Stormreaver Battlemaster", + [62555] = "Blackrock Pyremaster", + [62645] = "Shadow Council Alchemist", + [62647] = "Shadow Council Summoner", + [62651] = "Shadow Council Cultist", + [62659] = "Shadow Council Torturer", + [62663] = "Sinister Interrogator", + [62665] = "Lady Drazare", + [62666] = "Brutish Felguard", + [62669] = "Shadow Council Soulweaver", + [62711] = "Ursan Marauder", + [62712] = "Ursan Shaman", + [62713] = "Ursan Ambusher", + [62714] = "Ursan Furbolg", + [62715] = "Arkod the White", + [62717] = "Disturbed Treant", + [62725] = "Thromrar Darkflame", + [62753] = "Dark Iron Surveillance Bot", + [65100] = "Infinite Dragonspawn", + [65101] = "Infinite Riftguard", + [65102] = "Infinite Riftweaver", + [65105] = "Infinite Rift-Lord", + [80257] = "Red Fox", + [80269] = "Kin'Tozo", + [91916] = "Archlich Enkhraz", + [92109] = "Keeper Ranathos", + [93333] = "Mephistroth", +} + +MTH_DS_CreatureCoordsMeta = { + generated = "2026-02-15", + needed = 475, + covered = 475, + missing = 0, +} diff --git a/data/ds-items-atlas-sources.lua b/data/ds-items-atlas-sources.lua new file mode 100644 index 0000000..47f4992 --- /dev/null +++ b/data/ds-items-atlas-sources.lua @@ -0,0 +1,150 @@ +-- MetaHunt Atlas Source Enrichment +-- Adds Atlas-derived source category hints to existing ranged/ammo item entries +if not MTH_DS then MTH_DS = {} end +if not MTH_DS_AmmoItems then MTH_DS_AmmoItems = {} end + +local MTH_DS_AtlasSources = { + [10567] = {["boss"] = true, }, + [10624] = {["boss"] = true, }, + [11628] = {["boss"] = true, }, + [11629] = {["boss"] = true, }, + [11630] = {["boss"] = true, }, + [12651] = {["boss"] = true, }, + [12653] = {["boss"] = true, }, + [12654] = {["boss"] = true, }, + [13019] = {["worlddrop"] = true, }, + [13020] = {["worlddrop"] = true, }, + [13021] = {["worlddrop"] = true, }, + [13022] = {["worlddrop"] = true, }, + [13023] = {["worlddrop"] = true, }, + [13037] = {["worlddrop"] = true, }, + [13038] = {["worlddrop"] = true, }, + [13039] = {["worlddrop"] = true, }, + [13040] = {["worlddrop"] = true, }, + [13136] = {["worlddrop"] = true, }, + [13137] = {["worlddrop"] = true, }, + [13138] = {["worlddrop"] = true, }, + [13139] = {["worlddrop"] = true, }, + [13146] = {["worlddrop"] = true, }, + [13248] = {["boss"] = true, }, + [13377] = {["boss"] = true, }, + [13380] = {["boss"] = true, ["crafted"] = true, }, + [15294] = {["crafted"] = true, }, + [15295] = {["crafted"] = true, }, + [15296] = {["crafted"] = true, }, + [16992] = {["crafted"] = true, }, + [17042] = {["quest"] = true, }, + [17069] = {["boss"] = true, }, + [17072] = {["boss"] = true, }, + [17717] = {["boss"] = true, }, + [17753] = {["quest"] = true, }, + [18282] = {["worlddrop"] = true, }, + [18323] = {["boss"] = true, }, + [18388] = {["boss"] = true, }, + [18460] = {["boss"] = true, }, + [18482] = {["boss"] = true, }, + [18680] = {["boss"] = true, }, + [18729] = {["boss"] = true, }, + [18738] = {["boss"] = true, }, + [18755] = {["boss"] = true, }, + [18833] = {["pvp"] = true, }, + [18835] = {["pvp"] = true, }, + [18836] = {["pvp"] = true, }, + [18837] = {["pvp"] = true, }, + [18855] = {["pvp"] = true, }, + [18860] = {["pvp"] = true, }, + [19082] = {["crafted"] = true, }, + [19107] = {["crafted"] = true, ["quest"] = true, }, + [19316] = {["vendor"] = true, }, + [19317] = {["vendor"] = true, }, + [19350] = {["boss"] = true, }, + [19361] = {["boss"] = true, }, + [19368] = {["boss"] = true, }, + [19558] = {["vendor"] = true, }, + [19559] = {["vendor"] = true, }, + [19560] = {["vendor"] = true, }, + [19561] = {["vendor"] = true, }, + [19562] = {["vendor"] = true, }, + [19563] = {["vendor"] = true, }, + [19564] = {["vendor"] = true, }, + [19565] = {["vendor"] = true, }, + [19853] = {["boss"] = true, }, + [19993] = {["boss"] = true, }, + [20038] = {["boss"] = true, }, + [20437] = {["vendor"] = true, }, + [20438] = {["vendor"] = true, }, + [20599] = {["world"] = true, }, + [20663] = {["event"] = true, }, + [20722] = {["worlddrop"] = true, }, + [2098] = {["worlddrop"] = true, }, + [2099] = {["worlddrop"] = true, }, + [2100] = {["worlddrop"] = true, }, + [21459] = {["boss"] = true, }, + [21478] = {["boss"] = true, }, + [21616] = {["boss"] = true, }, + [21800] = {["boss"] = true, }, + [22318] = {["boss"] = true, }, + [22347] = {["quest"] = true, }, + [22656] = {["vendor"] = true, }, + [22810] = {["boss"] = true, }, + [22811] = {["boss"] = true, }, + [22812] = {["boss"] = true, }, + [23557] = {["boss"] = true, }, + [2824] = {["worlddrop"] = true, }, + [2825] = {["worlddrop"] = true, }, + [3021] = {["worlddrop"] = true, }, + [3078] = {["boss"] = true, }, + [3493] = {["crafted"] = true, }, + [3778] = {["crafted"] = true, }, + [3780] = {["crafted"] = true, }, + [41023] = {["crafted"] = true, }, + [41725] = {["boss"] = true, }, + [51780] = {["world"] = true, }, + [55096] = {["boss"] = true, }, + [55346] = {["boss"] = true, }, + [55379] = {["boss"] = true, }, + [5748] = {["vendor"] = true, }, + [58083] = {["quest"] = true, }, + [58193] = {["boss"] = true, }, + [60440] = {["world"] = true, }, + [60506] = {["boss"] = true, }, + [60545] = {["boss"] = true, }, + [60624] = {["quest"] = true, }, + [60882] = {["vendor"] = true, }, + [61011] = {["vendor"] = true, }, + [61068] = {["quest"] = true, }, + [61248] = {["boss"] = true, }, + [61307] = {["boss"] = true, }, + [61383] = {["quest"] = true, }, + [61525] = {["boss"] = true, }, + [61569] = {["boss"] = true, }, + [61629] = {["quest"] = true, }, + [6469] = {["boss"] = true, }, + [6696] = {["boss"] = true, }, + [7729] = {["boss"] = true, }, + [80503] = {["vendor"] = true, }, + [80546] = {["vendor"] = true, }, + [80603] = {["vendor"] = true, }, + [80646] = {["vendor"] = true, }, + [80745] = {["boss"] = true, }, + [83225] = {["boss"] = true, }, + [83257] = {["world"] = true, }, + [83452] = {["boss"] = true, }, + [9399] = {["boss"] = true, }, + [9400] = {["boss"] = true, }, + [9412] = {["boss"] = true, }, + [9422] = {["boss"] = true, }, + [9426] = {["boss"] = true, }, + [9456] = {["boss"] = true, }, + [9487] = {["boss"] = true, }, +} + +for itemId, sourceMap in pairs(MTH_DS_AtlasSources) do + local item = MTH_DS_AmmoItems[itemId] + if item then + if not item["atlas_sources"] then item["atlas_sources"] = {} end + for sourceType, enabled in pairs(sourceMap) do + if enabled then item["atlas_sources"][sourceType] = true end + end + end +end diff --git a/data/ds-items-extra.lua b/data/ds-items-extra.lua new file mode 100644 index 0000000..4b18b8f --- /dev/null +++ b/data/ds-items-extra.lua @@ -0,0 +1,7100 @@ +-- MetaHunt Items Extras (generated from pfQuest/pfQuest-turtle) +-- Adds missing ranged items and missing V/U/O source data +if not MTH_DS then MTH_DS = {} end +if not MTH_DS_AmmoItems then MTH_DS_AmmoItems = {} end + +do + local item = MTH_DS_AmmoItems[2098] + if not item then + item = {} + MTH_DS_AmmoItems[2098] = item + end + if not item["name"] then item["name"] = 'Double-barreled Shotgun' end + if not item["level"] then item["level"] = 27 end + if not item["reqlevel"] then item["reqlevel"] = 22 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_08' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 13.3 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][7] == nil then item["vendors"][7] = 0 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2849] == nil then item["objects"][2849] = 0.01 end + if item["objects"][2850] == nil then item["objects"][2850] = 0.01 end + if item["objects"][2852] == nil then item["objects"][2852] = 0.01 end + if item["objects"][2855] == nil then item["objects"][2855] = 0.01 end + if item["objects"][3714] == nil then item["objects"][3714] = 0.01 end + if item["objects"][3715] == nil then item["objects"][3715] = 0.01 end + if item["objects"][4095] == nil then item["objects"][4095] = 0.01 end + if item["objects"][4096] == nil then item["objects"][4096] = 0.01 end + if item["objects"][74447] == nil then item["objects"][74447] = 0.01 end + if item["objects"][74448] == nil then item["objects"][74448] = 0.01 end + if item["objects"][75295] == nil then item["objects"][75295] = 0.01 end + if item["objects"][75296] == nil then item["objects"][75296] = 0.01 end + if item["objects"][75297] == nil then item["objects"][75297] = 0.01 end + if item["objects"][75298] == nil then item["objects"][75298] = 0.01 end + if item["objects"][75299] == nil then item["objects"][75299] = 0.01 end + if item["objects"][75300] == nil then item["objects"][75300] = 0.01 end + if item["objects"][105570] == nil then item["objects"][105570] = 0.01 end + if item["objects"][105578] == nil then item["objects"][105578] = 0.01 end + if item["objects"][105579] == nil then item["objects"][105579] = 0.01 end + if item["objects"][111095] == nil then item["objects"][111095] = 0.01 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[2099] + if not item then + item = {} + MTH_DS_AmmoItems[2099] = item + end + if not item["name"] then item["name"] = 'Dwarven Hand Cannon' end + if not item["level"] then item["level"] = 58 end + if not item["reqlevel"] then item["reqlevel"] = 53 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_09' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 32.8 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[2100] + if not item then + item = {} + MTH_DS_AmmoItems[2100] = item + end + if not item["name"] then item["name"] = 'Precisely Calibrated Boomstick' end + if not item["level"] then item["level"] = 48 end + if not item["reqlevel"] then item["reqlevel"] = 43 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 27.7 end + if not item["speed"] then item["speed"] = 1.5 end +end + +do + local item = MTH_DS_AmmoItems[2504] + if not item then + item = {} + MTH_DS_AmmoItems[2504] = item + end + if not item["name"] then item["name"] = 'Worn Shortbow' end + if not item["level"] then item["level"] = 2 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 1.5 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1459] == nil then item["vendors"][1459] = 0 end + if item["vendors"][12029] == nil then item["vendors"][12029] = 0 end + if item["vendors"][14301] == nil then item["vendors"][14301] = 0 end +end + +do + local item = MTH_DS_AmmoItems[2505] + if not item then + item = {} + MTH_DS_AmmoItems[2505] = item + end + if not item["name"] then item["name"] = 'Polished Shortbow' end + if not item["level"] then item["level"] = 4 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 1.8 end + if not item["speed"] then item["speed"] = 2 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1459] == nil then item["vendors"][1459] = 0 end + if item["vendors"][12029] == nil then item["vendors"][12029] = 0 end + if item["vendors"][14301] == nil then item["vendors"][14301] = 0 end +end + +do + local item = MTH_DS_AmmoItems[2506] + if not item then + item = {} + MTH_DS_AmmoItems[2506] = item + end + if not item["name"] then item["name"] = 'Hornwood Recurve Bow' end + if not item["level"] then item["level"] = 8 end + if not item["reqlevel"] then item["reqlevel"] = 3 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.4 end + if not item["speed"] then item["speed"] = 2.1 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][150] == nil then item["vendors"][150] = 0 end + if item["vendors"][1198] == nil then item["vendors"][1198] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1459] == nil then item["vendors"][1459] = 0 end + if item["vendors"][1859] == nil then item["vendors"][1859] = 0 end + if item["vendors"][3165] == nil then item["vendors"][3165] = 0 end + if item["vendors"][3589] == nil then item["vendors"][3589] = 0 end + if item["vendors"][3610] == nil then item["vendors"][3610] = 0 end + if item["vendors"][10369] == nil then item["vendors"][10369] = 0 end + if item["vendors"][12029] == nil then item["vendors"][12029] = 0 end + if item["vendors"][14301] == nil then item["vendors"][14301] = 0 end + if item["vendors"][62088] == nil then item["vendors"][62088] = 0 end + if item["vendors"][91246] == nil then item["vendors"][91246] = 0 end +end + +do + local item = MTH_DS_AmmoItems[2507] + if not item then + item = {} + MTH_DS_AmmoItems[2507] = item + end + if not item["name"] then item["name"] = 'Laminated Recurve Bow' end + if not item["level"] then item["level"] = 16 end + if not item["reqlevel"] then item["reqlevel"] = 11 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 5.8 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][150] == nil then item["vendors"][150] = 0 end + if item["vendors"][1198] == nil then item["vendors"][1198] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1459] == nil then item["vendors"][1459] = 0 end + if item["vendors"][1668] == nil then item["vendors"][1668] = 0 end + if item["vendors"][1687] == nil then item["vendors"][1687] = 0 end + if item["vendors"][1859] == nil then item["vendors"][1859] = 0 end + if item["vendors"][3165] == nil then item["vendors"][3165] = 0 end + if item["vendors"][3409] == nil then item["vendors"][3409] = 0 end + if item["vendors"][3488] == nil then item["vendors"][3488] = 0 end + if item["vendors"][3589] == nil then item["vendors"][3589] = 0 end + if item["vendors"][3610] == nil then item["vendors"][3610] = 0 end + if item["vendors"][3951] == nil then item["vendors"][3951] = 0 end + if item["vendors"][4203] == nil then item["vendors"][4203] = 0 end + if item["vendors"][9549] == nil then item["vendors"][9549] = 0 end + if item["vendors"][9553] == nil then item["vendors"][9553] = 0 end + if item["vendors"][10369] == nil then item["vendors"][10369] = 0 end + if item["vendors"][12029] == nil then item["vendors"][12029] = 0 end + if item["vendors"][14301] == nil then item["vendors"][14301] = 0 end + if item["vendors"][60659] == nil then item["vendors"][60659] = 0 end + if item["vendors"][62088] == nil then item["vendors"][62088] = 0 end + if item["vendors"][91246] == nil then item["vendors"][91246] = 0 end + if item["vendors"][91977] == nil then item["vendors"][91977] = 0 end +end + +do + local item = MTH_DS_AmmoItems[2508] + if not item then + item = {} + MTH_DS_AmmoItems[2508] = item + end + if not item["name"] then item["name"] = 'Old Blunderbuss' end + if not item["level"] then item["level"] = 2 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 1.5 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][5510] == nil then item["vendors"][5510] = 0 end +end + +do + local item = MTH_DS_AmmoItems[2509] + if not item then + item = {} + MTH_DS_AmmoItems[2509] = item + end + if not item["name"] then item["name"] = 'Ornate Blunderbuss' end + if not item["level"] then item["level"] = 9 end + if not item["reqlevel"] then item["reqlevel"] = 4 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_02' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 3 end + if not item["speed"] then item["speed"] = 2.2 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][904] == nil then item["vendors"][904] = 0 end + if item["vendors"][1243] == nil then item["vendors"][1243] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1469] == nil then item["vendors"][1469] = 0 end + if item["vendors"][2997] == nil then item["vendors"][2997] = 0 end + if item["vendors"][3018] == nil then item["vendors"][3018] = 0 end + if item["vendors"][3078] == nil then item["vendors"][3078] = 0 end + if item["vendors"][3322] == nil then item["vendors"][3322] = 0 end + if item["vendors"][3488] == nil then item["vendors"][3488] = 0 end + if item["vendors"][4603] == nil then item["vendors"][4603] = 0 end + if item["vendors"][5123] == nil then item["vendors"][5123] = 0 end + if item["vendors"][5510] == nil then item["vendors"][5510] = 0 end + if item["vendors"][7976] == nil then item["vendors"][7976] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][61445] == nil then item["vendors"][61445] = 0 end + if item["vendors"][61751] == nil then item["vendors"][61751] = 0 end + if item["vendors"][62403] == nil then item["vendors"][62403] = 0 end + if item["vendors"][92217] == nil then item["vendors"][92217] = 0 end +end + +do + local item = MTH_DS_AmmoItems[2510] + if not item then + item = {} + MTH_DS_AmmoItems[2510] = item + end + if not item["name"] then item["name"] = 'Solid Blunderbuss' end + if not item["level"] then item["level"] = 3 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2 end + if not item["speed"] then item["speed"] = 2.2 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][904] == nil then item["vendors"][904] = 0 end + if item["vendors"][1243] == nil then item["vendors"][1243] = 0 end + if item["vendors"][3018] == nil then item["vendors"][3018] = 0 end + if item["vendors"][3078] == nil then item["vendors"][3078] = 0 end + if item["vendors"][4603] == nil then item["vendors"][4603] = 0 end + if item["vendors"][5123] == nil then item["vendors"][5123] = 0 end + if item["vendors"][5510] == nil then item["vendors"][5510] = 0 end +end + +do + local item = MTH_DS_AmmoItems[2511] + if not item then + item = {} + MTH_DS_AmmoItems[2511] = item + end + if not item["name"] then item["name"] = "Hunter's Boomstick" end + if not item["level"] then item["level"] = 14 end + if not item["reqlevel"] then item["reqlevel"] = 9 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 5 end + if not item["speed"] then item["speed"] = 2.1 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1469] == nil then item["vendors"][1469] = 0 end + if item["vendors"][1686] == nil then item["vendors"][1686] = 0 end + if item["vendors"][2997] == nil then item["vendors"][2997] = 0 end + if item["vendors"][3018] == nil then item["vendors"][3018] = 0 end + if item["vendors"][3088] == nil then item["vendors"][3088] = 0 end + if item["vendors"][3322] == nil then item["vendors"][3322] = 0 end + if item["vendors"][3488] == nil then item["vendors"][3488] = 0 end + if item["vendors"][5814] == nil then item["vendors"][5814] = 0 end + if item["vendors"][7976] == nil then item["vendors"][7976] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][61445] == nil then item["vendors"][61445] = 0 end + if item["vendors"][61751] == nil then item["vendors"][61751] = 0 end + if item["vendors"][62403] == nil then item["vendors"][62403] = 0 end + if item["vendors"][92217] == nil then item["vendors"][92217] = 0 end +end + +do + local item = MTH_DS_AmmoItems[2512] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][150] == nil then item["vendors"][150] = 0 end + if item["vendors"][151] == nil then item["vendors"][151] = 0 end + if item["vendors"][152] == nil then item["vendors"][152] = 0 end + if item["vendors"][228] == nil then item["vendors"][228] = 0 end + if item["vendors"][491] == nil then item["vendors"][491] = 0 end + if item["vendors"][789] == nil then item["vendors"][789] = 0 end + if item["vendors"][791] == nil then item["vendors"][791] = 0 end + if item["vendors"][829] == nil then item["vendors"][829] = 0 end + if item["vendors"][1198] == nil then item["vendors"][1198] = 0 end + if item["vendors"][1250] == nil then item["vendors"][1250] = 0 end + if item["vendors"][1285] == nil then item["vendors"][1285] = 0 end + if item["vendors"][1287] == nil then item["vendors"][1287] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1298] == nil then item["vendors"][1298] = 0 end + if item["vendors"][1455] == nil then item["vendors"][1455] = 0 end + if item["vendors"][1459] == nil then item["vendors"][1459] = 0 end + if item["vendors"][1462] == nil then item["vendors"][1462] = 0 end + if item["vendors"][1668] == nil then item["vendors"][1668] = 0 end + if item["vendors"][1682] == nil then item["vendors"][1682] = 0 end + if item["vendors"][1685] == nil then item["vendors"][1685] = 0 end + if item["vendors"][1687] == nil then item["vendors"][1687] = 0 end + if item["vendors"][1691] == nil then item["vendors"][1691] = 0 end + if item["vendors"][1859] == nil then item["vendors"][1859] = 0 end + if item["vendors"][2115] == nil then item["vendors"][2115] = 0 end + if item["vendors"][2134] == nil then item["vendors"][2134] = 0 end + if item["vendors"][2140] == nil then item["vendors"][2140] = 0 end + if item["vendors"][2286] == nil then item["vendors"][2286] = 0 end + if item["vendors"][2839] == nil then item["vendors"][2839] = 0 end + if item["vendors"][3015] == nil then item["vendors"][3015] = 0 end + if item["vendors"][3072] == nil then item["vendors"][3072] = 0 end + if item["vendors"][3076] == nil then item["vendors"][3076] = 0 end + if item["vendors"][3158] == nil then item["vendors"][3158] = 0 end + if item["vendors"][3164] == nil then item["vendors"][3164] = 0 end + if item["vendors"][3165] == nil then item["vendors"][3165] = 0 end + if item["vendors"][3186] == nil then item["vendors"][3186] = 0 end + if item["vendors"][3313] == nil then item["vendors"][3313] = 0 end + if item["vendors"][3409] == nil then item["vendors"][3409] = 0 end + if item["vendors"][3410] == nil then item["vendors"][3410] = 0 end + if item["vendors"][3481] == nil then item["vendors"][3481] = 0 end + if item["vendors"][3488] == nil then item["vendors"][3488] = 0 end + if item["vendors"][3498] == nil then item["vendors"][3498] = 0 end + if item["vendors"][3587] == nil then item["vendors"][3587] = 0 end + if item["vendors"][3589] == nil then item["vendors"][3589] = 0 end + if item["vendors"][3608] == nil then item["vendors"][3608] = 0 end + if item["vendors"][3610] == nil then item["vendors"][3610] = 0 end + if item["vendors"][3951] == nil then item["vendors"][3951] = 0 end + if item["vendors"][4082] == nil then item["vendors"][4082] = 0 end + if item["vendors"][4084] == nil then item["vendors"][4084] = 0 end + if item["vendors"][4170] == nil then item["vendors"][4170] = 0 end + if item["vendors"][4173] == nil then item["vendors"][4173] = 0 end + if item["vendors"][4182] == nil then item["vendors"][4182] = 0 end + if item["vendors"][4203] == nil then item["vendors"][4203] = 0 end + if item["vendors"][4241] == nil then item["vendors"][4241] = 0 end + if item["vendors"][4555] == nil then item["vendors"][4555] = 0 end + if item["vendors"][4602] == nil then item["vendors"][4602] = 0 end + if item["vendors"][4604] == nil then item["vendors"][4604] = 0 end + if item["vendors"][4892] == nil then item["vendors"][4892] = 0 end + if item["vendors"][5122] == nil then item["vendors"][5122] = 0 end + if item["vendors"][6028] == nil then item["vendors"][6028] = 0 end + if item["vendors"][7942] == nil then item["vendors"][7942] = 0 end + if item["vendors"][7976] == nil then item["vendors"][7976] = 0 end + if item["vendors"][8362] == nil then item["vendors"][8362] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9549] == nil then item["vendors"][9549] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][9552] == nil then item["vendors"][9552] = 0 end + if item["vendors"][9553] == nil then item["vendors"][9553] = 0 end + if item["vendors"][9555] == nil then item["vendors"][9555] = 0 end + if item["vendors"][10369] == nil then item["vendors"][10369] = 0 end + if item["vendors"][12027] == nil then item["vendors"][12027] = 0 end + if item["vendors"][12029] == nil then item["vendors"][12029] = 0 end + if item["vendors"][12960] == nil then item["vendors"][12960] = 0 end + if item["vendors"][14301] == nil then item["vendors"][14301] = 0 end + if item["vendors"][17598] == nil then item["vendors"][17598] = 0 end + if item["vendors"][21002] == nil then item["vendors"][21002] = 0 end + if item["vendors"][60659] == nil then item["vendors"][60659] = 0 end + if item["vendors"][61115] == nil then item["vendors"][61115] = 0 end + if item["vendors"][61443] == nil then item["vendors"][61443] = 0 end + if item["vendors"][61651] == nil then item["vendors"][61651] = 0 end + if item["vendors"][61742] == nil then item["vendors"][61742] = 0 end + if item["vendors"][62086] == nil then item["vendors"][62086] = 0 end + if item["vendors"][62088] == nil then item["vendors"][62088] = 0 end + if item["vendors"][62096] == nil then item["vendors"][62096] = 0 end + if item["vendors"][62307] == nil then item["vendors"][62307] = 0 end + if item["vendors"][62402] == nil then item["vendors"][62402] = 0 end + if item["vendors"][62404] == nil then item["vendors"][62404] = 0 end + if item["vendors"][62409] == nil then item["vendors"][62409] = 0 end + if item["vendors"][62437] == nil then item["vendors"][62437] = 0 end + if item["vendors"][62628] == nil then item["vendors"][62628] = 0 end + if item["vendors"][62739] == nil then item["vendors"][62739] = 0 end + if item["vendors"][80102] == nil then item["vendors"][80102] = 0 end + if item["vendors"][80214] == nil then item["vendors"][80214] = 0 end + if item["vendors"][80267] == nil then item["vendors"][80267] = 0 end + if item["vendors"][81035] == nil then item["vendors"][81035] = 0 end + if item["vendors"][91246] == nil then item["vendors"][91246] = 0 end + if item["vendors"][91248] == nil then item["vendors"][91248] = 0 end + if item["vendors"][91725] == nil then item["vendors"][91725] = 0 end + if item["vendors"][91977] == nil then item["vendors"][91977] = 0 end + if item["vendors"][92169] == nil then item["vendors"][92169] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[2515] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][150] == nil then item["vendors"][150] = 0 end + if item["vendors"][227] == nil then item["vendors"][227] = 0 end + if item["vendors"][228] == nil then item["vendors"][228] = 0 end + if item["vendors"][491] == nil then item["vendors"][491] = 0 end + if item["vendors"][734] == nil then item["vendors"][734] = 0 end + if item["vendors"][789] == nil then item["vendors"][789] = 0 end + if item["vendors"][791] == nil then item["vendors"][791] = 0 end + if item["vendors"][1149] == nil then item["vendors"][1149] = 0 end + if item["vendors"][1198] == nil then item["vendors"][1198] = 0 end + if item["vendors"][1285] == nil then item["vendors"][1285] = 0 end + if item["vendors"][1287] == nil then item["vendors"][1287] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1298] == nil then item["vendors"][1298] = 0 end + if item["vendors"][1448] == nil then item["vendors"][1448] = 0 end + if item["vendors"][1452] == nil then item["vendors"][1452] = 0 end + if item["vendors"][1455] == nil then item["vendors"][1455] = 0 end + if item["vendors"][1459] == nil then item["vendors"][1459] = 0 end + if item["vendors"][1462] == nil then item["vendors"][1462] = 0 end + if item["vendors"][1668] == nil then item["vendors"][1668] = 0 end + if item["vendors"][1682] == nil then item["vendors"][1682] = 0 end + if item["vendors"][1685] == nil then item["vendors"][1685] = 0 end + if item["vendors"][1687] == nil then item["vendors"][1687] = 0 end + if item["vendors"][1859] == nil then item["vendors"][1859] = 0 end + if item["vendors"][2084] == nil then item["vendors"][2084] = 0 end + if item["vendors"][2140] == nil then item["vendors"][2140] = 0 end + if item["vendors"][2286] == nil then item["vendors"][2286] = 0 end + if item["vendors"][2401] == nil then item["vendors"][2401] = 0 end + if item["vendors"][2803] == nil then item["vendors"][2803] = 0 end + if item["vendors"][2806] == nil then item["vendors"][2806] = 0 end + if item["vendors"][2808] == nil then item["vendors"][2808] = 0 end + if item["vendors"][2820] == nil then item["vendors"][2820] = 0 end + if item["vendors"][2839] == nil then item["vendors"][2839] = 0 end + if item["vendors"][2908] == nil then item["vendors"][2908] = 0 end + if item["vendors"][3015] == nil then item["vendors"][3015] = 0 end + if item["vendors"][3165] == nil then item["vendors"][3165] = 0 end + if item["vendors"][3313] == nil then item["vendors"][3313] = 0 end + if item["vendors"][3350] == nil then item["vendors"][3350] = 0 end + if item["vendors"][3409] == nil then item["vendors"][3409] = 0 end + if item["vendors"][3410] == nil then item["vendors"][3410] = 0 end + if item["vendors"][3481] == nil then item["vendors"][3481] = 0 end + if item["vendors"][3488] == nil then item["vendors"][3488] = 0 end + if item["vendors"][3498] == nil then item["vendors"][3498] = 0 end + if item["vendors"][3541] == nil then item["vendors"][3541] = 0 end + if item["vendors"][3589] == nil then item["vendors"][3589] = 0 end + if item["vendors"][3610] == nil then item["vendors"][3610] = 0 end + if item["vendors"][3625] == nil then item["vendors"][3625] = 0 end + if item["vendors"][3951] == nil then item["vendors"][3951] = 0 end + if item["vendors"][3962] == nil then item["vendors"][3962] = 0 end + if item["vendors"][4082] == nil then item["vendors"][4082] = 0 end + if item["vendors"][4084] == nil then item["vendors"][4084] = 0 end + if item["vendors"][4170] == nil then item["vendors"][4170] = 0 end + if item["vendors"][4173] == nil then item["vendors"][4173] = 0 end + if item["vendors"][4182] == nil then item["vendors"][4182] = 0 end + if item["vendors"][4203] == nil then item["vendors"][4203] = 0 end + if item["vendors"][4241] == nil then item["vendors"][4241] = 0 end + if item["vendors"][4555] == nil then item["vendors"][4555] = 0 end + if item["vendors"][4602] == nil then item["vendors"][4602] = 0 end + if item["vendors"][4604] == nil then item["vendors"][4604] = 0 end + if item["vendors"][4876] == nil then item["vendors"][4876] = 0 end + if item["vendors"][4892] == nil then item["vendors"][4892] = 0 end + if item["vendors"][4896] == nil then item["vendors"][4896] = 0 end + if item["vendors"][5101] == nil then item["vendors"][5101] = 0 end + if item["vendors"][5122] == nil then item["vendors"][5122] = 0 end + if item["vendors"][5134] == nil then item["vendors"][5134] = 0 end + if item["vendors"][6028] == nil then item["vendors"][6028] = 0 end + if item["vendors"][7942] == nil then item["vendors"][7942] = 0 end + if item["vendors"][7976] == nil then item["vendors"][7976] = 0 end + if item["vendors"][8139] == nil then item["vendors"][8139] = 0 end + if item["vendors"][8362] == nil then item["vendors"][8362] = 0 end + if item["vendors"][9320] == nil then item["vendors"][9320] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9549] == nil then item["vendors"][9549] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][9552] == nil then item["vendors"][9552] = 0 end + if item["vendors"][9553] == nil then item["vendors"][9553] = 0 end + if item["vendors"][9555] == nil then item["vendors"][9555] = 0 end + if item["vendors"][10369] == nil then item["vendors"][10369] = 0 end + if item["vendors"][11038] == nil then item["vendors"][11038] = 0 end + if item["vendors"][11555] == nil then item["vendors"][11555] = 0 end + if item["vendors"][12021] == nil then item["vendors"][12021] = 0 end + if item["vendors"][12027] == nil then item["vendors"][12027] = 0 end + if item["vendors"][12029] == nil then item["vendors"][12029] = 0 end + if item["vendors"][12036] == nil then item["vendors"][12036] = 0 end + if item["vendors"][12246] == nil then item["vendors"][12246] = 0 end + if item["vendors"][12959] == nil then item["vendors"][12959] = 0 end + if item["vendors"][12960] == nil then item["vendors"][12960] = 0 end + if item["vendors"][14301] == nil then item["vendors"][14301] = 0 end + if item["vendors"][14624] == nil then item["vendors"][14624] = 0 end + if item["vendors"][15174] == nil then item["vendors"][15174] = 0 end + if item["vendors"][17598] == nil then item["vendors"][17598] = 0 end + if item["vendors"][21002] == nil then item["vendors"][21002] = 0 end + if item["vendors"][51656] == nil then item["vendors"][51656] = 0 end + if item["vendors"][60456] == nil then item["vendors"][60456] = 0 end + if item["vendors"][60641] == nil then item["vendors"][60641] = 0 end + if item["vendors"][60646] == nil then item["vendors"][60646] = 0 end + if item["vendors"][60653] == nil then item["vendors"][60653] = 0 end + if item["vendors"][60659] == nil then item["vendors"][60659] = 0 end + if item["vendors"][60663] == nil then item["vendors"][60663] = 0 end + if item["vendors"][60740] == nil then item["vendors"][60740] = 0 end + if item["vendors"][60766] == nil then item["vendors"][60766] = 0 end + if item["vendors"][60790] == nil then item["vendors"][60790] = 0 end + if item["vendors"][60803] == nil then item["vendors"][60803] = 0 end + if item["vendors"][60813] == nil then item["vendors"][60813] = 0 end + if item["vendors"][60966] == nil then item["vendors"][60966] = 0 end + if item["vendors"][60989] == nil then item["vendors"][60989] = 0 end + if item["vendors"][61058] == nil then item["vendors"][61058] = 0 end + if item["vendors"][61115] == nil then item["vendors"][61115] = 0 end + if item["vendors"][61140] == nil then item["vendors"][61140] = 0 end + if item["vendors"][61272] == nil then item["vendors"][61272] = 0 end + if item["vendors"][61288] == nil then item["vendors"][61288] = 0 end + if item["vendors"][61369] == nil then item["vendors"][61369] = 0 end + if item["vendors"][61440] == nil then item["vendors"][61440] = 0 end + if item["vendors"][61443] == nil then item["vendors"][61443] = 0 end + if item["vendors"][61478] == nil then item["vendors"][61478] = 0 end + if item["vendors"][61523] == nil then item["vendors"][61523] = 0 end + if item["vendors"][61563] == nil then item["vendors"][61563] = 0 end + if item["vendors"][61651] == nil then item["vendors"][61651] = 0 end + if item["vendors"][61742] == nil then item["vendors"][61742] = 0 end + if item["vendors"][61743] == nil then item["vendors"][61743] = 0 end + if item["vendors"][61812] == nil then item["vendors"][61812] = 0 end + if item["vendors"][62086] == nil then item["vendors"][62086] = 0 end + if item["vendors"][62088] == nil then item["vendors"][62088] = 0 end + if item["vendors"][62096] == nil then item["vendors"][62096] = 0 end + if item["vendors"][62148] == nil then item["vendors"][62148] = 0 end + if item["vendors"][62307] == nil then item["vendors"][62307] = 0 end + if item["vendors"][62402] == nil then item["vendors"][62402] = 0 end + if item["vendors"][62404] == nil then item["vendors"][62404] = 0 end + if item["vendors"][62409] == nil then item["vendors"][62409] = 0 end + if item["vendors"][62436] == nil then item["vendors"][62436] = 0 end + if item["vendors"][62437] == nil then item["vendors"][62437] = 0 end + if item["vendors"][62462] == nil then item["vendors"][62462] = 0 end + if item["vendors"][62469] == nil then item["vendors"][62469] = 0 end + if item["vendors"][62628] == nil then item["vendors"][62628] = 0 end + if item["vendors"][62739] == nil then item["vendors"][62739] = 0 end + if item["vendors"][65002] == nil then item["vendors"][65002] = 0 end + if item["vendors"][65003] == nil then item["vendors"][65003] = 0 end + if item["vendors"][80267] == nil then item["vendors"][80267] = 0 end + if item["vendors"][80808] == nil then item["vendors"][80808] = 0 end + if item["vendors"][81035] == nil then item["vendors"][81035] = 0 end + if item["vendors"][91246] == nil then item["vendors"][91246] = 0 end + if item["vendors"][91403] == nil then item["vendors"][91403] = 0 end + if item["vendors"][91725] == nil then item["vendors"][91725] = 0 end + if item["vendors"][91868] == nil then item["vendors"][91868] = 0 end + if item["vendors"][91956] == nil then item["vendors"][91956] = 0 end + if item["vendors"][91977] == nil then item["vendors"][91977] = 0 end + if item["vendors"][92169] == nil then item["vendors"][92169] = 0 end + if item["vendors"][92177] == nil then item["vendors"][92177] = 0 end + if item["vendors"][92200] == nil then item["vendors"][92200] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[2516] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][151] == nil then item["vendors"][151] = 0 end + if item["vendors"][152] == nil then item["vendors"][152] = 0 end + if item["vendors"][491] == nil then item["vendors"][491] = 0 end + if item["vendors"][791] == nil then item["vendors"][791] = 0 end + if item["vendors"][829] == nil then item["vendors"][829] = 0 end + if item["vendors"][904] == nil then item["vendors"][904] = 0 end + if item["vendors"][1243] == nil then item["vendors"][1243] = 0 end + if item["vendors"][1250] == nil then item["vendors"][1250] = 0 end + if item["vendors"][1285] == nil then item["vendors"][1285] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1461] == nil then item["vendors"][1461] = 0 end + if item["vendors"][1469] == nil then item["vendors"][1469] = 0 end + if item["vendors"][1682] == nil then item["vendors"][1682] = 0 end + if item["vendors"][1685] == nil then item["vendors"][1685] = 0 end + if item["vendors"][1686] == nil then item["vendors"][1686] = 0 end + if item["vendors"][1691] == nil then item["vendors"][1691] = 0 end + if item["vendors"][2115] == nil then item["vendors"][2115] = 0 end + if item["vendors"][2134] == nil then item["vendors"][2134] = 0 end + if item["vendors"][2140] == nil then item["vendors"][2140] = 0 end + if item["vendors"][2685] == nil then item["vendors"][2685] = 0 end + if item["vendors"][2997] == nil then item["vendors"][2997] = 0 end + if item["vendors"][3018] == nil then item["vendors"][3018] = 0 end + if item["vendors"][3053] == nil then item["vendors"][3053] = 0 end + if item["vendors"][3072] == nil then item["vendors"][3072] = 0 end + if item["vendors"][3076] == nil then item["vendors"][3076] = 0 end + if item["vendors"][3078] == nil then item["vendors"][3078] = 0 end + if item["vendors"][3088] == nil then item["vendors"][3088] = 0 end + if item["vendors"][3158] == nil then item["vendors"][3158] = 0 end + if item["vendors"][3164] == nil then item["vendors"][3164] = 0 end + if item["vendors"][3186] == nil then item["vendors"][3186] = 0 end + if item["vendors"][3313] == nil then item["vendors"][3313] = 0 end + if item["vendors"][3322] == nil then item["vendors"][3322] = 0 end + if item["vendors"][3481] == nil then item["vendors"][3481] = 0 end + if item["vendors"][3488] == nil then item["vendors"][3488] = 0 end + if item["vendors"][3498] == nil then item["vendors"][3498] = 0 end + if item["vendors"][3587] == nil then item["vendors"][3587] = 0 end + if item["vendors"][3608] == nil then item["vendors"][3608] = 0 end + if item["vendors"][4082] == nil then item["vendors"][4082] = 0 end + if item["vendors"][4084] == nil then item["vendors"][4084] = 0 end + if item["vendors"][4170] == nil then item["vendors"][4170] = 0 end + if item["vendors"][4182] == nil then item["vendors"][4182] = 0 end + if item["vendors"][4241] == nil then item["vendors"][4241] = 0 end + if item["vendors"][4555] == nil then item["vendors"][4555] = 0 end + if item["vendors"][4603] == nil then item["vendors"][4603] = 0 end + if item["vendors"][4889] == nil then item["vendors"][4889] = 0 end + if item["vendors"][5123] == nil then item["vendors"][5123] = 0 end + if item["vendors"][5510] == nil then item["vendors"][5510] = 0 end + if item["vendors"][6028] == nil then item["vendors"][6028] = 0 end + if item["vendors"][7942] == nil then item["vendors"][7942] = 0 end + if item["vendors"][7976] == nil then item["vendors"][7976] = 0 end + if item["vendors"][8131] == nil then item["vendors"][8131] = 0 end + if item["vendors"][8362] == nil then item["vendors"][8362] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][11184] == nil then item["vendors"][11184] = 0 end + if item["vendors"][12027] == nil then item["vendors"][12027] = 0 end + if item["vendors"][12960] == nil then item["vendors"][12960] = 0 end + if item["vendors"][21002] == nil then item["vendors"][21002] = 0 end + if item["vendors"][61445] == nil then item["vendors"][61445] = 0 end + if item["vendors"][61651] == nil then item["vendors"][61651] = 0 end + if item["vendors"][61743] == nil then item["vendors"][61743] = 0 end + if item["vendors"][61751] == nil then item["vendors"][61751] = 0 end + if item["vendors"][62086] == nil then item["vendors"][62086] = 0 end + if item["vendors"][62096] == nil then item["vendors"][62096] = 0 end + if item["vendors"][62402] == nil then item["vendors"][62402] = 0 end + if item["vendors"][62403] == nil then item["vendors"][62403] = 0 end + if item["vendors"][62409] == nil then item["vendors"][62409] = 0 end + if item["vendors"][62628] == nil then item["vendors"][62628] = 0 end + if item["vendors"][80102] == nil then item["vendors"][80102] = 0 end + if item["vendors"][80214] == nil then item["vendors"][80214] = 0 end + if item["vendors"][80267] == nil then item["vendors"][80267] = 0 end + if item["vendors"][91248] == nil then item["vendors"][91248] = 0 end + if item["vendors"][91725] == nil then item["vendors"][91725] = 0 end + if item["vendors"][92169] == nil then item["vendors"][92169] = 0 end + if item["vendors"][92217] == nil then item["vendors"][92217] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[2519] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][227] == nil then item["vendors"][227] = 0 end + if item["vendors"][491] == nil then item["vendors"][491] = 0 end + if item["vendors"][734] == nil then item["vendors"][734] = 0 end + if item["vendors"][791] == nil then item["vendors"][791] = 0 end + if item["vendors"][904] == nil then item["vendors"][904] = 0 end + if item["vendors"][1149] == nil then item["vendors"][1149] = 0 end + if item["vendors"][1243] == nil then item["vendors"][1243] = 0 end + if item["vendors"][1285] == nil then item["vendors"][1285] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1448] == nil then item["vendors"][1448] = 0 end + if item["vendors"][1452] == nil then item["vendors"][1452] = 0 end + if item["vendors"][1461] == nil then item["vendors"][1461] = 0 end + if item["vendors"][1469] == nil then item["vendors"][1469] = 0 end + if item["vendors"][1682] == nil then item["vendors"][1682] = 0 end + if item["vendors"][1685] == nil then item["vendors"][1685] = 0 end + if item["vendors"][1686] == nil then item["vendors"][1686] = 0 end + if item["vendors"][2084] == nil then item["vendors"][2084] = 0 end + if item["vendors"][2140] == nil then item["vendors"][2140] = 0 end + if item["vendors"][2401] == nil then item["vendors"][2401] = 0 end + if item["vendors"][2685] == nil then item["vendors"][2685] = 0 end + if item["vendors"][2803] == nil then item["vendors"][2803] = 0 end + if item["vendors"][2806] == nil then item["vendors"][2806] = 0 end + if item["vendors"][2808] == nil then item["vendors"][2808] = 0 end + if item["vendors"][2820] == nil then item["vendors"][2820] = 0 end + if item["vendors"][2908] == nil then item["vendors"][2908] = 0 end + if item["vendors"][2997] == nil then item["vendors"][2997] = 0 end + if item["vendors"][3018] == nil then item["vendors"][3018] = 0 end + if item["vendors"][3053] == nil then item["vendors"][3053] = 0 end + if item["vendors"][3078] == nil then item["vendors"][3078] = 0 end + if item["vendors"][3088] == nil then item["vendors"][3088] = 0 end + if item["vendors"][3313] == nil then item["vendors"][3313] = 0 end + if item["vendors"][3322] == nil then item["vendors"][3322] = 0 end + if item["vendors"][3350] == nil then item["vendors"][3350] = 0 end + if item["vendors"][3481] == nil then item["vendors"][3481] = 0 end + if item["vendors"][3488] == nil then item["vendors"][3488] = 0 end + if item["vendors"][3498] == nil then item["vendors"][3498] = 0 end + if item["vendors"][3541] == nil then item["vendors"][3541] = 0 end + if item["vendors"][3625] == nil then item["vendors"][3625] = 0 end + if item["vendors"][3962] == nil then item["vendors"][3962] = 0 end + if item["vendors"][4082] == nil then item["vendors"][4082] = 0 end + if item["vendors"][4084] == nil then item["vendors"][4084] = 0 end + if item["vendors"][4170] == nil then item["vendors"][4170] = 0 end + if item["vendors"][4182] == nil then item["vendors"][4182] = 0 end + if item["vendors"][4241] == nil then item["vendors"][4241] = 0 end + if item["vendors"][4555] == nil then item["vendors"][4555] = 0 end + if item["vendors"][4603] == nil then item["vendors"][4603] = 0 end + if item["vendors"][4876] == nil then item["vendors"][4876] = 0 end + if item["vendors"][4889] == nil then item["vendors"][4889] = 0 end + if item["vendors"][4896] == nil then item["vendors"][4896] = 0 end + if item["vendors"][5101] == nil then item["vendors"][5101] = 0 end + if item["vendors"][5123] == nil then item["vendors"][5123] = 0 end + if item["vendors"][5134] == nil then item["vendors"][5134] = 0 end + if item["vendors"][5510] == nil then item["vendors"][5510] = 0 end + if item["vendors"][6028] == nil then item["vendors"][6028] = 0 end + if item["vendors"][7942] == nil then item["vendors"][7942] = 0 end + if item["vendors"][7976] == nil then item["vendors"][7976] = 0 end + if item["vendors"][8131] == nil then item["vendors"][8131] = 0 end + if item["vendors"][8139] == nil then item["vendors"][8139] = 0 end + if item["vendors"][8362] == nil then item["vendors"][8362] = 0 end + if item["vendors"][9320] == nil then item["vendors"][9320] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][11038] == nil then item["vendors"][11038] = 0 end + if item["vendors"][11184] == nil then item["vendors"][11184] = 0 end + if item["vendors"][11555] == nil then item["vendors"][11555] = 0 end + if item["vendors"][12021] == nil then item["vendors"][12021] = 0 end + if item["vendors"][12027] == nil then item["vendors"][12027] = 0 end + if item["vendors"][12036] == nil then item["vendors"][12036] = 0 end + if item["vendors"][12246] == nil then item["vendors"][12246] = 0 end + if item["vendors"][12959] == nil then item["vendors"][12959] = 0 end + if item["vendors"][12960] == nil then item["vendors"][12960] = 0 end + if item["vendors"][14624] == nil then item["vendors"][14624] = 0 end + if item["vendors"][15174] == nil then item["vendors"][15174] = 0 end + if item["vendors"][21002] == nil then item["vendors"][21002] = 0 end + if item["vendors"][51656] == nil then item["vendors"][51656] = 0 end + if item["vendors"][60456] == nil then item["vendors"][60456] = 0 end + if item["vendors"][60641] == nil then item["vendors"][60641] = 0 end + if item["vendors"][60646] == nil then item["vendors"][60646] = 0 end + if item["vendors"][60653] == nil then item["vendors"][60653] = 0 end + if item["vendors"][60663] == nil then item["vendors"][60663] = 0 end + if item["vendors"][60740] == nil then item["vendors"][60740] = 0 end + if item["vendors"][60766] == nil then item["vendors"][60766] = 0 end + if item["vendors"][60790] == nil then item["vendors"][60790] = 0 end + if item["vendors"][60813] == nil then item["vendors"][60813] = 0 end + if item["vendors"][60966] == nil then item["vendors"][60966] = 0 end + if item["vendors"][60989] == nil then item["vendors"][60989] = 0 end + if item["vendors"][61058] == nil then item["vendors"][61058] = 0 end + if item["vendors"][61115] == nil then item["vendors"][61115] = 0 end + if item["vendors"][61140] == nil then item["vendors"][61140] = 0 end + if item["vendors"][61272] == nil then item["vendors"][61272] = 0 end + if item["vendors"][61288] == nil then item["vendors"][61288] = 0 end + if item["vendors"][61369] == nil then item["vendors"][61369] = 0 end + if item["vendors"][61440] == nil then item["vendors"][61440] = 0 end + if item["vendors"][61443] == nil then item["vendors"][61443] = 0 end + if item["vendors"][61445] == nil then item["vendors"][61445] = 0 end + if item["vendors"][61478] == nil then item["vendors"][61478] = 0 end + if item["vendors"][61523] == nil then item["vendors"][61523] = 0 end + if item["vendors"][61563] == nil then item["vendors"][61563] = 0 end + if item["vendors"][61651] == nil then item["vendors"][61651] = 0 end + if item["vendors"][61743] == nil then item["vendors"][61743] = 0 end + if item["vendors"][61751] == nil then item["vendors"][61751] = 0 end + if item["vendors"][61812] == nil then item["vendors"][61812] = 0 end + if item["vendors"][62086] == nil then item["vendors"][62086] = 0 end + if item["vendors"][62096] == nil then item["vendors"][62096] = 0 end + if item["vendors"][62148] == nil then item["vendors"][62148] = 0 end + if item["vendors"][62402] == nil then item["vendors"][62402] = 0 end + if item["vendors"][62403] == nil then item["vendors"][62403] = 0 end + if item["vendors"][62404] == nil then item["vendors"][62404] = 0 end + if item["vendors"][62409] == nil then item["vendors"][62409] = 0 end + if item["vendors"][62436] == nil then item["vendors"][62436] = 0 end + if item["vendors"][62437] == nil then item["vendors"][62437] = 0 end + if item["vendors"][62462] == nil then item["vendors"][62462] = 0 end + if item["vendors"][62469] == nil then item["vendors"][62469] = 0 end + if item["vendors"][62628] == nil then item["vendors"][62628] = 0 end + if item["vendors"][65002] == nil then item["vendors"][65002] = 0 end + if item["vendors"][65003] == nil then item["vendors"][65003] = 0 end + if item["vendors"][80267] == nil then item["vendors"][80267] = 0 end + if item["vendors"][80808] == nil then item["vendors"][80808] = 0 end + if item["vendors"][91403] == nil then item["vendors"][91403] = 0 end + if item["vendors"][91725] == nil then item["vendors"][91725] = 0 end + if item["vendors"][91868] == nil then item["vendors"][91868] = 0 end + if item["vendors"][91956] == nil then item["vendors"][91956] = 0 end + if item["vendors"][92169] == nil then item["vendors"][92169] = 0 end + if item["vendors"][92177] == nil then item["vendors"][92177] = 0 end + if item["vendors"][92200] == nil then item["vendors"][92200] = 0 end + if item["vendors"][92217] == nil then item["vendors"][92217] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[2550] + if not item then + item = {} + MTH_DS_AmmoItems[2550] = item + end + if not item["name"] then item["name"] = 'Monster - Bow, Short' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[2551] + if not item then + item = {} + MTH_DS_AmmoItems[2551] = item + end + if not item["name"] then item["name"] = 'Monster - Crossbow' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_02' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[2552] + if not item then + item = {} + MTH_DS_AmmoItems[2552] = item + end + if not item["name"] then item["name"] = 'Monster - Gun' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[2773] + if not item then + item = {} + MTH_DS_AmmoItems[2773] = item + end + if not item["name"] then item["name"] = 'Cracked Shortbow' end + if not item["level"] then item["level"] = 8 end + if not item["reqlevel"] then item["reqlevel"] = 3 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 2 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][1769] == nil then item["drops"][1769] = 1.42 end + if item["drops"][2965] == nil then item["drops"][2965] = 0.39 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2039] == nil then item["objects"][2039] = 2.32 end +end + +do + local item = MTH_DS_AmmoItems[2774] + if not item then + item = {} + MTH_DS_AmmoItems[2774] = item + end + if not item["name"] then item["name"] = 'Rust-covered Blunderbuss' end + if not item["level"] then item["level"] = 7 end + if not item["reqlevel"] then item["reqlevel"] = 2 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 1.9 end + if not item["speed"] then item["speed"] = 2.4 end +end + +do + local item = MTH_DS_AmmoItems[2777] + if not item then + item = {} + MTH_DS_AmmoItems[2777] = item + end + if not item["name"] then item["name"] = 'Feeble Shortbow' end + if not item["level"] then item["level"] = 13 end + if not item["reqlevel"] then item["reqlevel"] = 8 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 3.3 end + if not item["speed"] then item["speed"] = 1.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2039] == nil then item["objects"][2039] = 0.62 end +end + +do + local item = MTH_DS_AmmoItems[2778] + if not item then + item = {} + MTH_DS_AmmoItems[2778] = item + end + if not item["name"] then item["name"] = 'Cheap Blunderbuss' end + if not item["level"] then item["level"] = 13 end + if not item["reqlevel"] then item["reqlevel"] = 8 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 3.2 end + if not item["speed"] then item["speed"] = 2.2 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2039] == nil then item["objects"][2039] = 0.72 end +end + +do + local item = MTH_DS_AmmoItems[2780] + if not item then + item = {} + MTH_DS_AmmoItems[2780] = item + end + if not item["name"] then item["name"] = 'Light Hunting Bow' end + if not item["level"] then item["level"] = 19 end + if not item["reqlevel"] then item["reqlevel"] = 14 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 4.4 end + if not item["speed"] then item["speed"] = 1.7 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][3642] == nil then item["objects"][3642] = 4.4 end + if item["objects"][152608] == nil then item["objects"][152608] = 4.4 end + if item["objects"][152618] == nil then item["objects"][152618] = 4.4 end +end + +do + local item = MTH_DS_AmmoItems[2781] + if not item then + item = {} + MTH_DS_AmmoItems[2781] = item + end + if not item["name"] then item["name"] = 'Dirty Blunderbuss' end + if not item["level"] then item["level"] = 18 end + if not item["reqlevel"] then item["reqlevel"] = 13 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 4.1 end + if not item["speed"] then item["speed"] = 2.2 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][3642] == nil then item["objects"][3642] = 3.3 end + if item["objects"][152608] == nil then item["objects"][152608] = 3.3 end + if item["objects"][152618] == nil then item["objects"][152618] = 3.3 end +end + +do + local item = MTH_DS_AmmoItems[2782] + if not item then + item = {} + MTH_DS_AmmoItems[2782] = item + end + if not item["name"] then item["name"] = 'Mishandled Recurve Bow' end + if not item["level"] then item["level"] = 24 end + if not item["reqlevel"] then item["reqlevel"] = 19 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_03' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 5.6 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][4279] == nil then item["drops"][4279] = 0.32 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.12 end +end + +do + local item = MTH_DS_AmmoItems[2783] + if not item then + item = {} + MTH_DS_AmmoItems[2783] = item + end + if not item["name"] then item["name"] = 'Shoddy Blunderbuss' end + if not item["level"] then item["level"] = 22 end + if not item["reqlevel"] then item["reqlevel"] = 17 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 5 end + if not item["speed"] then item["speed"] = 2.1 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.14 end +end + +do + local item = MTH_DS_AmmoItems[2785] + if not item then + item = {} + MTH_DS_AmmoItems[2785] = item + end + if not item["name"] then item["name"] = 'Stiff Recurve Bow' end + if not item["level"] then item["level"] = 28 end + if not item["reqlevel"] then item["reqlevel"] = 23 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 6.8 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.03 end +end + +do + local item = MTH_DS_AmmoItems[2786] + if not item then + item = {} + MTH_DS_AmmoItems[2786] = item + end + if not item["name"] then item["name"] = 'Oiled Blunderbuss' end + if not item["level"] then item["level"] = 29 end + if not item["reqlevel"] then item["reqlevel"] = 24 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 7 end + if not item["speed"] then item["speed"] = 2 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.03 end +end + +do + local item = MTH_DS_AmmoItems[2824] + if not item then + item = {} + MTH_DS_AmmoItems[2824] = item + end + if not item["name"] then item["name"] = 'Hurricane' end + if not item["level"] then item["level"] = 53 end + if not item["reqlevel"] then item["reqlevel"] = 48 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_12' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 30.3 end + if not item["speed"] then item["speed"] = 1.6 end +end + +do + local item = MTH_DS_AmmoItems[2825] + if not item then + item = {} + MTH_DS_AmmoItems[2825] = item + end + if not item["name"] then item["name"] = 'Bow of Searing Arrows' end + if not item["level"] then item["level"] = 42 end + if not item["reqlevel"] then item["reqlevel"] = 37 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_09' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 25 end + if not item["speed"] then item["speed"] = 2.7 end +end + +do + local item = MTH_DS_AmmoItems[2903] + if not item then + item = {} + MTH_DS_AmmoItems[2903] = item + end + if not item["name"] then item["name"] = "Daryl's Hunting Bow" end + if not item["level"] then item["level"] = 15 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 5.9 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[2904] + if not item then + item = {} + MTH_DS_AmmoItems[2904] = item + end + if not item["name"] then item["name"] = "Daryl's Hunting Rifle" end + if not item["level"] then item["level"] = 16 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 6.4 end + if not item["speed"] then item["speed"] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[3021] + if not item then + item = {} + MTH_DS_AmmoItems[3021] = item + end + if not item["name"] then item["name"] = 'Ranger Bow' end + if not item["level"] then item["level"] = 25 end + if not item["reqlevel"] then item["reqlevel"] = 20 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 12.6 end + if not item["speed"] then item["speed"] = 2.7 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2849] == nil then item["objects"][2849] = 0.01 end + if item["objects"][2850] == nil then item["objects"][2850] = 0.01 end + if item["objects"][2852] == nil then item["objects"][2852] = 0.01 end + if item["objects"][2855] == nil then item["objects"][2855] = 0.01 end + if item["objects"][3714] == nil then item["objects"][3714] = 0.01 end + if item["objects"][3715] == nil then item["objects"][3715] = 0.01 end + if item["objects"][4095] == nil then item["objects"][4095] = 0.01 end + if item["objects"][4096] == nil then item["objects"][4096] = 0.01 end + if item["objects"][74447] == nil then item["objects"][74447] = 0.01 end + if item["objects"][74448] == nil then item["objects"][74448] = 0.01 end + if item["objects"][75295] == nil then item["objects"][75295] = 0.01 end + if item["objects"][75296] == nil then item["objects"][75296] = 0.01 end + if item["objects"][75297] == nil then item["objects"][75297] = 0.01 end + if item["objects"][75298] == nil then item["objects"][75298] = 0.01 end + if item["objects"][75299] == nil then item["objects"][75299] = 0.01 end + if item["objects"][75300] == nil then item["objects"][75300] = 0.01 end + if item["objects"][105570] == nil then item["objects"][105570] = 0.01 end + if item["objects"][105578] == nil then item["objects"][105578] = 0.01 end + if item["objects"][105579] == nil then item["objects"][105579] = 0.01 end + if item["objects"][106319] == nil then item["objects"][106319] = 0.01 end + if item["objects"][111095] == nil then item["objects"][111095] = 0.01 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.01 end +end + +do + local item = MTH_DS_AmmoItems[3023] + if not item then + item = {} + MTH_DS_AmmoItems[3023] = item + end + if not item["name"] then item["name"] = 'Large Bore Blunderbuss' end + if not item["level"] then item["level"] = 21 end + if not item["reqlevel"] then item["reqlevel"] = 16 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 7.4 end + if not item["speed"] then item["speed"] = 2.5 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1461] == nil then item["vendors"][1461] = 0 end + if item["vendors"][1686] == nil then item["vendors"][1686] = 0 end + if item["vendors"][3018] == nil then item["vendors"][3018] = 0 end + if item["vendors"][3053] == nil then item["vendors"][3053] = 0 end + if item["vendors"][3088] == nil then item["vendors"][3088] = 0 end + if item["vendors"][3322] == nil then item["vendors"][3322] = 0 end + if item["vendors"][4603] == nil then item["vendors"][4603] = 0 end + if item["vendors"][4889] == nil then item["vendors"][4889] = 0 end + if item["vendors"][5123] == nil then item["vendors"][5123] = 0 end + if item["vendors"][8131] == nil then item["vendors"][8131] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][11184] == nil then item["vendors"][11184] = 0 end + if item["vendors"][61445] == nil then item["vendors"][61445] = 0 end + if item["vendors"][62403] == nil then item["vendors"][62403] = 0 end + if item["vendors"][92217] == nil then item["vendors"][92217] = 0 end +end + +do + local item = MTH_DS_AmmoItems[3024] + if not item then + item = {} + MTH_DS_AmmoItems[3024] = item + end + if not item["name"] then item["name"] = 'BKP 2700 "Enforcer"' end + if not item["level"] then item["level"] = 26 end + if not item["reqlevel"] then item["reqlevel"] = 21 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 9.6 end + if not item["speed"] then item["speed"] = 2.7 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1461] == nil then item["vendors"][1461] = 0 end + if item["vendors"][3018] == nil then item["vendors"][3018] = 0 end + if item["vendors"][3053] == nil then item["vendors"][3053] = 0 end + if item["vendors"][3322] == nil then item["vendors"][3322] = 0 end + if item["vendors"][4603] == nil then item["vendors"][4603] = 0 end + if item["vendors"][4889] == nil then item["vendors"][4889] = 0 end + if item["vendors"][5123] == nil then item["vendors"][5123] = 0 end + if item["vendors"][8131] == nil then item["vendors"][8131] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][11184] == nil then item["vendors"][11184] = 0 end + if item["vendors"][61445] == nil then item["vendors"][61445] = 0 end + if item["vendors"][61751] == nil then item["vendors"][61751] = 0 end + if item["vendors"][62403] == nil then item["vendors"][62403] = 0 end + if item["vendors"][92217] == nil then item["vendors"][92217] = 0 end +end + +do + local item = MTH_DS_AmmoItems[3025] + if not item then + item = {} + MTH_DS_AmmoItems[3025] = item + end + if not item["name"] then item["name"] = 'BKP 42 "Ultra"' end + if not item["level"] then item["level"] = 36 end + if not item["reqlevel"] then item["reqlevel"] = 31 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 13.8 end + if not item["speed"] then item["speed"] = 2.1 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][3322] == nil then item["vendors"][3322] = 0 end + if item["vendors"][8131] == nil then item["vendors"][8131] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][11184] == nil then item["vendors"][11184] = 0 end +end + +do + local item = MTH_DS_AmmoItems[3026] + if not item then + item = {} + MTH_DS_AmmoItems[3026] = item + end + if not item["name"] then item["name"] = 'Reinforced Bow' end + if not item["level"] then item["level"] = 21 end + if not item["reqlevel"] then item["reqlevel"] = 16 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 7.5 end + if not item["speed"] then item["speed"] = 2.2 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][228] == nil then item["vendors"][228] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1298] == nil then item["vendors"][1298] = 0 end + if item["vendors"][1455] == nil then item["vendors"][1455] = 0 end + if item["vendors"][1459] == nil then item["vendors"][1459] = 0 end + if item["vendors"][1668] == nil then item["vendors"][1668] = 0 end + if item["vendors"][1687] == nil then item["vendors"][1687] = 0 end + if item["vendors"][2286] == nil then item["vendors"][2286] = 0 end + if item["vendors"][2839] == nil then item["vendors"][2839] = 0 end + if item["vendors"][3015] == nil then item["vendors"][3015] = 0 end + if item["vendors"][3409] == nil then item["vendors"][3409] = 0 end + if item["vendors"][3410] == nil then item["vendors"][3410] = 0 end + if item["vendors"][3488] == nil then item["vendors"][3488] = 0 end + if item["vendors"][3951] == nil then item["vendors"][3951] = 0 end + if item["vendors"][4173] == nil then item["vendors"][4173] = 0 end + if item["vendors"][4203] == nil then item["vendors"][4203] = 0 end + if item["vendors"][4604] == nil then item["vendors"][4604] = 0 end + if item["vendors"][4892] == nil then item["vendors"][4892] = 0 end + if item["vendors"][5122] == nil then item["vendors"][5122] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9549] == nil then item["vendors"][9549] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][9552] == nil then item["vendors"][9552] = 0 end + if item["vendors"][9553] == nil then item["vendors"][9553] = 0 end + if item["vendors"][9555] == nil then item["vendors"][9555] = 0 end + if item["vendors"][12029] == nil then item["vendors"][12029] = 0 end + if item["vendors"][14301] == nil then item["vendors"][14301] = 0 end + if item["vendors"][60659] == nil then item["vendors"][60659] = 0 end + if item["vendors"][62739] == nil then item["vendors"][62739] = 0 end + if item["vendors"][91977] == nil then item["vendors"][91977] = 0 end +end + +do + local item = MTH_DS_AmmoItems[3027] + if not item then + item = {} + MTH_DS_AmmoItems[3027] = item + end + if not item["name"] then item["name"] = 'Heavy Recurve Bow' end + if not item["level"] then item["level"] = 25 end + if not item["reqlevel"] then item["reqlevel"] = 20 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 9.2 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][228] == nil then item["vendors"][228] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1298] == nil then item["vendors"][1298] = 0 end + if item["vendors"][1455] == nil then item["vendors"][1455] = 0 end + if item["vendors"][1459] == nil then item["vendors"][1459] = 0 end + if item["vendors"][2286] == nil then item["vendors"][2286] = 0 end + if item["vendors"][2839] == nil then item["vendors"][2839] = 0 end + if item["vendors"][3015] == nil then item["vendors"][3015] = 0 end + if item["vendors"][3410] == nil then item["vendors"][3410] = 0 end + if item["vendors"][4173] == nil then item["vendors"][4173] = 0 end + if item["vendors"][4604] == nil then item["vendors"][4604] = 0 end + if item["vendors"][4892] == nil then item["vendors"][4892] = 0 end + if item["vendors"][5122] == nil then item["vendors"][5122] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][9552] == nil then item["vendors"][9552] = 0 end + if item["vendors"][9555] == nil then item["vendors"][9555] = 0 end + if item["vendors"][12029] == nil then item["vendors"][12029] = 0 end + if item["vendors"][14301] == nil then item["vendors"][14301] = 0 end + if item["vendors"][62739] == nil then item["vendors"][62739] = 0 end +end + +do + local item = MTH_DS_AmmoItems[3028] + if not item then + item = {} + MTH_DS_AmmoItems[3028] = item + end + if not item["name"] then item["name"] = 'Longbow' end + if not item["level"] then item["level"] = 34 end + if not item["reqlevel"] then item["reqlevel"] = 29 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_07' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 13 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[3030] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][227] == nil then item["vendors"][227] = 0 end + if item["vendors"][228] == nil then item["vendors"][228] = 0 end + if item["vendors"][734] == nil then item["vendors"][734] = 0 end + if item["vendors"][789] == nil then item["vendors"][789] = 0 end + if item["vendors"][1149] == nil then item["vendors"][1149] = 0 end + if item["vendors"][1285] == nil then item["vendors"][1285] = 0 end + if item["vendors"][1287] == nil then item["vendors"][1287] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1298] == nil then item["vendors"][1298] = 0 end + if item["vendors"][1448] == nil then item["vendors"][1448] = 0 end + if item["vendors"][1452] == nil then item["vendors"][1452] = 0 end + if item["vendors"][1455] == nil then item["vendors"][1455] = 0 end + if item["vendors"][1459] == nil then item["vendors"][1459] = 0 end + if item["vendors"][1462] == nil then item["vendors"][1462] = 0 end + if item["vendors"][2084] == nil then item["vendors"][2084] = 0 end + if item["vendors"][2286] == nil then item["vendors"][2286] = 0 end + if item["vendors"][2401] == nil then item["vendors"][2401] = 0 end + if item["vendors"][2803] == nil then item["vendors"][2803] = 0 end + if item["vendors"][2806] == nil then item["vendors"][2806] = 0 end + if item["vendors"][2808] == nil then item["vendors"][2808] = 0 end + if item["vendors"][2820] == nil then item["vendors"][2820] = 0 end + if item["vendors"][2839] == nil then item["vendors"][2839] = 0 end + if item["vendors"][2908] == nil then item["vendors"][2908] = 0 end + if item["vendors"][3015] == nil then item["vendors"][3015] = 0 end + if item["vendors"][3313] == nil then item["vendors"][3313] = 0 end + if item["vendors"][3350] == nil then item["vendors"][3350] = 0 end + if item["vendors"][3410] == nil then item["vendors"][3410] = 0 end + if item["vendors"][3541] == nil then item["vendors"][3541] = 0 end + if item["vendors"][3625] == nil then item["vendors"][3625] = 0 end + if item["vendors"][3962] == nil then item["vendors"][3962] = 0 end + if item["vendors"][4084] == nil then item["vendors"][4084] = 0 end + if item["vendors"][4170] == nil then item["vendors"][4170] = 0 end + if item["vendors"][4173] == nil then item["vendors"][4173] = 0 end + if item["vendors"][4241] == nil then item["vendors"][4241] = 0 end + if item["vendors"][4555] == nil then item["vendors"][4555] = 0 end + if item["vendors"][4602] == nil then item["vendors"][4602] = 0 end + if item["vendors"][4604] == nil then item["vendors"][4604] = 0 end + if item["vendors"][4876] == nil then item["vendors"][4876] = 0 end + if item["vendors"][4892] == nil then item["vendors"][4892] = 0 end + if item["vendors"][4896] == nil then item["vendors"][4896] = 0 end + if item["vendors"][5101] == nil then item["vendors"][5101] = 0 end + if item["vendors"][5122] == nil then item["vendors"][5122] = 0 end + if item["vendors"][5134] == nil then item["vendors"][5134] = 0 end + if item["vendors"][6028] == nil then item["vendors"][6028] = 0 end + if item["vendors"][7942] == nil then item["vendors"][7942] = 0 end + if item["vendors"][7976] == nil then item["vendors"][7976] = 0 end + if item["vendors"][8139] == nil then item["vendors"][8139] = 0 end + if item["vendors"][8362] == nil then item["vendors"][8362] = 0 end + if item["vendors"][9320] == nil then item["vendors"][9320] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][9552] == nil then item["vendors"][9552] = 0 end + if item["vendors"][9555] == nil then item["vendors"][9555] = 0 end + if item["vendors"][11038] == nil then item["vendors"][11038] = 0 end + if item["vendors"][11555] == nil then item["vendors"][11555] = 0 end + if item["vendors"][12021] == nil then item["vendors"][12021] = 0 end + if item["vendors"][12027] == nil then item["vendors"][12027] = 0 end + if item["vendors"][12029] == nil then item["vendors"][12029] = 0 end + if item["vendors"][12036] == nil then item["vendors"][12036] = 0 end + if item["vendors"][12246] == nil then item["vendors"][12246] = 0 end + if item["vendors"][12959] == nil then item["vendors"][12959] = 0 end + if item["vendors"][12960] == nil then item["vendors"][12960] = 0 end + if item["vendors"][14301] == nil then item["vendors"][14301] = 0 end + if item["vendors"][14624] == nil then item["vendors"][14624] = 0 end + if item["vendors"][15174] == nil then item["vendors"][15174] = 0 end + if item["vendors"][17598] == nil then item["vendors"][17598] = 0 end + if item["vendors"][21002] == nil then item["vendors"][21002] = 0 end + if item["vendors"][51656] == nil then item["vendors"][51656] = 0 end + if item["vendors"][60456] == nil then item["vendors"][60456] = 0 end + if item["vendors"][60641] == nil then item["vendors"][60641] = 0 end + if item["vendors"][60646] == nil then item["vendors"][60646] = 0 end + if item["vendors"][60653] == nil then item["vendors"][60653] = 0 end + if item["vendors"][60663] == nil then item["vendors"][60663] = 0 end + if item["vendors"][60740] == nil then item["vendors"][60740] = 0 end + if item["vendors"][60766] == nil then item["vendors"][60766] = 0 end + if item["vendors"][60790] == nil then item["vendors"][60790] = 0 end + if item["vendors"][60803] == nil then item["vendors"][60803] = 0 end + if item["vendors"][60813] == nil then item["vendors"][60813] = 0 end + if item["vendors"][60966] == nil then item["vendors"][60966] = 0 end + if item["vendors"][60989] == nil then item["vendors"][60989] = 0 end + if item["vendors"][61058] == nil then item["vendors"][61058] = 0 end + if item["vendors"][61115] == nil then item["vendors"][61115] = 0 end + if item["vendors"][61140] == nil then item["vendors"][61140] = 0 end + if item["vendors"][61272] == nil then item["vendors"][61272] = 0 end + if item["vendors"][61288] == nil then item["vendors"][61288] = 0 end + if item["vendors"][61369] == nil then item["vendors"][61369] = 0 end + if item["vendors"][61440] == nil then item["vendors"][61440] = 0 end + if item["vendors"][61443] == nil then item["vendors"][61443] = 0 end + if item["vendors"][61478] == nil then item["vendors"][61478] = 0 end + if item["vendors"][61523] == nil then item["vendors"][61523] = 0 end + if item["vendors"][61563] == nil then item["vendors"][61563] = 0 end + if item["vendors"][61651] == nil then item["vendors"][61651] = 0 end + if item["vendors"][61743] == nil then item["vendors"][61743] = 0 end + if item["vendors"][61812] == nil then item["vendors"][61812] = 0 end + if item["vendors"][62086] == nil then item["vendors"][62086] = 0 end + if item["vendors"][62096] == nil then item["vendors"][62096] = 0 end + if item["vendors"][62148] == nil then item["vendors"][62148] = 0 end + if item["vendors"][62307] == nil then item["vendors"][62307] = 0 end + if item["vendors"][62402] == nil then item["vendors"][62402] = 0 end + if item["vendors"][62404] == nil then item["vendors"][62404] = 0 end + if item["vendors"][62409] == nil then item["vendors"][62409] = 0 end + if item["vendors"][62436] == nil then item["vendors"][62436] = 0 end + if item["vendors"][62437] == nil then item["vendors"][62437] = 0 end + if item["vendors"][62462] == nil then item["vendors"][62462] = 0 end + if item["vendors"][62469] == nil then item["vendors"][62469] = 0 end + if item["vendors"][62628] == nil then item["vendors"][62628] = 0 end + if item["vendors"][62739] == nil then item["vendors"][62739] = 0 end + if item["vendors"][65002] == nil then item["vendors"][65002] = 0 end + if item["vendors"][65003] == nil then item["vendors"][65003] = 0 end + if item["vendors"][80267] == nil then item["vendors"][80267] = 0 end + if item["vendors"][80808] == nil then item["vendors"][80808] = 0 end + if item["vendors"][91403] == nil then item["vendors"][91403] = 0 end + if item["vendors"][91725] == nil then item["vendors"][91725] = 0 end + if item["vendors"][91868] == nil then item["vendors"][91868] = 0 end + if item["vendors"][91956] == nil then item["vendors"][91956] = 0 end + if item["vendors"][92169] == nil then item["vendors"][92169] = 0 end + if item["vendors"][92177] == nil then item["vendors"][92177] = 0 end + if item["vendors"][92200] == nil then item["vendors"][92200] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[3033] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][227] == nil then item["vendors"][227] = 0 end + if item["vendors"][734] == nil then item["vendors"][734] = 0 end + if item["vendors"][1149] == nil then item["vendors"][1149] = 0 end + if item["vendors"][1285] == nil then item["vendors"][1285] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1448] == nil then item["vendors"][1448] = 0 end + if item["vendors"][1452] == nil then item["vendors"][1452] = 0 end + if item["vendors"][1461] == nil then item["vendors"][1461] = 0 end + if item["vendors"][1686] == nil then item["vendors"][1686] = 0 end + if item["vendors"][2084] == nil then item["vendors"][2084] = 0 end + if item["vendors"][2401] == nil then item["vendors"][2401] = 0 end + if item["vendors"][2685] == nil then item["vendors"][2685] = 0 end + if item["vendors"][2803] == nil then item["vendors"][2803] = 0 end + if item["vendors"][2806] == nil then item["vendors"][2806] = 0 end + if item["vendors"][2808] == nil then item["vendors"][2808] = 0 end + if item["vendors"][2820] == nil then item["vendors"][2820] = 0 end + if item["vendors"][2908] == nil then item["vendors"][2908] = 0 end + if item["vendors"][3018] == nil then item["vendors"][3018] = 0 end + if item["vendors"][3053] == nil then item["vendors"][3053] = 0 end + if item["vendors"][3088] == nil then item["vendors"][3088] = 0 end + if item["vendors"][3313] == nil then item["vendors"][3313] = 0 end + if item["vendors"][3322] == nil then item["vendors"][3322] = 0 end + if item["vendors"][3350] == nil then item["vendors"][3350] = 0 end + if item["vendors"][3541] == nil then item["vendors"][3541] = 0 end + if item["vendors"][3625] == nil then item["vendors"][3625] = 0 end + if item["vendors"][3962] == nil then item["vendors"][3962] = 0 end + if item["vendors"][4170] == nil then item["vendors"][4170] = 0 end + if item["vendors"][4241] == nil then item["vendors"][4241] = 0 end + if item["vendors"][4555] == nil then item["vendors"][4555] = 0 end + if item["vendors"][4603] == nil then item["vendors"][4603] = 0 end + if item["vendors"][4876] == nil then item["vendors"][4876] = 0 end + if item["vendors"][4889] == nil then item["vendors"][4889] = 0 end + if item["vendors"][4896] == nil then item["vendors"][4896] = 0 end + if item["vendors"][5101] == nil then item["vendors"][5101] = 0 end + if item["vendors"][5123] == nil then item["vendors"][5123] = 0 end + if item["vendors"][5134] == nil then item["vendors"][5134] = 0 end + if item["vendors"][6028] == nil then item["vendors"][6028] = 0 end + if item["vendors"][7942] == nil then item["vendors"][7942] = 0 end + if item["vendors"][8131] == nil then item["vendors"][8131] = 0 end + if item["vendors"][8139] == nil then item["vendors"][8139] = 0 end + if item["vendors"][8362] == nil then item["vendors"][8362] = 0 end + if item["vendors"][9320] == nil then item["vendors"][9320] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][11038] == nil then item["vendors"][11038] = 0 end + if item["vendors"][11184] == nil then item["vendors"][11184] = 0 end + if item["vendors"][11555] == nil then item["vendors"][11555] = 0 end + if item["vendors"][12021] == nil then item["vendors"][12021] = 0 end + if item["vendors"][12027] == nil then item["vendors"][12027] = 0 end + if item["vendors"][12036] == nil then item["vendors"][12036] = 0 end + if item["vendors"][12246] == nil then item["vendors"][12246] = 0 end + if item["vendors"][12959] == nil then item["vendors"][12959] = 0 end + if item["vendors"][12960] == nil then item["vendors"][12960] = 0 end + if item["vendors"][14624] == nil then item["vendors"][14624] = 0 end + if item["vendors"][15174] == nil then item["vendors"][15174] = 0 end + if item["vendors"][21002] == nil then item["vendors"][21002] = 0 end + if item["vendors"][51656] == nil then item["vendors"][51656] = 0 end + if item["vendors"][60456] == nil then item["vendors"][60456] = 0 end + if item["vendors"][60641] == nil then item["vendors"][60641] = 0 end + if item["vendors"][60646] == nil then item["vendors"][60646] = 0 end + if item["vendors"][60653] == nil then item["vendors"][60653] = 0 end + if item["vendors"][60663] == nil then item["vendors"][60663] = 0 end + if item["vendors"][60740] == nil then item["vendors"][60740] = 0 end + if item["vendors"][60766] == nil then item["vendors"][60766] = 0 end + if item["vendors"][60790] == nil then item["vendors"][60790] = 0 end + if item["vendors"][60813] == nil then item["vendors"][60813] = 0 end + if item["vendors"][60966] == nil then item["vendors"][60966] = 0 end + if item["vendors"][60989] == nil then item["vendors"][60989] = 0 end + if item["vendors"][61058] == nil then item["vendors"][61058] = 0 end + if item["vendors"][61115] == nil then item["vendors"][61115] = 0 end + if item["vendors"][61140] == nil then item["vendors"][61140] = 0 end + if item["vendors"][61272] == nil then item["vendors"][61272] = 0 end + if item["vendors"][61288] == nil then item["vendors"][61288] = 0 end + if item["vendors"][61369] == nil then item["vendors"][61369] = 0 end + if item["vendors"][61440] == nil then item["vendors"][61440] = 0 end + if item["vendors"][61443] == nil then item["vendors"][61443] = 0 end + if item["vendors"][61445] == nil then item["vendors"][61445] = 0 end + if item["vendors"][61478] == nil then item["vendors"][61478] = 0 end + if item["vendors"][61523] == nil then item["vendors"][61523] = 0 end + if item["vendors"][61563] == nil then item["vendors"][61563] = 0 end + if item["vendors"][61651] == nil then item["vendors"][61651] = 0 end + if item["vendors"][61743] == nil then item["vendors"][61743] = 0 end + if item["vendors"][61812] == nil then item["vendors"][61812] = 0 end + if item["vendors"][62086] == nil then item["vendors"][62086] = 0 end + if item["vendors"][62096] == nil then item["vendors"][62096] = 0 end + if item["vendors"][62148] == nil then item["vendors"][62148] = 0 end + if item["vendors"][62402] == nil then item["vendors"][62402] = 0 end + if item["vendors"][62403] == nil then item["vendors"][62403] = 0 end + if item["vendors"][62404] == nil then item["vendors"][62404] = 0 end + if item["vendors"][62409] == nil then item["vendors"][62409] = 0 end + if item["vendors"][62436] == nil then item["vendors"][62436] = 0 end + if item["vendors"][62437] == nil then item["vendors"][62437] = 0 end + if item["vendors"][62462] == nil then item["vendors"][62462] = 0 end + if item["vendors"][62469] == nil then item["vendors"][62469] = 0 end + if item["vendors"][62628] == nil then item["vendors"][62628] = 0 end + if item["vendors"][65002] == nil then item["vendors"][65002] = 0 end + if item["vendors"][65003] == nil then item["vendors"][65003] = 0 end + if item["vendors"][80267] == nil then item["vendors"][80267] = 0 end + if item["vendors"][80808] == nil then item["vendors"][80808] = 0 end + if item["vendors"][91403] == nil then item["vendors"][91403] = 0 end + if item["vendors"][91725] == nil then item["vendors"][91725] = 0 end + if item["vendors"][91868] == nil then item["vendors"][91868] = 0 end + if item["vendors"][91956] == nil then item["vendors"][91956] = 0 end + if item["vendors"][92169] == nil then item["vendors"][92169] = 0 end + if item["vendors"][92177] == nil then item["vendors"][92177] = 0 end + if item["vendors"][92200] == nil then item["vendors"][92200] = 0 end + if item["vendors"][92217] == nil then item["vendors"][92217] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[3036] + if not item then + item = {} + MTH_DS_AmmoItems[3036] = item + end + if not item["name"] then item["name"] = 'Heavy Shortbow' end + if not item["level"] then item["level"] = 15 end + if not item["reqlevel"] then item["reqlevel"] = 10 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_12' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 6 end + if not item["speed"] then item["speed"] = 2.5 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2039] == nil then item["objects"][2039] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[3037] + if not item then + item = {} + MTH_DS_AmmoItems[3037] = item + end + if not item["name"] then item["name"] = 'Whipwood Recurve Bow' end + if not item["level"] then item["level"] = 34 end + if not item["reqlevel"] then item["reqlevel"] = 29 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_12' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 13.6 end + if not item["speed"] then item["speed"] = 1.8 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][7073] == nil then item["drops"][7073] = 0.22 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.44 end +end + +do + local item = MTH_DS_AmmoItems[3039] + if not item then + item = {} + MTH_DS_AmmoItems[3039] = item + end + if not item["name"] then item["name"] = 'Short Ash Bow' end + if not item["level"] then item["level"] = 23 end + if not item["reqlevel"] then item["reqlevel"] = 18 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_03' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 9.2 end + if not item["speed"] then item["speed"] = 1.9 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.19 end +end + +do + local item = MTH_DS_AmmoItems[3040] + if not item then + item = {} + MTH_DS_AmmoItems[3040] = item + end + if not item["name"] then item["name"] = "Hunter's Muzzle Loader" end + if not item["level"] then item["level"] = 19 end + if not item["reqlevel"] then item["reqlevel"] = 14 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 7.5 end + if not item["speed"] then item["speed"] = 1.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][3642] == nil then item["objects"][3642] = 1.6 end + if item["objects"][152608] == nil then item["objects"][152608] = 1.6 end + if item["objects"][152618] == nil then item["objects"][152618] = 1.6 end +end + +do + local item = MTH_DS_AmmoItems[3041] + if not item then + item = {} + MTH_DS_AmmoItems[3041] = item + end + if not item["name"] then item["name"] = '"Mage-Eye" Blunderbuss' end + if not item["level"] then item["level"] = 31 end + if not item["reqlevel"] then item["reqlevel"] = 26 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_05' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 12.5 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.53 end +end + +do + local item = MTH_DS_AmmoItems[3042] + if not item then + item = {} + MTH_DS_AmmoItems[3042] = item + end + if not item["name"] then item["name"] = 'BKP "Sparrow" Smallbore' end + if not item["level"] then item["level"] = 33 end + if not item["reqlevel"] then item["reqlevel"] = 28 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 13.2 end + if not item["speed"] then item["speed"] = 1.7 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.42 end +end + +do + local item = MTH_DS_AmmoItems[3078] + if not item then + item = {} + MTH_DS_AmmoItems[3078] = item + end + if not item["name"] then item["name"] = 'Naga Heartpiercer' end + if not item["level"] then item["level"] = 26 end + if not item["reqlevel"] then item["reqlevel"] = 21 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_04' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 10.6 end + if not item["speed"] then item["speed"] = 1.8 end +end + +do + local item = MTH_DS_AmmoItems[3079] + if not item then + item = {} + MTH_DS_AmmoItems[3079] = item + end + if not item["name"] then item["name"] = "Skorn's Rifle" end + if not item["level"] then item["level"] = 12 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 5 end + if not item["speed"] then item["speed"] = 1.9 end +end + +do + local item = MTH_DS_AmmoItems[3430] + if not item then + item = {} + MTH_DS_AmmoItems[3430] = item + end + if not item["name"] then item["name"] = 'Sniper Rifle' end + if not item["level"] then item["level"] = 44 end + if not item["reqlevel"] then item["reqlevel"] = 39 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_05' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 20.2 end + if not item["speed"] then item["speed"] = 3 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][141979] == nil then item["objects"][141979] = 2.3 end + if item["objects"][142184] == nil then item["objects"][142184] = 2.1 end +end + +do + local item = MTH_DS_AmmoItems[3493] + if not item then + item = {} + MTH_DS_AmmoItems[3493] = item + end + if not item["name"] then item["name"] = "Raptor's End" end + if not item["level"] then item["level"] = 30 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_03' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 12.1 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[3567] + if not item then + item = {} + MTH_DS_AmmoItems[3567] = item + end + if not item["name"] then item["name"] = 'Dwarven Fishing Pole' end + if not item["level"] then item["level"] = 19 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 7.4 end + if not item["speed"] then item["speed"] = 1.9 end +end + +do + local item = MTH_DS_AmmoItems[3742] + if not item then + item = {} + MTH_DS_AmmoItems[3742] = item + end + if not item["name"] then item["name"] = 'Bow of Plunder' end + if not item["level"] then item["level"] = 28 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 11.3 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[3778] + if not item then + item = {} + MTH_DS_AmmoItems[3778] = item + end + if not item["name"] then item["name"] = 'Taut Compound Bow' end + if not item["level"] then item["level"] = 31 end + if not item["reqlevel"] then item["reqlevel"] = 26 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 7.6 end + if not item["speed"] then item["speed"] = 2.5 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.39 end +end + +do + local item = MTH_DS_AmmoItems[3780] + if not item then + item = {} + MTH_DS_AmmoItems[3780] = item + end + if not item["name"] then item["name"] = 'Long-barreled Musket' end + if not item["level"] then item["level"] = 33 end + if not item["reqlevel"] then item["reqlevel"] = 28 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 7.9 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.41 end +end + +do + local item = MTH_DS_AmmoItems[4025] + if not item then + item = {} + MTH_DS_AmmoItems[4025] = item + end + if not item["name"] then item["name"] = 'Balanced Long Bow' end + if not item["level"] then item["level"] = 45 end + if not item["reqlevel"] then item["reqlevel"] = 40 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 12.2 end + if not item["speed"] then item["speed"] = 1.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][141596] == nil then item["objects"][141596] = 0.4 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[4026] + if not item then + item = {} + MTH_DS_AmmoItems[4026] = item + end + if not item["name"] then item["name"] = 'Sentinel Musket' end + if not item["level"] then item["level"] = 43 end + if not item["reqlevel"] then item["reqlevel"] = 38 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 11.6 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][141596] == nil then item["objects"][141596] = 0.5 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[4086] + if not item then + item = {} + MTH_DS_AmmoItems[4086] = item + end + if not item["name"] then item["name"] = 'Flash Rifle' end + if not item["level"] then item["level"] = 37 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 15.3 end + if not item["speed"] then item["speed"] = 1.8 end +end + +do + local item = MTH_DS_AmmoItems[4087] + if not item then + item = {} + MTH_DS_AmmoItems[4087] = item + end + if not item["name"] then item["name"] = 'Trueshot Bow' end + if not item["level"] then item["level"] = 41 end + if not item["reqlevel"] then item["reqlevel"] = 36 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_10' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 18.2 end + if not item["speed"] then item["speed"] = 1.9 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][141979] == nil then item["objects"][141979] = 2 end + if item["objects"][142184] == nil then item["objects"][142184] = 0.4 end +end + +do + local item = MTH_DS_AmmoItems[4089] + if not item then + item = {} + MTH_DS_AmmoItems[4089] = item + end + if not item["name"] then item["name"] = 'Ricochet Blunderbuss' end + if not item["level"] then item["level"] = 48 end + if not item["reqlevel"] then item["reqlevel"] = 43 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 22.4 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[4110] + if not item then + item = {} + MTH_DS_AmmoItems[4110] = item + end + if not item["name"] then item["name"] = "Master Hunter's Bow" end + if not item["level"] then item["level"] = 45 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 20.8 end + if not item["speed"] then item["speed"] = 2.4 end +end + +do + local item = MTH_DS_AmmoItems[4111] + if not item then + item = {} + MTH_DS_AmmoItems[4111] = item + end + if not item["name"] then item["name"] = "Master Hunter's Rifle" end + if not item["level"] then item["level"] = 45 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_05' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 20.6 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[4127] + if not item then + item = {} + MTH_DS_AmmoItems[4127] = item + end + if not item["name"] then item["name"] = 'Shrapnel Blaster' end + if not item["level"] then item["level"] = 40 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_02' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 17.4 end + if not item["speed"] then item["speed"] = 1.9 end +end + +do + local item = MTH_DS_AmmoItems[4362] + if not item then + item = {} + MTH_DS_AmmoItems[4362] = item + end + if not item["name"] then item["name"] = 'Rough Boomstick' end + if not item["level"] then item["level"] = 10 end + if not item["reqlevel"] then item["reqlevel"] = 5 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 4.1 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[4369] + if not item then + item = {} + MTH_DS_AmmoItems[4369] = item + end + if not item["name"] then item["name"] = 'Deadly Blunderbuss' end + if not item["level"] then item["level"] = 21 end + if not item["reqlevel"] then item["reqlevel"] = 16 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 8.3 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[4372] + if not item then + item = {} + MTH_DS_AmmoItems[4372] = item + end + if not item["name"] then item["name"] = 'Lovingly Crafted Boomstick' end + if not item["level"] then item["level"] = 24 end + if not item["reqlevel"] then item["reqlevel"] = 19 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 9.7 end + if not item["speed"] then item["speed"] = 1.8 end +end + +do + local item = MTH_DS_AmmoItems[4379] + if not item then + item = {} + MTH_DS_AmmoItems[4379] = item + end + if not item["name"] then item["name"] = 'Silver-plated Shotgun' end + if not item["level"] then item["level"] = 26 end + if not item["reqlevel"] then item["reqlevel"] = 21 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 10.4 end + if not item["speed"] then item["speed"] = 2.7 end +end + +do + local item = MTH_DS_AmmoItems[4383] + if not item then + item = {} + MTH_DS_AmmoItems[4383] = item + end + if not item["name"] then item["name"] = 'Moonsight Rifle' end + if not item["level"] then item["level"] = 29 end + if not item["reqlevel"] then item["reqlevel"] = 24 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 11.8 end + if not item["speed"] then item["speed"] = 1.7 end +end + +do + local item = MTH_DS_AmmoItems[4474] + if not item then + item = {} + MTH_DS_AmmoItems[4474] = item + end + if not item["name"] then item["name"] = 'Ravenwood Bow' end + if not item["level"] then item["level"] = 32 end + if not item["reqlevel"] then item["reqlevel"] = 27 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_10' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 12.9 end + if not item["speed"] then item["speed"] = 1.9 end +end + +do + local item = MTH_DS_AmmoItems[4576] + if not item then + item = {} + MTH_DS_AmmoItems[4576] = item + end + if not item["name"] then item["name"] = 'Light Bow' end + if not item["level"] then item["level"] = 21 end + if not item["reqlevel"] then item["reqlevel"] = 16 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_07' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 7.9 end + if not item["speed"] then item["speed"] = 1.7 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.17 end +end + +do + local item = MTH_DS_AmmoItems[4577] + if not item then + item = {} + MTH_DS_AmmoItems[4577] = item + end + if not item["name"] then item["name"] = 'Compact Shotgun' end + if not item["level"] then item["level"] = 13 end + if not item["reqlevel"] then item["reqlevel"] = 8 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 5.3 end + if not item["speed"] then item["speed"] = 2 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2039] == nil then item["objects"][2039] = 0.12 end +end + +do + local item = MTH_DS_AmmoItems[4763] + if not item then + item = {} + MTH_DS_AmmoItems[4763] = item + end + if not item["name"] then item["name"] = 'Blackwood Recurve Bow' end + if not item["level"] then item["level"] = 9 end + if not item["reqlevel"] then item["reqlevel"] = 4 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_12' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 3.9 end + if not item["speed"] then item["speed"] = 2.7 end +end + +do + local item = MTH_DS_AmmoItems[4931] + if not item then + item = {} + MTH_DS_AmmoItems[4931] = item + end + if not item["name"] then item["name"] = 'Hickory Shortbow' end + if not item["level"] then item["level"] = 11 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 3.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[5258] + if not item then + item = {} + MTH_DS_AmmoItems[5258] = item + end + if not item["name"] then item["name"] = 'Monster - Bow, Black' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[5259] + if not item then + item = {} + MTH_DS_AmmoItems[5259] = item + end + if not item["name"] then item["name"] = 'Monster - Bow, Red' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[5260] + if not item then + item = {} + MTH_DS_AmmoItems[5260] = item + end + if not item["name"] then item["name"] = 'Monster - Bow, Brown' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[5261] + if not item then + item = {} + MTH_DS_AmmoItems[5261] = item + end + if not item["name"] then item["name"] = 'Monster - Bow, Gray' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[5262] + if not item then + item = {} + MTH_DS_AmmoItems[5262] = item + end + if not item["name"] then item["name"] = 'Monster - Bow, Dark Brown' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[5309] + if not item then + item = {} + MTH_DS_AmmoItems[5309] = item + end + if not item["name"] then item["name"] = 'Privateer Musket' end + if not item["level"] then item["level"] = 20 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 7.8 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[5346] + if not item then + item = {} + MTH_DS_AmmoItems[5346] = item + end + if not item["name"] then item["name"] = 'Orcish Battle Bow' end + if not item["level"] then item["level"] = 14 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 5.5 end + if not item["speed"] then item["speed"] = 1.9 end +end + +do + local item = MTH_DS_AmmoItems[5568] + if item then + if not item["drops"] then item["drops"] = {} end + if item["drops"][2156] == nil then item["drops"][2156] = 30.97 end + if item["drops"][2157] == nil then item["drops"][2157] = 30.53 end + if item["drops"][6206] == nil then item["drops"][6206] = 0.04 end + if item["drops"][6207] == nil then item["drops"][6207] = 0.02 end + if item["drops"][6210] == nil then item["drops"][6210] = 0.02 end + if item["drops"][6211] == nil then item["drops"][6211] = 0.06 end + if item["drops"][6218] == nil then item["drops"][6218] = 0.02 end + if item["drops"][6219] == nil then item["drops"][6219] = 0.02 end + if item["drops"][6220] == nil then item["drops"][6220] = 0.02 end + if item["drops"][6221] == nil then item["drops"][6221] = 0.02 end + if item["drops"][6223] == nil then item["drops"][6223] = 0.02 end + if item["drops"][6233] == nil then item["drops"][6233] = 0.02 end + if item["drops"][6234] == nil then item["drops"][6234] = 0.02 end + if item["drops"][6329] == nil then item["drops"][6329] = 0.08 end + if item["drops"][6391] == nil then item["drops"][6391] = 0.2 end + if item["drops"][6392] == nil then item["drops"][6392] = 0.56 end + if item["drops"][6407] == nil then item["drops"][6407] = 0.6 end + if item["drops"][9676] == nil then item["drops"][9676] = 25 end + if item["drops"][11920] == nil then item["drops"][11920] = 5.96 end + if item["drops"][60442] == nil then item["drops"][60442] = 30.97 end + end +end + +do + local item = MTH_DS_AmmoItems[5596] + if not item then + item = {} + MTH_DS_AmmoItems[5596] = item + end + if not item["name"] then item["name"] = 'Ashwood Bow' end + if not item["level"] then item["level"] = 11 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 3.8 end + if not item["speed"] then item["speed"] = 2.1 end +end + +do + local item = MTH_DS_AmmoItems[5748] + if not item then + item = {} + MTH_DS_AmmoItems[5748] = item + end + if not item["name"] then item["name"] = 'Centaur Longbow' end + if not item["level"] then item["level"] = 16 end + if not item["reqlevel"] then item["reqlevel"] = 11 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_07' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 6.1 end + if not item["speed"] then item["speed"] = 2.2 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][80941] == nil then item["vendors"][80941] = 0 end + if item["vendors"][80942] == nil then item["vendors"][80942] = 0 end +end + +do + local item = MTH_DS_AmmoItems[5817] + if not item then + item = {} + MTH_DS_AmmoItems[5817] = item + end + if not item["name"] then item["name"] = 'Lunaris Bow' end + if not item["level"] then item["level"] = 30 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 12.2 end + if not item["speed"] then item["speed"] = 2.7 end +end + +do + local item = MTH_DS_AmmoItems[6315] + if not item then + item = {} + MTH_DS_AmmoItems[6315] = item + end + if not item["name"] then item["name"] = 'Steelarrow Crossbow' end + if not item["level"] then item["level"] = 27 end + if not item["reqlevel"] then item["reqlevel"] = 22 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_01' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 10.9 end + if not item["speed"] then item["speed"] = 3.4 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][6523] == nil then item["drops"][6523] = 2 end +end + +do + local item = MTH_DS_AmmoItems[6469] + if not item then + item = {} + MTH_DS_AmmoItems[6469] = item + end + if not item["name"] then item["name"] = 'Venomstrike' end + if not item["level"] then item["level"] = 24 end + if not item["reqlevel"] then item["reqlevel"] = 19 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_10' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 9.6 end + if not item["speed"] then item["speed"] = 2.4 end +end + +do + local item = MTH_DS_AmmoItems[6696] + if not item then + item = {} + MTH_DS_AmmoItems[6696] = item + end + if not item["name"] then item["name"] = 'Nightstalker Bow' end + if not item["level"] then item["level"] = 32 end + if not item["reqlevel"] then item["reqlevel"] = 27 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_12' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 16.2 end + if not item["speed"] then item["speed"] = 1.7 end +end + +do + local item = MTH_DS_AmmoItems[6739] + if not item then + item = {} + MTH_DS_AmmoItems[6739] = item + end + if not item["name"] then item["name"] = "Cliffrunner's Aim" end + if not item["level"] then item["level"] = 29 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 12 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[6798] + if not item then + item = {} + MTH_DS_AmmoItems[6798] = item + end + if not item["name"] then item["name"] = 'Blasting Hackbut' end + if not item["level"] then item["level"] = 37 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 15.4 end + if not item["speed"] then item["speed"] = 2.8 end +end + +do + local item = MTH_DS_AmmoItems[7729] + if not item then + item = {} + MTH_DS_AmmoItems[7729] = item + end + if not item["name"] then item["name"] = 'Chesterfall Musket' end + if not item["level"] then item["level"] = 33 end + if not item["reqlevel"] then item["reqlevel"] = 28 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 16.5 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][4282] == nil then item["drops"][4282] = 0.02 end + if item["drops"][4283] == nil then item["drops"][4283] = 0.02 end + if item["drops"][4285] == nil then item["drops"][4285] = 0.02 end + if item["drops"][4286] == nil then item["drops"][4286] = 0.02 end + if item["drops"][4287] == nil then item["drops"][4287] = 0.02 end + if item["drops"][4288] == nil then item["drops"][4288] = 0.04 end + if item["drops"][4290] == nil then item["drops"][4290] = 0.02 end + if item["drops"][4291] == nil then item["drops"][4291] = 0.02 end + if item["drops"][4292] == nil then item["drops"][4292] = 0.02 end + if item["drops"][4293] == nil then item["drops"][4293] = 0.02 end + if item["drops"][4294] == nil then item["drops"][4294] = 0.02 end + if item["drops"][4295] == nil then item["drops"][4295] = 0.02 end + if item["drops"][4296] == nil then item["drops"][4296] = 0.02 end + if item["drops"][4298] == nil then item["drops"][4298] = 0.02 end + if item["drops"][4299] == nil then item["drops"][4299] = 0.02 end + if item["drops"][4300] == nil then item["drops"][4300] = 0.02 end + if item["drops"][4301] == nil then item["drops"][4301] = 0.02 end + if item["drops"][4302] == nil then item["drops"][4302] = 0.02 end + if item["drops"][4303] == nil then item["drops"][4303] = 0.02 end + if item["drops"][4304] == nil then item["drops"][4304] = 0.02 end + if item["drops"][4306] == nil then item["drops"][4306] = 0.02 end + if item["drops"][4308] == nil then item["drops"][4308] = 0.02 end + if item["drops"][4540] == nil then item["drops"][4540] = 0.02 end + if item["drops"][6426] == nil then item["drops"][6426] = 0.02 end + if item["drops"][6427] == nil then item["drops"][6427] = 0.02 end + if item["drops"][61971] == nil then item["drops"][61971] = 0.02 end + if item["drops"][62537] == nil then item["drops"][62537] = 0.02 end + if item["drops"][62645] == nil then item["drops"][62645] = 0.02 end + if item["drops"][62647] == nil then item["drops"][62647] = 0.02 end + if item["drops"][62651] == nil then item["drops"][62651] = 0.02 end + if item["drops"][62659] == nil then item["drops"][62659] = 0.02 end + if item["drops"][62663] == nil then item["drops"][62663] = 0.02 end + if item["drops"][62666] == nil then item["drops"][62666] = 0.02 end + if item["drops"][62669] == nil then item["drops"][62669] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[8179] + if not item then + item = {} + MTH_DS_AmmoItems[8179] = item + end + if not item["name"] then item["name"] = "Cadet's Bow" end + if not item["level"] then item["level"] = 6 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.3 end + if not item["speed"] then item["speed"] = 2 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][40] == nil then item["drops"][40] = 0.06 end + if item["drops"][43] == nil then item["drops"][43] = 0.02 end + if item["drops"][46] == nil then item["drops"][46] = 0.02 end + if item["drops"][97] == nil then item["drops"][97] = 0.02 end + if item["drops"][116] == nil then item["drops"][116] = 0.02 end + if item["drops"][285] == nil then item["drops"][285] = 0.1 end + if item["drops"][390] == nil then item["drops"][390] = 0.38 end + if item["drops"][475] == nil then item["drops"][475] = 0.02 end + if item["drops"][476] == nil then item["drops"][476] = 0.14 end + if item["drops"][524] == nil then item["drops"][524] = 0.1 end + if item["drops"][735] == nil then item["drops"][735] = 0.14 end + if item["drops"][880] == nil then item["drops"][880] = 0.04 end + if item["drops"][1117] == nil then item["drops"][1117] = 0.02 end + if item["drops"][1120] == nil then item["drops"][1120] = 0.14 end + if item["drops"][1124] == nil then item["drops"][1124] = 0.02 end + if item["drops"][1126] == nil then item["drops"][1126] = 0.12 end + if item["drops"][1127] == nil then item["drops"][1127] = 0.1 end + if item["drops"][1131] == nil then item["drops"][1131] = 0.1 end + if item["drops"][1135] == nil then item["drops"][1135] = 0.1 end + if item["drops"][1138] == nil then item["drops"][1138] = 0.12 end + if item["drops"][1196] == nil then item["drops"][1196] = 0.12 end + if item["drops"][1201] == nil then item["drops"][1201] = 0.06 end + if item["drops"][1520] == nil then item["drops"][1520] = 0.08 end + if item["drops"][1522] == nil then item["drops"][1522] = 0.12 end + if item["drops"][1526] == nil then item["drops"][1526] = 0.12 end + if item["drops"][1527] == nil then item["drops"][1527] = 0.12 end + if item["drops"][1531] == nil then item["drops"][1531] = 0.1 end + if item["drops"][1535] == nil then item["drops"][1535] = 0.12 end + if item["drops"][1536] == nil then item["drops"][1536] = 0.1 end + if item["drops"][1543] == nil then item["drops"][1543] = 0.05 end + if item["drops"][1548] == nil then item["drops"][1548] = 0.14 end + if item["drops"][1553] == nil then item["drops"][1553] = 0.12 end + if item["drops"][1554] == nil then item["drops"][1554] = 0.02 end + if item["drops"][1674] == nil then item["drops"][1674] = 0.12 end + if item["drops"][1675] == nil then item["drops"][1675] = 0.12 end + if item["drops"][1922] == nil then item["drops"][1922] = 0.05 end + if item["drops"][1934] == nil then item["drops"][1934] = 0.1 end + if item["drops"][1941] == nil then item["drops"][1941] = 0.12 end + if item["drops"][1993] == nil then item["drops"][1993] = 0.04 end + if item["drops"][1996] == nil then item["drops"][1996] = 0.12 end + if item["drops"][1997] == nil then item["drops"][1997] = 0.02 end + if item["drops"][1999] == nil then item["drops"][1999] = 0.14 end + if item["drops"][2004] == nil then item["drops"][2004] = 0.12 end + if item["drops"][2005] == nil then item["drops"][2005] = 0.24 end + if item["drops"][2008] == nil then item["drops"][2008] = 0.16 end + if item["drops"][2009] == nil then item["drops"][2009] = 0.14 end + if item["drops"][2010] == nil then item["drops"][2010] = 0.12 end + if item["drops"][2011] == nil then item["drops"][2011] = 0.02 end + if item["drops"][2015] == nil then item["drops"][2015] = 0.02 end + if item["drops"][2025] == nil then item["drops"][2025] = 0.14 end + if item["drops"][2027] == nil then item["drops"][2027] = 0.02 end + if item["drops"][2043] == nil then item["drops"][2043] = 0.08 end + if item["drops"][2152] == nil then item["drops"][2152] = 0.16 end + if item["drops"][2949] == nil then item["drops"][2949] = 0.02 end + if item["drops"][2950] == nil then item["drops"][2950] = 0.12 end + if item["drops"][2951] == nil then item["drops"][2951] = 0.14 end + if item["drops"][2956] == nil then item["drops"][2956] = 0.08 end + if item["drops"][2959] == nil then item["drops"][2959] = 0.12 end + if item["drops"][2960] == nil then item["drops"][2960] = 0.02 end + if item["drops"][2962] == nil then item["drops"][2962] = 0.08 end + if item["drops"][2970] == nil then item["drops"][2970] = 0.12 end + if item["drops"][2972] == nil then item["drops"][2972] = 0.1 end + if item["drops"][2976] == nil then item["drops"][2976] = 0.18 end + if item["drops"][2977] == nil then item["drops"][2977] = 0.14 end + if item["drops"][2989] == nil then item["drops"][2989] = 0.16 end + if item["drops"][3035] == nil then item["drops"][3035] = 0.1 end + if item["drops"][3099] == nil then item["drops"][3099] = 0.1 end + if item["drops"][3100] == nil then item["drops"][3100] = 0.02 end + if item["drops"][3103] == nil then item["drops"][3103] = 0.12 end + if item["drops"][3104] == nil then item["drops"][3104] = 0.12 end + if item["drops"][3107] == nil then item["drops"][3107] = 0.14 end + if item["drops"][3111] == nil then item["drops"][3111] = 0.18 end + if item["drops"][3112] == nil then item["drops"][3112] = 0.12 end + if item["drops"][3115] == nil then item["drops"][3115] = 0.12 end + if item["drops"][3116] == nil then item["drops"][3116] = 0.08 end + if item["drops"][3119] == nil then item["drops"][3119] = 0.12 end + if item["drops"][3120] == nil then item["drops"][3120] = 0.16 end + if item["drops"][3121] == nil then item["drops"][3121] = 0.12 end + if item["drops"][3122] == nil then item["drops"][3122] = 0.08 end + if item["drops"][3126] == nil then item["drops"][3126] = 0.12 end + if item["drops"][3129] == nil then item["drops"][3129] = 0.14 end + if item["drops"][3131] == nil then item["drops"][3131] = 0.02 end + if item["drops"][3197] == nil then item["drops"][3197] = 0.02 end + if item["drops"][3203] == nil then item["drops"][3203] = 0.04 end + if item["drops"][3204] == nil then item["drops"][3204] = 0.04 end + if item["drops"][5826] == nil then item["drops"][5826] = 0.1 end + if item["drops"][6866] == nil then item["drops"][6866] = 0.02 end + if item["drops"][7235] == nil then item["drops"][7235] = 0.12 end + if item["drops"][14428] == nil then item["drops"][14428] = 0.14 end + if item["drops"][61656] == nil then item["drops"][61656] = 0.08 end + if item["drops"][61657] == nil then item["drops"][61657] = 0.08 end + if item["drops"][61658] == nil then item["drops"][61658] = 0.12 end + if item["drops"][61659] == nil then item["drops"][61659] = 0.12 end + if item["drops"][61660] == nil then item["drops"][61660] = 0.12 end + if item["drops"][61661] == nil then item["drops"][61661] = 0.12 end + if item["drops"][61662] == nil then item["drops"][61662] = 0.12 end + if item["drops"][61682] == nil then item["drops"][61682] = 0.1 end + if item["drops"][61683] == nil then item["drops"][61683] = 0.1 end + if item["drops"][61684] == nil then item["drops"][61684] = 0.1 end + if item["drops"][61685] == nil then item["drops"][61685] = 0.1 end + if item["drops"][61686] == nil then item["drops"][61686] = 0.1 end + if item["drops"][61687] == nil then item["drops"][61687] = 0.1 end + if item["drops"][61695] == nil then item["drops"][61695] = 0.08 end + if item["drops"][61701] == nil then item["drops"][61701] = 0.14 end + if item["drops"][61709] == nil then item["drops"][61709] = 0.02 end + if item["drops"][61710] == nil then item["drops"][61710] = 0.02 end + if item["drops"][61712] == nil then item["drops"][61712] = 0.02 end + if item["drops"][61713] == nil then item["drops"][61713] = 0.02 end + if item["drops"][61714] == nil then item["drops"][61714] = 0.14 end + if item["drops"][61716] == nil then item["drops"][61716] = 0.08 end + if item["drops"][61759] == nil then item["drops"][61759] = 0.1 end + if item["drops"][61783] == nil then item["drops"][61783] = 0.24 end + if item["drops"][61789] == nil then item["drops"][61789] = 0.08 end + if item["drops"][61790] == nil then item["drops"][61790] = 0.14 end + if item["drops"][61895] == nil then item["drops"][61895] = 0.04 end + if item["drops"][62717] == nil then item["drops"][62717] = 0.14 end + if item["drops"][80250] == nil then item["drops"][80250] = 0.08 end +end + +do + local item = MTH_DS_AmmoItems[8180] + if not item then + item = {} + MTH_DS_AmmoItems[8180] = item + end + if not item["name"] then item["name"] = 'Hunting Bow' end + if not item["level"] then item["level"] = 11 end + if not item["reqlevel"] then item["reqlevel"] = 6 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_03' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 4.6 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2039] == nil then item["objects"][2039] = 0.24 end +end + +do + local item = MTH_DS_AmmoItems[8181] + if not item then + item = {} + MTH_DS_AmmoItems[8181] = item + end + if not item["name"] then item["name"] = 'Hunting Rifle' end + if not item["level"] then item["level"] = 9 end + if not item["reqlevel"] then item["reqlevel"] = 4 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 3 end + if not item["speed"] then item["speed"] = 2.7 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][40] == nil then item["drops"][40] = 0.03 end + if item["drops"][43] == nil then item["drops"][43] = 1 end + if item["drops"][46] == nil then item["drops"][46] = 0.28 end + if item["drops"][60] == nil then item["drops"][60] = 0.3 end + if item["drops"][79] == nil then item["drops"][79] = 0.4 end + if item["drops"][97] == nil then item["drops"][97] = 0.36 end + if item["drops"][99] == nil then item["drops"][99] = 0.24 end + if item["drops"][116] == nil then item["drops"][116] = 0.3 end + if item["drops"][118] == nil then item["drops"][118] = 0.34 end + if item["drops"][199] == nil then item["drops"][199] = 0.14 end + if item["drops"][285] == nil then item["drops"][285] = 0.06 end + if item["drops"][327] == nil then item["drops"][327] = 0.08 end + if item["drops"][330] == nil then item["drops"][330] = 0.18 end + if item["drops"][390] == nil then item["drops"][390] = 0.16 end + if item["drops"][471] == nil then item["drops"][471] = 0.15 end + if item["drops"][473] == nil then item["drops"][473] = 0.12 end + if item["drops"][474] == nil then item["drops"][474] = 0.34 end + if item["drops"][475] == nil then item["drops"][475] = 0.02 end + if item["drops"][476] == nil then item["drops"][476] = 0.14 end + if item["drops"][478] == nil then item["drops"][478] = 0.3 end + if item["drops"][480] == nil then item["drops"][480] = 0.26 end + if item["drops"][481] == nil then item["drops"][481] = 0.08 end + if item["drops"][524] == nil then item["drops"][524] = 0.16 end + if item["drops"][583] == nil then item["drops"][583] = 0.14 end + if item["drops"][732] == nil then item["drops"][732] = 0.28 end + if item["drops"][735] == nil then item["drops"][735] = 0.06 end + if item["drops"][822] == nil then item["drops"][822] = 0.26 end + if item["drops"][834] == nil then item["drops"][834] = 0.07 end + if item["drops"][880] == nil then item["drops"][880] = 0.11 end + if item["drops"][881] == nil then item["drops"][881] = 0.48 end + if item["drops"][1115] == nil then item["drops"][1115] = 0.14 end + if item["drops"][1116] == nil then item["drops"][1116] = 0.14 end + if item["drops"][1117] == nil then item["drops"][1117] = 0.36 end + if item["drops"][1120] == nil then item["drops"][1120] = 0.18 end + if item["drops"][1121] == nil then item["drops"][1121] = 0.34 end + if item["drops"][1122] == nil then item["drops"][1122] = 0.36 end + if item["drops"][1123] == nil then item["drops"][1123] = 0.32 end + if item["drops"][1124] == nil then item["drops"][1124] = 0.38 end + if item["drops"][1126] == nil then item["drops"][1126] = 0.04 end + if item["drops"][1127] == nil then item["drops"][1127] = 0.14 end + if item["drops"][1131] == nil then item["drops"][1131] = 0.14 end + if item["drops"][1133] == nil then item["drops"][1133] = 0.28 end + if item["drops"][1135] == nil then item["drops"][1135] = 0.04 end + if item["drops"][1138] == nil then item["drops"][1138] = 0.04 end + if item["drops"][1172] == nil then item["drops"][1172] = 0.12 end + if item["drops"][1173] == nil then item["drops"][1173] = 0.12 end + if item["drops"][1190] == nil then item["drops"][1190] = 0.05 end + if item["drops"][1195] == nil then item["drops"][1195] = 0.07 end + if item["drops"][1196] == nil then item["drops"][1196] = 0.12 end + if item["drops"][1201] == nil then item["drops"][1201] = 0.07 end + if item["drops"][1211] == nil then item["drops"][1211] = 0.32 end + if item["drops"][1397] == nil then item["drops"][1397] = 0.16 end + if item["drops"][1520] == nil then item["drops"][1520] = 0.06 end + if item["drops"][1522] == nil then item["drops"][1522] = 0.14 end + if item["drops"][1523] == nil then item["drops"][1523] = 0.3 end + if item["drops"][1526] == nil then item["drops"][1526] = 0.06 end + if item["drops"][1527] == nil then item["drops"][1527] = 0.22 end + if item["drops"][1528] == nil then item["drops"][1528] = 0.26 end + if item["drops"][1529] == nil then item["drops"][1529] = 0.32 end + if item["drops"][1530] == nil then item["drops"][1530] = 0.07 end + if item["drops"][1531] == nil then item["drops"][1531] = 0.12 end + if item["drops"][1532] == nil then item["drops"][1532] = 0.12 end + if item["drops"][1533] == nil then item["drops"][1533] = 0.62 end + if item["drops"][1534] == nil then item["drops"][1534] = 0.34 end + if item["drops"][1535] == nil then item["drops"][1535] = 0.06 end + if item["drops"][1536] == nil then item["drops"][1536] = 0.14 end + if item["drops"][1537] == nil then item["drops"][1537] = 0.28 end + if item["drops"][1538] == nil then item["drops"][1538] = 0.34 end + if item["drops"][1539] == nil then item["drops"][1539] = 0.22 end + if item["drops"][1540] == nil then item["drops"][1540] = 0.1 end + if item["drops"][1543] == nil then item["drops"][1543] = 0.06 end + if item["drops"][1544] == nil then item["drops"][1544] = 0.36 end + if item["drops"][1545] == nil then item["drops"][1545] = 0.32 end + if item["drops"][1548] == nil then item["drops"][1548] = 0.14 end + if item["drops"][1549] == nil then item["drops"][1549] = 0.5 end + if item["drops"][1553] == nil then item["drops"][1553] = 0.06 end + if item["drops"][1554] == nil then item["drops"][1554] = 0.36 end + if item["drops"][1555] == nil then item["drops"][1555] = 0.4 end + if item["drops"][1654] == nil then item["drops"][1654] = 0.1 end + if item["drops"][1655] == nil then item["drops"][1655] = 0.14 end + if item["drops"][1656] == nil then item["drops"][1656] = 0.14 end + if item["drops"][1657] == nil then item["drops"][1657] = 0.16 end + if item["drops"][1660] == nil then item["drops"][1660] = 0.26 end + if item["drops"][1662] == nil then item["drops"][1662] = 0.26 end + if item["drops"][1674] == nil then item["drops"][1674] = 0.06 end + if item["drops"][1675] == nil then item["drops"][1675] = 0.12 end + if item["drops"][1689] == nil then item["drops"][1689] = 0.32 end + if item["drops"][1753] == nil then item["drops"][1753] = 0.22 end + if item["drops"][1765] == nil then item["drops"][1765] = 0.14 end + if item["drops"][1769] == nil then item["drops"][1769] = 0.12 end + if item["drops"][1922] == nil then item["drops"][1922] = 0.07 end + if item["drops"][1934] == nil then item["drops"][1934] = 0.06 end + if item["drops"][1941] == nil then item["drops"][1941] = 0.06 end + if item["drops"][1981] == nil then item["drops"][1981] = 0.24 end + if item["drops"][1993] == nil then item["drops"][1993] = 0.28 end + if item["drops"][1996] == nil then item["drops"][1996] = 0.18 end + if item["drops"][1997] == nil then item["drops"][1997] = 0.24 end + if item["drops"][1999] == nil then item["drops"][1999] = 0.14 end + if item["drops"][2000] == nil then item["drops"][2000] = 0.38 end + if item["drops"][2001] == nil then item["drops"][2001] = 0.06 end + if item["drops"][2004] == nil then item["drops"][2004] = 0.06 end + if item["drops"][2005] == nil then item["drops"][2005] = 0.14 end + if item["drops"][2008] == nil then item["drops"][2008] = 0.04 end + if item["drops"][2009] == nil then item["drops"][2009] = 0.14 end + if item["drops"][2010] == nil then item["drops"][2010] = 0.14 end + if item["drops"][2011] == nil then item["drops"][2011] = 0.3 end + if item["drops"][2012] == nil then item["drops"][2012] = 0.24 end + if item["drops"][2013] == nil then item["drops"][2013] = 0.3 end + if item["drops"][2014] == nil then item["drops"][2014] = 0.02 end + if item["drops"][2015] == nil then item["drops"][2015] = 0.15 end + if item["drops"][2017] == nil then item["drops"][2017] = 0.12 end + if item["drops"][2018] == nil then item["drops"][2018] = 0.56 end + if item["drops"][2019] == nil then item["drops"][2019] = 0.22 end + if item["drops"][2020] == nil then item["drops"][2020] = 0.4 end + if item["drops"][2025] == nil then item["drops"][2025] = 0.12 end + if item["drops"][2027] == nil then item["drops"][2027] = 0.26 end + if item["drops"][2029] == nil then item["drops"][2029] = 0.26 end + if item["drops"][2030] == nil then item["drops"][2030] = 0.1 end + if item["drops"][2033] == nil then item["drops"][2033] = 0.26 end + if item["drops"][2034] == nil then item["drops"][2034] = 0.06 end + if item["drops"][2038] == nil then item["drops"][2038] = 0.1 end + if item["drops"][2043] == nil then item["drops"][2043] = 0.12 end + if item["drops"][2070] == nil then item["drops"][2070] = 0.12 end + if item["drops"][2152] == nil then item["drops"][2152] = 0.06 end + if item["drops"][2162] == nil then item["drops"][2162] = 0.08 end + if item["drops"][2166] == nil then item["drops"][2166] = 1 end + if item["drops"][2176] == nil then item["drops"][2176] = 0.12 end + if item["drops"][2189] == nil then item["drops"][2189] = 0.12 end + if item["drops"][2231] == nil then item["drops"][2231] = 0.44 end + if item["drops"][2234] == nil then item["drops"][2234] = 0.18 end + if item["drops"][2950] == nil then item["drops"][2950] = 0.04 end + if item["drops"][2951] == nil then item["drops"][2951] = 0.1 end + if item["drops"][2956] == nil then item["drops"][2956] = 0.08 end + if item["drops"][2957] == nil then item["drops"][2957] = 0.26 end + if item["drops"][2959] == nil then item["drops"][2959] = 0.14 end + if item["drops"][2960] == nil then item["drops"][2960] = 0.3 end + if item["drops"][2962] == nil then item["drops"][2962] = 0.12 end + if item["drops"][2963] == nil then item["drops"][2963] = 0.26 end + if item["drops"][2964] == nil then item["drops"][2964] = 0.36 end + if item["drops"][2965] == nil then item["drops"][2965] = 0.1 end + if item["drops"][2967] == nil then item["drops"][2967] = 0.42 end + if item["drops"][2968] == nil then item["drops"][2968] = 0.12 end + if item["drops"][2970] == nil then item["drops"][2970] = 0.28 end + if item["drops"][2971] == nil then item["drops"][2971] = 0.32 end + if item["drops"][2972] == nil then item["drops"][2972] = 0.1 end + if item["drops"][2973] == nil then item["drops"][2973] = 0.08 end + if item["drops"][2976] == nil then item["drops"][2976] = 0.02 end + if item["drops"][2977] == nil then item["drops"][2977] = 0.12 end + if item["drops"][2978] == nil then item["drops"][2978] = 0.32 end + if item["drops"][2979] == nil then item["drops"][2979] = 0.26 end + if item["drops"][2989] == nil then item["drops"][2989] = 0.08 end + if item["drops"][2990] == nil then item["drops"][2990] = 0.22 end + if item["drops"][3035] == nil then item["drops"][3035] = 0.14 end + if item["drops"][3068] == nil then item["drops"][3068] = 0.06 end + if item["drops"][3099] == nil then item["drops"][3099] = 0.06 end + if item["drops"][3100] == nil then item["drops"][3100] = 0.32 end + if item["drops"][3103] == nil then item["drops"][3103] = 0.06 end + if item["drops"][3104] == nil then item["drops"][3104] = 0.1 end + if item["drops"][3105] == nil then item["drops"][3105] = 0.6 end + if item["drops"][3107] == nil then item["drops"][3107] = 0.2 end + if item["drops"][3108] == nil then item["drops"][3108] = 0.58 end + if item["drops"][3110] == nil then item["drops"][3110] = 0.22 end + if item["drops"][3111] == nil then item["drops"][3111] = 0.06 end + if item["drops"][3112] == nil then item["drops"][3112] = 0.14 end + if item["drops"][3113] == nil then item["drops"][3113] = 0.3 end + if item["drops"][3114] == nil then item["drops"][3114] = 0.14 end + if item["drops"][3115] == nil then item["drops"][3115] = 0.14 end + if item["drops"][3116] == nil then item["drops"][3116] = 0.14 end + if item["drops"][3117] == nil then item["drops"][3117] = 0.32 end + if item["drops"][3118] == nil then item["drops"][3118] = 0.12 end + if item["drops"][3119] == nil then item["drops"][3119] = 0.06 end + if item["drops"][3120] == nil then item["drops"][3120] = 0.14 end + if item["drops"][3121] == nil then item["drops"][3121] = 0.2 end + if item["drops"][3122] == nil then item["drops"][3122] = 0.12 end + if item["drops"][3123] == nil then item["drops"][3123] = 0.4 end + if item["drops"][3126] == nil then item["drops"][3126] = 0.16 end + if item["drops"][3127] == nil then item["drops"][3127] = 0.32 end + if item["drops"][3129] == nil then item["drops"][3129] = 0.08 end + if item["drops"][3130] == nil then item["drops"][3130] = 0.36 end + if item["drops"][3131] == nil then item["drops"][3131] = 0.1 end + if item["drops"][3141] == nil then item["drops"][3141] = 0.36 end + if item["drops"][3192] == nil then item["drops"][3192] = 0.12 end + if item["drops"][3195] == nil then item["drops"][3195] = 0.34 end + if item["drops"][3196] == nil then item["drops"][3196] = 0.46 end + if item["drops"][3197] == nil then item["drops"][3197] = 0.32 end + if item["drops"][3198] == nil then item["drops"][3198] = 0.12 end + if item["drops"][3199] == nil then item["drops"][3199] = 0.08 end + if item["drops"][3205] == nil then item["drops"][3205] = 0.3 end + if item["drops"][3206] == nil then item["drops"][3206] = 0.28 end + if item["drops"][3207] == nil then item["drops"][3207] = 0.32 end + if item["drops"][3225] == nil then item["drops"][3225] = 0.08 end + if item["drops"][3227] == nil then item["drops"][3227] = 0.1 end + if item["drops"][3228] == nil then item["drops"][3228] = 0.08 end + if item["drops"][3232] == nil then item["drops"][3232] = 0.38 end + if item["drops"][3267] == nil then item["drops"][3267] = 0.12 end + if item["drops"][3268] == nil then item["drops"][3268] = 0.12 end + if item["drops"][3379] == nil then item["drops"][3379] = 0.12 end + if item["drops"][3566] == nil then item["drops"][3566] = 0.38 end + if item["drops"][5807] == nil then item["drops"][5807] = 0.18 end + if item["drops"][6123] == nil then item["drops"][6123] = 0.3 end + if item["drops"][6789] == nil then item["drops"][6789] = 0.28 end + if item["drops"][6846] == nil then item["drops"][6846] = 0.34 end + if item["drops"][6866] == nil then item["drops"][6866] = 0.3 end + if item["drops"][6911] == nil then item["drops"][6911] = 0.46 end + if item["drops"][6927] == nil then item["drops"][6927] = 0.52 end + if item["drops"][7234] == nil then item["drops"][7234] = 0.1 end + if item["drops"][7235] == nil then item["drops"][7235] = 0.04 end + if item["drops"][7318] == nil then item["drops"][7318] = 0.2 end + if item["drops"][14428] == nil then item["drops"][14428] = 0.08 end + if item["drops"][14431] == nil then item["drops"][14431] = 0.08 end + if item["drops"][60708] == nil then item["drops"][60708] = 0.14 end + if item["drops"][60893] == nil then item["drops"][60893] = 0.14 end + if item["drops"][60898] == nil then item["drops"][60898] = 0.14 end + if item["drops"][60980] == nil then item["drops"][60980] = 0.08 end + if item["drops"][61464] == nil then item["drops"][61464] = 0.26 end + if item["drops"][61656] == nil then item["drops"][61656] = 0.14 end + if item["drops"][61657] == nil then item["drops"][61657] = 0.14 end + if item["drops"][61658] == nil then item["drops"][61658] = 0.14 end + if item["drops"][61659] == nil then item["drops"][61659] = 0.14 end + if item["drops"][61660] == nil then item["drops"][61660] = 0.16 end + if item["drops"][61661] == nil then item["drops"][61661] = 0.16 end + if item["drops"][61662] == nil then item["drops"][61662] = 0.16 end + if item["drops"][61663] == nil then item["drops"][61663] = 0.3 end + if item["drops"][61664] == nil then item["drops"][61664] = 0.3 end + if item["drops"][61665] == nil then item["drops"][61665] = 0.3 end + if item["drops"][61666] == nil then item["drops"][61666] = 0.3 end + if item["drops"][61667] == nil then item["drops"][61667] = 0.3 end + if item["drops"][61668] == nil then item["drops"][61668] = 0.3 end + if item["drops"][61669] == nil then item["drops"][61669] = 0.3 end + if item["drops"][61670] == nil then item["drops"][61670] = 0.28 end + if item["drops"][61672] == nil then item["drops"][61672] = 0.28 end + if item["drops"][61673] == nil then item["drops"][61673] = 0.28 end + if item["drops"][61674] == nil then item["drops"][61674] = 0.28 end + if item["drops"][61675] == nil then item["drops"][61675] = 0.28 end + if item["drops"][61676] == nil then item["drops"][61676] = 0.28 end + if item["drops"][61677] == nil then item["drops"][61677] = 0.28 end + if item["drops"][61678] == nil then item["drops"][61678] = 0.6 end + if item["drops"][61679] == nil then item["drops"][61679] = 0.6 end + if item["drops"][61680] == nil then item["drops"][61680] = 0.58 end + if item["drops"][61681] == nil then item["drops"][61681] = 0.58 end + if item["drops"][61682] == nil then item["drops"][61682] = 0.06 end + if item["drops"][61683] == nil then item["drops"][61683] = 0.06 end + if item["drops"][61684] == nil then item["drops"][61684] = 0.06 end + if item["drops"][61685] == nil then item["drops"][61685] = 0.06 end + if item["drops"][61686] == nil then item["drops"][61686] = 0.06 end + if item["drops"][61687] == nil then item["drops"][61687] = 0.06 end + if item["drops"][61694] == nil then item["drops"][61694] = 0.4 end + if item["drops"][61695] == nil then item["drops"][61695] = 0.08 end + if item["drops"][61699] == nil then item["drops"][61699] = 0.26 end + if item["drops"][61700] == nil then item["drops"][61700] = 0.26 end + if item["drops"][61701] == nil then item["drops"][61701] = 0.12 end + if item["drops"][61702] == nil then item["drops"][61702] = 0.28 end + if item["drops"][61703] == nil then item["drops"][61703] = 0.28 end + if item["drops"][61704] == nil then item["drops"][61704] = 0.58 end + if item["drops"][61705] == nil then item["drops"][61705] = 0.28 end + if item["drops"][61706] == nil then item["drops"][61706] = 0.28 end + if item["drops"][61707] == nil then item["drops"][61707] = 0.28 end + if item["drops"][61708] == nil then item["drops"][61708] = 0.28 end + if item["drops"][61709] == nil then item["drops"][61709] = 0.28 end + if item["drops"][61710] == nil then item["drops"][61710] = 0.28 end + if item["drops"][61712] == nil then item["drops"][61712] = 0.28 end + if item["drops"][61713] == nil then item["drops"][61713] = 0.28 end + if item["drops"][61714] == nil then item["drops"][61714] = 0.12 end + if item["drops"][61715] == nil then item["drops"][61715] = 0.4 end + if item["drops"][61716] == nil then item["drops"][61716] = 0.08 end + if item["drops"][61717] == nil then item["drops"][61717] = 0.14 end + if item["drops"][61718] == nil then item["drops"][61718] = 0.12 end + if item["drops"][61719] == nil then item["drops"][61719] = 0.12 end + if item["drops"][61759] == nil then item["drops"][61759] = 0.06 end + if item["drops"][61783] == nil then item["drops"][61783] = 0.14 end + if item["drops"][61789] == nil then item["drops"][61789] = 0.08 end + if item["drops"][61790] == nil then item["drops"][61790] = 0.12 end + if item["drops"][61792] == nil then item["drops"][61792] = 0.6 end + if item["drops"][61794] == nil then item["drops"][61794] = 0.28 end + if item["drops"][61863] == nil then item["drops"][61863] = 0.3 end + if item["drops"][62711] == nil then item["drops"][62711] = 0.3 end + if item["drops"][62712] == nil then item["drops"][62712] = 0.3 end + if item["drops"][62713] == nil then item["drops"][62713] = 0.3 end + if item["drops"][62714] == nil then item["drops"][62714] = 0.3 end + if item["drops"][62715] == nil then item["drops"][62715] = 0.3 end + if item["drops"][62717] == nil then item["drops"][62717] = 0.12 end + if item["drops"][62753] == nil then item["drops"][62753] = 0.3 end + if item["drops"][80250] == nil then item["drops"][80250] = 0.12 end + if item["drops"][80257] == nil then item["drops"][80257] = 0.14 end +end + +do + local item = MTH_DS_AmmoItems[8182] + if not item then + item = {} + MTH_DS_AmmoItems[8182] = item + end + if not item["name"] then item["name"] = 'Pellet Rifle' end + if not item["level"] then item["level"] = 7 end + if not item["reqlevel"] then item["reqlevel"] = 2 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.5 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][40] == nil then item["drops"][40] = 0.11 end + if item["drops"][43] == nil then item["drops"][43] = 0.1 end + if item["drops"][46] == nil then item["drops"][46] = 0.02 end + if item["drops"][60] == nil then item["drops"][60] = 0.44 end + if item["drops"][97] == nil then item["drops"][97] = 0.08 end + if item["drops"][116] == nil then item["drops"][116] = 0.12 end + if item["drops"][285] == nil then item["drops"][285] = 0.3 end + if item["drops"][327] == nil then item["drops"][327] = 0.06 end + if item["drops"][390] == nil then item["drops"][390] = 0.46 end + if item["drops"][471] == nil then item["drops"][471] = 0.02 end + if item["drops"][475] == nil then item["drops"][475] = 0.02 end + if item["drops"][476] == nil then item["drops"][476] = 0.26 end + if item["drops"][478] == nil then item["drops"][478] = 0.02 end + if item["drops"][524] == nil then item["drops"][524] = 0.34 end + if item["drops"][583] == nil then item["drops"][583] = 0.14 end + if item["drops"][735] == nil then item["drops"][735] = 0.24 end + if item["drops"][822] == nil then item["drops"][822] = 0.1 end + if item["drops"][880] == nil then item["drops"][880] = 0.34 end + if item["drops"][1115] == nil then item["drops"][1115] = 0.07 end + if item["drops"][1117] == nil then item["drops"][1117] = 0.02 end + if item["drops"][1120] == nil then item["drops"][1120] = 0.36 end + if item["drops"][1121] == nil then item["drops"][1121] = 0.12 end + if item["drops"][1123] == nil then item["drops"][1123] = 0.12 end + if item["drops"][1126] == nil then item["drops"][1126] = 0.2 end + if item["drops"][1127] == nil then item["drops"][1127] = 0.3 end + if item["drops"][1131] == nil then item["drops"][1131] = 0.38 end + if item["drops"][1133] == nil then item["drops"][1133] = 0.22 end + if item["drops"][1135] == nil then item["drops"][1135] = 0.18 end + if item["drops"][1138] == nil then item["drops"][1138] = 0.24 end + if item["drops"][1196] == nil then item["drops"][1196] = 0.3 end + if item["drops"][1201] == nil then item["drops"][1201] = 0.18 end + if item["drops"][1211] == nil then item["drops"][1211] = 0.1 end + if item["drops"][1397] == nil then item["drops"][1397] = 0.07 end + if item["drops"][1520] == nil then item["drops"][1520] = 0.22 end + if item["drops"][1522] == nil then item["drops"][1522] = 0.32 end + if item["drops"][1523] == nil then item["drops"][1523] = 0.08 end + if item["drops"][1526] == nil then item["drops"][1526] = 0.22 end + if item["drops"][1527] == nil then item["drops"][1527] = 0.34 end + if item["drops"][1528] == nil then item["drops"][1528] = 0.16 end + if item["drops"][1531] == nil then item["drops"][1531] = 0.12 end + if item["drops"][1533] == nil then item["drops"][1533] = 0.02 end + if item["drops"][1535] == nil then item["drops"][1535] = 0.26 end + if item["drops"][1536] == nil then item["drops"][1536] = 0.36 end + if item["drops"][1537] == nil then item["drops"][1537] = 0.1 end + if item["drops"][1543] == nil then item["drops"][1543] = 0.16 end + if item["drops"][1544] == nil then item["drops"][1544] = 0.16 end + if item["drops"][1548] == nil then item["drops"][1548] = 0.36 end + if item["drops"][1553] == nil then item["drops"][1553] = 0.22 end + if item["drops"][1554] == nil then item["drops"][1554] = 0.12 end + if item["drops"][1660] == nil then item["drops"][1660] = 0.2 end + if item["drops"][1674] == nil then item["drops"][1674] = 0.18 end + if item["drops"][1675] == nil then item["drops"][1675] = 0.36 end + if item["drops"][1922] == nil then item["drops"][1922] = 0.2 end + if item["drops"][1934] == nil then item["drops"][1934] = 0.18 end + if item["drops"][1941] == nil then item["drops"][1941] = 0.2 end + if item["drops"][1996] == nil then item["drops"][1996] = 0.34 end + if item["drops"][1997] == nil then item["drops"][1997] = 0.12 end + if item["drops"][1999] == nil then item["drops"][1999] = 0.42 end + if item["drops"][2000] == nil then item["drops"][2000] = 0.06 end + if item["drops"][2004] == nil then item["drops"][2004] = 0.2 end + if item["drops"][2005] == nil then item["drops"][2005] = 0.48 end + if item["drops"][2008] == nil then item["drops"][2008] = 0.22 end + if item["drops"][2009] == nil then item["drops"][2009] = 0.34 end + if item["drops"][2010] == nil then item["drops"][2010] = 0.3 end + if item["drops"][2011] == nil then item["drops"][2011] = 0.12 end + if item["drops"][2015] == nil then item["drops"][2015] = 0.06 end + if item["drops"][2017] == nil then item["drops"][2017] = 0.07 end + if item["drops"][2025] == nil then item["drops"][2025] = 0.3 end + if item["drops"][2027] == nil then item["drops"][2027] = 0.14 end + if item["drops"][2033] == nil then item["drops"][2033] = 0.1 end + if item["drops"][2038] == nil then item["drops"][2038] = 0.22 end + if item["drops"][2043] == nil then item["drops"][2043] = 0.3 end + if item["drops"][2152] == nil then item["drops"][2152] = 0.2 end + if item["drops"][2162] == nil then item["drops"][2162] = 0.34 end + if item["drops"][2950] == nil then item["drops"][2950] = 0.18 end + if item["drops"][2951] == nil then item["drops"][2951] = 0.42 end + if item["drops"][2956] == nil then item["drops"][2956] = 0.24 end + if item["drops"][2957] == nil then item["drops"][2957] = 0.18 end + if item["drops"][2959] == nil then item["drops"][2959] = 0.3 end + if item["drops"][2962] == nil then item["drops"][2962] = 0.36 end + if item["drops"][2963] == nil then item["drops"][2963] = 0.08 end + if item["drops"][2964] == nil then item["drops"][2964] = 0.02 end + if item["drops"][2965] == nil then item["drops"][2965] = 0.02 end + if item["drops"][2967] == nil then item["drops"][2967] = 0.14 end + if item["drops"][2970] == nil then item["drops"][2970] = 0.18 end + if item["drops"][2971] == nil then item["drops"][2971] = 0.13 end + if item["drops"][2972] == nil then item["drops"][2972] = 0.24 end + if item["drops"][2976] == nil then item["drops"][2976] = 0.22 end + if item["drops"][2977] == nil then item["drops"][2977] = 0.38 end + if item["drops"][2978] == nil then item["drops"][2978] = 0.1 end + if item["drops"][2989] == nil then item["drops"][2989] = 0.32 end + if item["drops"][2990] == nil then item["drops"][2990] = 0.08 end + if item["drops"][3035] == nil then item["drops"][3035] = 0.32 end + if item["drops"][3099] == nil then item["drops"][3099] = 0.26 end + if item["drops"][3100] == nil then item["drops"][3100] = 0.12 end + if item["drops"][3103] == nil then item["drops"][3103] = 0.2 end + if item["drops"][3104] == nil then item["drops"][3104] = 0.2 end + if item["drops"][3107] == nil then item["drops"][3107] = 0.38 end + if item["drops"][3111] == nil then item["drops"][3111] = 0.18 end + if item["drops"][3112] == nil then item["drops"][3112] = 0.34 end + if item["drops"][3113] == nil then item["drops"][3113] = 0.1 end + if item["drops"][3115] == nil then item["drops"][3115] = 0.36 end + if item["drops"][3116] == nil then item["drops"][3116] = 0.3 end + if item["drops"][3118] == nil then item["drops"][3118] = 0.02 end + if item["drops"][3119] == nil then item["drops"][3119] = 0.22 end + if item["drops"][3120] == nil then item["drops"][3120] = 0.26 end + if item["drops"][3121] == nil then item["drops"][3121] = 0.36 end + if item["drops"][3122] == nil then item["drops"][3122] = 0.26 end + if item["drops"][3123] == nil then item["drops"][3123] = 0.1 end + if item["drops"][3126] == nil then item["drops"][3126] = 0.34 end + if item["drops"][3129] == nil then item["drops"][3129] = 0.2 end + if item["drops"][3141] == nil then item["drops"][3141] = 1.1 end + if item["drops"][3192] == nil then item["drops"][3192] = 0.2 end + if item["drops"][3195] == nil then item["drops"][3195] = 0.12 end + if item["drops"][3197] == nil then item["drops"][3197] = 0.02 end + if item["drops"][3198] == nil then item["drops"][3198] = 0.02 end + if item["drops"][3203] == nil then item["drops"][3203] = 0.02 end + if item["drops"][3206] == nil then item["drops"][3206] = 0.16 end + if item["drops"][3207] == nil then item["drops"][3207] = 0.14 end + if item["drops"][6123] == nil then item["drops"][6123] = 0.02 end + if item["drops"][6846] == nil then item["drops"][6846] = 0.04 end + if item["drops"][6927] == nil then item["drops"][6927] = 0.16 end + if item["drops"][7234] == nil then item["drops"][7234] = 0.2 end + if item["drops"][7235] == nil then item["drops"][7235] = 0.18 end + if item["drops"][8996] == nil then item["drops"][8996] = 0.98 end + if item["drops"][14428] == nil then item["drops"][14428] = 0.43 end + if item["drops"][14431] == nil then item["drops"][14431] = 0.08 end + if item["drops"][61656] == nil then item["drops"][61656] = 0.3 end + if item["drops"][61657] == nil then item["drops"][61657] = 0.3 end + if item["drops"][61658] == nil then item["drops"][61658] = 0.3 end + if item["drops"][61659] == nil then item["drops"][61659] = 0.3 end + if item["drops"][61660] == nil then item["drops"][61660] = 0.34 end + if item["drops"][61661] == nil then item["drops"][61661] = 0.34 end + if item["drops"][61662] == nil then item["drops"][61662] = 0.34 end + if item["drops"][61663] == nil then item["drops"][61663] = 0.02 end + if item["drops"][61664] == nil then item["drops"][61664] = 0.02 end + if item["drops"][61665] == nil then item["drops"][61665] = 0.02 end + if item["drops"][61666] == nil then item["drops"][61666] = 0.02 end + if item["drops"][61667] == nil then item["drops"][61667] = 0.02 end + if item["drops"][61668] == nil then item["drops"][61668] = 0.02 end + if item["drops"][61669] == nil then item["drops"][61669] = 0.02 end + if item["drops"][61670] == nil then item["drops"][61670] = 0.1 end + if item["drops"][61672] == nil then item["drops"][61672] = 0.1 end + if item["drops"][61673] == nil then item["drops"][61673] = 0.1 end + if item["drops"][61674] == nil then item["drops"][61674] = 0.1 end + if item["drops"][61675] == nil then item["drops"][61675] = 0.1 end + if item["drops"][61676] == nil then item["drops"][61676] = 0.1 end + if item["drops"][61677] == nil then item["drops"][61677] = 0.1 end + if item["drops"][61682] == nil then item["drops"][61682] = 0.3 end + if item["drops"][61683] == nil then item["drops"][61683] = 0.3 end + if item["drops"][61684] == nil then item["drops"][61684] = 0.3 end + if item["drops"][61685] == nil then item["drops"][61685] = 0.3 end + if item["drops"][61686] == nil then item["drops"][61686] = 0.3 end + if item["drops"][61687] == nil then item["drops"][61687] = 0.3 end + if item["drops"][61694] == nil then item["drops"][61694] = 0.1 end + if item["drops"][61695] == nil then item["drops"][61695] = 0.24 end + if item["drops"][61699] == nil then item["drops"][61699] = 0.1 end + if item["drops"][61700] == nil then item["drops"][61700] = 0.1 end + if item["drops"][61701] == nil then item["drops"][61701] = 0.3 end + if item["drops"][61709] == nil then item["drops"][61709] = 0.02 end + if item["drops"][61710] == nil then item["drops"][61710] = 0.02 end + if item["drops"][61712] == nil then item["drops"][61712] = 0.02 end + if item["drops"][61713] == nil then item["drops"][61713] = 0.02 end + if item["drops"][61714] == nil then item["drops"][61714] = 0.3 end + if item["drops"][61715] == nil then item["drops"][61715] = 0.1 end + if item["drops"][61716] == nil then item["drops"][61716] = 0.24 end + if item["drops"][61759] == nil then item["drops"][61759] = 0.3 end + if item["drops"][61783] == nil then item["drops"][61783] = 0.48 end + if item["drops"][61789] == nil then item["drops"][61789] = 0.24 end + if item["drops"][61790] == nil then item["drops"][61790] = 0.3 end + if item["drops"][61794] == nil then item["drops"][61794] = 0.1 end + if item["drops"][61863] == nil then item["drops"][61863] = 0.02 end + if item["drops"][61895] == nil then item["drops"][61895] = 0.02 end + if item["drops"][62717] == nil then item["drops"][62717] = 0.3 end + if item["drops"][62753] == nil then item["drops"][62753] = 0.02 end + if item["drops"][80250] == nil then item["drops"][80250] = 0.3 end +end + +do + local item = MTH_DS_AmmoItems[8183] + if not item then + item = {} + MTH_DS_AmmoItems[8183] = item + end + if not item["name"] then item["name"] = 'Precision Bow' end + if not item["level"] then item["level"] = 27 end + if not item["reqlevel"] then item["reqlevel"] = 22 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_04' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 11 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.08 end +end + +do + local item = MTH_DS_AmmoItems[8188] + if not item then + item = {} + MTH_DS_AmmoItems[8188] = item + end + if not item["name"] then item["name"] = 'Explosive Shotgun' end + if not item["level"] then item["level"] = 37 end + if not item["reqlevel"] then item["reqlevel"] = 32 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_08' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 15.2 end + if not item["speed"] then item["speed"] = 2.1 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.27 end +end + +do + local item = MTH_DS_AmmoItems[9399] + if item then + if not item["drops"] then item["drops"] = {} end + if item["drops"][6906] == nil then item["drops"][6906] = 100 end + end +end + +do + local item = MTH_DS_AmmoItems[9400] + if not item then + item = {} + MTH_DS_AmmoItems[9400] = item + end + if not item["name"] then item["name"] = "Baelog's Shortbow" end + if not item["level"] then item["level"] = 41 end + if not item["reqlevel"] then item["reqlevel"] = 36 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 18.3 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[9412] + if not item then + item = {} + MTH_DS_AmmoItems[9412] = item + end + if not item["name"] then item["name"] = "Galgann's Fireblaster" end + if not item["level"] then item["level"] = 47 end + if not item["reqlevel"] then item["reqlevel"] = 42 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 24.6 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[9422] + if not item then + item = {} + MTH_DS_AmmoItems[9422] = item + end + if not item["name"] then item["name"] = 'Shadowforge Bushmaster' end + if not item["level"] then item["level"] = 43 end + if not item["reqlevel"] then item["reqlevel"] = 38 end + if not item["icon"] then item["icon"] = 'INV_Musket_02' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 22.8 end + if not item["speed"] then item["speed"] = 2.9 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][4849] == nil then item["drops"][4849] = 0.02 end + if item["drops"][4853] == nil then item["drops"][4853] = 0.02 end + if item["drops"][4855] == nil then item["drops"][4855] = 0.02 end + if item["drops"][4857] == nil then item["drops"][4857] = 0.02 end + if item["drops"][4860] == nil then item["drops"][4860] = 0.02 end + if item["drops"][7012] == nil then item["drops"][7012] = 0.02 end + if item["drops"][7022] == nil then item["drops"][7022] = 0.02 end + if item["drops"][7290] == nil then item["drops"][7290] = 0.02 end + if item["drops"][7320] == nil then item["drops"][7320] = 0.02 end + if item["drops"][7321] == nil then item["drops"][7321] = 0.04 end + if item["drops"][60906] == nil then item["drops"][60906] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[9426] + if not item then + item = {} + MTH_DS_AmmoItems[9426] = item + end + if not item["name"] then item["name"] = 'Monolithic Bow' end + if not item["level"] then item["level"] = 41 end + if not item["reqlevel"] then item["reqlevel"] = 36 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 21.9 end + if not item["speed"] then item["speed"] = 2.7 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][4848] == nil then item["drops"][4848] = 0.02 end + if item["drops"][4850] == nil then item["drops"][4850] = 0.02 end + if item["drops"][4852] == nil then item["drops"][4852] = 0.02 end + if item["drops"][4853] == nil then item["drops"][4853] = 0.02 end + if item["drops"][4855] == nil then item["drops"][4855] = 0.04 end + if item["drops"][4857] == nil then item["drops"][4857] = 0.02 end + if item["drops"][4860] == nil then item["drops"][4860] = 0.02 end + if item["drops"][4861] == nil then item["drops"][4861] = 0.02 end + if item["drops"][4863] == nil then item["drops"][4863] = 0.02 end + if item["drops"][7022] == nil then item["drops"][7022] = 0.02 end + if item["drops"][7023] == nil then item["drops"][7023] = 0.06 end + if item["drops"][7320] == nil then item["drops"][7320] = 0.02 end + if item["drops"][7321] == nil then item["drops"][7321] = 0.02 end + if item["drops"][62725] == nil then item["drops"][62725] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[9456] + if not item then + item = {} + MTH_DS_AmmoItems[9456] = item + end + if not item["name"] then item["name"] = 'Glass Shooter' end + if not item["level"] then item["level"] = 35 end + if not item["reqlevel"] then item["reqlevel"] = 30 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_02' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 17.9 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[9487] + if not item then + item = {} + MTH_DS_AmmoItems[9487] = item + end + if not item["name"] then item["name"] = 'Hi-tech Supergun' end + if not item["level"] then item["level"] = 29 end + if not item["reqlevel"] then item["reqlevel"] = 24 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 14.3 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][6206] == nil then item["drops"][6206] = 0.02 end + if item["drops"][6207] == nil then item["drops"][6207] = 0.02 end + if item["drops"][6208] == nil then item["drops"][6208] = 0.02 end + if item["drops"][6211] == nil then item["drops"][6211] = 0.04 end + if item["drops"][6212] == nil then item["drops"][6212] = 0.04 end + if item["drops"][6213] == nil then item["drops"][6213] = 0.02 end + if item["drops"][6220] == nil then item["drops"][6220] = 0.12 end + if item["drops"][6223] == nil then item["drops"][6223] = 0.04 end + if item["drops"][6225] == nil then item["drops"][6225] = 0.06 end + if item["drops"][6226] == nil then item["drops"][6226] = 0.02 end + if item["drops"][6227] == nil then item["drops"][6227] = 0.02 end + if item["drops"][6230] == nil then item["drops"][6230] = 0.04 end + if item["drops"][6231] == nil then item["drops"][6231] = 0.04 end + if item["drops"][6232] == nil then item["drops"][6232] = 0.1 end + if item["drops"][6233] == nil then item["drops"][6233] = 0.04 end + if item["drops"][6234] == nil then item["drops"][6234] = 0.04 end + if item["drops"][6329] == nil then item["drops"][6329] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[10508] + if not item then + item = {} + MTH_DS_AmmoItems[10508] = item + end + if not item["name"] then item["name"] = 'Mithril Blunderbuss' end + if not item["level"] then item["level"] = 41 end + if not item["reqlevel"] then item["reqlevel"] = 36 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 17.9 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[10510] + if not item then + item = {} + MTH_DS_AmmoItems[10510] = item + end + if not item["name"] then item["name"] = 'Mithril Heavy-bore Rifle' end + if not item["level"] then item["level"] = 44 end + if not item["reqlevel"] then item["reqlevel"] = 39 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 20.2 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[10567] + if not item then + item = {} + MTH_DS_AmmoItems[10567] = item + end + if not item["name"] then item["name"] = 'Quillshooter' end + if not item["level"] then item["level"] = 38 end + if not item["reqlevel"] then item["reqlevel"] = 33 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 15.9 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][7328] == nil then item["drops"][7328] = 0.08 end + if item["drops"][7329] == nil then item["drops"][7329] = 0.02 end + if item["drops"][7332] == nil then item["drops"][7332] = 0.02 end + if item["drops"][7335] == nil then item["drops"][7335] = 0.06 end + if item["drops"][7337] == nil then item["drops"][7337] = 0.02 end + if item["drops"][7341] == nil then item["drops"][7341] = 0.04 end + if item["drops"][7342] == nil then item["drops"][7342] = 0.06 end + if item["drops"][7345] == nil then item["drops"][7345] = 0.04 end + if item["drops"][7347] == nil then item["drops"][7347] = 0.02 end + if item["drops"][7348] == nil then item["drops"][7348] = 0.04 end + if item["drops"][7349] == nil then item["drops"][7349] = 0.02 end + if item["drops"][7352] == nil then item["drops"][7352] = 0.04 end + if item["drops"][7353] == nil then item["drops"][7353] = 0.04 end +end + +do + local item = MTH_DS_AmmoItems[10624] + if not item then + item = {} + MTH_DS_AmmoItems[10624] = item + end + if not item["name"] then item["name"] = 'Stinging Bow' end + if not item["level"] then item["level"] = 47 end + if not item["reqlevel"] then item["reqlevel"] = 42 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 24.5 end + if not item["speed"] then item["speed"] = 2.1 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][5224] == nil then item["drops"][5224] = 0.04 end + if item["drops"][5225] == nil then item["drops"][5225] = 0.03 end + if item["drops"][5226] == nil then item["drops"][5226] = 0.06 end + if item["drops"][5228] == nil then item["drops"][5228] = 0.02 end + if item["drops"][5235] == nil then item["drops"][5235] = 0.03 end + if item["drops"][5243] == nil then item["drops"][5243] = 0.04 end + if item["drops"][5256] == nil then item["drops"][5256] = 0.04 end + if item["drops"][5259] == nil then item["drops"][5259] = 0.02 end + if item["drops"][5261] == nil then item["drops"][5261] = 0.02 end + if item["drops"][5263] == nil then item["drops"][5263] = 0.04 end + if item["drops"][5267] == nil then item["drops"][5267] = 0.04 end + if item["drops"][5269] == nil then item["drops"][5269] = 0.04 end + if item["drops"][5270] == nil then item["drops"][5270] = 0.02 end + if item["drops"][5271] == nil then item["drops"][5271] = 0.02 end + if item["drops"][5273] == nil then item["drops"][5273] = 0.02 end + if item["drops"][5277] == nil then item["drops"][5277] = 0.04 end + if item["drops"][5280] == nil then item["drops"][5280] = 0.02 end + if item["drops"][5283] == nil then item["drops"][5283] = 0.01 end + if item["drops"][5291] == nil then item["drops"][5291] = 0.02 end + if item["drops"][5708] == nil then item["drops"][5708] = 0.02 end + if item["drops"][8336] == nil then item["drops"][8336] = 0.02 end + if item["drops"][8438] == nil then item["drops"][8438] = 0.02 end + if item["drops"][8497] == nil then item["drops"][8497] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[11021] + if not item then + item = {} + MTH_DS_AmmoItems[11021] = item + end + if not item["name"] then item["name"] = 'Monster - Big Sniper Gun' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[11284] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][734] == nil then item["vendors"][734] = 0 end + if item["vendors"][1149] == nil then item["vendors"][1149] = 0 end + if item["vendors"][1285] == nil then item["vendors"][1285] = 0 end + if item["vendors"][1297] == nil then item["vendors"][1297] = 0 end + if item["vendors"][1461] == nil then item["vendors"][1461] = 0 end + if item["vendors"][2084] == nil then item["vendors"][2084] = 0 end + if item["vendors"][2401] == nil then item["vendors"][2401] = 0 end + if item["vendors"][2803] == nil then item["vendors"][2803] = 0 end + if item["vendors"][2806] == nil then item["vendors"][2806] = 0 end + if item["vendors"][2808] == nil then item["vendors"][2808] = 0 end + if item["vendors"][2820] == nil then item["vendors"][2820] = 0 end + if item["vendors"][2908] == nil then item["vendors"][2908] = 0 end + if item["vendors"][3018] == nil then item["vendors"][3018] = 0 end + if item["vendors"][3053] == nil then item["vendors"][3053] = 0 end + if item["vendors"][3313] == nil then item["vendors"][3313] = 0 end + if item["vendors"][3322] == nil then item["vendors"][3322] = 0 end + if item["vendors"][3350] == nil then item["vendors"][3350] = 0 end + if item["vendors"][3541] == nil then item["vendors"][3541] = 0 end + if item["vendors"][3625] == nil then item["vendors"][3625] = 0 end + if item["vendors"][4170] == nil then item["vendors"][4170] = 0 end + if item["vendors"][4241] == nil then item["vendors"][4241] = 0 end + if item["vendors"][4555] == nil then item["vendors"][4555] = 0 end + if item["vendors"][4603] == nil then item["vendors"][4603] = 0 end + if item["vendors"][4889] == nil then item["vendors"][4889] = 0 end + if item["vendors"][4896] == nil then item["vendors"][4896] = 0 end + if item["vendors"][5101] == nil then item["vendors"][5101] = 0 end + if item["vendors"][5123] == nil then item["vendors"][5123] = 0 end + if item["vendors"][5134] == nil then item["vendors"][5134] = 0 end + if item["vendors"][7942] == nil then item["vendors"][7942] = 0 end + if item["vendors"][8131] == nil then item["vendors"][8131] = 0 end + if item["vendors"][8139] == nil then item["vendors"][8139] = 0 end + if item["vendors"][8362] == nil then item["vendors"][8362] = 0 end + if item["vendors"][9320] == nil then item["vendors"][9320] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][11038] == nil then item["vendors"][11038] = 0 end + if item["vendors"][11184] == nil then item["vendors"][11184] = 0 end + if item["vendors"][11555] == nil then item["vendors"][11555] = 0 end + if item["vendors"][12021] == nil then item["vendors"][12021] = 0 end + if item["vendors"][12027] == nil then item["vendors"][12027] = 0 end + if item["vendors"][12036] == nil then item["vendors"][12036] = 0 end + if item["vendors"][12246] == nil then item["vendors"][12246] = 0 end + if item["vendors"][12959] == nil then item["vendors"][12959] = 0 end + if item["vendors"][12960] == nil then item["vendors"][12960] = 0 end + if item["vendors"][14624] == nil then item["vendors"][14624] = 0 end + if item["vendors"][15174] == nil then item["vendors"][15174] = 0 end + if item["vendors"][21002] == nil then item["vendors"][21002] = 0 end + if item["vendors"][51656] == nil then item["vendors"][51656] = 0 end + if item["vendors"][60456] == nil then item["vendors"][60456] = 0 end + if item["vendors"][60641] == nil then item["vendors"][60641] = 0 end + if item["vendors"][60646] == nil then item["vendors"][60646] = 0 end + if item["vendors"][60653] == nil then item["vendors"][60653] = 0 end + if item["vendors"][60663] == nil then item["vendors"][60663] = 0 end + if item["vendors"][60766] == nil then item["vendors"][60766] = 0 end + if item["vendors"][60790] == nil then item["vendors"][60790] = 0 end + if item["vendors"][61115] == nil then item["vendors"][61115] = 0 end + if item["vendors"][61140] == nil then item["vendors"][61140] = 0 end + if item["vendors"][61272] == nil then item["vendors"][61272] = 0 end + if item["vendors"][61288] == nil then item["vendors"][61288] = 0 end + if item["vendors"][61369] == nil then item["vendors"][61369] = 0 end + if item["vendors"][61440] == nil then item["vendors"][61440] = 0 end + if item["vendors"][61443] == nil then item["vendors"][61443] = 0 end + if item["vendors"][61445] == nil then item["vendors"][61445] = 0 end + if item["vendors"][61478] == nil then item["vendors"][61478] = 0 end + if item["vendors"][61523] == nil then item["vendors"][61523] = 0 end + if item["vendors"][61563] == nil then item["vendors"][61563] = 0 end + if item["vendors"][61651] == nil then item["vendors"][61651] = 0 end + if item["vendors"][61812] == nil then item["vendors"][61812] = 0 end + if item["vendors"][62086] == nil then item["vendors"][62086] = 0 end + if item["vendors"][62096] == nil then item["vendors"][62096] = 0 end + if item["vendors"][62148] == nil then item["vendors"][62148] = 0 end + if item["vendors"][62403] == nil then item["vendors"][62403] = 0 end + if item["vendors"][62404] == nil then item["vendors"][62404] = 0 end + if item["vendors"][62409] == nil then item["vendors"][62409] = 0 end + if item["vendors"][62436] == nil then item["vendors"][62436] = 0 end + if item["vendors"][62437] == nil then item["vendors"][62437] = 0 end + if item["vendors"][62462] == nil then item["vendors"][62462] = 0 end + if item["vendors"][62469] == nil then item["vendors"][62469] = 0 end + if item["vendors"][62628] == nil then item["vendors"][62628] = 0 end + if item["vendors"][65002] == nil then item["vendors"][65002] = 0 end + if item["vendors"][65003] == nil then item["vendors"][65003] = 0 end + if item["vendors"][80008] == nil then item["vendors"][80008] = 0 end + if item["vendors"][80267] == nil then item["vendors"][80267] = 0 end + if item["vendors"][80808] == nil then item["vendors"][80808] = 0 end + if item["vendors"][91403] == nil then item["vendors"][91403] = 0 end + if item["vendors"][91725] == nil then item["vendors"][91725] = 0 end + if item["vendors"][91868] == nil then item["vendors"][91868] = 0 end + if item["vendors"][91956] == nil then item["vendors"][91956] = 0 end + if item["vendors"][92169] == nil then item["vendors"][92169] = 0 end + if item["vendors"][92177] == nil then item["vendors"][92177] = 0 end + if item["vendors"][92200] == nil then item["vendors"][92200] = 0 end + if item["vendors"][92217] == nil then item["vendors"][92217] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[11285] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][228] == nil then item["vendors"][228] = 0 end + if item["vendors"][734] == nil then item["vendors"][734] = 0 end + if item["vendors"][789] == nil then item["vendors"][789] = 0 end + if item["vendors"][1149] == nil then item["vendors"][1149] = 0 end + if item["vendors"][1285] == nil then item["vendors"][1285] = 0 end + if item["vendors"][1298] == nil then item["vendors"][1298] = 0 end + if item["vendors"][1455] == nil then item["vendors"][1455] = 0 end + if item["vendors"][1462] == nil then item["vendors"][1462] = 0 end + if item["vendors"][2084] == nil then item["vendors"][2084] = 0 end + if item["vendors"][2286] == nil then item["vendors"][2286] = 0 end + if item["vendors"][2401] == nil then item["vendors"][2401] = 0 end + if item["vendors"][2803] == nil then item["vendors"][2803] = 0 end + if item["vendors"][2806] == nil then item["vendors"][2806] = 0 end + if item["vendors"][2808] == nil then item["vendors"][2808] = 0 end + if item["vendors"][2820] == nil then item["vendors"][2820] = 0 end + if item["vendors"][2839] == nil then item["vendors"][2839] = 0 end + if item["vendors"][2908] == nil then item["vendors"][2908] = 0 end + if item["vendors"][3015] == nil then item["vendors"][3015] = 0 end + if item["vendors"][3313] == nil then item["vendors"][3313] = 0 end + if item["vendors"][3350] == nil then item["vendors"][3350] = 0 end + if item["vendors"][3410] == nil then item["vendors"][3410] = 0 end + if item["vendors"][3541] == nil then item["vendors"][3541] = 0 end + if item["vendors"][3625] == nil then item["vendors"][3625] = 0 end + if item["vendors"][4170] == nil then item["vendors"][4170] = 0 end + if item["vendors"][4173] == nil then item["vendors"][4173] = 0 end + if item["vendors"][4241] == nil then item["vendors"][4241] = 0 end + if item["vendors"][4555] == nil then item["vendors"][4555] = 0 end + if item["vendors"][4604] == nil then item["vendors"][4604] = 0 end + if item["vendors"][4892] == nil then item["vendors"][4892] = 0 end + if item["vendors"][4896] == nil then item["vendors"][4896] = 0 end + if item["vendors"][5101] == nil then item["vendors"][5101] = 0 end + if item["vendors"][5122] == nil then item["vendors"][5122] = 0 end + if item["vendors"][5134] == nil then item["vendors"][5134] = 0 end + if item["vendors"][6028] == nil then item["vendors"][6028] = 0 end + if item["vendors"][7942] == nil then item["vendors"][7942] = 0 end + if item["vendors"][8139] == nil then item["vendors"][8139] = 0 end + if item["vendors"][8362] == nil then item["vendors"][8362] = 0 end + if item["vendors"][9320] == nil then item["vendors"][9320] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][9552] == nil then item["vendors"][9552] = 0 end + if item["vendors"][9555] == nil then item["vendors"][9555] = 0 end + if item["vendors"][11038] == nil then item["vendors"][11038] = 0 end + if item["vendors"][11555] == nil then item["vendors"][11555] = 0 end + if item["vendors"][12021] == nil then item["vendors"][12021] = 0 end + if item["vendors"][12027] == nil then item["vendors"][12027] = 0 end + if item["vendors"][12036] == nil then item["vendors"][12036] = 0 end + if item["vendors"][12246] == nil then item["vendors"][12246] = 0 end + if item["vendors"][12959] == nil then item["vendors"][12959] = 0 end + if item["vendors"][12960] == nil then item["vendors"][12960] = 0 end + if item["vendors"][14301] == nil then item["vendors"][14301] = 0 end + if item["vendors"][14624] == nil then item["vendors"][14624] = 0 end + if item["vendors"][15174] == nil then item["vendors"][15174] = 0 end + if item["vendors"][17598] == nil then item["vendors"][17598] = 0 end + if item["vendors"][21002] == nil then item["vendors"][21002] = 0 end + if item["vendors"][51656] == nil then item["vendors"][51656] = 0 end + if item["vendors"][60456] == nil then item["vendors"][60456] = 0 end + if item["vendors"][60641] == nil then item["vendors"][60641] = 0 end + if item["vendors"][60646] == nil then item["vendors"][60646] = 0 end + if item["vendors"][60653] == nil then item["vendors"][60653] = 0 end + if item["vendors"][60663] == nil then item["vendors"][60663] = 0 end + if item["vendors"][60766] == nil then item["vendors"][60766] = 0 end + if item["vendors"][60790] == nil then item["vendors"][60790] = 0 end + if item["vendors"][60803] == nil then item["vendors"][60803] = 0 end + if item["vendors"][61115] == nil then item["vendors"][61115] = 0 end + if item["vendors"][61140] == nil then item["vendors"][61140] = 0 end + if item["vendors"][61272] == nil then item["vendors"][61272] = 0 end + if item["vendors"][61288] == nil then item["vendors"][61288] = 0 end + if item["vendors"][61369] == nil then item["vendors"][61369] = 0 end + if item["vendors"][61440] == nil then item["vendors"][61440] = 0 end + if item["vendors"][61443] == nil then item["vendors"][61443] = 0 end + if item["vendors"][61478] == nil then item["vendors"][61478] = 0 end + if item["vendors"][61523] == nil then item["vendors"][61523] = 0 end + if item["vendors"][61563] == nil then item["vendors"][61563] = 0 end + if item["vendors"][61651] == nil then item["vendors"][61651] = 0 end + if item["vendors"][61812] == nil then item["vendors"][61812] = 0 end + if item["vendors"][62086] == nil then item["vendors"][62086] = 0 end + if item["vendors"][62096] == nil then item["vendors"][62096] = 0 end + if item["vendors"][62148] == nil then item["vendors"][62148] = 0 end + if item["vendors"][62307] == nil then item["vendors"][62307] = 0 end + if item["vendors"][62402] == nil then item["vendors"][62402] = 0 end + if item["vendors"][62404] == nil then item["vendors"][62404] = 0 end + if item["vendors"][62409] == nil then item["vendors"][62409] = 0 end + if item["vendors"][62436] == nil then item["vendors"][62436] = 0 end + if item["vendors"][62437] == nil then item["vendors"][62437] = 0 end + if item["vendors"][62462] == nil then item["vendors"][62462] = 0 end + if item["vendors"][62469] == nil then item["vendors"][62469] = 0 end + if item["vendors"][62628] == nil then item["vendors"][62628] = 0 end + if item["vendors"][62739] == nil then item["vendors"][62739] = 0 end + if item["vendors"][65002] == nil then item["vendors"][65002] = 0 end + if item["vendors"][65003] == nil then item["vendors"][65003] = 0 end + if item["vendors"][80267] == nil then item["vendors"][80267] = 0 end + if item["vendors"][80808] == nil then item["vendors"][80808] = 0 end + if item["vendors"][91403] == nil then item["vendors"][91403] = 0 end + if item["vendors"][91725] == nil then item["vendors"][91725] = 0 end + if item["vendors"][91868] == nil then item["vendors"][91868] = 0 end + if item["vendors"][91956] == nil then item["vendors"][91956] = 0 end + if item["vendors"][92169] == nil then item["vendors"][92169] = 0 end + if item["vendors"][92177] == nil then item["vendors"][92177] = 0 end + if item["vendors"][92200] == nil then item["vendors"][92200] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[11303] + if not item then + item = {} + MTH_DS_AmmoItems[11303] = item + end + if not item["name"] then item["name"] = 'Fine Shortbow' end + if not item["level"] then item["level"] = 16 end + if not item["reqlevel"] then item["reqlevel"] = 11 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_07' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 6.2 end + if not item["speed"] then item["speed"] = 1.7 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][150] == nil then item["vendors"][150] = 0 end + if item["vendors"][1198] == nil then item["vendors"][1198] = 0 end + if item["vendors"][1298] == nil then item["vendors"][1298] = 0 end + if item["vendors"][1455] == nil then item["vendors"][1455] = 0 end + if item["vendors"][1859] == nil then item["vendors"][1859] = 0 end + if item["vendors"][3015] == nil then item["vendors"][3015] = 0 end + if item["vendors"][3410] == nil then item["vendors"][3410] = 0 end + if item["vendors"][4173] == nil then item["vendors"][4173] = 0 end + if item["vendors"][4604] == nil then item["vendors"][4604] = 0 end + if item["vendors"][5122] == nil then item["vendors"][5122] = 0 end + if item["vendors"][14846] == nil then item["vendors"][14846] = 0 end +end + +do + local item = MTH_DS_AmmoItems[11304] + if not item then + item = {} + MTH_DS_AmmoItems[11304] = item + end + if not item["name"] then item["name"] = 'Fine Longbow' end + if not item["level"] then item["level"] = 19 end + if not item["reqlevel"] then item["reqlevel"] = 14 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 7.4 end + if not item["speed"] then item["speed"] = 2.7 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][228] == nil then item["vendors"][228] = 0 end + if item["vendors"][1459] == nil then item["vendors"][1459] = 0 end + if item["vendors"][1668] == nil then item["vendors"][1668] = 0 end + if item["vendors"][1687] == nil then item["vendors"][1687] = 0 end + if item["vendors"][3488] == nil then item["vendors"][3488] = 0 end + if item["vendors"][3534] == nil then item["vendors"][3534] = 0 end + if item["vendors"][9549] == nil then item["vendors"][9549] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][9553] == nil then item["vendors"][9553] = 0 end +end + +do + local item = MTH_DS_AmmoItems[11305] + if not item then + item = {} + MTH_DS_AmmoItems[11305] = item + end + if not item["name"] then item["name"] = 'Dense Shortbow' end + if not item["level"] then item["level"] = 35 end + if not item["reqlevel"] then item["reqlevel"] = 30 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_07' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 14.2 end + if not item["speed"] then item["speed"] = 1.9 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][2839] == nil then item["vendors"][2839] = 0 end + if item["vendors"][3951] == nil then item["vendors"][3951] = 0 end + if item["vendors"][4892] == nil then item["vendors"][4892] = 0 end + if item["vendors"][9551] == nil then item["vendors"][9551] = 0 end + if item["vendors"][9552] == nil then item["vendors"][9552] = 0 end + if item["vendors"][9555] == nil then item["vendors"][9555] = 0 end + if item["vendors"][14301] == nil then item["vendors"][14301] = 0 end + if item["vendors"][14846] == nil then item["vendors"][14846] = 0 end +end + +do + local item = MTH_DS_AmmoItems[11306] + if not item then + item = {} + MTH_DS_AmmoItems[11306] = item + end + if not item["name"] then item["name"] = 'Sturdy Recurve' end + if not item["level"] then item["level"] = 32 end + if not item["reqlevel"] then item["reqlevel"] = 27 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_07' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 13 end + if not item["speed"] then item["speed"] = 2.2 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][1298] == nil then item["vendors"][1298] = 0 end + if item["vendors"][1455] == nil then item["vendors"][1455] = 0 end + if item["vendors"][3015] == nil then item["vendors"][3015] = 0 end + if item["vendors"][4173] == nil then item["vendors"][4173] = 0 end + if item["vendors"][4604] == nil then item["vendors"][4604] = 0 end + if item["vendors"][5122] == nil then item["vendors"][5122] = 0 end + if item["vendors"][14846] == nil then item["vendors"][14846] = 0 end +end + +do + local item = MTH_DS_AmmoItems[11307] + if not item then + item = {} + MTH_DS_AmmoItems[11307] = item + end + if not item["name"] then item["name"] = 'Massive Longbow' end + if not item["level"] then item["level"] = 47 end + if not item["reqlevel"] then item["reqlevel"] = 42 end + if not item["icon"] then item["icon"] = 'GenBow_twow3F' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 22 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][1298] == nil then item["vendors"][1298] = 0 end + if item["vendors"][1455] == nil then item["vendors"][1455] = 0 end + if item["vendors"][3015] == nil then item["vendors"][3015] = 0 end + if item["vendors"][4604] == nil then item["vendors"][4604] = 0 end + if item["vendors"][5122] == nil then item["vendors"][5122] = 0 end +end + +do + local item = MTH_DS_AmmoItems[11308] + if not item then + item = {} + MTH_DS_AmmoItems[11308] = item + end + if not item["name"] then item["name"] = 'Sylvan Shortbow' end + if not item["level"] then item["level"] = 49 end + if not item["reqlevel"] then item["reqlevel"] = 44 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_07' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 22.8 end + if not item["speed"] then item["speed"] = 2 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][4173] == nil then item["vendors"][4173] = 0 end + if item["vendors"][9548] == nil then item["vendors"][9548] = 0 end + if item["vendors"][12029] == nil then item["vendors"][12029] = 0 end + if item["vendors"][14846] == nil then item["vendors"][14846] = 0 end +end + +do + local item = MTH_DS_AmmoItems[11628] + if not item then + item = {} + MTH_DS_AmmoItems[11628] = item + end + if not item["name"] then item["name"] = "Houndmaster's Bow" end + if not item["level"] then item["level"] = 53 end + if not item["reqlevel"] then item["reqlevel"] = 48 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_04' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 27.2 end + if not item["speed"] then item["speed"] = 1.8 end +end + +do + local item = MTH_DS_AmmoItems[11629] + if not item then + item = {} + MTH_DS_AmmoItems[11629] = item + end + if not item["name"] then item["name"] = "Houndmaster's Rifle" end + if not item["level"] then item["level"] = 53 end + if not item["reqlevel"] then item["reqlevel"] = 48 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 27.4 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[11630] + if item then + if not item["drops"] then item["drops"] = {} end + if item["drops"][9025] == nil then item["drops"][9025] = 16 end + end +end + +do + local item = MTH_DS_AmmoItems[12446] + if not item then + item = {} + MTH_DS_AmmoItems[12446] = item + end + if not item["name"] then item["name"] = 'Anvilmar Musket' end + if not item["level"] then item["level"] = 5 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.2 end + if not item["speed"] then item["speed"] = 2.7 end +end + +do + local item = MTH_DS_AmmoItems[12447] + if not item then + item = {} + MTH_DS_AmmoItems[12447] = item + end + if not item["name"] then item["name"] = 'Thistlewood Bow' end + if not item["level"] then item["level"] = 5 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.2 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[12448] + if not item then + item = {} + MTH_DS_AmmoItems[12448] = item + end + if not item["name"] then item["name"] = 'Light Hunting Rifle' end + if not item["level"] then item["level"] = 5 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.1 end + if not item["speed"] then item["speed"] = 1.9 end +end + +do + local item = MTH_DS_AmmoItems[12449] + if not item then + item = {} + MTH_DS_AmmoItems[12449] = item + end + if not item["name"] then item["name"] = 'Primitive Bow' end + if not item["level"] then item["level"] = 5 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.2 end + if not item["speed"] then item["speed"] = 2.7 end +end + +do + local item = MTH_DS_AmmoItems[12523] + if not item then + item = {} + MTH_DS_AmmoItems[12523] = item + end + if not item["name"] then item["name"] = 'Monster - Gun, Silver Musket' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[12651] + if not item then + item = {} + MTH_DS_AmmoItems[12651] = item + end + if not item["name"] then item["name"] = 'Blackcrow' end + if not item["level"] then item["level"] = 59 end + if not item["reqlevel"] then item["reqlevel"] = 54 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_04' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 30.3 end + if not item["speed"] then item["speed"] = 3.2 end +end + +do + local item = MTH_DS_AmmoItems[12653] + if not item then + item = {} + MTH_DS_AmmoItems[12653] = item + end + if not item["name"] then item["name"] = 'Riphook' end + if not item["level"] then item["level"] = 59 end + if not item["reqlevel"] then item["reqlevel"] = 54 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 30.2 end + if not item["speed"] then item["speed"] = 2.2 end +end + +do + local item = MTH_DS_AmmoItems[12654] + if item then + if not item["drops"] then item["drops"] = {} end + if item["drops"][9236] == nil then item["drops"][9236] = 100 end + end +end + +do + local item = MTH_DS_AmmoItems[13019] + if not item then + item = {} + MTH_DS_AmmoItems[13019] = item + end + if not item["name"] then item["name"] = 'Harpyclaw Short Bow' end + if not item["level"] then item["level"] = 32 end + if not item["reqlevel"] then item["reqlevel"] = 27 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 16.1 end + if not item["speed"] then item["speed"] = 1.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2852] == nil then item["objects"][2852] = 0.01 end + if item["objects"][2855] == nil then item["objects"][2855] = 0.01 end + if item["objects"][2857] == nil then item["objects"][2857] = 0.01 end + if item["objects"][4095] == nil then item["objects"][4095] = 0.01 end + if item["objects"][4096] == nil then item["objects"][4096] = 0.01 end + if item["objects"][4149] == nil then item["objects"][4149] = 0.01 end + if item["objects"][75296] == nil then item["objects"][75296] = 0.01 end + if item["objects"][75297] == nil then item["objects"][75297] = 0.01 end + if item["objects"][75299] == nil then item["objects"][75299] = 0.01 end + if item["objects"][75300] == nil then item["objects"][75300] = 0.01 end + if item["objects"][105570] == nil then item["objects"][105570] = 0.01 end + if item["objects"][105578] == nil then item["objects"][105578] = 0.01 end + if item["objects"][105579] == nil then item["objects"][105579] = 0.01 end + if item["objects"][105581] == nil then item["objects"][105581] = 0.01 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.03 end +end + +do + local item = MTH_DS_AmmoItems[13020] + if not item then + item = {} + MTH_DS_AmmoItems[13020] = item + end + if not item["name"] then item["name"] = 'Skystriker Bow' end + if not item["level"] then item["level"] = 39 end + if not item["reqlevel"] then item["reqlevel"] = 34 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 20.7 end + if not item["speed"] then item["speed"] = 2.1 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2855] == nil then item["objects"][2855] = 0.01 end + if item["objects"][2857] == nil then item["objects"][2857] = 0.01 end + if item["objects"][4149] == nil then item["objects"][4149] = 0.01 end + if item["objects"][105570] == nil then item["objects"][105570] = 0.01 end + if item["objects"][105578] == nil then item["objects"][105578] = 0.01 end + if item["objects"][105579] == nil then item["objects"][105579] = 0.01 end + if item["objects"][105581] == nil then item["objects"][105581] = 0.01 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.03 end +end + +do + local item = MTH_DS_AmmoItems[13021] + if not item then + item = {} + MTH_DS_AmmoItems[13021] = item + end + if not item["name"] then item["name"] = 'Needle Threader' end + if not item["level"] then item["level"] = 47 end + if not item["reqlevel"] then item["reqlevel"] = 42 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 24.5 end + if not item["speed"] then item["speed"] = 2 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][1653] == nil then item["drops"][1653] = 3.85 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][4149] == nil then item["objects"][4149] = 0.01 end + if item["objects"][153451] == nil then item["objects"][153451] = 0.01 end + if item["objects"][153453] == nil then item["objects"][153453] = 0.01 end +end + +do + local item = MTH_DS_AmmoItems[13022] + if not item then + item = {} + MTH_DS_AmmoItems[13022] = item + end + if not item["name"] then item["name"] = 'Gryphonwing Long Bow' end + if not item["level"] then item["level"] = 55 end + if not item["reqlevel"] then item["reqlevel"] = 50 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 28.3 end + if not item["speed"] then item["speed"] = 2.7 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153451] == nil then item["objects"][153451] = 0.01 end + if item["objects"][153453] == nil then item["objects"][153453] = 0.01 end + if item["objects"][153454] == nil then item["objects"][153454] = 0.01 end + if item["objects"][153463] == nil then item["objects"][153463] = 0.01 end + if item["objects"][153464] == nil then item["objects"][153464] = 0.01 end + if item["objects"][153468] == nil then item["objects"][153468] = 0.01 end + if item["objects"][153469] == nil then item["objects"][153469] = 0.01 end + if item["objects"][160836] == nil then item["objects"][160836] = 0.1 end +end + +do + local item = MTH_DS_AmmoItems[13023] + if not item then + item = {} + MTH_DS_AmmoItems[13023] = item + end + if not item["name"] then item["name"] = 'Eaglehorn Long Bow' end + if not item["level"] then item["level"] = 63 end + if not item["reqlevel"] then item["reqlevel"] = 58 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 32.2 end + if not item["speed"] then item["speed"] = 1.8 end +end + +do + local item = MTH_DS_AmmoItems[13037] + if not item then + item = {} + MTH_DS_AmmoItems[13037] = item + end + if not item["name"] then item["name"] = 'Crystalpine Stinger' end + if not item["level"] then item["level"] = 32 end + if not item["reqlevel"] then item["reqlevel"] = 27 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_04' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 15.9 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2852] == nil then item["objects"][2852] = 0.01 end + if item["objects"][2855] == nil then item["objects"][2855] = 0.01 end + if item["objects"][2857] == nil then item["objects"][2857] = 0.01 end + if item["objects"][3703] == nil then item["objects"][3703] = 0.01 end + if item["objects"][4095] == nil then item["objects"][4095] = 0.01 end + if item["objects"][4096] == nil then item["objects"][4096] = 0.01 end + if item["objects"][4149] == nil then item["objects"][4149] = 0.01 end + if item["objects"][75296] == nil then item["objects"][75296] = 0.01 end + if item["objects"][75297] == nil then item["objects"][75297] = 0.01 end + if item["objects"][75299] == nil then item["objects"][75299] = 0.01 end + if item["objects"][75300] == nil then item["objects"][75300] = 0.01 end + if item["objects"][105570] == nil then item["objects"][105570] = 0.01 end + if item["objects"][105578] == nil then item["objects"][105578] = 0.01 end + if item["objects"][105579] == nil then item["objects"][105579] = 0.01 end + if item["objects"][105581] == nil then item["objects"][105581] = 0.01 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.03 end +end + +do + local item = MTH_DS_AmmoItems[13038] + if not item then + item = {} + MTH_DS_AmmoItems[13038] = item + end + if not item["name"] then item["name"] = 'Swiftwind' end + if not item["level"] then item["level"] = 40 end + if not item["reqlevel"] then item["reqlevel"] = 35 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_04' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 21.3 end + if not item["speed"] then item["speed"] = 2 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2855] == nil then item["objects"][2855] = 0.01 end + if item["objects"][2857] == nil then item["objects"][2857] = 0.01 end + if item["objects"][4149] == nil then item["objects"][4149] = 0.01 end + if item["objects"][105570] == nil then item["objects"][105570] = 0.01 end + if item["objects"][105578] == nil then item["objects"][105578] = 0.01 end + if item["objects"][105579] == nil then item["objects"][105579] = 0.01 end + if item["objects"][105581] == nil then item["objects"][105581] = 0.01 end + if item["objects"][153451] == nil then item["objects"][153451] = 0.01 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.04 end +end + +do + local item = MTH_DS_AmmoItems[13039] + if not item then + item = {} + MTH_DS_AmmoItems[13039] = item + end + if not item["name"] then item["name"] = 'Skull Splitting Crossbow' end + if not item["level"] then item["level"] = 48 end + if not item["reqlevel"] then item["reqlevel"] = 43 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_04' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 25.2 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][4149] == nil then item["objects"][4149] = 0.01 end + if item["objects"][153451] == nil then item["objects"][153451] = 0.01 end + if item["objects"][153453] == nil then item["objects"][153453] = 0.01 end +end + +do + local item = MTH_DS_AmmoItems[13040] + if not item then + item = {} + MTH_DS_AmmoItems[13040] = item + end + if not item["name"] then item["name"] = 'Heartseeking Crossbow' end + if not item["level"] then item["level"] = 56 end + if not item["reqlevel"] then item["reqlevel"] = 51 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_04' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 28.9 end + if not item["speed"] then item["speed"] = 3.1 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153453] == nil then item["objects"][153453] = 0.01 end + if item["objects"][153454] == nil then item["objects"][153454] = 0.01 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[13136] + if not item then + item = {} + MTH_DS_AmmoItems[13136] = item + end + if not item["name"] then item["name"] = "Lil Timmy's Peashooter" end + if not item["level"] then item["level"] = 21 end + if not item["reqlevel"] then item["reqlevel"] = 16 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_08' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 11 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2849] == nil then item["objects"][2849] = 0.01 end + if item["objects"][2850] == nil then item["objects"][2850] = 0.01 end + if item["objects"][2852] == nil then item["objects"][2852] = 0.01 end + if item["objects"][2855] == nil then item["objects"][2855] = 0.01 end + if item["objects"][3714] == nil then item["objects"][3714] = 0.01 end + if item["objects"][3715] == nil then item["objects"][3715] = 0.01 end + if item["objects"][4095] == nil then item["objects"][4095] = 0.01 end + if item["objects"][4096] == nil then item["objects"][4096] = 0.01 end + if item["objects"][74447] == nil then item["objects"][74447] = 0.01 end + if item["objects"][74448] == nil then item["objects"][74448] = 0.01 end + if item["objects"][75295] == nil then item["objects"][75295] = 0.01 end + if item["objects"][75296] == nil then item["objects"][75296] = 0.01 end + if item["objects"][75297] == nil then item["objects"][75297] = 0.01 end + if item["objects"][75298] == nil then item["objects"][75298] = 0.01 end + if item["objects"][75299] == nil then item["objects"][75299] = 0.01 end + if item["objects"][75300] == nil then item["objects"][75300] = 0.01 end + if item["objects"][105570] == nil then item["objects"][105570] = 0.01 end + if item["objects"][105578] == nil then item["objects"][105578] = 0.01 end + if item["objects"][105579] == nil then item["objects"][105579] = 0.01 end + if item["objects"][106319] == nil then item["objects"][106319] = 0.01 end + if item["objects"][111095] == nil then item["objects"][111095] = 0.01 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.01 end +end + +do + local item = MTH_DS_AmmoItems[13137] + if not item then + item = {} + MTH_DS_AmmoItems[13137] = item + end + if not item["name"] then item["name"] = 'Ironweaver' end + if not item["level"] then item["level"] = 34 end + if not item["reqlevel"] then item["reqlevel"] = 29 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_08' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 17.3 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2852] == nil then item["objects"][2852] = 0.01 end + if item["objects"][2855] == nil then item["objects"][2855] = 0.01 end + if item["objects"][2857] == nil then item["objects"][2857] = 0.01 end + if item["objects"][4095] == nil then item["objects"][4095] = 0.01 end + if item["objects"][4096] == nil then item["objects"][4096] = 0.01 end + if item["objects"][4149] == nil then item["objects"][4149] = 0.01 end + if item["objects"][75296] == nil then item["objects"][75296] = 0.01 end + if item["objects"][75297] == nil then item["objects"][75297] = 0.01 end + if item["objects"][75299] == nil then item["objects"][75299] = 0.01 end + if item["objects"][75300] == nil then item["objects"][75300] = 0.01 end + if item["objects"][105570] == nil then item["objects"][105570] = 0.01 end + if item["objects"][105578] == nil then item["objects"][105578] = 0.01 end + if item["objects"][105579] == nil then item["objects"][105579] = 0.01 end + if item["objects"][105581] == nil then item["objects"][105581] = 0.01 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[13138] + if not item then + item = {} + MTH_DS_AmmoItems[13138] = item + end + if not item["name"] then item["name"] = 'The Silencer' end + if not item["level"] then item["level"] = 42 end + if not item["reqlevel"] then item["reqlevel"] = 37 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_08' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 22.3 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2855] == nil then item["objects"][2855] = 0.01 end + if item["objects"][2857] == nil then item["objects"][2857] = 0.01 end + if item["objects"][4149] == nil then item["objects"][4149] = 0.01 end + if item["objects"][105570] == nil then item["objects"][105570] = 0.01 end + if item["objects"][105578] == nil then item["objects"][105578] = 0.01 end + if item["objects"][105579] == nil then item["objects"][105579] = 0.01 end + if item["objects"][105581] == nil then item["objects"][105581] = 0.01 end + if item["objects"][153451] == nil then item["objects"][153451] = 0.01 end +end + +do + local item = MTH_DS_AmmoItems[13139] + if not item then + item = {} + MTH_DS_AmmoItems[13139] = item + end + if not item["name"] then item["name"] = 'Guttbuster' end + if not item["level"] then item["level"] = 50 end + if not item["reqlevel"] then item["reqlevel"] = 45 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_08' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 26.1 end + if not item["speed"] then item["speed"] = 2.7 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][4149] == nil then item["objects"][4149] = 0.01 end + if item["objects"][153451] == nil then item["objects"][153451] = 0.01 end + if item["objects"][153453] == nil then item["objects"][153453] = 0.01 end + if item["objects"][153454] == nil then item["objects"][153454] = 0.01 end +end + +do + local item = MTH_DS_AmmoItems[13146] + if not item then + item = {} + MTH_DS_AmmoItems[13146] = item + end + if not item["name"] then item["name"] = 'Shell Launcher Shotgun' end + if not item["level"] then item["level"] = 58 end + if not item["reqlevel"] then item["reqlevel"] = 53 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_08' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 29.8 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153453] == nil then item["objects"][153453] = 0.01 end + if item["objects"][153454] == nil then item["objects"][153454] = 0.01 end + if item["objects"][153462] == nil then item["objects"][153462] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[13147] + if not item then + item = {} + MTH_DS_AmmoItems[13147] = item + end + if not item["name"] then item["name"] = 'Monster - Bow, White' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[13175] + if not item then + item = {} + MTH_DS_AmmoItems[13175] = item + end + if not item["name"] then item["name"] = "Voone's Twitchbow" end + if not item["level"] then item["level"] = 60 end + if not item["reqlevel"] then item["reqlevel"] = 55 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_10' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 27.8 end + if not item["speed"] then item["speed"] = 1.6 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][9237] == nil then item["drops"][9237] = 1 end +end + +do + local item = MTH_DS_AmmoItems[13248] + if not item then + item = {} + MTH_DS_AmmoItems[13248] = item + end + if not item["name"] then item["name"] = 'Burstshot Harquebus' end + if not item["level"] then item["level"] = 56 end + if not item["reqlevel"] then item["reqlevel"] = 51 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 28.8 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[13377] + if item then + if not item["drops"] then item["drops"] = {} end + if item["drops"][10997] == nil then item["drops"][10997] = 100 end + end +end + +do + local item = MTH_DS_AmmoItems[13380] + if not item then + item = {} + MTH_DS_AmmoItems[13380] = item + end + if not item["name"] then item["name"] = "Willey's Portable Howitzer" end + if not item["level"] then item["level"] = 61 end + if not item["reqlevel"] then item["reqlevel"] = 56 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 31.2 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[13474] + if not item then + item = {} + MTH_DS_AmmoItems[13474] = item + end + if not item["name"] then item["name"] = "Farmer Dalson's Shotgun" end + if not item["level"] then item["level"] = 56 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 25.8 end + if not item["speed"] then item["speed"] = 1.9 end +end + +do + local item = MTH_DS_AmmoItems[13824] + if not item then + item = {} + MTH_DS_AmmoItems[13824] = item + end + if not item["name"] then item["name"] = 'Recurve Long Bow' end + if not item["level"] then item["level"] = 55 end + if not item["reqlevel"] then item["reqlevel"] = 45 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 15.2 end + if not item["speed"] then item["speed"] = 2.5 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][68] == nil then item["drops"][68] = 0.46 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.03 end + if item["objects"][176224] == nil then item["objects"][176224] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[13825] + if not item then + item = {} + MTH_DS_AmmoItems[13825] = item + end + if not item["name"] then item["name"] = 'Primed Musket' end + if not item["level"] then item["level"] = 57 end + if not item["reqlevel"] then item["reqlevel"] = 52 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 15.8 end + if not item["speed"] then item["speed"] = 1.8 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][68] == nil then item["drops"][68] = 0.7 end + if item["drops"][4624] == nil then item["drops"][4624] = 0.74 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.02 end + if item["objects"][176224] == nil then item["objects"][176224] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[13923] + if not item then + item = {} + MTH_DS_AmmoItems[13923] = item + end + if not item["name"] then item["name"] = 'Monster - Gun, Tauren Blade Silver' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[13924] + if not item then + item = {} + MTH_DS_AmmoItems[13924] = item + end + if not item["name"] then item["name"] = 'Monster - Gun, Tauren Scope Blade Feathered Silver Deluxe' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[14105] + if not item then + item = {} + MTH_DS_AmmoItems[14105] = item + end + if not item["name"] then item["name"] = 'Monster - Bow, C01/B02 White' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[14118] + if not item then + item = {} + MTH_DS_AmmoItems[14118] = item + end + if not item["name"] then item["name"] = 'Monster - Bow, C02/B02 Black' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[14394] + if not item then + item = {} + MTH_DS_AmmoItems[14394] = item + end + if not item["name"] then item["name"] = 'Durability Bow' end + if not item["level"] then item["level"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 1.1 end + if not item["speed"] then item["speed"] = 1.8 end +end + +do + local item = MTH_DS_AmmoItems[14642] + if not item then + item = {} + MTH_DS_AmmoItems[14642] = item + end + if not item["name"] then item["name"] = 'Monster - Gun, Tauren Feathers Silver' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[15205] + if not item then + item = {} + MTH_DS_AmmoItems[15205] = item + end + if not item["name"] then item["name"] = 'Owlsight Rifle' end + if not item["level"] then item["level"] = 20 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 7.9 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[15284] + if not item then + item = {} + MTH_DS_AmmoItems[15284] = item + end + if not item["name"] then item["name"] = 'Long Battle Bow' end + if not item["level"] then item["level"] = 29 end + if not item["reqlevel"] then item["reqlevel"] = 24 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 11.8 end + if not item["speed"] then item["speed"] = 2.2 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.07 end +end + +do + local item = MTH_DS_AmmoItems[15285] + if not item then + item = {} + MTH_DS_AmmoItems[15285] = item + end + if not item["name"] then item["name"] = "Archer's Longbow" end + if not item["level"] then item["level"] = 32 end + if not item["reqlevel"] then item["reqlevel"] = 27 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_03' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 12.9 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.45 end +end + +do + local item = MTH_DS_AmmoItems[15286] + if not item then + item = {} + MTH_DS_AmmoItems[15286] = item + end + if not item["name"] then item["name"] = 'Long Redwood Bow' end + if not item["level"] then item["level"] = 35 end + if not item["reqlevel"] then item["reqlevel"] = 30 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_10' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 14.3 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.47 end +end + +do + local item = MTH_DS_AmmoItems[15287] + if not item then + item = {} + MTH_DS_AmmoItems[15287] = item + end + if not item["name"] then item["name"] = 'Crusader Bow' end + if not item["level"] then item["level"] = 45 end + if not item["reqlevel"] then item["reqlevel"] = 40 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 20.9 end + if not item["speed"] then item["speed"] = 2.2 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][141979] == nil then item["objects"][141979] = 2.4 end + if item["objects"][142184] == nil then item["objects"][142184] = 0.4 end +end + +do + local item = MTH_DS_AmmoItems[15288] + if not item then + item = {} + MTH_DS_AmmoItems[15288] = item + end + if not item["name"] then item["name"] = 'Blasthorn Bow' end + if not item["level"] then item["level"] = 61 end + if not item["reqlevel"] then item["reqlevel"] = 56 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_07' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 28.3 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[15289] + if not item then + item = {} + MTH_DS_AmmoItems[15289] = item + end + if not item["name"] then item["name"] = 'Archstrike Bow' end + if not item["level"] then item["level"] = 65 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_06' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 30.2 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[15291] + if not item then + item = {} + MTH_DS_AmmoItems[15291] = item + end + if not item["name"] then item["name"] = 'Harpy Needler' end + if not item["level"] then item["level"] = 51 end + if not item["reqlevel"] then item["reqlevel"] = 46 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 23.7 end + if not item["speed"] then item["speed"] = 2.7 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.02 end + if item["objects"][179697] == nil then item["objects"][179697] = 0.3 end +end + +do + local item = MTH_DS_AmmoItems[15294] + if not item then + item = {} + MTH_DS_AmmoItems[15294] = item + end + if not item["name"] then item["name"] = 'Siege Bow' end + if not item["level"] then item["level"] = 53 end + if not item["reqlevel"] then item["reqlevel"] = 48 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_07' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 24.6 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[15295] + if not item then + item = {} + MTH_DS_AmmoItems[15295] = item + end + if not item["name"] then item["name"] = 'Quillfire Bow' end + if not item["level"] then item["level"] = 55 end + if not item["reqlevel"] then item["reqlevel"] = 50 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_10' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 25.7 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][179697] == nil then item["objects"][179697] = 0.5 end +end + +do + local item = MTH_DS_AmmoItems[15296] + if not item then + item = {} + MTH_DS_AmmoItems[15296] = item + end + if not item["name"] then item["name"] = 'Hawkeye Bow' end + if not item["level"] then item["level"] = 63 end + if not item["reqlevel"] then item["reqlevel"] = 58 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_12' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 29.4 end + if not item["speed"] then item["speed"] = 1.7 end +end + +do + local item = MTH_DS_AmmoItems[15322] + if not item then + item = {} + MTH_DS_AmmoItems[15322] = item + end + if not item["name"] then item["name"] = 'Smoothbore Gun' end + if not item["level"] then item["level"] = 39 end + if not item["reqlevel"] then item["reqlevel"] = 34 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 16.6 end + if not item["speed"] then item["speed"] = 2.5 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.33 end +end + +do + local item = MTH_DS_AmmoItems[15323] + if not item then + item = {} + MTH_DS_AmmoItems[15323] = item + end + if not item["name"] then item["name"] = 'Percussion Shotgun' end + if not item["level"] then item["level"] = 50 end + if not item["reqlevel"] then item["reqlevel"] = 45 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 23.3 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.02 end +end + +do + local item = MTH_DS_AmmoItems[15324] + if not item then + item = {} + MTH_DS_AmmoItems[15324] = item + end + if not item["name"] then item["name"] = 'Burnside Rifle' end + if not item["level"] then item["level"] = 56 end + if not item["reqlevel"] then item["reqlevel"] = 51 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 26 end + if not item["speed"] then item["speed"] = 2.5 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.14 end + if item["objects"][160845] == nil then item["objects"][160845] = 0.7 end + if item["objects"][176224] == nil then item["objects"][176224] = 0.1 end +end + +do + local item = MTH_DS_AmmoItems[15325] + if not item then + item = {} + MTH_DS_AmmoItems[15325] = item + end + if not item["name"] then item["name"] = 'Sharpshooter Harquebus' end + if not item["level"] then item["level"] = 60 end + if not item["reqlevel"] then item["reqlevel"] = 55 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 28 end + if not item["speed"] then item["speed"] = 2.2 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][153462] == nil then item["objects"][153462] = 0.12 end + if item["objects"][160845] == nil then item["objects"][160845] = 0.3 end +end + +do + local item = MTH_DS_AmmoItems[15460] + if not item then + item = {} + MTH_DS_AmmoItems[15460] = item + end + if not item["name"] then item["name"] = 'Monster - Gun, Shotgun' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[15691] + if not item then + item = {} + MTH_DS_AmmoItems[15691] = item + end + if not item["name"] then item["name"] = 'Sidegunner Shottie' end + if not item["level"] then item["level"] = 38 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 16 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[15807] + if not item then + item = {} + MTH_DS_AmmoItems[15807] = item + end + if not item["name"] then item["name"] = 'Light Crossbow' end + if not item["level"] then item["level"] = 8 end + if not item["reqlevel"] then item["reqlevel"] = 3 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_02' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.6 end + if not item["speed"] then item["speed"] = 2.5 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][1287] == nil then item["vendors"][1287] = 0 end + if item["vendors"][4602] == nil then item["vendors"][4602] = 0 end + if item["vendors"][7976] == nil then item["vendors"][7976] = 0 end + if item["vendors"][81035] == nil then item["vendors"][81035] = 0 end +end + +do + local item = MTH_DS_AmmoItems[15808] + if not item then + item = {} + MTH_DS_AmmoItems[15808] = item + end + if not item["name"] then item["name"] = 'Fine Light Crossbow' end + if not item["level"] then item["level"] = 21 end + if not item["reqlevel"] then item["reqlevel"] = 16 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_02' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 7.4 end + if not item["speed"] then item["speed"] = 2.7 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][1287] == nil then item["vendors"][1287] = 0 end + if item["vendors"][4602] == nil then item["vendors"][4602] = 0 end + if item["vendors"][7976] == nil then item["vendors"][7976] = 0 end +end + +do + local item = MTH_DS_AmmoItems[15809] + if not item then + item = {} + MTH_DS_AmmoItems[15809] = item + end + if not item["name"] then item["name"] = 'Heavy Crossbow' end + if not item["level"] then item["level"] = 34 end + if not item["reqlevel"] then item["reqlevel"] = 29 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_03' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 13 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][1287] == nil then item["vendors"][1287] = 0 end + if item["vendors"][4602] == nil then item["vendors"][4602] = 0 end + if item["vendors"][7976] == nil then item["vendors"][7976] = 0 end +end + +do + local item = MTH_DS_AmmoItems[15995] + if not item then + item = {} + MTH_DS_AmmoItems[15995] = item + end + if not item["name"] then item["name"] = 'Thorium Rifle' end + if not item["level"] then item["level"] = 52 end + if not item["reqlevel"] then item["reqlevel"] = 47 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 24.2 end + if not item["speed"] then item["speed"] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[15997] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][17078] == nil then item["vendors"][17078] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[16004] + if not item then + item = {} + MTH_DS_AmmoItems[16004] = item + end + if not item["name"] then item["name"] = 'Dark Iron Rifle' end + if not item["level"] then item["level"] = 55 end + if not item["reqlevel"] then item["reqlevel"] = 50 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_08' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 28.4 end + if not item["speed"] then item["speed"] = 3.1 end +end + +do + local item = MTH_DS_AmmoItems[16007] + if not item then + item = {} + MTH_DS_AmmoItems[16007] = item + end + if not item["name"] then item["name"] = 'Flawless Arcanite Rifle' end + if not item["level"] then item["level"] = 61 end + if not item["reqlevel"] then item["reqlevel"] = 56 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 31.3 end + if not item["speed"] then item["speed"] = 3.1 end +end + +do + local item = MTH_DS_AmmoItems[16622] + if not item then + item = {} + MTH_DS_AmmoItems[16622] = item + end + if not item["name"] then item["name"] = 'Thornflinger' end + if not item["level"] then item["level"] = 57 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_06' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 26.6 end + if not item["speed"] then item["speed"] = 2.8 end +end + +do + local item = MTH_DS_AmmoItems[16992] + if not item then + item = {} + MTH_DS_AmmoItems[16992] = item + end + if not item["name"] then item["name"] = "Smokey's Explosive Launcher" end + if not item["level"] then item["level"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_08' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 27.8 end + if not item["speed"] then item["speed"] = 2.7 end +end + +do + local item = MTH_DS_AmmoItems[16996] + if not item then + item = {} + MTH_DS_AmmoItems[16996] = item + end + if not item["name"] then item["name"] = 'Gorewood Bow' end + if not item["level"] then item["level"] = 62 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_04' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 31.8 end + if not item["speed"] then item["speed"] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[17042] + if not item then + item = {} + MTH_DS_AmmoItems[17042] = item + end + if not item["name"] then item["name"] = 'Nail Spitter' end + if not item["level"] then item["level"] = 36 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 14.7 end + if not item["speed"] then item["speed"] = 1.9 end +end + +do + local item = MTH_DS_AmmoItems[17069] + if not item then + item = {} + MTH_DS_AmmoItems[17069] = item + end + if not item["name"] then item["name"] = "Striker's Mark" end + if not item["level"] then item["level"] = 69 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 39.6 end + if not item["speed"] then item["speed"] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[17072] + if not item then + item = {} + MTH_DS_AmmoItems[17072] = item + end + if not item["name"] then item["name"] = 'Blastershot Launcher' end + if not item["level"] then item["level"] = 70 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_09' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 40.2 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[17686] + if not item then + item = {} + MTH_DS_AmmoItems[17686] = item + end + if not item["name"] then item["name"] = "Master Hunter's Bow" end + if not item["level"] then item["level"] = 43 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 19.4 end + if not item["speed"] then item["speed"] = 2.4 end +end + +do + local item = MTH_DS_AmmoItems[17687] + if not item then + item = {} + MTH_DS_AmmoItems[17687] = item + end + if not item["name"] then item["name"] = "Master Hunter's Rifle" end + if not item["level"] then item["level"] = 43 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_05' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 19.4 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[17717] + if not item then + item = {} + MTH_DS_AmmoItems[17717] = item + end + if not item["name"] then item["name"] = 'Megashot Rifle' end + if not item["level"] then item["level"] = 53 end + if not item["reqlevel"] then item["reqlevel"] = 48 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_05' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 27.4 end + if not item["speed"] then item["speed"] = 1.7 end +end + +do + local item = MTH_DS_AmmoItems[17753] + if not item then + item = {} + MTH_DS_AmmoItems[17753] = item + end + if not item["name"] then item["name"] = "Verdant Keeper's Aim" end + if not item["level"] then item["level"] = 53 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_03' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 27.3 end + if not item["speed"] then item["speed"] = 2.8 end +end + +do + local item = MTH_DS_AmmoItems[18042] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][17078] == nil then item["vendors"][17078] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[18282] + if not item then + item = {} + MTH_DS_AmmoItems[18282] = item + end + if not item["name"] then item["name"] = 'Core Marksman Rifle' end + if not item["level"] then item["level"] = 66 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_05' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 37.5 end + if not item["speed"] then item["speed"] = 3.2 end +end + +do + local item = MTH_DS_AmmoItems[18323] + if not item then + item = {} + MTH_DS_AmmoItems[18323] = item + end + if not item["name"] then item["name"] = "Satyr's Bow" end + if not item["level"] then item["level"] = 58 end + if not item["reqlevel"] then item["reqlevel"] = 53 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_03' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 29.8 end + if not item["speed"] then item["speed"] = 2.4 end +end + +do + local item = MTH_DS_AmmoItems[18388] + if not item then + item = {} + MTH_DS_AmmoItems[18388] = item + end + if not item["name"] then item["name"] = 'Stoneshatter' end + if not item["level"] then item["level"] = 62 end + if not item["reqlevel"] then item["reqlevel"] = 57 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_08' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 31.7 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[18460] + if not item then + item = {} + MTH_DS_AmmoItems[18460] = item + end + if not item["name"] then item["name"] = 'Unsophisticated Hand Cannon' end + if not item["level"] then item["level"] = 60 end + if not item["reqlevel"] then item["reqlevel"] = 55 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 27.8 end + if not item["speed"] then item["speed"] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[18482] + if not item then + item = {} + MTH_DS_AmmoItems[18482] = item + end + if not item["name"] then item["name"] = 'Ogre Toothpick Shooter' end + if not item["level"] then item["level"] = 60 end + if not item["reqlevel"] then item["reqlevel"] = 55 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 28 end + if not item["speed"] then item["speed"] = 2.2 end +end + +do + local item = MTH_DS_AmmoItems[18680] + if not item then + item = {} + MTH_DS_AmmoItems[18680] = item + end + if not item["name"] then item["name"] = 'Ancient Bone Bow' end + if not item["level"] then item["level"] = 61 end + if not item["reqlevel"] then item["reqlevel"] = 56 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 31.3 end + if not item["speed"] then item["speed"] = 2.8 end +end + +do + local item = MTH_DS_AmmoItems[18713] + if not item then + item = {} + MTH_DS_AmmoItems[18713] = item + end + if not item["name"] then item["name"] = "Rhok'delar, Longbow of the Ancient Keepers" end + if not item["level"] then item["level"] = 75 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 44 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[18729] + if not item then + item = {} + MTH_DS_AmmoItems[18729] = item + end + if not item["name"] then item["name"] = 'Screeching Bow' end + if not item["level"] then item["level"] = 60 end + if not item["reqlevel"] then item["reqlevel"] = 55 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_12' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 30.7 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[18738] + if not item then + item = {} + MTH_DS_AmmoItems[18738] = item + end + if not item["name"] then item["name"] = 'Carapace Spine Crossbow' end + if not item["level"] then item["level"] = 61 end + if not item["reqlevel"] then item["reqlevel"] = 56 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_04' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 31.2 end + if not item["speed"] then item["speed"] = 3.3 end +end + +do + local item = MTH_DS_AmmoItems[18755] + if not item then + item = {} + MTH_DS_AmmoItems[18755] = item + end + if not item["name"] then item["name"] = 'Xorothian Firestick' end + if not item["level"] then item["level"] = 62 end + if not item["reqlevel"] then item["reqlevel"] = 57 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 31.7 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[18833] + if not item then + item = {} + MTH_DS_AmmoItems[18833] = item + end + if not item["name"] then item["name"] = "Grand Marshal's Bullseye" end + if not item["level"] then item["level"] = 83 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_12' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 50.8 end + if not item["speed"] then item["speed"] = 1.8 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][12782] == nil then item["vendors"][12782] = 0 end +end + +do + local item = MTH_DS_AmmoItems[18835] + if not item then + item = {} + MTH_DS_AmmoItems[18835] = item + end + if not item["name"] then item["name"] = "High Warlord's Recurve" end + if not item["level"] then item["level"] = 83 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 50.8 end + if not item["speed"] then item["speed"] = 1.8 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14581] == nil then item["vendors"][14581] = 0 end +end + +do + local item = MTH_DS_AmmoItems[18836] + if not item then + item = {} + MTH_DS_AmmoItems[18836] = item + end + if not item["name"] then item["name"] = "Grand Marshal's Repeater" end + if not item["level"] then item["level"] = 83 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_04' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 50.9 end + if not item["speed"] then item["speed"] = 2.9 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][12782] == nil then item["vendors"][12782] = 0 end +end + +do + local item = MTH_DS_AmmoItems[18837] + if not item then + item = {} + MTH_DS_AmmoItems[18837] = item + end + if not item["name"] then item["name"] = "High Warlord's Crossbow" end + if not item["level"] then item["level"] = 83 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_10' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 50.9 end + if not item["speed"] then item["speed"] = 2.9 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14581] == nil then item["vendors"][14581] = 0 end +end + +do + local item = MTH_DS_AmmoItems[18855] + if not item then + item = {} + MTH_DS_AmmoItems[18855] = item + end + if not item["name"] then item["name"] = "Grand Marshal's Hand Cannon" end + if not item["level"] then item["level"] = 83 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_08' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 50.9 end + if not item["speed"] then item["speed"] = 2.9 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][12782] == nil then item["vendors"][12782] = 0 end +end + +do + local item = MTH_DS_AmmoItems[18860] + if not item then + item = {} + MTH_DS_AmmoItems[18860] = item + end + if not item["name"] then item["name"] = "High Warlord's Street Sweeper" end + if not item["level"] then item["level"] = 83 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 50.9 end + if not item["speed"] then item["speed"] = 2.9 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14581] == nil then item["vendors"][14581] = 0 end +end + +do + local item = MTH_DS_AmmoItems[19107] + if not item then + item = {} + MTH_DS_AmmoItems[19107] = item + end + if not item["name"] then item["name"] = 'Bloodseeker' end + if not item["level"] then item["level"] = 63 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_07' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 32.3 end + if not item["speed"] then item["speed"] = 3.3 end +end + +do + local item = MTH_DS_AmmoItems[19114] + if not item then + item = {} + MTH_DS_AmmoItems[19114] = item + end + if not item["name"] then item["name"] = 'Highland Bow' end + if not item["level"] then item["level"] = 51 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 23.6 end + if not item["speed"] then item["speed"] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[19316] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][13216] == nil then item["vendors"][13216] = 0 end + if item["vendors"][13217] == nil then item["vendors"][13217] = 0 end + if item["vendors"][13218] == nil then item["vendors"][13218] = 0 end + if item["vendors"][13219] == nil then item["vendors"][13219] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[19317] + if item then + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][13216] == nil then item["vendors"][13216] = 0 end + if item["vendors"][13217] == nil then item["vendors"][13217] = 0 end + if item["vendors"][13218] == nil then item["vendors"][13218] = 0 end + if item["vendors"][13219] == nil then item["vendors"][13219] = 0 end + end +end + +do + local item = MTH_DS_AmmoItems[19350] + if not item then + item = {} + MTH_DS_AmmoItems[19350] = item + end + if not item["name"] then item["name"] = 'Heartstriker' end + if not item["level"] then item["level"] = 75 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_09' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 44 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[19361] + if not item then + item = {} + MTH_DS_AmmoItems[19361] = item + end + if not item["name"] then item["name"] = "Ashjre'thul, Crossbow of Smiting" end + if not item["level"] then item["level"] = 77 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_09' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 45.6 end + if not item["speed"] then item["speed"] = 3.4 end +end + +do + local item = MTH_DS_AmmoItems[19368] + if not item then + item = {} + MTH_DS_AmmoItems[19368] = item + end + if not item["name"] then item["name"] = 'Dragonbreath Hand Cannon' end + if not item["level"] then item["level"] = 75 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_02' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 43.9 end + if not item["speed"] then item["speed"] = 3.1 end +end + +do + local item = MTH_DS_AmmoItems[19558] + if not item then + item = {} + MTH_DS_AmmoItems[19558] = item + end + if not item["name"] then item["name"] = "Outrider's Bow" end + if not item["level"] then item["level"] = 71 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_06' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 36.7 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14754] == nil then item["vendors"][14754] = 0 end +end + +do + local item = MTH_DS_AmmoItems[19559] + if not item then + item = {} + MTH_DS_AmmoItems[19559] = item + end + if not item["name"] then item["name"] = "Outrider's Bow" end + if not item["level"] then item["level"] = 53 end + if not item["reqlevel"] then item["reqlevel"] = 48 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_06' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 27.5 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14754] == nil then item["vendors"][14754] = 0 end +end + +do + local item = MTH_DS_AmmoItems[19560] + if not item then + item = {} + MTH_DS_AmmoItems[19560] = item + end + if not item["name"] then item["name"] = "Outrider's Bow" end + if not item["level"] then item["level"] = 43 end + if not item["reqlevel"] then item["reqlevel"] = 38 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_06' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 22.7 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14754] == nil then item["vendors"][14754] = 0 end +end + +do + local item = MTH_DS_AmmoItems[19561] + if not item then + item = {} + MTH_DS_AmmoItems[19561] = item + end + if not item["name"] then item["name"] = "Outrider's Bow" end + if not item["level"] then item["level"] = 33 end + if not item["reqlevel"] then item["reqlevel"] = 28 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_06' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 16.7 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14754] == nil then item["vendors"][14754] = 0 end +end + +do + local item = MTH_DS_AmmoItems[19562] + if not item then + item = {} + MTH_DS_AmmoItems[19562] = item + end + if not item["name"] then item["name"] = "Outrunner's Bow" end + if not item["level"] then item["level"] = 71 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 36.7 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14753] == nil then item["vendors"][14753] = 0 end +end + +do + local item = MTH_DS_AmmoItems[19563] + if not item then + item = {} + MTH_DS_AmmoItems[19563] = item + end + if not item["name"] then item["name"] = "Outrunner's Bow" end + if not item["level"] then item["level"] = 53 end + if not item["reqlevel"] then item["reqlevel"] = 48 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 27.5 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14753] == nil then item["vendors"][14753] = 0 end +end + +do + local item = MTH_DS_AmmoItems[19564] + if not item then + item = {} + MTH_DS_AmmoItems[19564] = item + end + if not item["name"] then item["name"] = "Outrunner's Bow" end + if not item["level"] then item["level"] = 43 end + if not item["reqlevel"] then item["reqlevel"] = 38 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 22.7 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14753] == nil then item["vendors"][14753] = 0 end +end + +do + local item = MTH_DS_AmmoItems[19565] + if not item then + item = {} + MTH_DS_AmmoItems[19565] = item + end + if not item["name"] then item["name"] = "Outrunner's Bow" end + if not item["level"] then item["level"] = 33 end + if not item["reqlevel"] then item["reqlevel"] = 28 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 16.7 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14753] == nil then item["vendors"][14753] = 0 end +end + +do + local item = MTH_DS_AmmoItems[19853] + if not item then + item = {} + MTH_DS_AmmoItems[19853] = item + end + if not item["name"] then item["name"] = 'Gurubashi Dwarf Destroyer' end + if not item["level"] then item["level"] = 68 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_10' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 38.9 end + if not item["speed"] then item["speed"] = 2.8 end +end + +do + local item = MTH_DS_AmmoItems[19993] + if not item then + item = {} + MTH_DS_AmmoItems[19993] = item + end + if not item["name"] then item["name"] = 'Hoodoo Hunting Bow' end + if not item["level"] then item["level"] = 68 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Waepon_Bow_ZulGrub_D_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 35 end + if not item["speed"] then item["speed"] = 2.8 end +end + +do + local item = MTH_DS_AmmoItems[20038] + if not item then + item = {} + MTH_DS_AmmoItems[20038] = item + end + if not item["name"] then item["name"] = "Mandokir's Sting" end + if not item["level"] then item["level"] = 66 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Waepon_Bow_ZulGrub_D_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 37.5 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[20245] + if not item then + item = {} + MTH_DS_AmmoItems[20245] = item + end + if not item["name"] then item["name"] = '90 Green Warrior Gun' end + if not item["level"] then item["level"] = 90 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_05' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 46.4 end + if not item["speed"] then item["speed"] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[20285] + if not item then + item = {} + MTH_DS_AmmoItems[20285] = item + end + if not item["name"] then item["name"] = '63 Green Warrior Gun' end + if not item["level"] then item["level"] = 63 end + if not item["reqlevel"] then item["reqlevel"] = 58 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_05' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 29.4 end + if not item["speed"] then item["speed"] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[20299] + if not item then + item = {} + MTH_DS_AmmoItems[20299] = item + end + if not item["name"] then item["name"] = '90 Green Rogue Bow' end + if not item["level"] then item["level"] = 90 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_04' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 46.4 end + if not item["speed"] then item["speed"] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[20313] + if not item then + item = {} + MTH_DS_AmmoItems[20313] = item + end + if not item["name"] then item["name"] = '63 Green Rogue Bow' end + if not item["level"] then item["level"] = 63 end + if not item["reqlevel"] then item["reqlevel"] = 58 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_04' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 29.4 end + if not item["speed"] then item["speed"] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[20368] + if not item then + item = {} + MTH_DS_AmmoItems[20368] = item + end + if not item["name"] then item["name"] = 'Bland Bow of Steadiness' end + if not item["level"] then item["level"] = 60 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 30.7 end + if not item["speed"] then item["speed"] = 1.5 end +end + +do + local item = MTH_DS_AmmoItems[20437] + if not item then + item = {} + MTH_DS_AmmoItems[20437] = item + end + if not item["name"] then item["name"] = "Outrider's Bow" end + if not item["level"] then item["level"] = 23 end + if not item["reqlevel"] then item["reqlevel"] = 18 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_06' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 11.7 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14754] == nil then item["vendors"][14754] = 0 end +end + +do + local item = MTH_DS_AmmoItems[20438] + if not item then + item = {} + MTH_DS_AmmoItems[20438] = item + end + if not item["name"] then item["name"] = "Outrunner's Bow" end + if not item["level"] then item["level"] = 23 end + if not item["reqlevel"] then item["reqlevel"] = 18 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 11.7 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][14753] == nil then item["vendors"][14753] = 0 end +end + +do + local item = MTH_DS_AmmoItems[20488] + if not item then + item = {} + MTH_DS_AmmoItems[20488] = item + end + if not item["name"] then item["name"] = "Rhok'delar, Longbow of the Ancient Keepers DEP" end + if not item["level"] then item["level"] = 75 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 44 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[20599] + if not item then + item = {} + MTH_DS_AmmoItems[20599] = item + end + if not item["name"] then item["name"] = 'Polished Ironwood Crossbow' end + if not item["level"] then item["level"] = 76 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_11' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 44.8 end + if not item["speed"] then item["speed"] = 3.1 end +end + +do + local item = MTH_DS_AmmoItems[20646] + if not item then + item = {} + MTH_DS_AmmoItems[20646] = item + end + if not item["name"] then item["name"] = "Sandstrider's Mark" end + if not item["level"] then item["level"] = 59 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_03' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 27.4 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[20663] + if not item then + item = {} + MTH_DS_AmmoItems[20663] = item + end + if not item["name"] then item["name"] = 'Deep Strike Bow' end + if not item["level"] then item["level"] = 60 end + if not item["reqlevel"] then item["reqlevel"] = 55 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_10' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 30.7 end + if not item["speed"] then item["speed"] = 2.7 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][15307] == nil then item["drops"][15307] = 5.72 end +end + +do + local item = MTH_DS_AmmoItems[20722] + if not item then + item = {} + MTH_DS_AmmoItems[20722] = item + end + if not item["name"] then item["name"] = 'Crystal Slugthrower' end + if not item["level"] then item["level"] = 65 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 33.4 end + if not item["speed"] then item["speed"] = 2.8 end +end + +do + local item = MTH_DS_AmmoItems[21272] + if not item then + item = {} + MTH_DS_AmmoItems[21272] = item + end + if not item["name"] then item["name"] = 'Blessed Qiraji Musket' end + if not item["level"] then item["level"] = 79 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_11' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 47.3 end + if not item["speed"] then item["speed"] = 3.1 end +end + +do + local item = MTH_DS_AmmoItems[21459] + if not item then + item = {} + MTH_DS_AmmoItems[21459] = item + end + if not item["name"] then item["name"] = 'Crossbow of Imminent Doom' end + if not item["level"] then item["level"] = 72 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_06' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 41.6 end + if not item["speed"] then item["speed"] = 3.1 end +end + +do + local item = MTH_DS_AmmoItems[21478] + if not item then + item = {} + MTH_DS_AmmoItems[21478] = item + end + if not item["name"] then item["name"] = 'Bow of Taut Sinew' end + if not item["level"] then item["level"] = 68 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_13' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 38.6 end + if not item["speed"] then item["speed"] = 2.2 end +end + +do + local item = MTH_DS_AmmoItems[21550] + if not item then + item = {} + MTH_DS_AmmoItems[21550] = item + end + if not item["name"] then item["name"] = 'Monster - Bow, Kaldorei' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_05' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[21554] + if not item then + item = {} + MTH_DS_AmmoItems[21554] = item + end + if not item["name"] then item["name"] = 'Monster - Gun, PvP Horde' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[21564] + if not item then + item = {} + MTH_DS_AmmoItems[21564] = item + end + if not item["name"] then item["name"] = 'Monster - Gun, Kaldorei PVP Alliance' end + if not item["level"] then item["level"] = 1 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 0.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[21616] + if not item then + item = {} + MTH_DS_AmmoItems[21616] = item + end + if not item["name"] then item["name"] = "Huhuran's Stinger" end + if not item["level"] then item["level"] = 78 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_14' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 46.3 end + if not item["speed"] then item["speed"] = 2.7 end +end + +do + local item = MTH_DS_AmmoItems[21800] + if not item then + item = {} + MTH_DS_AmmoItems[21800] = item + end + if not item["name"] then item["name"] = 'Silithid Husked Launcher' end + if not item["level"] then item["level"] = 68 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_10' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 35 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][15318] == nil then item["drops"][15318] = 0.08 end + if item["drops"][15320] == nil then item["drops"][15320] = 0.04 end + if item["drops"][15323] == nil then item["drops"][15323] = 0.04 end + if item["drops"][15324] == nil then item["drops"][15324] = 0.1 end + if item["drops"][15325] == nil then item["drops"][15325] = 0.05 end + if item["drops"][15327] == nil then item["drops"][15327] = 0.05 end + if item["drops"][15333] == nil then item["drops"][15333] = 0.05 end + if item["drops"][15335] == nil then item["drops"][15335] = 0.08 end + if item["drops"][15336] == nil then item["drops"][15336] = 0.11 end + if item["drops"][15338] == nil then item["drops"][15338] = 0.06 end + if item["drops"][15343] == nil then item["drops"][15343] = 0.06 end + if item["drops"][15355] == nil then item["drops"][15355] = 0.05 end + if item["drops"][15386] == nil then item["drops"][15386] = 0.1 end + if item["drops"][15387] == nil then item["drops"][15387] = 0.22 end + if item["drops"][15389] == nil then item["drops"][15389] = 0.03 end + if item["drops"][15390] == nil then item["drops"][15390] = 0.04 end + if item["drops"][15391] == nil then item["drops"][15391] = 0.07 end + if item["drops"][15392] == nil then item["drops"][15392] = 0.02 end + if item["drops"][15462] == nil then item["drops"][15462] = 0.1 end +end + +do + local item = MTH_DS_AmmoItems[22318] + if not item then + item = {} + MTH_DS_AmmoItems[22318] = item + end + if not item["name"] then item["name"] = "Malgen's Long Bow" end + if not item["level"] then item["level"] = 61 end + if not item["reqlevel"] then item["reqlevel"] = 56 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_12' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 31.2 end + if not item["speed"] then item["speed"] = 2.9 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][8925] == nil then item["drops"][8925] = 0.03 end + if item["drops"][8927] == nil then item["drops"][8927] = 0.02 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][181074] == nil then item["objects"][181074] = 26 end +end + +do + local item = MTH_DS_AmmoItems[22347] + if not item then + item = {} + MTH_DS_AmmoItems[22347] = item + end + if not item["name"] then item["name"] = "Fahrad's Reloading Repeater" end + if not item["level"] then item["level"] = 65 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_04' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 33.3 end + if not item["speed"] then item["speed"] = 3.2 end +end + +do + local item = MTH_DS_AmmoItems[22656] + if not item then + item = {} + MTH_DS_AmmoItems[22656] = item + end + if not item["name"] then item["name"] = 'The Purifier' end + if not item["level"] then item["level"] = 63 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 35.3 end + if not item["speed"] then item["speed"] = 3 end +end + +do + local item = MTH_DS_AmmoItems[22810] + if not item then + item = {} + MTH_DS_AmmoItems[22810] = item + end + if not item["name"] then item["name"] = 'Toxin Injector' end + if not item["level"] then item["level"] = 83 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_weapon_rifle_13' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 50.8 end + if not item["speed"] then item["speed"] = 2 end +end + +do + local item = MTH_DS_AmmoItems[22811] + if not item then + item = {} + MTH_DS_AmmoItems[22811] = item + end + if not item["name"] then item["name"] = 'Soulstring' end + if not item["level"] then item["level"] = 86 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_13' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 54.1 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[22812] + if not item then + item = {} + MTH_DS_AmmoItems[22812] = item + end + if not item["name"] then item["name"] = 'Nerubian Slavemaker' end + if not item["level"] then item["level"] = 89 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_12' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 57.2 end + if not item["speed"] then item["speed"] = 3.2 end +end + +do + local item = MTH_DS_AmmoItems[23557] + if not item then + item = {} + MTH_DS_AmmoItems[23557] = item + end + if not item["name"] then item["name"] = 'Larvae of the Great Worm' end + if not item["level"] then item["level"] = 81 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_10' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 49.2 end + if not item["speed"] then item["speed"] = 3 end +end + +do + local item = MTH_DS_AmmoItems[41023] + if not item then + item = {} + MTH_DS_AmmoItems[41023] = item + end + if not item["name"] then item["name"] = 'Water-logged Musket' end + if not item["level"] then item["level"] = 5 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 1.8 end + if not item["speed"] then item["speed"] = 2.2 end +end + +do + local item = MTH_DS_AmmoItems[41190] + if not item then + item = {} + MTH_DS_AmmoItems[41190] = item + end + if not item["name"] then item["name"] = "Bow of Alah'Thalas" end + if not item["level"] then item["level"] = 13 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 5.4 end + if not item["speed"] then item["speed"] = 2.5 end +end + +do + local item = MTH_DS_AmmoItems[41725] + if not item then + item = {} + MTH_DS_AmmoItems[41725] = item + end + if not item["name"] then item["name"] = 'Dragonmaw Battle Bow' end + if not item["level"] then item["level"] = 33 end + if not item["reqlevel"] then item["reqlevel"] = 28 end + if not item["icon"] then item["icon"] = 'GenBow_twow3F' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 16.7 end + if not item["speed"] then item["speed"] = 2.4 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][62070] == nil then item["drops"][62070] = 25 end +end + +do + local item = MTH_DS_AmmoItems[51746] + if not item then + item = {} + MTH_DS_AmmoItems[51746] = item + end + if not item["name"] then item["name"] = 'Shadowblaster' end + if not item["level"] then item["level"] = 62 end + if not item["reqlevel"] then item["reqlevel"] = 57 end + if not item["icon"] then item["icon"] = 'INV_Musket_02' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 31.9 end + if not item["speed"] then item["speed"] = 3.1 end +end + +do + local item = MTH_DS_AmmoItems[51759] + if not item then + item = {} + MTH_DS_AmmoItems[51759] = item + end + if not item["name"] then item["name"] = 'Siege Bomber' end + if not item["level"] then item["level"] = 42 end + if not item["reqlevel"] then item["reqlevel"] = 37 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 18.6 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][2749] == nil then item["drops"][2749] = 70 end +end + +do + local item = MTH_DS_AmmoItems[51768] + if not item then + item = {} + MTH_DS_AmmoItems[51768] = item + end + if not item["name"] then item["name"] = "Bow of Quel'Danil" end + if not item["level"] then item["level"] = 49 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_16' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 21.7 end + if not item["speed"] then item["speed"] = 2.1 end +end + +do + local item = MTH_DS_AmmoItems[51780] + if not item then + item = {} + MTH_DS_AmmoItems[51780] = item + end + if not item["name"] then item["name"] = 'Ghoulslayer Shotgun' end + if not item["level"] then item["level"] = 66 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_weapon_rifle_13' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 37.4 end + if not item["speed"] then item["speed"] = 3.1 end +end + +do + local item = MTH_DS_AmmoItems[51794] + if not item then + item = {} + MTH_DS_AmmoItems[51794] = item + end + if not item["name"] then item["name"] = 'Otherworldly Rifle' end + if not item["level"] then item["level"] = 52 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 24.1 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[51828] + if not item then + item = {} + MTH_DS_AmmoItems[51828] = item + end + if not item["name"] then item["name"] = 'Hawkwind Rifle' end + if not item["level"] then item["level"] = 10 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 3.5 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[51844] + if not item then + item = {} + MTH_DS_AmmoItems[51844] = item + end + if not item["name"] then item["name"] = 'Stunning Crossbow' end + if not item["level"] then item["level"] = 41 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_08' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 16.6 end + if not item["speed"] then item["speed"] = 3.4 end +end + +do + local item = MTH_DS_AmmoItems[51862] + if not item then + item = {} + MTH_DS_AmmoItems[51862] = item + end + if not item["name"] then item["name"] = "Brave's Rifle" end + if not item["level"] then item["level"] = 20 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 9 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[55028] + if not item then + item = {} + MTH_DS_AmmoItems[55028] = item + end + if not item["name"] then item["name"] = "Kavdan's Patient Watch" end + if not item["level"] then item["level"] = 62 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_04' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 37.4 end + if not item["speed"] then item["speed"] = 3.1 end +end + +do + local item = MTH_DS_AmmoItems[55068] + if not item then + item = {} + MTH_DS_AmmoItems[55068] = item + end + if not item["name"] then item["name"] = "Zan's Leperification Blaster" end + if not item["level"] then item["level"] = 50 end + if not item["reqlevel"] then item["reqlevel"] = 45 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 26.1 end + if not item["speed"] then item["speed"] = 2.7 end +end + +do + local item = MTH_DS_AmmoItems[55096] + if not item then + item = {} + MTH_DS_AmmoItems[55096] = item + end + if not item["name"] then item["name"] = 'Phase-shifting Crossbow' end + if not item["level"] then item["level"] = 88 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_18' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 56.1 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][61951] == nil then item["drops"][61951] = 25 end +end + +do + local item = MTH_DS_AmmoItems[55346] + if not item then + item = {} + MTH_DS_AmmoItems[55346] = item + end + if not item["name"] then item["name"] = 'Rain of Spiders' end + if not item["level"] then item["level"] = 96 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_BoneBow2' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 64.4 end + if not item["speed"] then item["speed"] = 3.1 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][93333] == nil then item["drops"][93333] = 5.6 end +end + +do + local item = MTH_DS_AmmoItems[55379] + if not item then + item = {} + MTH_DS_AmmoItems[55379] = item + end + if not item["name"] then item["name"] = 'Slag Slugger' end + if not item["level"] then item["level"] = 23 end + if not item["reqlevel"] then item["reqlevel"] = 18 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 9.3 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][61963] == nil then item["drops"][61963] = 25 end +end + +do + local item = MTH_DS_AmmoItems[58016] + if not item then + item = {} + MTH_DS_AmmoItems[58016] = item + end + if not item["name"] then item["name"] = 'Ashwood Bow' end + if not item["level"] then item["level"] = 39 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 17.2 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[58083] + if not item then + item = {} + MTH_DS_AmmoItems[58083] = item + end + if not item["name"] then item["name"] = "Nippsy's Precision Rifle" end + if not item["level"] then item["level"] = 47 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_02' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 24.7 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[58086] + if not item then + item = {} + MTH_DS_AmmoItems[58086] = item + end + if not item["name"] then item["name"] = 'Standard Grade Rifle' end + if not item["level"] then item["level"] = 8 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_04' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.5 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[58193] + if not item then + item = {} + MTH_DS_AmmoItems[58193] = item + end + if not item["name"] then item["name"] = 'Demon Hair Bow' end + if not item["level"] then item["level"] = 40 end + if not item["reqlevel"] then item["reqlevel"] = 35 end + if not item["icon"] then item["icon"] = 'GenBow_twow3F' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 21.3 end + if not item["speed"] then item["speed"] = 3.1 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][62665] == nil then item["drops"][62665] = 25 end +end + +do + local item = MTH_DS_AmmoItems[58284] + if not item then + item = {} + MTH_DS_AmmoItems[58284] = item + end + if not item["name"] then item["name"] = 'Waterlogged Blunderbuss' end + if not item["level"] then item["level"] = 35 end + if not item["reqlevel"] then item["reqlevel"] = 30 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 14.3 end + if not item["speed"] then item["speed"] = 3 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][12843] == nil then item["objects"][12843] = 12.5 end +end + +do + local item = MTH_DS_AmmoItems[60165] + if not item then + item = {} + MTH_DS_AmmoItems[60165] = item + end + if not item["name"] then item["name"] = 'Wobblefree Fizz-rifle' end + if not item["level"] then item["level"] = 59 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 34.2 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[60309] + if not item then + item = {} + MTH_DS_AmmoItems[60309] = item + end + if not item["name"] then item["name"] = "'Jadewood' Longbow" end + if not item["level"] then item["level"] = 40 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 17.9 end + if not item["speed"] then item["speed"] = 1.9 end +end + +do + local item = MTH_DS_AmmoItems[60338] + if not item then + item = {} + MTH_DS_AmmoItems[60338] = item + end + if not item["name"] then item["name"] = 'Lordaeron Rusty Crossbow' end + if not item["level"] then item["level"] = 15 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_01' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 5.8 end + if not item["speed"] then item["speed"] = 3 end +end + +do + local item = MTH_DS_AmmoItems[60339] + if not item then + item = {} + MTH_DS_AmmoItems[60339] = item + end + if not item["name"] then item["name"] = 'High Elven Rotten Bow' end + if not item["level"] then item["level"] = 15 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 5.9 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[60440] + if not item then + item = {} + MTH_DS_AmmoItems[60440] = item + end + if not item["name"] then item["name"] = 'Bloodscalp Longbow' end + if not item["level"] then item["level"] = 50 end + if not item["reqlevel"] then item["reqlevel"] = 45 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 27.3 end + if not item["speed"] then item["speed"] = 3 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][80269] == nil then item["drops"][80269] = 25 end +end + +do + local item = MTH_DS_AmmoItems[60506] + if not item then + item = {} + MTH_DS_AmmoItems[60506] = item + end + if not item["name"] then item["name"] = 'Vigilance' end + if not item["level"] then item["level"] = 65 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_05' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 33.2 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["objects"] then item["objects"] = {} end + if item["objects"][2010867] == nil then item["objects"][2010867] = 25 end +end + +do + local item = MTH_DS_AmmoItems[60545] + if not item then + item = {} + MTH_DS_AmmoItems[60545] = item + end + if not item["name"] then item["name"] = 'Techrifle X-TREME 5200' end + if not item["level"] then item["level"] = 56 end + if not item["reqlevel"] then item["reqlevel"] = 51 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 28.1 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][60736] == nil then item["drops"][60736] = 20 end +end + +do + local item = MTH_DS_AmmoItems[60624] + if not item then + item = {} + MTH_DS_AmmoItems[60624] = item + end + if not item["name"] then item["name"] = 'Goldplated Royal Crossbow' end + if not item["level"] then item["level"] = 65 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_03' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 32.9 end + if not item["speed"] then item["speed"] = 3.3 end +end + +do + local item = MTH_DS_AmmoItems[60782] + if not item then + item = {} + MTH_DS_AmmoItems[60782] = item + end + if not item["name"] then item["name"] = 'Shieldbreaker Arbalest' end + if not item["level"] then item["level"] = 61 end + if not item["reqlevel"] then item["reqlevel"] = 56 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_15' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 34.3 end + if not item["speed"] then item["speed"] = 3 end +end + +do + local item = MTH_DS_AmmoItems[60821] + if not item then + item = {} + MTH_DS_AmmoItems[60821] = item + end + if not item["name"] then item["name"] = 'Theramore Arbalest' end + if not item["level"] then item["level"] = 47 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_02' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 24 end + if not item["speed"] then item["speed"] = 3.4 end +end + +do + local item = MTH_DS_AmmoItems[60882] + if not item then + item = {} + MTH_DS_AmmoItems[60882] = item + end + if not item["name"] then item["name"] = 'Magram Windstriker' end + if not item["level"] then item["level"] = 53 end + if not item["reqlevel"] then item["reqlevel"] = 48 end + if not item["icon"] then item["icon"] = 'GenBow_twow3F' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 30.2 end + if not item["speed"] then item["speed"] = 2.9 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][80942] == nil then item["vendors"][80942] = 0 end +end + +do + local item = MTH_DS_AmmoItems[60953] + if not item then + item = {} + MTH_DS_AmmoItems[60953] = item + end + if not item["name"] then item["name"] = 'Brackenwall Longbow' end + if not item["level"] then item["level"] = 47 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_07' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 24 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[61011] + if not item then + item = {} + MTH_DS_AmmoItems[61011] = item + end + if not item["name"] then item["name"] = "Flintlocke's Hand Cannon" end + if not item["level"] then item["level"] = 65 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_09' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 36.9 end + if not item["speed"] then item["speed"] = 3.2 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][80943] == nil then item["vendors"][80943] = 0 end +end + +do + local item = MTH_DS_AmmoItems[61068] + if not item then + item = {} + MTH_DS_AmmoItems[61068] = item + end + if not item["name"] then item["name"] = 'Dark Iron Desecrator' end + if not item["level"] then item["level"] = 65 end + if not item["icon"] then item["icon"] = 'Spell_frost_fireresistancetotem' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 39.5 end + if not item["speed"] then item["speed"] = 2.9 end +end + +do + local item = MTH_DS_AmmoItems[61165] + if not item then + item = {} + MTH_DS_AmmoItems[61165] = item + end + if not item["name"] then item["name"] = 'Bow of the Night Huntress' end + if not item["level"] then item["level"] = 35 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 14.3 end + if not item["speed"] then item["speed"] = 2.2 end +end + +do + local item = MTH_DS_AmmoItems[61248] + if not item then + item = {} + MTH_DS_AmmoItems[61248] = item + end + if not item["name"] then item["name"] = "Beasthunter's Blunderbuss" end + if not item["level"] then item["level"] = 66 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_TWoW_02_Gray' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 37.5 end + if not item["speed"] then item["speed"] = 2.8 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][61223] == nil then item["drops"][61223] = 20 end +end + +do + local item = MTH_DS_AmmoItems[61307] + if not item then + item = {} + MTH_DS_AmmoItems[61307] = item + end + if not item["name"] then item["name"] = 'Worgen Hunter Musket' end + if not item["level"] then item["level"] = 46 end + if not item["reqlevel"] then item["reqlevel"] = 41 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_TWoW_02_Ironblack' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 24.5 end + if not item["speed"] then item["speed"] = 2.9 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][61419] == nil then item["drops"][61419] = 2 end +end + +do + local item = MTH_DS_AmmoItems[61383] + if not item then + item = {} + MTH_DS_AmmoItems[61383] = item + end + if not item["name"] then item["name"] = 'Intricate Gnomish Blunderbuss' end + if not item["level"] then item["level"] = 37 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_TWoW_01_Gold_Noglow' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 18 end + if not item["speed"] then item["speed"] = 3 end +end + +do + local item = MTH_DS_AmmoItems[61472] + if not item then + item = {} + MTH_DS_AmmoItems[61472] = item + end + if not item["name"] then item["name"] = 'Brigade Rifle' end + if not item["level"] then item["level"] = 45 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_TWoW_02_Gray' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 21.1 end + if not item["speed"] then item["speed"] = 2.8 end +end + +do + local item = MTH_DS_AmmoItems[61525] + if not item then + item = {} + MTH_DS_AmmoItems[61525] = item + end + if not item["name"] then item["name"] = "Nature's Call" end + if not item["level"] then item["level"] = 81 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_04' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 2 end + if not item["dps"] then item["dps"] = 49 end + if not item["speed"] then item["speed"] = 3 end +end + +do + local item = MTH_DS_AmmoItems[61569] + if not item then + item = {} + MTH_DS_AmmoItems[61569] = item + end + if not item["name"] then item["name"] = 'Time Frozen Bow' end + if not item["level"] then item["level"] = 64 end + if not item["reqlevel"] then item["reqlevel"] = 59 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_09' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 32.7 end + if not item["speed"] then item["speed"] = 3.5 end +end + +do + local item = MTH_DS_AmmoItems[61629] + if not item then + item = {} + MTH_DS_AmmoItems[61629] = item + end + if not item["name"] then item["name"] = "Farmer's Musket" end + if not item["level"] then item["level"] = 46 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_TWoW_02_Purple' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 21.9 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[61683] + if not item then + item = {} + MTH_DS_AmmoItems[61683] = item + end + if not item["name"] then item["name"] = 'Battered Arbalest' end + if not item["level"] then item["level"] = 45 end + if not item["reqlevel"] then item["reqlevel"] = 40 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_02' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 6 end + if not item["dps"] then item["dps"] = 12.3 end + if not item["speed"] then item["speed"] = 2.2 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][61364] == nil then item["drops"][61364] = 6 end +end + +do + local item = MTH_DS_AmmoItems[70049] + if not item then + item = {} + MTH_DS_AmmoItems[70049] = item + end + if not item["name"] then item["name"] = 'Worn Crossbow' end + if not item["level"] then item["level"] = 2 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_02' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 1.5 end + if not item["speed"] then item["speed"] = 3 end +end + +do + local item = MTH_DS_AmmoItems[80106] + if not item then + item = {} + MTH_DS_AmmoItems[80106] = item + end + if not item["name"] then item["name"] = 'Miscalibrated Rifle' end + if not item["level"] then item["level"] = 6 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_01' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.5 end + if not item["speed"] then item["speed"] = 2.8 end +end + +do + local item = MTH_DS_AmmoItems[80127] + if not item then + item = {} + MTH_DS_AmmoItems[80127] = item + end + if not item["name"] then item["name"] = 'Polished Boomstick' end + if not item["level"] then item["level"] = 11 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_03' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 4.8 end + if not item["speed"] then item["speed"] = 2.4 end +end + +do + local item = MTH_DS_AmmoItems[80207] + if not item then + item = {} + MTH_DS_AmmoItems[80207] = item + end + if not item["name"] then item["name"] = 'Worn Wooden Bow' end + if not item["level"] then item["level"] = 6 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.5 end + if not item["speed"] then item["speed"] = 2.8 end +end + +do + local item = MTH_DS_AmmoItems[80223] + if not item then + item = {} + MTH_DS_AmmoItems[80223] = item + end + if not item["name"] then item["name"] = "Farstrider Lodge Protector's Bow" end + if not item["level"] then item["level"] = 23 end + if not item["reqlevel"] then item["reqlevel"] = 18 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_17' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 11.7 end + if not item["speed"] then item["speed"] = 2.4 end +end + +do + local item = MTH_DS_AmmoItems[80503] + if not item then + item = {} + MTH_DS_AmmoItems[80503] = item + end + if not item["name"] then item["name"] = 'Well-balanced Short Bow' end + if not item["level"] then item["level"] = 16 end + if not item["reqlevel"] then item["reqlevel"] = 10 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 6.8 end + if not item["speed"] then item["speed"] = 1.7 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][80266] == nil then item["vendors"][80266] = 0 end +end + +do + local item = MTH_DS_AmmoItems[80546] + if not item then + item = {} + MTH_DS_AmmoItems[80546] = item + end + if not item["name"] then item["name"] = "Quel'dorei Ranger's Longbow" end + if not item["level"] then item["level"] = 65 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_17' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 33.3 end + if not item["speed"] then item["speed"] = 2.9 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][80266] == nil then item["vendors"][80266] = 0 end +end + +do + local item = MTH_DS_AmmoItems[80603] + if not item then + item = {} + MTH_DS_AmmoItems[80603] = item + end + if not item["name"] then item["name"] = 'Sturdy Short Bow' end + if not item["level"] then item["level"] = 16 end + if not item["reqlevel"] then item["reqlevel"] = 10 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 6.2 end + if not item["speed"] then item["speed"] = 1.7 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][80807] == nil then item["vendors"][80807] = 0 end + if item["vendors"][80915] == nil then item["vendors"][80915] = 0 end +end + +do + local item = MTH_DS_AmmoItems[80646] + if not item then + item = {} + MTH_DS_AmmoItems[80646] = item + end + if not item["name"] then item["name"] = "Revantusk Shadow Hunter's Longbow" end + if not item["level"] then item["level"] = 65 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_08' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 33.3 end + if not item["speed"] then item["speed"] = 2.9 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][80807] == nil then item["vendors"][80807] = 0 end + if item["vendors"][80915] == nil then item["vendors"][80915] = 0 end +end + +do + local item = MTH_DS_AmmoItems[80745] + if not item then + item = {} + MTH_DS_AmmoItems[80745] = item + end + if not item["name"] then item["name"] = 'Abomination Crossbow' end + if not item["level"] then item["level"] = 42 end + if not item["reqlevel"] then item["reqlevel"] = 37 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_04' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 22.3 end + if not item["speed"] then item["speed"] = 3 end +end + +do + local item = MTH_DS_AmmoItems[80795] + if not item then + item = {} + MTH_DS_AmmoItems[80795] = item + end + if not item["name"] then item["name"] = 'Burstshot Harquebus' end + if not item["level"] then item["level"] = 56 end + if not item["reqlevel"] then item["reqlevel"] = 51 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_06' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 28.8 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][9045] == nil then item["drops"][9045] = 0.01 end + if item["drops"][9097] == nil then item["drops"][9097] = 0.01 end + if item["drops"][9098] == nil then item["drops"][9098] = 0.01 end + if item["drops"][9196] == nil then item["drops"][9196] = 0.01 end + if item["drops"][9197] == nil then item["drops"][9197] = 0.005 end + if item["drops"][9199] == nil then item["drops"][9199] = 0.005 end + if item["drops"][9200] == nil then item["drops"][9200] = 0.005 end + if item["drops"][9201] == nil then item["drops"][9201] = 0.005 end + if item["drops"][9216] == nil then item["drops"][9216] = 0.005 end + if item["drops"][9219] == nil then item["drops"][9219] = 0.005 end + if item["drops"][9236] == nil then item["drops"][9236] = 0.02 end + if item["drops"][9239] == nil then item["drops"][9239] = 0.01 end + if item["drops"][9240] == nil then item["drops"][9240] = 0.01 end + if item["drops"][9257] == nil then item["drops"][9257] = 0.01 end + if item["drops"][9258] == nil then item["drops"][9258] = 0.02 end + if item["drops"][9259] == nil then item["drops"][9259] = 0.005 end + if item["drops"][9260] == nil then item["drops"][9260] = 0.005 end + if item["drops"][9261] == nil then item["drops"][9261] = 0.005 end + if item["drops"][9262] == nil then item["drops"][9262] = 0.005 end + if item["drops"][9264] == nil then item["drops"][9264] = 0.005 end + if item["drops"][9265] == nil then item["drops"][9265] = 0.02 end + if item["drops"][9268] == nil then item["drops"][9268] = 0.005 end + if item["drops"][9583] == nil then item["drops"][9583] = 0.01 end + if item["drops"][9692] == nil then item["drops"][9692] = 0.02 end + if item["drops"][9693] == nil then item["drops"][9693] = 0.005 end + if item["drops"][9716] == nil then item["drops"][9716] = 0.01 end + if item["drops"][9717] == nil then item["drops"][9717] = 0.005 end + if item["drops"][61191] == nil then item["drops"][61191] = 0.01 end + if item["drops"][61192] == nil then item["drops"][61192] = 0.01 end + if item["drops"][61193] == nil then item["drops"][61193] = 0.01 end + if item["drops"][61194] == nil then item["drops"][61194] = 0.01 end + if item["drops"][61211] == nil then item["drops"][61211] = 0.01 end + if item["drops"][62555] == nil then item["drops"][62555] = 0.005 end + if item["drops"][65100] == nil then item["drops"][65100] = 0.01 end + if item["drops"][65101] == nil then item["drops"][65101] = 0.01 end + if item["drops"][65102] == nil then item["drops"][65102] = 0.01 end + if item["drops"][65105] == nil then item["drops"][65105] = 0.01 end +end + +do + local item = MTH_DS_AmmoItems[80820] + if not item then + item = {} + MTH_DS_AmmoItems[80820] = item + end + if not item["name"] then item["name"] = 'Quilflinger' end + if not item["level"] then item["level"] = 13 end + if not item["reqlevel"] then item["reqlevel"] = 8 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 4 end + if not item["dps"] then item["dps"] = 5.5 end + if not item["speed"] then item["speed"] = 2.1 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][5785] == nil then item["drops"][5785] = 35 end +end + +do + local item = MTH_DS_AmmoItems[80825] + if not item then + item = {} + MTH_DS_AmmoItems[80825] = item + end + if not item["name"] then item["name"] = 'Flamestring Bow' end + if not item["level"] then item["level"] = 47 end + if not item["reqlevel"] then item["reqlevel"] = 42 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_09' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 26.3 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][2447] == nil then item["drops"][2447] = 33.3 end +end + +do + local item = MTH_DS_AmmoItems[80876] + if not item then + item = {} + MTH_DS_AmmoItems[80876] = item + end + if not item["name"] then item["name"] = 'Silvermoon Bow' end + if not item["level"] then item["level"] = 2 end + if not item["reqlevel"] then item["reqlevel"] = 1 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_02' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 1.5 end + if not item["speed"] then item["speed"] = 2.3 end +end + +do + local item = MTH_DS_AmmoItems[83094] + if not item then + item = {} + MTH_DS_AmmoItems[83094] = item + end + if not item["name"] then item["name"] = 'Monster - Rifle2H, Mechagnome' end + if not item["level"] then item["level"] = 7 end + if not item["reqlevel"] then item["reqlevel"] = 2 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_29' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 2.5 end + if not item["speed"] then item["speed"] = 2.6 end +end + +do + local item = MTH_DS_AmmoItems[83225] + if not item then + item = {} + MTH_DS_AmmoItems[83225] = item + end + if not item["name"] then item["name"] = 'Bow of the Grove' end + if not item["level"] then item["level"] = 40 end + if not item["reqlevel"] then item["reqlevel"] = 35 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_11' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 21.3 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][92109] == nil then item["drops"][92109] = 23.75 end +end + +do + local item = MTH_DS_AmmoItems[83257] + if not item then + item = {} + MTH_DS_AmmoItems[83257] = item + end + if not item["name"] then item["name"] = 'Caer Darrow Reserve Rifle' end + if not item["level"] then item["level"] = 62 end + if not item["reqlevel"] then item["reqlevel"] = 56 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Rifle_07' end + if not item["subtype"] then item["subtype"] = 'gun' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 32.7 end + if not item["speed"] then item["speed"] = 2.6 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][49007] == nil then item["drops"][49007] = 25 end +end + +do + local item = MTH_DS_AmmoItems[83452] + if not item then + item = {} + MTH_DS_AmmoItems[83452] = item + end + if not item["name"] then item["name"] = 'Windbreaker' end + if not item["level"] then item["level"] = 65 end + if not item["reqlevel"] then item["reqlevel"] = 60 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Bow_01' end + if not item["subtype"] then item["subtype"] = 'bow' end + if not item["quality"] then item["quality"] = 3 end + if not item["dps"] then item["dps"] = 35 end + if not item["speed"] then item["speed"] = 2.3 end + if not item["drops"] then item["drops"] = {} end + if item["drops"][91916] == nil then item["drops"][91916] = 10 end +end + +do + local item = MTH_DS_AmmoItems[83517] + if not item then + item = {} + MTH_DS_AmmoItems[83517] = item + end + if not item["name"] then item["name"] = 'Balanced Light Crossbow' end + if not item["level"] then item["level"] = 16 end + if not item["reqlevel"] then item["reqlevel"] = 11 end + if not item["icon"] then item["icon"] = 'INV_Weapon_Crossbow_01' end + if not item["subtype"] then item["subtype"] = 'crossbow' end + if not item["quality"] then item["quality"] = 5 end + if not item["dps"] then item["dps"] = 5.7 end + if not item["speed"] then item["speed"] = 2.9 end + if not item["vendors"] then item["vendors"] = {} end + if item["vendors"][81035] == nil then item["vendors"][81035] = 0 end +end + diff --git a/data/ds-items.lua b/data/ds-items.lua new file mode 100644 index 0000000..e946a6e --- /dev/null +++ b/data/ds-items.lua @@ -0,0 +1,1133 @@ +-- MetaHunt Items Database +-- Projectile ammo with all known sources +-- Total: 28 items + +if not MTH_DS then MTH_DS = {} end + +MTH_DS_AmmoItems = { + [2512] = { + ["name"] = "Rough Arrow", + ["level"] = 5, + ["reqlevel"] = 1, + ["icon"] = "INV_Ammo_Arrow_02", + ["subtype"] = "arrow", + ["vendors"] = { + [150] = 0, + [151] = 0, + [152] = 0, + [228] = 0, + [491] = 0, + [789] = 0, + [791] = 0, + [829] = 0, + [1198] = 0, + [1250] = 0, + [1285] = 0, + [1287] = 0, + [1297] = 0, + [1298] = 0, + [1455] = 0, + [1459] = 0, + [1462] = 0, + [1668] = 0, + [1682] = 0, + [1685] = 0, + [1687] = 0, + [1691] = 0, + [1859] = 0, + [2115] = 0, + [2134] = 0, + [2140] = 0, + [2286] = 0, + [2839] = 0, + [3015] = 0, + [3072] = 0, + [3076] = 0, + [3158] = 0, + [3164] = 0, + [3165] = 0, + [3186] = 0, + [3313] = 0, + [3409] = 0, + [3410] = 0, + [3481] = 0, + [3488] = 0, + [3498] = 0, + [3587] = 0, + [3589] = 0, + [3608] = 0, + [3610] = 0, + [3951] = 0, + [4082] = 0, + [4084] = 0, + [4170] = 0, + [4173] = 0, + [4182] = 0, + [4203] = 0, + [4241] = 0, + [4555] = 0, + [4602] = 0, + [4604] = 0, + [4892] = 0, + [5122] = 0, + [6028] = 0, + [7942] = 0, + [7976] = 0, + [8362] = 0, + [9548] = 0, + [9549] = 0, + [9551] = 0, + [9552] = 0, + [9553] = 0, + [9555] = 0, + [10369] = 0, + [12027] = 0, + [12029] = 0, + [12960] = 0, + [14301] = 0, + [17598] = 0, + [21002] = 0, + [60659] = 0, + [61115] = 0, + [61443] = 0, + [61651] = 0, + [61742] = 0, + [62086] = 0, + [62088] = 0, + [62096] = 0, + [62307] = 0, + [62402] = 0, + [62404] = 0, + [62409] = 0, + [62437] = 0, + [62628] = 0, + [62739] = 0, + [80102] = 0, + [80214] = 0, + [80267] = 0, + [81035] = 0, + [91246] = 0, + [91248] = 0, + [91725] = 0, + [91977] = 0, + [92169] = 0, + }, + }, + [2515] = { + ["name"] = "Sharp Arrow", + ["level"] = 15, + ["reqlevel"] = 10, + ["icon"] = "INV_Ammo_Arrow_02", + ["subtype"] = "arrow", + ["vendors"] = { + [150] = 0, + [227] = 0, + [228] = 0, + [491] = 0, + [734] = 0, + [789] = 0, + [791] = 0, + [1149] = 0, + [1198] = 0, + [1285] = 0, + [1287] = 0, + [1297] = 0, + [1298] = 0, + [1448] = 0, + [1452] = 0, + [1455] = 0, + [1459] = 0, + [1462] = 0, + [1668] = 0, + [1682] = 0, + [1685] = 0, + [1687] = 0, + [1859] = 0, + [2084] = 0, + [2140] = 0, + [2286] = 0, + [2401] = 0, + [2803] = 0, + [2806] = 0, + [2808] = 0, + [2820] = 0, + [2839] = 0, + [2908] = 0, + [3015] = 0, + [3165] = 0, + [3313] = 0, + [3350] = 0, + [3409] = 0, + [3410] = 0, + [3481] = 0, + [3488] = 0, + [3498] = 0, + [3541] = 0, + [3589] = 0, + [3610] = 0, + [3625] = 0, + [3951] = 0, + [3962] = 0, + [4082] = 0, + [4084] = 0, + [4170] = 0, + [4173] = 0, + [4182] = 0, + [4203] = 0, + [4241] = 0, + [4555] = 0, + [4602] = 0, + [4604] = 0, + [4876] = 0, + [4892] = 0, + [4896] = 0, + [5101] = 0, + [5122] = 0, + [5134] = 0, + [6028] = 0, + [7942] = 0, + [7976] = 0, + [8139] = 0, + [8362] = 0, + [9320] = 0, + [9548] = 0, + [9549] = 0, + [9551] = 0, + [9552] = 0, + [9553] = 0, + [9555] = 0, + [10369] = 0, + [11038] = 0, + [11555] = 0, + [12021] = 0, + [12027] = 0, + [12029] = 0, + [12036] = 0, + [12246] = 0, + [12959] = 0, + [12960] = 0, + [14301] = 0, + [14624] = 0, + [15174] = 0, + [17598] = 0, + [21002] = 0, + [51656] = 0, + [60456] = 0, + [60641] = 0, + [60646] = 0, + [60653] = 0, + [60659] = 0, + [60663] = 0, + [60740] = 0, + [60766] = 0, + [60790] = 0, + [60803] = 0, + [60813] = 0, + [60966] = 0, + [60989] = 0, + [61058] = 0, + [61115] = 0, + [61140] = 0, + [61272] = 0, + [61288] = 0, + [61369] = 0, + [61440] = 0, + [61443] = 0, + [61478] = 0, + [61523] = 0, + [61563] = 0, + [61651] = 0, + [61742] = 0, + [61743] = 0, + [61812] = 0, + [62086] = 0, + [62088] = 0, + [62096] = 0, + [62148] = 0, + [62307] = 0, + [62402] = 0, + [62404] = 0, + [62409] = 0, + [62436] = 0, + [62437] = 0, + [62462] = 0, + [62469] = 0, + [62628] = 0, + [62739] = 0, + [65002] = 0, + [65003] = 0, + [80267] = 0, + [80808] = 0, + [81035] = 0, + [91246] = 0, + [91403] = 0, + [91725] = 0, + [91868] = 0, + [91956] = 0, + [91977] = 0, + [92169] = 0, + [92177] = 0, + [92200] = 0, + }, + }, + [2516] = { + ["name"] = "Light Shot", + ["level"] = 5, + ["reqlevel"] = 1, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + ["vendors"] = { + [151] = 0, + [152] = 0, + [491] = 0, + [791] = 0, + [829] = 0, + [904] = 0, + [1243] = 0, + [1250] = 0, + [1285] = 0, + [1297] = 0, + [1461] = 0, + [1469] = 0, + [1682] = 0, + [1685] = 0, + [1686] = 0, + [1691] = 0, + [2115] = 0, + [2134] = 0, + [2140] = 0, + [2685] = 0, + [2997] = 0, + [3018] = 0, + [3053] = 0, + [3072] = 0, + [3076] = 0, + [3078] = 0, + [3088] = 0, + [3158] = 0, + [3164] = 0, + [3186] = 0, + [3313] = 0, + [3322] = 0, + [3481] = 0, + [3488] = 0, + [3498] = 0, + [3587] = 0, + [3608] = 0, + [4082] = 0, + [4084] = 0, + [4170] = 0, + [4182] = 0, + [4241] = 0, + [4555] = 0, + [4603] = 0, + [4889] = 0, + [5123] = 0, + [5510] = 0, + [6028] = 0, + [7942] = 0, + [7976] = 0, + [8131] = 0, + [8362] = 0, + [9548] = 0, + [9551] = 0, + [11184] = 0, + [12027] = 0, + [12960] = 0, + [21002] = 0, + [61445] = 0, + [61651] = 0, + [61743] = 0, + [61751] = 0, + [62086] = 0, + [62096] = 0, + [62402] = 0, + [62403] = 0, + [62409] = 0, + [62628] = 0, + [80102] = 0, + [80214] = 0, + [80267] = 0, + [91248] = 0, + [91725] = 0, + [92169] = 0, + [92217] = 0, + }, + }, + [2519] = { + ["name"] = "Heavy Shot", + ["level"] = 15, + ["reqlevel"] = 10, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + ["vendors"] = { + [227] = 0, + [491] = 0, + [734] = 0, + [791] = 0, + [904] = 0, + [1149] = 0, + [1243] = 0, + [1285] = 0, + [1297] = 0, + [1448] = 0, + [1452] = 0, + [1461] = 0, + [1469] = 0, + [1682] = 0, + [1685] = 0, + [1686] = 0, + [2084] = 0, + [2140] = 0, + [2401] = 0, + [2685] = 0, + [2803] = 0, + [2806] = 0, + [2808] = 0, + [2820] = 0, + [2908] = 0, + [2997] = 0, + [3018] = 0, + [3053] = 0, + [3078] = 0, + [3088] = 0, + [3313] = 0, + [3322] = 0, + [3350] = 0, + [3481] = 0, + [3488] = 0, + [3498] = 0, + [3541] = 0, + [3625] = 0, + [3962] = 0, + [4082] = 0, + [4084] = 0, + [4170] = 0, + [4182] = 0, + [4241] = 0, + [4555] = 0, + [4603] = 0, + [4876] = 0, + [4889] = 0, + [4896] = 0, + [5101] = 0, + [5123] = 0, + [5134] = 0, + [5510] = 0, + [6028] = 0, + [7942] = 0, + [7976] = 0, + [8131] = 0, + [8139] = 0, + [8362] = 0, + [9320] = 0, + [9548] = 0, + [9551] = 0, + [11038] = 0, + [11184] = 0, + [11555] = 0, + [12021] = 0, + [12027] = 0, + [12036] = 0, + [12246] = 0, + [12959] = 0, + [12960] = 0, + [14624] = 0, + [15174] = 0, + [21002] = 0, + [51656] = 0, + [60456] = 0, + [60641] = 0, + [60646] = 0, + [60653] = 0, + [60663] = 0, + [60740] = 0, + [60766] = 0, + [60790] = 0, + [60813] = 0, + [60966] = 0, + [60989] = 0, + [61058] = 0, + [61115] = 0, + [61140] = 0, + [61272] = 0, + [61288] = 0, + [61369] = 0, + [61440] = 0, + [61443] = 0, + [61445] = 0, + [61478] = 0, + [61523] = 0, + [61563] = 0, + [61651] = 0, + [61743] = 0, + [61751] = 0, + [61812] = 0, + [62086] = 0, + [62096] = 0, + [62148] = 0, + [62402] = 0, + [62403] = 0, + [62404] = 0, + [62409] = 0, + [62436] = 0, + [62437] = 0, + [62462] = 0, + [62469] = 0, + [62628] = 0, + [65002] = 0, + [65003] = 0, + [80267] = 0, + [80808] = 0, + [91403] = 0, + [91725] = 0, + [91868] = 0, + [91956] = 0, + [92169] = 0, + [92177] = 0, + [92200] = 0, + [92217] = 0, + }, + }, + [3030] = { + ["name"] = "Razor Arrow", + ["level"] = 30, + ["reqlevel"] = 25, + ["icon"] = "INV_Ammo_Arrow_02", + ["subtype"] = "arrow", + ["vendors"] = { + [227] = 0, + [228] = 0, + [734] = 0, + [789] = 0, + [1149] = 0, + [1285] = 0, + [1287] = 0, + [1297] = 0, + [1298] = 0, + [1448] = 0, + [1452] = 0, + [1455] = 0, + [1459] = 0, + [1462] = 0, + [2084] = 0, + [2286] = 0, + [2401] = 0, + [2803] = 0, + [2806] = 0, + [2808] = 0, + [2820] = 0, + [2839] = 0, + [2908] = 0, + [3015] = 0, + [3313] = 0, + [3350] = 0, + [3410] = 0, + [3541] = 0, + [3625] = 0, + [3962] = 0, + [4084] = 0, + [4170] = 0, + [4173] = 0, + [4241] = 0, + [4555] = 0, + [4602] = 0, + [4604] = 0, + [4876] = 0, + [4892] = 0, + [4896] = 0, + [5101] = 0, + [5122] = 0, + [5134] = 0, + [6028] = 0, + [7942] = 0, + [7976] = 0, + [8139] = 0, + [8362] = 0, + [9320] = 0, + [9548] = 0, + [9551] = 0, + [9552] = 0, + [9555] = 0, + [11038] = 0, + [11555] = 0, + [12021] = 0, + [12027] = 0, + [12029] = 0, + [12036] = 0, + [12246] = 0, + [12959] = 0, + [12960] = 0, + [14301] = 0, + [14624] = 0, + [15174] = 0, + [17598] = 0, + [21002] = 0, + [51656] = 0, + [60456] = 0, + [60641] = 0, + [60646] = 0, + [60653] = 0, + [60663] = 0, + [60740] = 0, + [60766] = 0, + [60790] = 0, + [60803] = 0, + [60813] = 0, + [60966] = 0, + [60989] = 0, + [61058] = 0, + [61115] = 0, + [61140] = 0, + [61272] = 0, + [61288] = 0, + [61369] = 0, + [61440] = 0, + [61443] = 0, + [61478] = 0, + [61523] = 0, + [61563] = 0, + [61651] = 0, + [61743] = 0, + [61812] = 0, + [62086] = 0, + [62096] = 0, + [62148] = 0, + [62307] = 0, + [62402] = 0, + [62404] = 0, + [62409] = 0, + [62436] = 0, + [62437] = 0, + [62462] = 0, + [62469] = 0, + [62628] = 0, + [62739] = 0, + [65002] = 0, + [65003] = 0, + [80267] = 0, + [80808] = 0, + [91403] = 0, + [91725] = 0, + [91868] = 0, + [91956] = 0, + [92169] = 0, + [92177] = 0, + [92200] = 0, + }, + }, + [3033] = { + ["name"] = "Solid Shot", + ["level"] = 30, + ["reqlevel"] = 25, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + ["vendors"] = { + [227] = 0, + [734] = 0, + [1149] = 0, + [1285] = 0, + [1297] = 0, + [1448] = 0, + [1452] = 0, + [1461] = 0, + [1686] = 0, + [2084] = 0, + [2401] = 0, + [2685] = 0, + [2803] = 0, + [2806] = 0, + [2808] = 0, + [2820] = 0, + [2908] = 0, + [3018] = 0, + [3053] = 0, + [3088] = 0, + [3313] = 0, + [3322] = 0, + [3350] = 0, + [3541] = 0, + [3625] = 0, + [3962] = 0, + [4170] = 0, + [4241] = 0, + [4555] = 0, + [4603] = 0, + [4876] = 0, + [4889] = 0, + [4896] = 0, + [5101] = 0, + [5123] = 0, + [5134] = 0, + [6028] = 0, + [7942] = 0, + [8131] = 0, + [8139] = 0, + [8362] = 0, + [9320] = 0, + [9548] = 0, + [9551] = 0, + [11038] = 0, + [11184] = 0, + [11555] = 0, + [12021] = 0, + [12027] = 0, + [12036] = 0, + [12246] = 0, + [12959] = 0, + [12960] = 0, + [14624] = 0, + [15174] = 0, + [21002] = 0, + [51656] = 0, + [60456] = 0, + [60641] = 0, + [60646] = 0, + [60653] = 0, + [60663] = 0, + [60740] = 0, + [60766] = 0, + [60790] = 0, + [60813] = 0, + [60966] = 0, + [60989] = 0, + [61058] = 0, + [61115] = 0, + [61140] = 0, + [61272] = 0, + [61288] = 0, + [61369] = 0, + [61440] = 0, + [61443] = 0, + [61445] = 0, + [61478] = 0, + [61523] = 0, + [61563] = 0, + [61651] = 0, + [61743] = 0, + [61812] = 0, + [62086] = 0, + [62096] = 0, + [62148] = 0, + [62402] = 0, + [62403] = 0, + [62404] = 0, + [62409] = 0, + [62436] = 0, + [62437] = 0, + [62462] = 0, + [62469] = 0, + [62628] = 0, + [65002] = 0, + [65003] = 0, + [80267] = 0, + [80808] = 0, + [91403] = 0, + [91725] = 0, + [91868] = 0, + [91956] = 0, + [92169] = 0, + [92177] = 0, + [92200] = 0, + [92217] = 0, + }, + }, + [3034] = { + ["name"] = "BKP \"Impact\" Shot", + ["level"] = 35, + ["reqlevel"] = 30, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + }, + [3464] = { + ["name"] = "Feathered Arrow", + ["level"] = 35, + ["icon"] = "INV_Ammo_Arrow_02", + ["subtype"] = "arrow", + }, + [3465] = { + ["name"] = "Exploding Shot", + ["level"] = 36, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + }, + [4960] = { + ["name"] = "Flash Pellet", + ["level"] = 7, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + }, + [5568] = { + ["name"] = "Smooth Pebble", + ["level"] = 18, + ["reqlevel"] = 13, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + ["drops"] = { + [2156] = 30.97, + [2157] = 30.53, + [6206] = 0.04, + [6207] = 0.02, + [6210] = 0.02, + [6211] = 0.06, + [6218] = 0.02, + [6219] = 0.02, + [6220] = 0.02, + [6221] = 0.02, + [6223] = 0.02, + [6233] = 0.02, + [6329] = 0.08, + [6391] = 0.2, + [6392] = 0.56, + [6407] = 0.6, + [9676] = 25, + }, + }, + [8067] = { + ["name"] = "Crafted Light Shot", + ["level"] = 10, + ["reqlevel"] = 5, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + }, + [8068] = { + ["name"] = "Crafted Heavy Shot", + ["level"] = 20, + ["reqlevel"] = 15, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + }, + [8069] = { + ["name"] = "Crafted Solid Shot", + ["level"] = 35, + ["reqlevel"] = 30, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + }, + [9399] = { + ["name"] = "Precision Arrow", + ["level"] = 40, + ["reqlevel"] = 35, + ["icon"] = "INV_Ammo_Arrow_01", + ["subtype"] = "arrow", + ["drops"] = { + [6906] = 100, + }, + }, + [10512] = { + ["name"] = "Hi-Impact Mithril Slugs", + ["level"] = 42, + ["reqlevel"] = 37, + ["icon"] = "INV_Ammo_Bullet_01", + ["subtype"] = "bullet", + }, + [10513] = { + ["name"] = "Mithril Gyro-Shot", + ["level"] = 49, + ["reqlevel"] = 44, + ["icon"] = "INV_Ammo_Bullet_01", + ["subtype"] = "bullet", + }, + [10579] = { + ["name"] = "Explosive Arrow", + ["level"] = 42, + ["reqlevel"] = 37, + ["icon"] = "INV_Ammo_Arrow_02", + ["subtype"] = "arrow", + }, + [11284] = { + ["name"] = "Accurate Slugs", + ["level"] = 45, + ["reqlevel"] = 40, + ["icon"] = "INV_Ammo_Bullet_01", + ["subtype"] = "bullet", + ["vendors"] = { + [734] = 0, + [1149] = 0, + [1285] = 0, + [1297] = 0, + [1461] = 0, + [2084] = 0, + [2401] = 0, + [2803] = 0, + [2806] = 0, + [2808] = 0, + [2820] = 0, + [2908] = 0, + [3018] = 0, + [3053] = 0, + [3313] = 0, + [3322] = 0, + [3350] = 0, + [3541] = 0, + [3625] = 0, + [4170] = 0, + [4241] = 0, + [4555] = 0, + [4603] = 0, + [4889] = 0, + [4896] = 0, + [5101] = 0, + [5123] = 0, + [5134] = 0, + [7942] = 0, + [8131] = 0, + [8139] = 0, + [8362] = 0, + [9320] = 0, + [9548] = 0, + [9551] = 0, + [11038] = 0, + [11184] = 0, + [11555] = 0, + [12021] = 0, + [12027] = 0, + [12036] = 0, + [12246] = 0, + [12959] = 0, + [12960] = 0, + [14624] = 0, + [15174] = 0, + [21002] = 0, + [51656] = 0, + [60456] = 0, + [60641] = 0, + [60646] = 0, + [60653] = 0, + [60663] = 0, + [60766] = 0, + [60790] = 0, + [61115] = 0, + [61140] = 0, + [61272] = 0, + [61288] = 0, + [61369] = 0, + [61440] = 0, + [61443] = 0, + [61445] = 0, + [61478] = 0, + [61523] = 0, + [61563] = 0, + [61651] = 0, + [61812] = 0, + [62086] = 0, + [62096] = 0, + [62148] = 0, + [62403] = 0, + [62404] = 0, + [62409] = 0, + [62436] = 0, + [62437] = 0, + [62462] = 0, + [62469] = 0, + [62628] = 0, + [65002] = 0, + [65003] = 0, + [80008] = 0, + [80267] = 0, + [80808] = 0, + [91403] = 0, + [91725] = 0, + [91868] = 0, + [91956] = 0, + [92169] = 0, + [92177] = 0, + [92200] = 0, + [92217] = 0, + }, + }, + [11285] = { + ["name"] = "Jagged Arrow", + ["level"] = 45, + ["reqlevel"] = 40, + ["icon"] = "INV_Weapon_ShortBlade_25", + ["subtype"] = "arrow", + ["vendors"] = { + [228] = 0, + [734] = 0, + [789] = 0, + [1149] = 0, + [1285] = 0, + [1298] = 0, + [1455] = 0, + [1462] = 0, + [2084] = 0, + [2286] = 0, + [2401] = 0, + [2803] = 0, + [2806] = 0, + [2808] = 0, + [2820] = 0, + [2839] = 0, + [2908] = 0, + [3015] = 0, + [3313] = 0, + [3350] = 0, + [3410] = 0, + [3541] = 0, + [3625] = 0, + [4170] = 0, + [4173] = 0, + [4241] = 0, + [4555] = 0, + [4604] = 0, + [4892] = 0, + [4896] = 0, + [5101] = 0, + [5122] = 0, + [5134] = 0, + [6028] = 0, + [7942] = 0, + [8139] = 0, + [8362] = 0, + [9320] = 0, + [9548] = 0, + [9551] = 0, + [9552] = 0, + [9555] = 0, + [11038] = 0, + [11555] = 0, + [12021] = 0, + [12027] = 0, + [12036] = 0, + [12246] = 0, + [12959] = 0, + [12960] = 0, + [14301] = 0, + [14624] = 0, + [15174] = 0, + [17598] = 0, + [21002] = 0, + [51656] = 0, + [60456] = 0, + [60641] = 0, + [60646] = 0, + [60653] = 0, + [60663] = 0, + [60766] = 0, + [60790] = 0, + [60803] = 0, + [61115] = 0, + [61140] = 0, + [61272] = 0, + [61288] = 0, + [61369] = 0, + [61440] = 0, + [61443] = 0, + [61478] = 0, + [61523] = 0, + [61563] = 0, + [61651] = 0, + [61812] = 0, + [62086] = 0, + [62096] = 0, + [62148] = 0, + [62307] = 0, + [62402] = 0, + [62404] = 0, + [62409] = 0, + [62436] = 0, + [62437] = 0, + [62462] = 0, + [62469] = 0, + [62628] = 0, + [62739] = 0, + [65002] = 0, + [65003] = 0, + [80267] = 0, + [80808] = 0, + [91403] = 0, + [91725] = 0, + [91868] = 0, + [91956] = 0, + [92169] = 0, + [92177] = 0, + [92200] = 0, + }, + }, + [11630] = { + ["name"] = "Rockshard Pellets", + ["level"] = 52, + ["reqlevel"] = 47, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + ["drops"] = { + [9025] = 16, + }, + }, + [12654] = { + ["name"] = "Doomshot", + ["level"] = 59, + ["reqlevel"] = 54, + ["icon"] = "Ability_Hunter_CriticalShot", + ["subtype"] = "arrow", + ["drops"] = { + [9236] = 100, + }, + }, + [13377] = { + ["name"] = "Miniature Cannon Balls", + ["level"] = 61, + ["reqlevel"] = 56, + ["icon"] = "INV_Ammo_Bullet_02", + ["subtype"] = "bullet", + ["drops"] = { + [10997] = 100, + }, + }, + [15997] = { + ["name"] = "Thorium Shells", + ["level"] = 57, + ["reqlevel"] = 52, + ["icon"] = "INV_Ammo_Bullet_03", + ["subtype"] = "bullet", + ["vendors"] = { + [17078] = 0, + }, + }, + [18042] = { + ["name"] = "Thorium Headed Arrow", + ["level"] = 57, + ["reqlevel"] = 52, + ["icon"] = "INV_Ammo_Arrow_02", + ["subtype"] = "arrow", + ["vendors"] = { + [17078] = 0, + }, + }, + [19082] = { + ["name"] = "Monster - Fire Arrow", + ["level"] = 1, + ["reqlevel"] = 1, + ["icon"] = "INV_Ammo_Arrow_02", + ["subtype"] = "arrow", + }, + [19316] = { + ["name"] = "Ice Threaded Arrow", + ["level"] = 54, + ["reqlevel"] = 51, + ["icon"] = "Spell_Frost_IceShard", + ["subtype"] = "arrow", + ["vendors"] = { + [13216] = 0, + [13217] = 0, + [13218] = 0, + [13219] = 0, + }, + }, + [19317] = { + ["name"] = "Ice Threaded Bullet", + ["level"] = 54, + ["reqlevel"] = 51, + ["icon"] = "Spell_Frost_FrostBlast", + ["subtype"] = "bullet", + ["vendors"] = { + [13216] = 0, + [13217] = 0, + [13218] = 0, + [13219] = 0, + }, + }, +} + +MTH_DS_Items = MTH_DS_AmmoItems \ No newline at end of file diff --git a/data/ds-minimap-sizes.lua b/data/ds-minimap-sizes.lua new file mode 100644 index 0000000..34943b3 --- /dev/null +++ b/data/ds-minimap-sizes.lua @@ -0,0 +1,121 @@ +if not MTH_DS then MTH_DS = {} end + +MTH_DS_MinimapSizes = { + [0] = { 0, 0 }, + [1] = { 4925, 3283.34 }, + [3] = { 2487.5, 1658.34 }, + [4] = { 3350, 2233.3 }, + [8] = { 2293.75, 1529.17 }, + [10] = { 2700, 1800.03 }, + [11] = { 4135.42, 2756.25 }, + [12] = { 3470.84, 2314.62 }, + [14] = { 5287.5, 3525 }, + [15] = { 5250, 3500 }, + [16] = { 5070.84, 3381.25 }, + [17] = { 10133.34, 6756.25 }, + [25] = { 711.56, 468.68 }, + [28] = { 4300, 2866.67 }, + [33] = { 6381.25, 4254.1 }, + [36] = { 2800, 1866.67 }, + [38] = { 2758.33, 1839.58 }, + [40] = { 3500, 2333.3 }, + [41] = { 2500, 1666.63 }, + [44] = { 2170.84, 1447.9 }, + [45] = { 3600, 2400 }, + [46] = { 2929.16, 1952.08 }, + [47] = { 3850, 2566.67 }, + [51] = { 2231.25, 1487.5 }, + [85] = { 4518.75, 3012.5 }, + [130] = { 4200, 2800 }, + [139] = { 3870.83, 2581.25 }, + [141] = { 5091.66, 3393.7 }, + [148] = { 6550, 4366.66 }, + [209] = { 381, 254 }, + [215] = { 5137.5, 3425 }, + [267] = { 3200, 2133.33 }, + [331] = { 5766.67, 3843.75 }, + [357] = { 6950, 4633.33 }, + [361] = { 5750, 3833.33 }, + [400] = { 4400, 2933.33 }, + [405] = { 4495.83, 2997.91 }, + [406] = { 6018, 4010 }, + [408] = { 3092.1, 2047 }, + [409] = { 2901.45, 1915.9 }, + [440] = { 6900, 4600 }, + [490] = { 3700, 2466.66 }, + [491] = { 736.45, 490.96 }, + [493] = { 2308.33, 1539.59 }, + [616] = { 3206, 2142 }, + [618] = { 7100, 4733.33 }, + [717] = { 378.15, 252.1 }, + [718] = { 572.78, 381.85 }, + [719] = { 1221.87, 806.42 }, + [721] = { 769.67, 513.11 }, + [722] = { 881, 589 }, + [796] = { 203.66, 135.04 }, + [876] = { 828, 541 }, + [1176] = { 1383.33, 922.91 }, + [1337] = { 893.67, 595.78 }, + [1377] = { 3483.33, 2322.92 }, + [1477] = { 695.03, 463.35 }, + [1497] = { 959.37, 640.11 }, + [1519] = { 1737.5, 1158.34 }, + [1537] = { 790.63, 527.61 }, + [1581] = { 449.89, 300 }, + [1583] = { 886.84, 591.23 }, + [1584] = { 1985, 1268 }, + [1637] = { 1402.61, 935.42 }, + [1638] = { 1043.75, 695.83 }, + [1657] = { 1058.33, 705.71 }, + [1941] = { 1348.24, 888.09 }, + [1977] = { 2120.83, 1414.6 }, + [2017] = { 1185.35, 789.86 }, + [2040] = { 1468, 976 }, + [2057] = { 320.05, 213.37 }, + [2100] = { 2112.09, 1410.89 }, + [2159] = { 483.11, 322.08 }, + [2257] = { 312, 208 }, + [2366] = { 1085.86, 726.61 }, + [2437] = { 738.86, 492.57 }, + [2557] = { 1324, 869 }, + [2597] = { 4237.5, 2825 }, + [2677] = { 499.43, 332.95 }, + [2717] = { 1495, 983 }, + [3277] = { 1145.84, 764.58 }, + [3358] = { 1756.25, 1170.83 }, + [3428] = { 977.56, 651.7 }, + [3429] = { 2512.5, 1675 }, + [3456] = { 1991.69, 1318.42 }, + [3457] = { 823, 549 }, + [3478] = { 4139.02, 2946 }, + [4012] = { 3159, 2108 }, + [5023] = { 988.63, 1393.17 }, + [5024] = { 1608, 1075 }, + [5053] = { 1007.7, 671.8 }, + [5077] = { 2643.21, 1751.16 }, + [5086] = { 546.75, 391.9 }, + [5087] = { 354.5, 234.74 }, + [5097] = { 1273.1, 853.72 }, + [5103] = { 752.12, 510.33 }, + [5121] = { 3227, 2187 }, + [5130] = { 1432, 977 }, + [5135] = { 320.19, 213.46 }, + [5136] = { 619.98, 413.32 }, + [5138] = { 656.59, 434.97 }, + [5144] = { 309, 208 }, + [5147] = { 2777.54, 1851.69 }, + [5148] = { 652.1, 439.67 }, + [5153] = { 612.69, 408.46 }, + [5163] = { 703.3, 468.87 }, + [5179] = { 3666, 2442 }, + [5204] = { 1271.99, 845.48 }, + [5208] = { 1250.18, 837.44 }, + [5225] = { 3082, 2061 }, + [5536] = { 2472, 1665 }, + [5557] = { 2800, 1868 }, + [5561] = { 3098, 2068 }, + [5581] = { 3241, 2157 }, + [5601] = { 1634, 1073 }, + [5602] = { 5387, 3584 }, + [5628] = { 1980, 1350 }, +} diff --git a/data/ds-pet-spells-trainer.lua b/data/ds-pet-spells-trainer.lua new file mode 100644 index 0000000..8f55840 --- /dev/null +++ b/data/ds-pet-spells-trainer.lua @@ -0,0 +1,236 @@ +-- MetaHunt Pet Spells Trainer Overlay +-- Adds trainer-learned pet abilities and normalizes learn method metadata. + +if not MTH_DS_PetSpells then + return +end + +local function MTH_DSPS_EnsureArray(tbl) + if type(tbl) ~= "table" then + return {} + end + return tbl +end + +MTH_DS_PetSpells.abilitiesDiscovered = MTH_DSPS_EnsureArray(MTH_DS_PetSpells.abilitiesDiscovered) +MTH_DS_PetSpells.allSpells = MTH_DSPS_EnsureArray(MTH_DS_PetSpells.allSpells) +MTH_DS_PetSpells.byAbility = MTH_DSPS_EnsureArray(MTH_DS_PetSpells.byAbility) + +for _, row in ipairs(MTH_DS_PetSpells.allSpells) do + if type(row) == "table" and not row.learnMethod then + row.learnMethod = "beast" + end +end + +for _, bundle in pairs(MTH_DS_PetSpells.byAbility) do + if type(bundle) == "table" and type(bundle.spells) == "table" then + for _, row in ipairs(bundle.spells) do + if type(row) == "table" and not row.learnMethod then + row.learnMethod = "beast" + end + end + end +end + +local growlTrainerRows = { + { + ["ability"] = "Growl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "10 mana", + ["description"] = "Taunt the target, increasing the likelihood the creature will focus attacks on you.", + ["effects"] = { + "(63) Threat Value: 50", + }, + ["icon"] = "Ability_Physical_Taunt", + ["id"] = 2649, + ["learnMethod"] = "trainer", + ["name"] = "Growl", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=2649", + ["trainLevel"] = 1, + }, + { + ["ability"] = "Growl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "10 mana", + ["description"] = "Taunt the target, increasing the likelihood the creature will focus attacks on you.", + ["effects"] = { + "(63) Threat Value: 65", + }, + ["icon"] = "Ability_Physical_Taunt", + ["id"] = 14916, + ["learnMethod"] = "trainer", + ["name"] = "Growl", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14916", + ["trainLevel"] = 10, + }, + { + ["ability"] = "Growl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "10 mana", + ["description"] = "Taunt the target, increasing the likelihood the creature will focus attacks on you.", + ["effects"] = { + "(63) Threat Value: 110", + }, + ["icon"] = "Ability_Physical_Taunt", + ["id"] = 14917, + ["learnMethod"] = "trainer", + ["name"] = "Growl", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14917", + ["trainLevel"] = 20, + }, + { + ["ability"] = "Growl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "10 mana", + ["description"] = "Taunt the target, increasing the likelihood the creature will focus attacks on you.", + ["effects"] = { + "(63) Threat Value: 170", + }, + ["icon"] = "Ability_Physical_Taunt", + ["id"] = 14918, + ["learnMethod"] = "trainer", + ["name"] = "Growl", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14918", + ["trainLevel"] = 30, + }, + { + ["ability"] = "Growl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "10 mana", + ["description"] = "Taunt the target, increasing the likelihood the creature will focus attacks on you.", + ["effects"] = { + "(63) Threat Value: 240", + }, + ["icon"] = "Ability_Physical_Taunt", + ["id"] = 14919, + ["learnMethod"] = "trainer", + ["name"] = "Growl", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14919", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Growl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "10 mana", + ["description"] = "Taunt the target, increasing the likelihood the creature will focus attacks on you.", + ["effects"] = { + "(63) Threat Value: 320", + }, + ["icon"] = "Ability_Physical_Taunt", + ["id"] = 14920, + ["learnMethod"] = "trainer", + ["name"] = "Growl", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14920", + ["trainLevel"] = 50, + }, + { + ["ability"] = "Growl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "10 mana", + ["description"] = "Taunt the target, increasing the likelihood the creature will focus attacks on you.", + ["effects"] = { + "(63) Threat Value: 415", + }, + ["icon"] = "Ability_Physical_Taunt", + ["id"] = 14921, + ["learnMethod"] = "trainer", + ["name"] = "Growl", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 7", + ["rankNumber"] = 7, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14921", + ["trainLevel"] = 60, + }, +} + +local function MTH_DSPS_Contains(array, value) + for _, item in ipairs(array) do + if item == value then + return true + end + end + return false +end + +if not MTH_DSPS_Contains(MTH_DS_PetSpells.abilitiesDiscovered, "Growl") then + table.insert(MTH_DS_PetSpells.abilitiesDiscovered, "Growl") + table.sort(MTH_DS_PetSpells.abilitiesDiscovered) +end + +local allById = {} +for i, row in ipairs(MTH_DS_PetSpells.allSpells) do + if type(row) == "table" and row.id then + allById[row.id] = i + end +end + +for _, row in ipairs(growlTrainerRows) do + local idx = allById[row.id] + if idx then + MTH_DS_PetSpells.allSpells[idx] = row + else + table.insert(MTH_DS_PetSpells.allSpells, row) + end +end + +table.sort(MTH_DS_PetSpells.allSpells, function(a, b) + local aa = (a and a.ability) or "" + local bb = (b and b.ability) or "" + if aa ~= bb then + return aa < bb + end + local ar = (a and a.rankNumber) or 0 + local br = (b and b.rankNumber) or 0 + if ar ~= br then + return ar < br + end + return ((a and a.id) or 0) < ((b and b.id) or 0) +end) + +MTH_DS_PetSpells.byAbility["Growl"] = { + ["ability"] = "Growl", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Growl", + ["spellCount"] = table.getn(growlTrainerRows), + ["spells"] = growlTrainerRows, +} + +MTH_DS_PetSpells.totalSpellRows = table.getn(MTH_DS_PetSpells.allSpells) diff --git a/data/ds-pet-spells.lua b/data/ds-pet-spells.lua new file mode 100644 index 0000000..f4106f7 --- /dev/null +++ b/data/ds-pet-spells.lua @@ -0,0 +1,4352 @@ + +if not MTH_DS then MTH_DS = {} end + +MTH_DS_PetSpells = { + ["abilitiesDiscovered"] = { + "Bite", + "Bubble Barrier", + "Charge", + "Claw", + "Cower", + "Dash", + "Death Roll", + "Dive", + "Furious Howl", + "Grace", + "Lightning Breath", + "Packleader", + "Poison Spit", + "Prowl", + "Roar of Fortitude", + "Savage Rend", + "Scorpid Poison", + "Screech", + "Shell Shield", + "Strider Presence", + "Thunderstomp", + "Web", + }, + ["allSpells"] = { + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 7 to 10 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 7", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17253, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17253", + ["trainLevel"] = 1, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 16 to 19 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 16", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17255, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17255", + ["trainLevel"] = 8, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 24 to 29 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 24", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17256, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17256", + ["trainLevel"] = 16, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 31 to 38 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 31", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17257, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17257", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 40 to 49 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 40", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17258, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17258", + ["trainLevel"] = 32, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 49 to 60 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 49", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17259, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17259", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 66 to 81 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 66", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17260, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 7", + ["rankNumber"] = 7, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17260", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 81 to 100 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 81", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17261, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 8", + ["rankNumber"] = 8, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17261", + ["trainLevel"] = 56, + }, + { + ["ability"] = "Bubble Barrier", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Inflicts Frost damage to nearby enemies.", + ["effects"] = { + "(2) School Damage (Frost) Value: 15 Radius: 10 yards", + }, + ["icon"] = "Spell_Frost_FrostNova", + ["id"] = 36527, + ["name"] = "Bubble Barrier", + ["range"] = "0 yards (Self Only)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Frost", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36527", + ["trainLevel"] = 10, + }, + { + ["ability"] = "Bubble Barrier", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Envelop yourself in a bubble, absorbing 50 damage. When the bubble breaks or expires it deals 15 to 18 Frost damage to enemies within 10 yards. Lasts 8 sec.", + ["effects"] = { + "(6) Apply Aura #69: School Absorb (127) Value: 50 Bubble Barrier", + }, + ["icon"] = "spell_bubl1", + ["id"] = 36523, + ["name"] = "Bubble Barrier", + ["range"] = "40 yards (Long Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36523", + ["trainLevel"] = 10, + }, + { + ["ability"] = "Bubble Barrier", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Envelop yourself in a bubble, absorbing 125 damage. When the bubble breaks or expires it deals 31 to 37 Frost damage to enemies within 10 yards. Lasts 8 sec.", + ["effects"] = { + "(6) Apply Aura #69: School Absorb (127) Value: 125 Bubble Barrier", + }, + ["icon"] = "spell_bubl1", + ["id"] = 36524, + ["name"] = "Bubble Barrier", + ["range"] = "40 yards (Long Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36524", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Bubble Barrier", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Envelop yourself in a bubble, absorbing 235 damage. When the bubble breaks or expires it deals 44 to 54 Frost damage to enemies within 10 yards. Lasts 8 sec.", + ["effects"] = { + "(6) Apply Aura #69: School Absorb (127) Value: 235 Bubble Barrier", + }, + ["icon"] = "spell_bubl1", + ["id"] = 36525, + ["name"] = "Bubble Barrier", + ["range"] = "40 yards (Long Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36525", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Bubble Barrier", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Envelop yourself in a bubble, absorbing 335 damage. When the bubble breaks or expires it deals 72 to 85 Frost damage to enemies within 10 yards. Lasts 8 sec.", + ["effects"] = { + "(6) Apply Aura #69: School Absorb (127) Value: 335 Bubble Barrier", + }, + ["icon"] = "spell_bubl1", + ["id"] = 36526, + ["name"] = "Bubble Barrier", + ["range"] = "40 yards (Long Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36526", + ["trainLevel"] = 56, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Charges an enemy, inflicting normal damage plus 1.", + ["effects"] = { + "(96) Charge", + "(58) Weapon Damage + Value: 75", + }, + ["icon"] = "Ability_Warrior_Charge", + ["id"] = 22120, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=22120", + ["trainLevel"] = 20, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 50 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 50", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 7371, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=7371", + ["trainLevel"] = 1, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 100 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 100", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 26177, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26177", + ["trainLevel"] = 12, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 180 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 180", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 26178, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26178", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 250 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 250", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 26179, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26179", + ["trainLevel"] = 36, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 390 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 390", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 26201, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26201", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 550 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 550", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 27685, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=27685", + ["trainLevel"] = 60, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 4 to 7 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 4", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16827, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16827", + ["trainLevel"] = 1, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 8 to 13 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 8", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16828, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16828", + ["trainLevel"] = 8, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 12 to 17 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 12", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16829, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16829", + ["trainLevel"] = 16, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 16 to 23 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 16", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16830, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16830", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 21 to 30 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 21", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16831, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16831", + ["trainLevel"] = 32, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 26 to 37 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 26", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16832, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16832", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 35 to 50 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 35", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 3010, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 7", + ["rankNumber"] = 7, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=3010", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 43 to 60 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 43", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 3009, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 8", + ["rankNumber"] = 8, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=3009", + ["trainLevel"] = 56, + }, + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -30", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 1742, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1742", + ["trainLevel"] = 5, + }, + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -55", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 1753, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1753", + ["trainLevel"] = 15, + }, + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -85", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 1754, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1754", + ["trainLevel"] = 25, + }, + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -125", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 1755, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1755", + ["trainLevel"] = 35, + }, + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -175", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 1756, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1756", + ["trainLevel"] = 45, + }, + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -225", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 16697, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16697", + ["trainLevel"] = 55, + }, + { + ["ability"] = "Dash", + ["castTime"] = "Instant", + ["categoryCooldown"] = "300 seconds", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Increases movement speed by 50% for 15 sec. Does not break prowling.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 50", + }, + ["icon"] = "Ability_Druid_Dash", + ["id"] = 1850, + ["name"] = "Dash", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1850", + ["trainLevel"] = 26, + }, + { + ["ability"] = "Dash", + ["castTime"] = "Instant", + ["categoryCooldown"] = "300 seconds", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Increases movement speed by 60% for 15 sec. Does not break prowling.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 60", + }, + ["icon"] = "Ability_Druid_Dash", + ["id"] = 9821, + ["name"] = "Dash", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=9821", + ["trainLevel"] = 46, + }, + { + ["ability"] = "Dash", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Increases movement speed by 80% for 15 sec.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 80", + }, + ["icon"] = "Ability_Druid_Dash", + ["id"] = 23110, + ["name"] = "Dash", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=23110", + ["trainLevel"] = 50, + }, + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 10 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36548, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36548", + ["trainLevel"] = 10, + }, + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 20 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36549, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36549", + ["trainLevel"] = 20, + }, + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 30 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36550, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36550", + ["trainLevel"] = 30, + }, + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 40 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36551, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36551", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 50 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36552, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36552", + ["trainLevel"] = 50, + }, + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 60 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36553, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36553", + ["trainLevel"] = 60, + }, + { + ["ability"] = "Dive", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Increases movement speed by 40% for 15 sec.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 40", + }, + ["icon"] = "Spell_Shadow_BurningSpirit", + ["id"] = 23145, + ["name"] = "Dive", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=23145", + ["trainLevel"] = 30, + }, + { + ["ability"] = "Dive", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Increases movement speed by 60% for 15 sec.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 60", + }, + ["icon"] = "Spell_Shadow_BurningSpirit", + ["id"] = 23147, + ["name"] = "Dive", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=23147", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Dive", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Increases movement speed by 80% for 15 sec.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 80", + }, + ["icon"] = "Spell_Shadow_BurningSpirit", + ["id"] = 23148, + ["name"] = "Dive", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=23148", + ["trainLevel"] = 50, + }, + { + ["ability"] = "Furious Howl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Party members within 40 yards receive an extra 9 to 12 damage to their next Physical attack. The extra damage scales with attack power. Lasts 10 sec.", + ["effects"] = { + "(6) Apply Aura #13: Mod Damage Done (1) Value: 9 Radius: 40 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Wolf", + ["id"] = 24604, + ["name"] = "Furious Howl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24604", + ["trainLevel"] = 10, + }, + { + ["ability"] = "Furious Howl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Party members within 40 yards receive an extra 18 to 23 damage to their next Physical attack. The extra damage scales with attack power. Lasts 10 sec.", + ["effects"] = { + "(6) Apply Aura #13: Mod Damage Done (1) Value: 18 Radius: 40 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Wolf", + ["id"] = 24605, + ["name"] = "Furious Howl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24605", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Furious Howl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Party members within 40 yards receive an extra 28 to 35 damage to their next Physical attack. The extra damage scales with attack power. Lasts 10 sec.", + ["effects"] = { + "(6) Apply Aura #13: Mod Damage Done (1) Value: 28 Radius: 40 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Wolf", + ["id"] = 24603, + ["name"] = "Furious Howl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24603", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Furious Howl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Party members within 40 yards receive an extra 45 to 58 damage to their next Physical attack. The extra damage scales with attack power. Lasts 10 sec.", + ["effects"] = { + "(6) Apply Aura #13: Mod Damage Done (1) Value: 45 Radius: 40 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Wolf", + ["id"] = 24597, + ["name"] = "Furious Howl", + ["range"] = "30 yards (Medium Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24597", + ["trainLevel"] = 56, + }, + { + ["ability"] = "Grace", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "60 seconds", + ["cost"] = "10 mana", + ["description"] = "Increases chance to dodge by 30% for 12 sec.", + ["effects"] = { + "(6) Apply Aura #49: Mod Dodge Percent Value: 30", + }, + ["icon"] = "Spell_Nature_Invisibilty", + ["id"] = 46296, + ["name"] = "Grace", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=46296", + ["trainLevel"] = 10, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "2 seconds", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Breathes lightning at an enemy, inflicting Nature damage.", + ["effects"] = { + "(2) School Damage (Nature) Value: 36", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 15797, + ["name"] = "Lightning Breath", + ["range"] = "30 yards (Medium Range)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=15797", + ["trainLevel"] = 20, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 11 to 14 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 11", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 24844, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24844", + ["trainLevel"] = 1, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 21 to 24 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 21", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 25008, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=25008", + ["trainLevel"] = 12, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 36 to 41 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 36", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 25009, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=25009", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 51 to 60 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 51", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 25010, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=25010", + ["trainLevel"] = 36, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 78 to 91 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 78", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 25011, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=25011", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 99 to 114 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 99", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 25012, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=25012", + ["trainLevel"] = 60, + }, + { + ["ability"] = "Packleader", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "90 seconds", + ["cost"] = "50 mana", + ["description"] = "Let out a cackle, calling in a Pack Ally to assist you for 20 sec. The Pack Ally also follows the commands given to your pet and does not generate threat.", + ["effects"] = { + "(3) Dummy Packleader", + }, + ["icon"] = "Ability_hunter_pet_hyena", + ["id"] = 36532, + ["name"] = "Packleader", + ["range"] = "0 yards (Self Only)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36532", + ["trainLevel"] = 20, + }, + { + ["ability"] = "Poison Spit", + ["castTime"] = "Instant", + ["categoryCooldown"] = "8 seconds", + ["cooldown"] = "8 seconds", + ["cost"] = "35 mana", + ["description"] = "Spits poison at an enemy, dealing 32 Nature damage over 8 sec.", + ["effects"] = { + "(6) Apply Aura #3: Periodic Damage Value: 8 Interval: 2 seconds", + }, + ["icon"] = "Spell_Nature_CorrosiveBreath", + ["id"] = 46271, + ["name"] = "Poison Spit", + ["range"] = "30 yards (Medium Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=46271", + ["trainLevel"] = 15, + }, + { + ["ability"] = "Poison Spit", + ["castTime"] = "Instant", + ["categoryCooldown"] = "8 seconds", + ["cooldown"] = "8 seconds", + ["cost"] = "35 mana", + ["description"] = "Spits poison at an enemy, dealing 100 Nature damage over 8 sec.", + ["effects"] = { + "(6) Apply Aura #3: Periodic Damage Value: 25 Interval: 2 seconds", + }, + ["icon"] = "Spell_Nature_CorrosiveBreath", + ["id"] = 46272, + ["name"] = "Poison Spit", + ["range"] = "30 yards (Medium Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=46272", + ["trainLevel"] = 45, + }, + { + ["ability"] = "Poison Spit", + ["castTime"] = "Instant", + ["categoryCooldown"] = "8 seconds", + ["cooldown"] = "8 seconds", + ["cost"] = "35 mana", + ["description"] = "Spits poison at an enemy, dealing 140 Nature damage over 8 sec.", + ["effects"] = { + "(6) Apply Aura #3: Periodic Damage Value: 35 Interval: 2 seconds", + }, + ["icon"] = "Spell_Nature_CorrosiveBreath", + ["id"] = 46273, + ["name"] = "Poison Spit", + ["range"] = "30 yards (Medium Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=46273", + ["trainLevel"] = 60, + }, + { + ["ability"] = "Prowl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "40 mana", + ["description"] = "Puts your pet in stealth mode, but slows its movement speed by 50%. The first attack from stealth receives a 20% bonus to damage. Lasts until cancelled.", + ["effects"] = { + "(6) Apply Aura #16: Mod Stealth Value: 100", + "(6) Apply Aura #33: Mod Speed Slow Value: -50", + "(6) Apply Aura #79: Mod Dmg % (1) Value: 20", + }, + ["icon"] = "Ability_Druid_SupriseAttack", + ["id"] = 24450, + ["name"] = "Prowl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24450", + ["trainLevel"] = 30, + }, + { + ["ability"] = "Prowl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "40 mana", + ["description"] = "Puts your pet in stealth mode, but slows its movement speed by 45%. The first attack from stealth receives a 35% bonus to damage. Lasts until cancelled.", + ["effects"] = { + "(6) Apply Aura #16: Mod Stealth Value: 200", + "(6) Apply Aura #33: Mod Speed Slow Value: -45", + "(6) Apply Aura #79: Mod Dmg % (1) Value: 35", + }, + ["icon"] = "Ability_Druid_SupriseAttack", + ["id"] = 24452, + ["name"] = "Prowl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24452", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Prowl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "40 mana", + ["description"] = "Puts your pet in stealth mode, but slows its movement speed by 40%. The first attack from stealth receives a 50% bonus to damage. Lasts until cancelled.", + ["effects"] = { + "(6) Apply Aura #16: Mod Stealth Value: 300", + "(6) Apply Aura #33: Mod Speed Slow Value: -40", + "(6) Apply Aura #79: Mod Dmg % (1) Value: 50", + }, + ["icon"] = "Ability_Druid_SupriseAttack", + ["id"] = 24453, + ["name"] = "Prowl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24453", + ["trainLevel"] = 50, + }, + { + ["ability"] = "Roar of Fortitude", + ["castTime"] = "Instant", + ["categoryCooldown"] = "180 seconds", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Increases max hitpoints of allies in your party by 200 for 120 sec", + ["effects"] = { + "(6) Apply Aura #34: Mod Increase Health Value: 200 Radius: 20 yards", + }, + ["icon"] = "Ability_BullRush", + ["id"] = 36535, + ["name"] = "Roar of Fortitude", + ["range"] = "0 yards (Self Only)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36535", + ["trainLevel"] = 36, + }, + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Slashes the enemy with the raptor's talons for 6 damage, and causes the target to bleed for 2 damage every 5 sec for 15 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 6", + "(6) Apply Aura #3: Periodic Damage Value: 2 Interval: 5 seconds", + }, + ["icon"] = "Ability_Gouge", + ["id"] = 46282, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=46282", + ["trainLevel"] = 1, + }, + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "10 seconds", + ["cost"] = "35 mana", + ["description"] = "Slash at the target with your talons, dealing 5 to 9 damage and causing bleeding for 8 damage over 4 sec. Critically striking with this ability increases its bleed duration by 1 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 5", + "(6) Apply Aura #3: Periodic Damage Value: 2 Interval: 1 seconds", + }, + ["icon"] = "Ability_Druid_Disembowel", + ["id"] = 36537, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36537", + ["trainLevel"] = 12, + }, + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "10 seconds", + ["cost"] = "35 mana", + ["description"] = "Slash at the target with your talons, dealing 8 to 13 damage and causing bleeding for 16 damage over 4 sec. Critically striking with this ability increases its bleed duration by 1 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 8", + "(6) Apply Aura #3: Periodic Damage Value: 4 Interval: 1 seconds", + }, + ["icon"] = "Ability_Druid_Disembowel", + ["id"] = 36538, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36538", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "10 seconds", + ["cost"] = "35 mana", + ["description"] = "Slash at the target with your talons, dealing 13 to 19 damage and causing bleeding for 28 damage over 4 sec. Critically striking with this ability increases its bleed duration by 1 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 13", + "(6) Apply Aura #3: Periodic Damage Value: 7 Interval: 1 seconds", + }, + ["icon"] = "Ability_Druid_Disembowel", + ["id"] = 36539, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36539", + ["trainLevel"] = 36, + }, + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "10 seconds", + ["cost"] = "35 mana", + ["description"] = "Slash at the target with your talons, dealing 19 to 26 damage and causing bleeding for 44 damage over 4 sec. Critically striking with this ability increases its bleed duration by 1 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 19", + "(6) Apply Aura #3: Periodic Damage Value: 11 Interval: 1 seconds", + }, + ["icon"] = "Ability_Druid_Disembowel", + ["id"] = 36540, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36540", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "10 seconds", + ["cost"] = "35 mana", + ["description"] = "Slash at the target with your talons, dealing 27 to 37 damage and causing bleeding for 64 damage over 4 sec. Critically striking with this ability increases its bleed duration by 1 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 27", + "(6) Apply Aura #3: Periodic Damage Value: 16 Interval: 1 seconds", + }, + ["icon"] = "Ability_Druid_Disembowel", + ["id"] = 36541, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36541", + ["trainLevel"] = 60, + }, + { + ["ability"] = "Scorpid Poison", + ["castTime"] = "Instant", + ["categoryCooldown"] = "3 seconds", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Inflicts 12.004801920768 Nature damage over 10 sec. Effect can stack up to 5 times on a single target.", + ["effects"] = { + "(63) Threat Value: 5", + "(6) Apply Aura #3: Periodic Damage Value: 2 Interval: 1.666 seconds", + }, + ["icon"] = "Ability_PoisonSting", + ["id"] = 24640, + ["name"] = "Scorpid Poison", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24640", + ["trainLevel"] = 8, + }, + { + ["ability"] = "Scorpid Poison", + ["castTime"] = "Instant", + ["categoryCooldown"] = "3 seconds", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Inflicts 18.007202881152 Nature damage over 10 sec. Effect can stack up to 5 times on a single target.", + ["effects"] = { + "(63) Threat Value: 5", + "(6) Apply Aura #3: Periodic Damage Value: 3 Interval: 1.666 seconds", + }, + ["icon"] = "Ability_PoisonSting", + ["id"] = 24583, + ["name"] = "Scorpid Poison", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24583", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Scorpid Poison", + ["castTime"] = "Instant", + ["categoryCooldown"] = "3 seconds", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Inflicts 36.014405762305 Nature damage over 10 sec. Effect can stack up to 5 times on a single target.", + ["effects"] = { + "(63) Threat Value: 5", + "(6) Apply Aura #3: Periodic Damage Value: 6 Interval: 1.666 seconds", + }, + ["icon"] = "Ability_PoisonSting", + ["id"] = 24586, + ["name"] = "Scorpid Poison", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24586", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Scorpid Poison", + ["castTime"] = "Instant", + ["categoryCooldown"] = "3 seconds", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Inflicts 48.019207683073 Nature damage over 10 sec. Effect can stack up to 5 times on a single target.", + ["effects"] = { + "(63) Threat Value: 5", + "(6) Apply Aura #3: Periodic Damage Value: 8 Interval: 1.666 seconds", + }, + ["icon"] = "Ability_PoisonSting", + ["id"] = 24587, + ["name"] = "Scorpid Poison", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24587", + ["trainLevel"] = 56, + }, + { + ["ability"] = "Screech", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "5 seconds", + ["cost"] = "20 mana", + ["description"] = "Blasts a single enemy for 7 to 10 damage and lowers the melee attack power of all enemies in melee range by 25. Damage scales with attack power. Effect lasts 4 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 7", + "(6) Apply Aura #99: Mod Attack Power Value: -25 Radius: 5 yards", + "(63) Threat Value: 26 Radius: 5 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Bat", + ["id"] = 24423, + ["name"] = "Screech", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24423", + ["trainLevel"] = 8, + }, + { + ["ability"] = "Screech", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "5 seconds", + ["cost"] = "20 mana", + ["description"] = "Blasts a single enemy for 12 to 17 damage and lowers the melee attack power of all enemies in melee range by 50. Damage scales with attack power. Effect lasts 4 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 12", + "(6) Apply Aura #99: Mod Attack Power Value: -50 Radius: 5 yards", + "(63) Threat Value: 68 Radius: 5 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Bat", + ["id"] = 24577, + ["name"] = "Screech", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24577", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Screech", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "5 seconds", + ["cost"] = "20 mana", + ["description"] = "Blasts a single enemy for 19 to 26 damage and lowers the melee attack power of all enemies in melee range by 75. Damage scales with attack power. Effect lasts 4 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 19", + "(6) Apply Aura #99: Mod Attack Power Value: -75 Radius: 5 yards", + "(63) Threat Value: 153 Radius: 5 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Bat", + ["id"] = 24578, + ["name"] = "Screech", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24578", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Screech", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "5 seconds", + ["cost"] = "20 mana", + ["description"] = "Blasts a single enemy for 26 to 47 damage and lowers the melee attack power of all enemies in melee range by 100. Damage scales with attack power. Effect lasts 4 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 26", + "(6) Apply Aura #99: Mod Attack Power Value: -100 Radius: 5 yards", + "(63) Threat Value: 189 Radius: 5 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Bat", + ["id"] = 24579, + ["name"] = "Screech", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24579", + ["trainLevel"] = 56, + }, + { + ["ability"] = "Shell Shield", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "60 seconds", + ["cost"] = "10 mana", + ["description"] = "Reduces all damage your pet takes by 50%, but increases the time between your pet's attacks by 35%. Lasts 12 sec.", + ["effects"] = { + "(6) Apply Aura #87: Mod Dmg % Taken (127) Value: -50", + "(6) Apply Aura #138: Haste - Melee Value: -35", + }, + ["icon"] = "Ability_Hunter_Pet_Turtle", + ["id"] = 26064, + ["name"] = "Shell Shield", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26064", + ["trainLevel"] = 20, + }, + { + ["ability"] = "Strider Presence", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Party members within 50 yards of the Tallstrider continue 10% of their Mana regeneration and 20% of their Health regeneration while in combat. Lasts until cancelled.", + ["effects"] = { + "(35) Apply Area Aura #134: Mod Interrupted Mana Regen Value: 10 Radius: 50 yards", + "(35) Apply Area Aura #116: Regen During Combat Value: 20 Radius: 50 yards", + }, + ["icon"] = "Spell_Holy_Dizzy", + ["id"] = 36531, + ["name"] = "Strider Presence", + ["range"] = "0 yards (Self Only)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36531", + ["trainLevel"] = 10, + }, + { + ["ability"] = "Thunderstomp", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "40 seconds", + ["cost"] = "60 mana", + ["description"] = "Shakes the ground with thundering force, doing 67 to 78 Nature damage to all enemies within 8 yards. Causes a moderate amount of threat.", + ["effects"] = { + "(2) School Damage (Nature) Value: 67 Radius: 8 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Gorilla", + ["id"] = 26090, + ["name"] = "Thunderstomp", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26090", + ["trainLevel"] = 30, + }, + { + ["ability"] = "Thunderstomp", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "40 seconds", + ["cost"] = "60 mana", + ["description"] = "Shakes the ground with thundering force, doing 87 to 100 Nature damage to all enemies within 8 yards. Causes a moderate amount of threat.", + ["effects"] = { + "(2) School Damage (Nature) Value: 87 Radius: 8 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Gorilla", + ["id"] = 26187, + ["name"] = "Thunderstomp", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26187", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Thunderstomp", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "40 seconds", + ["cost"] = "60 mana", + ["description"] = "Shakes the ground with thundering force, doing 115 to 134 Nature damage to all enemies within 8 yards. Causes a moderate amount of threat.", + ["effects"] = { + "(2) School Damage (Nature) Value: 115 Radius: 8 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Gorilla", + ["id"] = 26188, + ["name"] = "Thunderstomp", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26188", + ["trainLevel"] = 50, + }, + { + ["ability"] = "Thunderstomp", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "40 seconds", + ["cost"] = "60 mana", + ["description"] = "Shakes the ground with thundering force, doing 153 to 179 Nature damage to all enemies within 8 yards. Causes a moderate amount of threat.", + ["effects"] = { + "(2) School Damage (Nature) Value: 153 Radius: 8 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Gorilla", + ["id"] = 51156, + ["name"] = "Thunderstomp", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=51156", + ["trainLevel"] = 56, + }, + { + ["ability"] = "Web", + ["castTime"] = "Instant", + ["categoryCooldown"] = "120 seconds", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Encases the target in sticky webs rendering them unable to move for 8 sec.", + ["effects"] = { + "(6) Apply Aura #26: Root", + }, + ["icon"] = "Ability_Ensnare", + ["id"] = 4167, + ["name"] = "Web", + ["range"] = "20 yards (Short Range)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=4167", + ["trainLevel"] = 22, + }, + }, + ["byAbility"] = { + ["Bite"] = { + ["ability"] = "Bite", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Bite", + ["spellCount"] = 8, + ["spells"] = { + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 7 to 10 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 7", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17253, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17253", + ["trainLevel"] = 1, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 16 to 19 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 16", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17255, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17255", + ["trainLevel"] = 8, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 24 to 29 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 24", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17256, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17256", + ["trainLevel"] = 16, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 31 to 38 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 31", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17257, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17257", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 40 to 49 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 40", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17258, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17258", + ["trainLevel"] = 32, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 49 to 60 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 49", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17259, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17259", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 66 to 81 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 66", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17260, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 7", + ["rankNumber"] = 7, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17260", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Bite", + ["castTime"] = "Instant", + ["categoryCooldown"] = "6 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Bite the enemy, causing 81 to 100 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 81", + }, + ["icon"] = "Ability_Racial_Cannibalize", + ["id"] = 17261, + ["name"] = "Bite", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 8", + ["rankNumber"] = 8, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=17261", + ["trainLevel"] = 56, + }, + }, + }, + ["Bubble Barrier"] = { + ["ability"] = "Bubble Barrier", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Bubble+Barrier", + ["spellCount"] = 5, + ["spells"] = { + { + ["ability"] = "Bubble Barrier", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Inflicts Frost damage to nearby enemies.", + ["effects"] = { + "(2) School Damage (Frost) Value: 15 Radius: 10 yards", + }, + ["icon"] = "Spell_Frost_FrostNova", + ["id"] = 36527, + ["name"] = "Bubble Barrier", + ["range"] = "0 yards (Self Only)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Frost", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36527", + ["trainLevel"] = 10, + }, + { + ["ability"] = "Bubble Barrier", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Envelop yourself in a bubble, absorbing 50 damage. When the bubble breaks or expires it deals 15 to 18 Frost damage to enemies within 10 yards. Lasts 8 sec.", + ["effects"] = { + "(6) Apply Aura #69: School Absorb (127) Value: 50 Bubble Barrier", + }, + ["icon"] = "spell_bubl1", + ["id"] = 36523, + ["name"] = "Bubble Barrier", + ["range"] = "40 yards (Long Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36523", + ["trainLevel"] = 10, + }, + { + ["ability"] = "Bubble Barrier", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Envelop yourself in a bubble, absorbing 125 damage. When the bubble breaks or expires it deals 31 to 37 Frost damage to enemies within 10 yards. Lasts 8 sec.", + ["effects"] = { + "(6) Apply Aura #69: School Absorb (127) Value: 125 Bubble Barrier", + }, + ["icon"] = "spell_bubl1", + ["id"] = 36524, + ["name"] = "Bubble Barrier", + ["range"] = "40 yards (Long Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36524", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Bubble Barrier", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Envelop yourself in a bubble, absorbing 235 damage. When the bubble breaks or expires it deals 44 to 54 Frost damage to enemies within 10 yards. Lasts 8 sec.", + ["effects"] = { + "(6) Apply Aura #69: School Absorb (127) Value: 235 Bubble Barrier", + }, + ["icon"] = "spell_bubl1", + ["id"] = 36525, + ["name"] = "Bubble Barrier", + ["range"] = "40 yards (Long Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36525", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Bubble Barrier", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Envelop yourself in a bubble, absorbing 335 damage. When the bubble breaks or expires it deals 72 to 85 Frost damage to enemies within 10 yards. Lasts 8 sec.", + ["effects"] = { + "(6) Apply Aura #69: School Absorb (127) Value: 335 Bubble Barrier", + }, + ["icon"] = "spell_bubl1", + ["id"] = 36526, + ["name"] = "Bubble Barrier", + ["range"] = "40 yards (Long Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36526", + ["trainLevel"] = 56, + }, + }, + }, + ["Charge"] = { + ["ability"] = "Charge", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Charge", + ["spellCount"] = 7, + ["spells"] = { + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Charges an enemy, inflicting normal damage plus 1.", + ["effects"] = { + "(96) Charge", + "(58) Weapon Damage + Value: 75", + }, + ["icon"] = "Ability_Warrior_Charge", + ["id"] = 22120, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=22120", + ["trainLevel"] = 20, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 50 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 50", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 7371, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=7371", + ["trainLevel"] = 1, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 100 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 100", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 26177, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26177", + ["trainLevel"] = 12, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 180 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 180", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 26178, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26178", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 250 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 250", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 26179, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26179", + ["trainLevel"] = 36, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 390 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 390", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 26201, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26201", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Charge", + ["castTime"] = "Instant", + ["categoryCooldown"] = "25 seconds", + ["cooldown"] = "n/a", + ["cost"] = "35 mana", + ["description"] = "Charges an enemy, immobilizes it for 1 sec, and increases the boar's attack power by 550 and an additional 35% for the next attack.", + ["effects"] = { + "(96) Charge", + "(6) Apply Aura #99: Mod Attack Power Value: 550", + "(64) Trigger Spell Boar Charge", + }, + ["icon"] = "Ability_Hunter_Pet_Boar", + ["id"] = 27685, + ["name"] = "Charge", + ["range"] = "8-25 yards (Charge)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=27685", + ["trainLevel"] = 60, + }, + }, + }, + ["Claw"] = { + ["ability"] = "Claw", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Claw", + ["spellCount"] = 8, + ["spells"] = { + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 4 to 7 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 4", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16827, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16827", + ["trainLevel"] = 1, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 8 to 13 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 8", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16828, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16828", + ["trainLevel"] = 8, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 12 to 17 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 12", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16829, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16829", + ["trainLevel"] = 16, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 16 to 23 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 16", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16830, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16830", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 21 to 30 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 21", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16831, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16831", + ["trainLevel"] = 32, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 26 to 37 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 26", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 16832, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16832", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 35 to 50 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 35", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 3010, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 7", + ["rankNumber"] = 7, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=3010", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Claw", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Claw the enemy, causing 43 to 60 damage.", + ["effects"] = { + "(2) School Damage (Physical) Value: 43", + }, + ["icon"] = "Ability_Druid_Rake", + ["id"] = 3009, + ["name"] = "Claw", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 8", + ["rankNumber"] = 8, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=3009", + ["trainLevel"] = 56, + }, + }, + }, + ["Cower"] = { + ["ability"] = "Cower", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Cower", + ["spellCount"] = 6, + ["spells"] = { + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -30", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 1742, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1742", + ["trainLevel"] = 5, + }, + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -55", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 1753, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1753", + ["trainLevel"] = 15, + }, + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -85", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 1754, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1754", + ["trainLevel"] = 25, + }, + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -125", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 1755, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1755", + ["trainLevel"] = 35, + }, + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -175", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 1756, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1756", + ["trainLevel"] = 45, + }, + { + ["ability"] = "Cower", + ["castTime"] = "Instant", + ["categoryCooldown"] = "5 seconds", + ["cooldown"] = "n/a", + ["cost"] = "15 mana", + ["description"] = "Cower, causing no damage but lowering your threat, making the enemy less likely to attack you.", + ["effects"] = { + "(63) Threat Value: -225", + }, + ["icon"] = "Ability_Druid_Cower", + ["id"] = 16697, + ["name"] = "Cower", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=16697", + ["trainLevel"] = 55, + }, + }, + }, + ["Dash"] = { + ["ability"] = "Dash", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Dash", + ["spellCount"] = 3, + ["spells"] = { + { + ["ability"] = "Dash", + ["castTime"] = "Instant", + ["categoryCooldown"] = "300 seconds", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Increases movement speed by 50% for 15 sec. Does not break prowling.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 50", + }, + ["icon"] = "Ability_Druid_Dash", + ["id"] = 1850, + ["name"] = "Dash", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=1850", + ["trainLevel"] = 26, + }, + { + ["ability"] = "Dash", + ["castTime"] = "Instant", + ["categoryCooldown"] = "300 seconds", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Increases movement speed by 60% for 15 sec. Does not break prowling.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 60", + }, + ["icon"] = "Ability_Druid_Dash", + ["id"] = 9821, + ["name"] = "Dash", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=9821", + ["trainLevel"] = 46, + }, + { + ["ability"] = "Dash", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Increases movement speed by 80% for 15 sec.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 80", + }, + ["icon"] = "Ability_Druid_Dash", + ["id"] = 23110, + ["name"] = "Dash", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=23110", + ["trainLevel"] = 50, + }, + }, + }, + ["Death Roll"] = { + ["ability"] = "Death Roll", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Death+Roll", + ["spellCount"] = 6, + ["spells"] = { + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 10 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36548, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36548", + ["trainLevel"] = 10, + }, + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 20 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36549, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36549", + ["trainLevel"] = 20, + }, + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 30 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36550, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36550", + ["trainLevel"] = 30, + }, + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 40 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36551, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36551", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 50 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36552, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36552", + ["trainLevel"] = 50, + }, + { + ["ability"] = "Death Roll", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "30 seconds", + ["cost"] = "50 mana", + ["description"] = "Initiates a death roll on the target, reducing movement speed by 80% and dealing 60 damage every 0.5 sec. for 3 sec. Deals 25% more damage to targets below 35% health.", + ["effects"] = { + "(6) Apply Aura #23: Periodic Trigger Interval: 0.5 seconds Death Roll", + "(6) Apply Aura #4: Dummy", + }, + ["icon"] = "Ability_hunter_pet_crocolisk", + ["id"] = 36553, + ["name"] = "Death Roll", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36553", + ["trainLevel"] = 60, + }, + }, + }, + ["Dive"] = { + ["ability"] = "Dive", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Dive", + ["spellCount"] = 3, + ["spells"] = { + { + ["ability"] = "Dive", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Increases movement speed by 40% for 15 sec.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 40", + }, + ["icon"] = "Spell_Shadow_BurningSpirit", + ["id"] = 23145, + ["name"] = "Dive", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=23145", + ["trainLevel"] = 30, + }, + { + ["ability"] = "Dive", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Increases movement speed by 60% for 15 sec.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 60", + }, + ["icon"] = "Spell_Shadow_BurningSpirit", + ["id"] = 23147, + ["name"] = "Dive", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=23147", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Dive", + ["castTime"] = "Instant", + ["categoryCooldown"] = "30 seconds", + ["cooldown"] = "n/a", + ["cost"] = "20 mana", + ["description"] = "Increases movement speed by 80% for 15 sec.", + ["effects"] = { + "(6) Apply Aura #31: Mod Speed Value: 80", + }, + ["icon"] = "Spell_Shadow_BurningSpirit", + ["id"] = 23148, + ["name"] = "Dive", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=23148", + ["trainLevel"] = 50, + }, + }, + }, + ["Furious Howl"] = { + ["ability"] = "Furious Howl", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Furious+Howl", + ["spellCount"] = 4, + ["spells"] = { + { + ["ability"] = "Furious Howl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Party members within 40 yards receive an extra 9 to 12 damage to their next Physical attack. The extra damage scales with attack power. Lasts 10 sec.", + ["effects"] = { + "(6) Apply Aura #13: Mod Damage Done (1) Value: 9 Radius: 40 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Wolf", + ["id"] = 24604, + ["name"] = "Furious Howl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24604", + ["trainLevel"] = 10, + }, + { + ["ability"] = "Furious Howl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Party members within 40 yards receive an extra 18 to 23 damage to their next Physical attack. The extra damage scales with attack power. Lasts 10 sec.", + ["effects"] = { + "(6) Apply Aura #13: Mod Damage Done (1) Value: 18 Radius: 40 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Wolf", + ["id"] = 24605, + ["name"] = "Furious Howl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24605", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Furious Howl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Party members within 40 yards receive an extra 28 to 35 damage to their next Physical attack. The extra damage scales with attack power. Lasts 10 sec.", + ["effects"] = { + "(6) Apply Aura #13: Mod Damage Done (1) Value: 28 Radius: 40 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Wolf", + ["id"] = 24603, + ["name"] = "Furious Howl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24603", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Furious Howl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Party members within 40 yards receive an extra 45 to 58 damage to their next Physical attack. The extra damage scales with attack power. Lasts 10 sec.", + ["effects"] = { + "(6) Apply Aura #13: Mod Damage Done (1) Value: 45 Radius: 40 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Wolf", + ["id"] = 24597, + ["name"] = "Furious Howl", + ["range"] = "30 yards (Medium Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24597", + ["trainLevel"] = 56, + }, + }, + }, + ["Grace"] = { + ["ability"] = "Grace", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Grace", + ["spellCount"] = 1, + ["spells"] = { + { + ["ability"] = "Grace", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "60 seconds", + ["cost"] = "10 mana", + ["description"] = "Increases chance to dodge by 30% for 12 sec.", + ["effects"] = { + "(6) Apply Aura #49: Mod Dodge Percent Value: 30", + }, + ["icon"] = "Spell_Nature_Invisibilty", + ["id"] = 46296, + ["name"] = "Grace", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=46296", + ["trainLevel"] = 10, + }, + }, + }, + ["Lightning Breath"] = { + ["ability"] = "Lightning Breath", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Lightning+Breath", + ["spellCount"] = 7, + ["spells"] = { + { + ["ability"] = "Lightning Breath", + ["castTime"] = "2 seconds", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Breathes lightning at an enemy, inflicting Nature damage.", + ["effects"] = { + "(2) School Damage (Nature) Value: 36", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 15797, + ["name"] = "Lightning Breath", + ["range"] = "30 yards (Medium Range)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=15797", + ["trainLevel"] = 20, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 11 to 14 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 11", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 24844, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24844", + ["trainLevel"] = 1, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 21 to 24 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 21", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 25008, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=25008", + ["trainLevel"] = 12, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 36 to 41 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 36", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 25009, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=25009", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 51 to 60 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 51", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 25010, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=25010", + ["trainLevel"] = 36, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 78 to 91 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 78", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 25011, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=25011", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Lightning Breath", + ["castTime"] = "Channeled", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Breathes lightning, instantly dealing 99 to 114 Nature damage to a single target.", + ["effects"] = { + "(2) School Damage (Nature) Value: 99", + }, + ["icon"] = "Spell_Nature_Lightning", + ["id"] = 25012, + ["name"] = "Lightning Breath", + ["range"] = "20 yards (Short Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=25012", + ["trainLevel"] = 60, + }, + }, + }, + ["Packleader"] = { + ["ability"] = "Packleader", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Packleader", + ["spellCount"] = 1, + ["spells"] = { + { + ["ability"] = "Packleader", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "90 seconds", + ["cost"] = "50 mana", + ["description"] = "Let out a cackle, calling in a Pack Ally to assist you for 20 sec. The Pack Ally also follows the commands given to your pet and does not generate threat.", + ["effects"] = { + "(3) Dummy Packleader", + }, + ["icon"] = "Ability_hunter_pet_hyena", + ["id"] = 36532, + ["name"] = "Packleader", + ["range"] = "0 yards (Self Only)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36532", + ["trainLevel"] = 20, + }, + }, + }, + ["Poison Spit"] = { + ["ability"] = "Poison Spit", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Poison+Spit", + ["spellCount"] = 3, + ["spells"] = { + { + ["ability"] = "Poison Spit", + ["castTime"] = "Instant", + ["categoryCooldown"] = "8 seconds", + ["cooldown"] = "8 seconds", + ["cost"] = "35 mana", + ["description"] = "Spits poison at an enemy, dealing 32 Nature damage over 8 sec.", + ["effects"] = { + "(6) Apply Aura #3: Periodic Damage Value: 8 Interval: 2 seconds", + }, + ["icon"] = "Spell_Nature_CorrosiveBreath", + ["id"] = 46271, + ["name"] = "Poison Spit", + ["range"] = "30 yards (Medium Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=46271", + ["trainLevel"] = 15, + }, + { + ["ability"] = "Poison Spit", + ["castTime"] = "Instant", + ["categoryCooldown"] = "8 seconds", + ["cooldown"] = "8 seconds", + ["cost"] = "35 mana", + ["description"] = "Spits poison at an enemy, dealing 100 Nature damage over 8 sec.", + ["effects"] = { + "(6) Apply Aura #3: Periodic Damage Value: 25 Interval: 2 seconds", + }, + ["icon"] = "Spell_Nature_CorrosiveBreath", + ["id"] = 46272, + ["name"] = "Poison Spit", + ["range"] = "30 yards (Medium Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=46272", + ["trainLevel"] = 45, + }, + { + ["ability"] = "Poison Spit", + ["castTime"] = "Instant", + ["categoryCooldown"] = "8 seconds", + ["cooldown"] = "8 seconds", + ["cost"] = "35 mana", + ["description"] = "Spits poison at an enemy, dealing 140 Nature damage over 8 sec.", + ["effects"] = { + "(6) Apply Aura #3: Periodic Damage Value: 35 Interval: 2 seconds", + }, + ["icon"] = "Spell_Nature_CorrosiveBreath", + ["id"] = 46273, + ["name"] = "Poison Spit", + ["range"] = "30 yards (Medium Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=46273", + ["trainLevel"] = 60, + }, + }, + }, + ["Prowl"] = { + ["ability"] = "Prowl", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Prowl", + ["spellCount"] = 3, + ["spells"] = { + { + ["ability"] = "Prowl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "40 mana", + ["description"] = "Puts your pet in stealth mode, but slows its movement speed by 50%. The first attack from stealth receives a 20% bonus to damage. Lasts until cancelled.", + ["effects"] = { + "(6) Apply Aura #16: Mod Stealth Value: 100", + "(6) Apply Aura #33: Mod Speed Slow Value: -50", + "(6) Apply Aura #79: Mod Dmg % (1) Value: 20", + }, + ["icon"] = "Ability_Druid_SupriseAttack", + ["id"] = 24450, + ["name"] = "Prowl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24450", + ["trainLevel"] = 30, + }, + { + ["ability"] = "Prowl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "40 mana", + ["description"] = "Puts your pet in stealth mode, but slows its movement speed by 45%. The first attack from stealth receives a 35% bonus to damage. Lasts until cancelled.", + ["effects"] = { + "(6) Apply Aura #16: Mod Stealth Value: 200", + "(6) Apply Aura #33: Mod Speed Slow Value: -45", + "(6) Apply Aura #79: Mod Dmg % (1) Value: 35", + }, + ["icon"] = "Ability_Druid_SupriseAttack", + ["id"] = 24452, + ["name"] = "Prowl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24452", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Prowl", + ["castTime"] = "Instant", + ["categoryCooldown"] = "10 seconds", + ["cooldown"] = "n/a", + ["cost"] = "40 mana", + ["description"] = "Puts your pet in stealth mode, but slows its movement speed by 40%. The first attack from stealth receives a 50% bonus to damage. Lasts until cancelled.", + ["effects"] = { + "(6) Apply Aura #16: Mod Stealth Value: 300", + "(6) Apply Aura #33: Mod Speed Slow Value: -40", + "(6) Apply Aura #79: Mod Dmg % (1) Value: 50", + }, + ["icon"] = "Ability_Druid_SupriseAttack", + ["id"] = 24453, + ["name"] = "Prowl", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24453", + ["trainLevel"] = 50, + }, + }, + }, + ["Roar of Fortitude"] = { + ["ability"] = "Roar of Fortitude", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Roar+of+Fortitude", + ["spellCount"] = 1, + ["spells"] = { + { + ["ability"] = "Roar of Fortitude", + ["castTime"] = "Instant", + ["categoryCooldown"] = "180 seconds", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Increases max hitpoints of allies in your party by 200 for 120 sec", + ["effects"] = { + "(6) Apply Aura #34: Mod Increase Health Value: 200 Radius: 20 yards", + }, + ["icon"] = "Ability_BullRush", + ["id"] = 36535, + ["name"] = "Roar of Fortitude", + ["range"] = "0 yards (Self Only)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36535", + ["trainLevel"] = 36, + }, + }, + }, + ["Savage Rend"] = { + ["ability"] = "Savage Rend", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Savage+Rend", + ["spellCount"] = 6, + ["spells"] = { + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Slashes the enemy with the raptor's talons for 6 damage, and causes the target to bleed for 2 damage every 5 sec for 15 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 6", + "(6) Apply Aura #3: Periodic Damage Value: 2 Interval: 5 seconds", + }, + ["icon"] = "Ability_Gouge", + ["id"] = 46282, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=46282", + ["trainLevel"] = 1, + }, + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "10 seconds", + ["cost"] = "35 mana", + ["description"] = "Slash at the target with your talons, dealing 5 to 9 damage and causing bleeding for 8 damage over 4 sec. Critically striking with this ability increases its bleed duration by 1 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 5", + "(6) Apply Aura #3: Periodic Damage Value: 2 Interval: 1 seconds", + }, + ["icon"] = "Ability_Druid_Disembowel", + ["id"] = 36537, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36537", + ["trainLevel"] = 12, + }, + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "10 seconds", + ["cost"] = "35 mana", + ["description"] = "Slash at the target with your talons, dealing 8 to 13 damage and causing bleeding for 16 damage over 4 sec. Critically striking with this ability increases its bleed duration by 1 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 8", + "(6) Apply Aura #3: Periodic Damage Value: 4 Interval: 1 seconds", + }, + ["icon"] = "Ability_Druid_Disembowel", + ["id"] = 36538, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36538", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "10 seconds", + ["cost"] = "35 mana", + ["description"] = "Slash at the target with your talons, dealing 13 to 19 damage and causing bleeding for 28 damage over 4 sec. Critically striking with this ability increases its bleed duration by 1 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 13", + "(6) Apply Aura #3: Periodic Damage Value: 7 Interval: 1 seconds", + }, + ["icon"] = "Ability_Druid_Disembowel", + ["id"] = 36539, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36539", + ["trainLevel"] = 36, + }, + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "10 seconds", + ["cost"] = "35 mana", + ["description"] = "Slash at the target with your talons, dealing 19 to 26 damage and causing bleeding for 44 damage over 4 sec. Critically striking with this ability increases its bleed duration by 1 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 19", + "(6) Apply Aura #3: Periodic Damage Value: 11 Interval: 1 seconds", + }, + ["icon"] = "Ability_Druid_Disembowel", + ["id"] = 36540, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 5", + ["rankNumber"] = 5, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36540", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Savage Rend", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "10 seconds", + ["cost"] = "35 mana", + ["description"] = "Slash at the target with your talons, dealing 27 to 37 damage and causing bleeding for 64 damage over 4 sec. Critically striking with this ability increases its bleed duration by 1 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 27", + "(6) Apply Aura #3: Periodic Damage Value: 16 Interval: 1 seconds", + }, + ["icon"] = "Ability_Druid_Disembowel", + ["id"] = 36541, + ["name"] = "Savage Rend", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 6", + ["rankNumber"] = 6, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36541", + ["trainLevel"] = 60, + }, + }, + }, + ["Scorpid Poison"] = { + ["ability"] = "Scorpid Poison", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Scorpid+Poison", + ["spellCount"] = 4, + ["spells"] = { + { + ["ability"] = "Scorpid Poison", + ["castTime"] = "Instant", + ["categoryCooldown"] = "3 seconds", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Inflicts 12.004801920768 Nature damage over 10 sec. Effect can stack up to 5 times on a single target.", + ["effects"] = { + "(63) Threat Value: 5", + "(6) Apply Aura #3: Periodic Damage Value: 2 Interval: 1.666 seconds", + }, + ["icon"] = "Ability_PoisonSting", + ["id"] = 24640, + ["name"] = "Scorpid Poison", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24640", + ["trainLevel"] = 8, + }, + { + ["ability"] = "Scorpid Poison", + ["castTime"] = "Instant", + ["categoryCooldown"] = "3 seconds", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Inflicts 18.007202881152 Nature damage over 10 sec. Effect can stack up to 5 times on a single target.", + ["effects"] = { + "(63) Threat Value: 5", + "(6) Apply Aura #3: Periodic Damage Value: 3 Interval: 1.666 seconds", + }, + ["icon"] = "Ability_PoisonSting", + ["id"] = 24583, + ["name"] = "Scorpid Poison", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24583", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Scorpid Poison", + ["castTime"] = "Instant", + ["categoryCooldown"] = "3 seconds", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Inflicts 36.014405762305 Nature damage over 10 sec. Effect can stack up to 5 times on a single target.", + ["effects"] = { + "(63) Threat Value: 5", + "(6) Apply Aura #3: Periodic Damage Value: 6 Interval: 1.666 seconds", + }, + ["icon"] = "Ability_PoisonSting", + ["id"] = 24586, + ["name"] = "Scorpid Poison", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24586", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Scorpid Poison", + ["castTime"] = "Instant", + ["categoryCooldown"] = "3 seconds", + ["cooldown"] = "n/a", + ["cost"] = "25 mana", + ["description"] = "Inflicts 48.019207683073 Nature damage over 10 sec. Effect can stack up to 5 times on a single target.", + ["effects"] = { + "(63) Threat Value: 5", + "(6) Apply Aura #3: Periodic Damage Value: 8 Interval: 1.666 seconds", + }, + ["icon"] = "Ability_PoisonSting", + ["id"] = 24587, + ["name"] = "Scorpid Poison", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24587", + ["trainLevel"] = 56, + }, + }, + }, + ["Screech"] = { + ["ability"] = "Screech", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Screech", + ["spellCount"] = 4, + ["spells"] = { + { + ["ability"] = "Screech", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "5 seconds", + ["cost"] = "20 mana", + ["description"] = "Blasts a single enemy for 7 to 10 damage and lowers the melee attack power of all enemies in melee range by 25. Damage scales with attack power. Effect lasts 4 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 7", + "(6) Apply Aura #99: Mod Attack Power Value: -25 Radius: 5 yards", + "(63) Threat Value: 26 Radius: 5 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Bat", + ["id"] = 24423, + ["name"] = "Screech", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24423", + ["trainLevel"] = 8, + }, + { + ["ability"] = "Screech", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "5 seconds", + ["cost"] = "20 mana", + ["description"] = "Blasts a single enemy for 12 to 17 damage and lowers the melee attack power of all enemies in melee range by 50. Damage scales with attack power. Effect lasts 4 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 12", + "(6) Apply Aura #99: Mod Attack Power Value: -50 Radius: 5 yards", + "(63) Threat Value: 68 Radius: 5 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Bat", + ["id"] = 24577, + ["name"] = "Screech", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24577", + ["trainLevel"] = 24, + }, + { + ["ability"] = "Screech", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "5 seconds", + ["cost"] = "20 mana", + ["description"] = "Blasts a single enemy for 19 to 26 damage and lowers the melee attack power of all enemies in melee range by 75. Damage scales with attack power. Effect lasts 4 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 19", + "(6) Apply Aura #99: Mod Attack Power Value: -75 Radius: 5 yards", + "(63) Threat Value: 153 Radius: 5 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Bat", + ["id"] = 24578, + ["name"] = "Screech", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24578", + ["trainLevel"] = 48, + }, + { + ["ability"] = "Screech", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "5 seconds", + ["cost"] = "20 mana", + ["description"] = "Blasts a single enemy for 26 to 47 damage and lowers the melee attack power of all enemies in melee range by 100. Damage scales with attack power. Effect lasts 4 sec.", + ["effects"] = { + "(2) School Damage (Physical) Value: 26", + "(6) Apply Aura #99: Mod Attack Power Value: -100 Radius: 5 yards", + "(63) Threat Value: 189 Radius: 5 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Bat", + ["id"] = 24579, + ["name"] = "Screech", + ["range"] = "5 yards (Combat Range)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=24579", + ["trainLevel"] = 56, + }, + }, + }, + ["Shell Shield"] = { + ["ability"] = "Shell Shield", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Shell+Shield", + ["spellCount"] = 1, + ["spells"] = { + { + ["ability"] = "Shell Shield", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "60 seconds", + ["cost"] = "10 mana", + ["description"] = "Reduces all damage your pet takes by 50%, but increases the time between your pet's attacks by 35%. Lasts 12 sec.", + ["effects"] = { + "(6) Apply Aura #87: Mod Dmg % Taken (127) Value: -50", + "(6) Apply Aura #138: Haste - Melee Value: -35", + }, + ["icon"] = "Ability_Hunter_Pet_Turtle", + ["id"] = 26064, + ["name"] = "Shell Shield", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26064", + ["trainLevel"] = 20, + }, + }, + }, + ["Strider Presence"] = { + ["ability"] = "Strider Presence", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Strider+Presence", + ["spellCount"] = 1, + ["spells"] = { + { + ["ability"] = "Strider Presence", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "n/a", + ["cost"] = "50 mana", + ["description"] = "Party members within 50 yards of the Tallstrider continue 10% of their Mana regeneration and 20% of their Health regeneration while in combat. Lasts until cancelled.", + ["effects"] = { + "(35) Apply Area Aura #134: Mod Interrupted Mana Regen Value: 10 Radius: 50 yards", + "(35) Apply Area Aura #116: Regen During Combat Value: 20 Radius: 50 yards", + }, + ["icon"] = "Spell_Holy_Dizzy", + ["id"] = 36531, + ["name"] = "Strider Presence", + ["range"] = "0 yards (Self Only)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=36531", + ["trainLevel"] = 10, + }, + }, + }, + ["Thunderstomp"] = { + ["ability"] = "Thunderstomp", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Thunderstomp", + ["spellCount"] = 4, + ["spells"] = { + { + ["ability"] = "Thunderstomp", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "40 seconds", + ["cost"] = "60 mana", + ["description"] = "Shakes the ground with thundering force, doing 67 to 78 Nature damage to all enemies within 8 yards. Causes a moderate amount of threat.", + ["effects"] = { + "(2) School Damage (Nature) Value: 67 Radius: 8 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Gorilla", + ["id"] = 26090, + ["name"] = "Thunderstomp", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 1", + ["rankNumber"] = 1, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26090", + ["trainLevel"] = 30, + }, + { + ["ability"] = "Thunderstomp", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "40 seconds", + ["cost"] = "60 mana", + ["description"] = "Shakes the ground with thundering force, doing 87 to 100 Nature damage to all enemies within 8 yards. Causes a moderate amount of threat.", + ["effects"] = { + "(2) School Damage (Nature) Value: 87 Radius: 8 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Gorilla", + ["id"] = 26187, + ["name"] = "Thunderstomp", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 2", + ["rankNumber"] = 2, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26187", + ["trainLevel"] = 40, + }, + { + ["ability"] = "Thunderstomp", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "40 seconds", + ["cost"] = "60 mana", + ["description"] = "Shakes the ground with thundering force, doing 115 to 134 Nature damage to all enemies within 8 yards. Causes a moderate amount of threat.", + ["effects"] = { + "(2) School Damage (Nature) Value: 115 Radius: 8 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Gorilla", + ["id"] = 26188, + ["name"] = "Thunderstomp", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 3", + ["rankNumber"] = 3, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=26188", + ["trainLevel"] = 50, + }, + { + ["ability"] = "Thunderstomp", + ["castTime"] = "Instant", + ["categoryCooldown"] = "n/a", + ["cooldown"] = "40 seconds", + ["cost"] = "60 mana", + ["description"] = "Shakes the ground with thundering force, doing 153 to 179 Nature damage to all enemies within 8 yards. Causes a moderate amount of threat.", + ["effects"] = { + "(2) School Damage (Nature) Value: 153 Radius: 8 yards", + }, + ["icon"] = "Ability_Hunter_Pet_Gorilla", + ["id"] = 51156, + ["name"] = "Thunderstomp", + ["range"] = "0 yards (Self Only)", + ["rank"] = "Rank 4", + ["rankNumber"] = 4, + ["school"] = "Nature", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=51156", + ["trainLevel"] = 56, + }, + }, + }, + ["Web"] = { + ["ability"] = "Web", + ["searchUrl"] = "https://database.turtlecraft.gg/?search=Web", + ["spellCount"] = 1, + ["spells"] = { + { + ["ability"] = "Web", + ["castTime"] = "Instant", + ["categoryCooldown"] = "120 seconds", + ["cooldown"] = "n/a", + ["cost"] = "None", + ["description"] = "Encases the target in sticky webs rendering them unable to move for 8 sec.", + ["effects"] = { + "(6) Apply Aura #26: Root", + }, + ["icon"] = "Ability_Ensnare", + ["id"] = 4167, + ["name"] = "Web", + ["range"] = "20 yards (Short Range)", + ["rank"] = "", + ["rankNumber"] = 0, + ["school"] = "Physical", + ["sourceUrl"] = "https://database.turtlecraft.gg/?spell=4167", + ["trainLevel"] = 22, + }, + }, + }, + }, + ["cleanedBy"] = "post-process quality rules v2 hard-gate", + ["droppedDuplicates"] = { + { + ["ability"] = "Bubble Barrier", + ["droppedId"] = 36528, + ["keptId"] = 36527, + ["mode"] = "lower-score", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Bubble Barrier", + ["droppedId"] = 36529, + ["keptId"] = 36527, + ["mode"] = "lower-score", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Bubble Barrier", + ["droppedId"] = 36530, + ["keptId"] = 36527, + ["mode"] = "lower-score", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Charge", + ["droppedId"] = 100, + ["droppedReasons"] = { + "non-pet-charge-signature", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Charge", + ["droppedId"] = 1738, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Charge", + ["droppedId"] = 6178, + ["droppedReasons"] = { + "non-pet-charge-signature", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Charge", + ["droppedId"] = 6180, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Charge", + ["droppedId"] = 11578, + ["droppedReasons"] = { + "non-pet-charge-signature", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 3, + }, + { + ["ability"] = "Charge", + ["droppedId"] = 11579, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 3, + }, + { + ["ability"] = "Claw", + ["droppedId"] = 24187, + ["droppedReasons"] = { + "bad-or-missing-description", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Claw", + ["droppedId"] = 1082, + ["droppedReasons"] = { + "non-pet-claw-signature", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Claw", + ["droppedId"] = 1448, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Claw", + ["droppedId"] = 3029, + ["droppedReasons"] = { + "non-pet-claw-signature", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Claw", + ["droppedId"] = 3030, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Claw", + ["droppedId"] = 5201, + ["droppedReasons"] = { + "non-pet-claw-signature", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 3, + }, + { + ["ability"] = "Claw", + ["droppedId"] = 5203, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 3, + }, + { + ["ability"] = "Claw", + ["droppedId"] = 5204, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 4, + }, + { + ["ability"] = "Claw", + ["droppedId"] = 9849, + ["droppedReasons"] = { + "non-pet-claw-signature", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 4, + }, + { + ["ability"] = "Claw", + ["droppedId"] = 9850, + ["droppedReasons"] = { + "non-pet-claw-signature", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 5, + }, + { + ["ability"] = "Claw", + ["droppedId"] = 9851, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 5, + }, + { + ["ability"] = "Cower", + ["droppedId"] = 8999, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Cower", + ["droppedId"] = 8998, + ["keptId"] = 1742, + ["mode"] = "lower-score", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Cower", + ["droppedId"] = 9001, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Cower", + ["droppedId"] = 9000, + ["keptId"] = 1753, + ["mode"] = "lower-score", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Cower", + ["droppedId"] = 9893, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 3, + }, + { + ["ability"] = "Cower", + ["droppedId"] = 9892, + ["keptId"] = 1754, + ["mode"] = "lower-score", + ["rankNumber"] = 3, + }, + { + ["ability"] = "Dash", + ["droppedId"] = 1151, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Dash", + ["droppedId"] = 23099, + ["keptId"] = 1850, + ["mode"] = "lower-score", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Dash", + ["droppedId"] = 9822, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Dash", + ["droppedId"] = 23109, + ["keptId"] = 9821, + ["mode"] = "lower-score", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Grace", + ["droppedId"] = 46307, + ["droppedReasons"] = { + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Packleader", + ["droppedId"] = 36559, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Poison Spit", + ["droppedId"] = 46303, + ["droppedReasons"] = { + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Poison Spit", + ["droppedId"] = 51154, + ["droppedReasons"] = { + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Poison Spit", + ["droppedId"] = 51155, + ["droppedReasons"] = { + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 3, + }, + { + ["ability"] = "Prowl", + ["droppedId"] = 5216, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Prowl", + ["droppedId"] = 8152, + ["keptId"] = 24450, + ["mode"] = "lower-score", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Prowl", + ["droppedId"] = 5215, + ["keptId"] = 24450, + ["mode"] = "lower-score", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Prowl", + ["droppedId"] = 6784, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Prowl", + ["droppedId"] = 9914, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 3, + }, + { + ["ability"] = "Prowl", + ["droppedId"] = 9913, + ["keptId"] = 24453, + ["mode"] = "lower-score", + ["rankNumber"] = 3, + }, + { + ["ability"] = "Roar of Fortitude", + ["droppedId"] = 4732, + ["droppedReasons"] = { + "learn-spell-effect", + "teach-spell-description", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Roar of Fortitude", + ["droppedId"] = 4238, + ["keptId"] = 36535, + ["mode"] = "lower-score", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Roar of Fortitude", + ["droppedId"] = 36561, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Roar of Fortitude", + ["droppedId"] = 46276, + ["droppedReasons"] = { + "bad-or-missing-description", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Savage Rend", + ["droppedId"] = 46304, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Savage Rend", + ["droppedId"] = 46283, + ["keptId"] = 36537, + ["mode"] = "lower-score", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Savage Rend", + ["droppedId"] = 46284, + ["keptId"] = 36538, + ["mode"] = "lower-score", + ["rankNumber"] = 3, + }, + { + ["ability"] = "Savage Rend", + ["droppedId"] = 46285, + ["keptId"] = 36539, + ["mode"] = "lower-score", + ["rankNumber"] = 4, + }, + { + ["ability"] = "Savage Rend", + ["droppedId"] = 46286, + ["keptId"] = 36540, + ["mode"] = "lower-score", + ["rankNumber"] = 5, + }, + { + ["ability"] = "Scorpid Poison", + ["droppedId"] = 6411, + ["droppedReasons"] = { + "bad-or-missing-description", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Screech", + ["droppedId"] = 24581, + ["droppedReasons"] = { + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 3, + }, + { + ["ability"] = "Strider Presence", + ["droppedId"] = 4814, + ["droppedReasons"] = { + "vision-range", + "learn-spell-effect", + "teach-spell-description", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Strider Presence", + ["droppedId"] = 4312, + ["keptId"] = 36531, + ["mode"] = "lower-score", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Strider Presence", + ["droppedId"] = 36558, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Thunderstomp", + ["droppedId"] = 51157, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 4, + }, + { + ["ability"] = "Web", + ["droppedId"] = 46310, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Web", + ["droppedId"] = 12023, + ["keptId"] = 4167, + ["mode"] = "lower-score", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Web", + ["droppedId"] = 36533, + ["keptId"] = 4167, + ["mode"] = "lower-score", + ["rankNumber"] = 0, + }, + { + ["ability"] = "Web", + ["droppedId"] = 4782, + ["droppedReasons"] = { + "vision-range", + "learn-spell-effect", + "teach-spell-description", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Web", + ["droppedId"] = 36560, + ["droppedReasons"] = { + "bad-or-missing-description", + "vision-range", + "learn-spell-effect", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 1, + }, + { + ["ability"] = "Web", + ["droppedId"] = 6027, + ["droppedReasons"] = { + "vision-range", + "teach-spell-description", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 2, + }, + { + ["ability"] = "Web", + ["droppedId"] = 6028, + ["droppedReasons"] = { + "vision-range", + "teach-spell-description", + }, + ["mode"] = "hard-reject", + ["rankNumber"] = 3, + }, + }, + ["droppedDuplicatesCount"] = 63, + ["generatedAt"] = "2026-02-15 20:10:11", + ["source"] = "https://database.turtlecraft.gg/", + ["totalSpellRows"] = 88, +} diff --git a/data/ds-racial.lua b/data/ds-racial.lua new file mode 100644 index 0000000..8e9d032 --- /dev/null +++ b/data/ds-racial.lua @@ -0,0 +1,70 @@ +if not MTH_DS_Racial then + MTH_DS_Racial = {} +end + +MTH_DS_Racial = { + version = "1.0", + generatedAtUTC = "2026-02-20T00:00:00Z", + source = { + site = "https://database.turtlecraft.gg", + runRoot = ".TurtleDBparsing/racial-audit/runs/20260220-164554Z", + selection = "user-curated-approved", + }, + stats = { + raceCount = 9, + spellCount = 41, + }, + byRace = { + ["Tauren"] = { 20549, 20550, 20552, 20551 }, + ["Orc"] = { 20572, 20573, 20574, 20575, 20576, 21563 }, + ["Troll"] = { 20554, 26296, 26297, 20555, 20557, 20558, 26290 }, + ["Undead"] = { 7744, 20577, 20579, 5227, 52522 }, + ["Night Elf"] = { 20580, 21009, 20582, 20585, 20583 }, + ["Gnome"] = { 20589, 20591, 20592, 20593, 7340 }, + ["Dwarf"] = { 2481, 20596, 20595, 20594 }, + ["Human"] = { 20599, 20600, 20597, 20864, 20598 }, + }, + spellsById = { + [2481] = { id = 2481, name = "Find Treasure", rank = "Racial", level = 1, school = 0, race = "Dwarf", sourceUrl = "https://database.turtlecraft.gg/?spell=2481" }, + [5227] = { id = 5227, name = "Underwater Breathing", rank = "Racial Passive", level = 0, school = 0, race = "Undead", sourceUrl = "https://database.turtlecraft.gg/?spell=5227" }, + [7340] = { id = 7340, name = "Language Gnomish", rank = "", level = 1, school = 0, race = "Gnome", sourceUrl = "https://database.turtlecraft.gg/?spell=7340" }, + [7744] = { id = 7744, name = "Will of the Forsaken", rank = "Racial", level = 0, school = 0, race = "Undead", sourceUrl = "https://database.turtlecraft.gg/?spell=7744" }, + [20549] = { id = 20549, name = "War Stomp", rank = "Racial", level = 0, school = 0, race = "Tauren", sourceUrl = "https://database.turtlecraft.gg/?spell=20549" }, + [20550] = { id = 20550, name = "Endurance", rank = "Racial Passive", level = 0, school = 0, race = "Tauren", sourceUrl = "https://database.turtlecraft.gg/?spell=20550" }, + [20551] = { id = 20551, name = "Nature Resistance", rank = "Racial Passive", level = 0, school = 0, race = "Tauren", raceGuess = true, sourceUrl = "https://database.turtlecraft.gg/?spell=20551" }, + [20552] = { id = 20552, name = "Cultivation", rank = "Racial Passive", level = 0, school = 0, race = "Tauren", sourceUrl = "https://database.turtlecraft.gg/?spell=20552" }, + [20554] = { id = 20554, name = "Berserking", rank = "Racial", level = 0, school = 0, race = "Troll", sourceUrl = "https://database.turtlecraft.gg/?spell=20554" }, + [20555] = { id = 20555, name = "Regeneration", rank = "Racial Passive", level = 0, school = 0, race = "Troll", sourceUrl = "https://database.turtlecraft.gg/?spell=20555" }, + [20557] = { id = 20557, name = "Beast Slaying", rank = "Racial Passive", level = 0, school = 0, race = "Troll", sourceUrl = "https://database.turtlecraft.gg/?spell=20557" }, + [20558] = { id = 20558, name = "Throwing Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Troll", sourceUrl = "https://database.turtlecraft.gg/?spell=20558" }, + [20572] = { id = 20572, name = "Blood Fury", rank = "Racial", level = 0, school = 0, race = "Orc", sourceUrl = "https://database.turtlecraft.gg/?spell=20572" }, + [20573] = { id = 20573, name = "Hardiness", rank = "Racial Passive", level = 0, school = 0, race = "Orc", sourceUrl = "https://database.turtlecraft.gg/?spell=20573" }, + [20574] = { id = 20574, name = "Axe Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Orc", sourceUrl = "https://database.turtlecraft.gg/?spell=20574" }, + [20575] = { id = 20575, name = "Command", rank = "Racial Passive", level = 0, school = 0, race = "Orc", sourceUrl = "https://database.turtlecraft.gg/?spell=20575" }, + [20576] = { id = 20576, name = "Command", rank = "Racial Passive", level = 0, school = 0, race = "Orc", sourceUrl = "https://database.turtlecraft.gg/?spell=20576" }, + [20577] = { id = 20577, name = "Cannibalize", rank = "Racial", level = 0, school = 0, race = "Undead", sourceUrl = "https://database.turtlecraft.gg/?spell=20577" }, + [20579] = { id = 20579, name = "Shadow Resistance", rank = "Racial Passive", level = 0, school = 0, race = "Undead", sourceUrl = "https://database.turtlecraft.gg/?spell=20579" }, + [20580] = { id = 20580, name = "Shadowmeld", rank = "Racial", level = 1, school = 0, race = "Night Elf", sourceUrl = "https://database.turtlecraft.gg/?spell=20580" }, + [20582] = { id = 20582, name = "Quickness", rank = "Racial Passive", level = 0, school = 0, race = "Night Elf", sourceUrl = "https://database.turtlecraft.gg/?spell=20582" }, + [20583] = { id = 20583, name = "Nature Resistance", rank = "Racial Passive", level = 0, school = 0, race = "Night Elf", raceGuess = true, sourceUrl = "https://database.turtlecraft.gg/?spell=20583" }, + [20585] = { id = 20585, name = "Wisp Spirit", rank = "Racial Passive", level = 0, school = 0, race = "Night Elf", sourceUrl = "https://database.turtlecraft.gg/?spell=20585" }, + [20589] = { id = 20589, name = "Escape Artist", rank = "Racial", level = 0, school = 0, race = "Gnome", sourceUrl = "https://database.turtlecraft.gg/?spell=20589" }, + [20591] = { id = 20591, name = "Expansive Mind", rank = "Racial Passive", level = 0, school = 0, race = "Gnome", sourceUrl = "https://database.turtlecraft.gg/?spell=20591" }, + [20592] = { id = 20592, name = "Arcane Resistance", rank = "Racial Passive", level = 0, school = 0, race = "Gnome", sourceUrl = "https://database.turtlecraft.gg/?spell=20592" }, + [20593] = { id = 20593, name = "Engineering Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Gnome", sourceUrl = "https://database.turtlecraft.gg/?spell=20593" }, + [20594] = { id = 20594, name = "Stoneform", rank = "Racial", level = 0, school = 0, race = "Dwarf", sourceUrl = "https://database.turtlecraft.gg/?spell=20594" }, + [20595] = { id = 20595, name = "Gun Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Dwarf", sourceUrl = "https://database.turtlecraft.gg/?spell=20595" }, + [20596] = { id = 20596, name = "Frost Resistance", rank = "Racial Passive", level = 0, school = 0, race = "Dwarf", sourceUrl = "https://database.turtlecraft.gg/?spell=20596" }, + [20597] = { id = 20597, name = "Sword Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Human", sourceUrl = "https://database.turtlecraft.gg/?spell=20597" }, + [20598] = { id = 20598, name = "The Human Spirit", rank = "Racial Passive", level = 0, school = 0, race = "Human", sourceUrl = "https://database.turtlecraft.gg/?spell=20598" }, + [20599] = { id = 20599, name = "Diplomacy", rank = "Racial Passive", level = 0, school = 0, race = "Human", sourceUrl = "https://database.turtlecraft.gg/?spell=20599" }, + [20600] = { id = 20600, name = "Perception", rank = "Racial", level = 0, school = 0, race = "Human", sourceUrl = "https://database.turtlecraft.gg/?spell=20600" }, + [20864] = { id = 20864, name = "Mace Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Human", sourceUrl = "https://database.turtlecraft.gg/?spell=20864" }, + [21009] = { id = 21009, name = "Shadowmeld Passive", rank = "Racial Passive", level = 0, school = 0, race = "Night Elf", sourceUrl = "https://database.turtlecraft.gg/?spell=21009" }, + [21563] = { id = 21563, name = "Command", rank = "Racial Passive", level = 0, school = 0, race = "Orc", sourceUrl = "https://database.turtlecraft.gg/?spell=21563" }, + [26290] = { id = 26290, name = "Bow Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Troll", sourceUrl = "https://database.turtlecraft.gg/?spell=26290" }, + [26296] = { id = 26296, name = "Berserking", rank = "Racial", level = 0, school = 0, race = "Troll", sourceUrl = "https://database.turtlecraft.gg/?spell=26296" }, + [26297] = { id = 26297, name = "Berserking", rank = "Racial", level = 0, school = 0, race = "Troll", sourceUrl = "https://database.turtlecraft.gg/?spell=26297" }, + [52522] = { id = 52522, name = "Vengeance", rank = "Racial Passive", level = 0, school = 0, race = "Undead", sourceUrl = "https://database.turtlecraft.gg/?spell=52522" }, + }, +} \ No newline at end of file diff --git a/data/ds-unit-coords.lua b/data/ds-unit-coords.lua new file mode 100644 index 0000000..819a6de --- /dev/null +++ b/data/ds-unit-coords.lua @@ -0,0 +1,25145 @@ +-- MetaHunt Unit Coordinate Fallback (generated from pfQuest + pfQuest-turtle) +-- Scope: all unit IDs referenced by MetaHunt beasts, vendors, and item drop creatures +if not MTH_DS then MTH_DS = {} end + +MTH_DS_UnitCoords = { + [30] = { + {39.5, 78.7, 12}, + {39, 76.8, 12}, + {43.9, 76.7, 12}, + {40.8, 75.1, 12}, + {33.5, 73.8, 12}, + {38.8, 73.3, 12}, + {38.2, 72.8, 12}, + {41.9, 72.2, 12}, + {44.7, 71, 12}, + {38.1, 69.7, 12}, + {35.8, 69.1, 12}, + {31.9, 68.5, 12}, + {28.5, 66.7, 12}, + {36, 62.7, 12}, + {38.5, 62.4, 12}, + {31.4, 57, 12}, + {36.5, 55.9, 12}, + {35.2, 87, 12}, + {42, 81.6, 12}, + {34.3, 79.4, 12}, + {46.5, 72, 12}, + {44.6, 71.2, 12}, + {38, 70, 12}, + {34.1, 69.9, 12}, + {50.3, 59.9, 12}, + {37.5, 58.9, 12}, + {42.1, 56.4, 12}, + {36.4, 55.9, 12}, + }, + [43] = { + {60, 48.7, 12}, + {62, 47.9, 12}, + {60.2, 47.6, 12}, + {61.7, 47.5, 12}, + {61.1, 47.3, 12}, + {61.9, 47.1, 12}, + {61.6, 46.9, 12}, + {60.5, 46.9, 12}, + {61.8, 47.5, 12}, + }, + [69] = { + {49.5, 49.3, 12}, + {49.3, 47.5, 12}, + {49, 47, 12}, + {49.9, 46.6, 12}, + {49.2, 45.4, 12}, + {51.4, 45.2, 12}, + {50.5, 45.1, 12}, + {51, 44.4, 12}, + {50.8, 44.1, 12}, + {51.5, 43.6, 12}, + {51.9, 42.9, 12}, + {52.4, 42.1, 12}, + {51.5, 42.1, 12}, + {51.9, 41.5, 12}, + {51.2, 40.4, 12}, + {45.9, 40.3, 12}, + {52.9, 39.9, 12}, + {46.8, 39.4, 12}, + {52.5, 39.1, 12}, + {53.1, 38.7, 12}, + {52.7, 38.4, 12}, + {49.5, 37.9, 12}, + {50.2, 37.2, 12}, + {48.1, 37.2, 12}, + {46.2, 36.7, 12}, + {46, 36.4, 12}, + {50.9, 35.7, 12}, + {46.1, 35.5, 12}, + {45.2, 35.2, 12}, + {52.7, 35.1, 12}, + {46.7, 35, 12}, + {45.7, 35, 12}, + {48.1, 34.9, 12}, + {46.3, 34.4, 12}, + {47.1, 34.3, 12}, + {48.5, 33.7, 12}, + {51.4, 45.6, 12}, + {50.4, 44.9, 12}, + {50.9, 44.1, 12}, + {51.5, 43.7, 12}, + {51.7, 43.1, 12}, + {51.6, 42.4, 12}, + {52.5, 42.2, 12}, + {51.9, 41.6, 12}, + {46.7, 39.4, 12}, + {49.4, 37.8, 12}, + {50.2, 37.4, 12}, + {48.2, 37.4, 12}, + {46.6, 35.1, 12}, + {47, 34.5, 12}, + }, + [113] = { + {40.9, 91.9, 12}, + {36.2, 91.3, 12}, + {39.6, 90.8, 12}, + {37.8, 90.7, 12}, + {38.8, 90.1, 12}, + {37.4, 89.9, 12}, + {40.8, 89.9, 12}, + {35.2, 89.8, 12}, + {33.3, 89.7, 12}, + {33.8, 89.4, 12}, + {38.1, 89.2, 12}, + {37, 88.6, 12}, + {39.7, 88.5, 12}, + {41.8, 88.2, 12}, + {41, 88.2, 12}, + {35.3, 87.8, 12}, + {42.8, 86.9, 12}, + {40.9, 86.8, 12}, + {41.9, 86.8, 12}, + {32, 86.6, 12}, + {33.4, 86.6, 12}, + {38.6, 86.2, 12}, + {31.5, 86, 12}, + {31.9, 85.9, 12}, + {41.6, 85.8, 12}, + {40.9, 85.4, 12}, + {33.2, 85.4, 12}, + {30.9, 84.1, 12}, + {32.2, 83.9, 12}, + {31.3, 82.6, 12}, + {32.4, 81.4, 12}, + {34.1, 81, 12}, + {31.1, 81, 12}, + {33, 80.2, 12}, + {31.7, 80.1, 12}, + {38.8, 78.6, 12}, + {41.7, 77.7, 12}, + {37.1, 76.8, 12}, + {38.1, 75.4, 12}, + {41, 75.3, 12}, + {41.8, 75.3, 12}, + {42.6, 73.2, 12}, + {43.7, 72.3, 12}, + {42.7, 71.5, 12}, + {37.6, 70.8, 12}, + {40.6, 70.7, 12}, + {39.9, 69.7, 12}, + {40.9, 69.5, 12}, + {41.8, 69.5, 12}, + {44.1, 69.4, 12}, + {38.8, 68.7, 12}, + {39, 76.8, 12}, + {41, 88.3, 12}, + {41.8, 88.1, 12}, + {41, 86.8, 12}, + {42.9, 86.6, 12}, + {41.8, 86.5, 12}, + {33.4, 85.1, 12}, + {41, 85, 12}, + {34, 81.3, 12}, + }, + [118] = { + {83, 85.5, 12}, + {85.6, 85.5, 12}, + {83.6, 84.1, 12}, + {82.2, 84, 12}, + {85.5, 83.9, 12}, + {80.2, 83.2, 12}, + {85, 82.9, 12}, + {79.2, 82.8, 12}, + {81.3, 82.5, 12}, + {87.4, 81.8, 12}, + {80.1, 81.8, 12}, + {80.4, 81.7, 12}, + {90.4, 81.7, 12}, + {87.6, 81.3, 12}, + {80.8, 81.2, 12}, + {80.3, 81.1, 12}, + {80.6, 80.2, 12}, + {79.3, 79.7, 12}, + {91.2, 79.4, 12}, + {87.1, 79.4, 12}, + {80.6, 78.8, 12}, + {87.9, 78.3, 12}, + {80.3, 78.2, 12}, + {80.3, 78.1, 12}, + {77.9, 77.9, 12}, + {81.5, 77.9, 12}, + {76.2, 77.9, 12}, + {82.1, 77.6, 12}, + {87.1, 77.4, 12}, + {88.8, 77.4, 12}, + {90.3, 77.4, 12}, + {77.3, 76.5, 12}, + {79.1, 76.3, 12}, + {75.6, 76, 12}, + {76.2, 76, 12}, + {89.6, 75, 12}, + {77.4, 74.5, 12}, + {75.5, 74.3, 12}, + {85.9, 72.3, 12}, + {88, 72.1, 12}, + {83.8, 72, 12}, + {88.4, 70.9, 12}, + {87.1, 69.2, 12}, + {87.3, 68.8, 12}, + {87.4, 68.5, 12}, + {88.7, 68.2, 12}, + {86.9, 66.1, 12}, + {72.3, 65.8, 12}, + {71.8, 65.3, 12}, + {72.4, 65.2, 12}, + {72.3, 64.8, 12}, + {86.4, 64.2, 12}, + {86.4, 63.8, 12}, + {87.8, 63.8, 12}, + {76.2, 63.3, 12}, + {78.4, 62.7, 12}, + {85, 62.4, 12}, + {79.3, 62.3, 12}, + {85.1, 62, 12}, + {77.7, 61.7, 12}, + {85.9, 61.3, 12}, + {84.6, 61.2, 12}, + {80.2, 60.9, 12}, + {82.1, 60.8, 12}, + {78, 60.7, 12}, + {83.2, 59.6, 12}, + {66.6, 45.2, 12}, + {65.4, 43.3, 12}, + {70.5, 41.9, 12}, + {77.5, 40.9, 12}, + {78.9, 40.9, 12}, + {69.9, 40.8, 12}, + {76.6, 40.6, 12}, + {72.1, 40.5, 12}, + {73.7, 40.5, 12}, + {75.2, 40.4, 12}, + {73.6, 40, 12}, + {75.2, 39.7, 12}, + {70.3, 39.6, 12}, + {73.6, 38.1, 12}, + {74.2, 37.7, 12}, + {71.4, 37.5, 12}, + {75.4, 37.3, 12}, + {2.8, 83.1, 44}, + {75.5, 74.2, 12}, + {88.7, 68.6, 12}, + {79.3, 62.2, 12}, + {85, 62, 12}, + {80, 60.7, 12}, + {81.9, 60.7, 12}, + {83.2, 59.7, 12}, + }, + [119] = { + {24.8, 87.2, 12}, + {25.2, 86.1, 12}, + {24.4, 83.7, 12}, + {25.6, 82.5, 12}, + {25.6, 79.9, 12}, + {66.9, 23.9, 40}, + {67.3, 22.8, 40}, + {24.6, 88.5, 12}, + {24.7, 87, 12}, + {25, 85.8, 12}, + {25, 84.8, 12}, + {24.3, 83.7, 12}, + {24.3, 82.7, 12}, + {26.4, 82.3, 12}, + {25.5, 82.2, 12}, + {25.7, 79.8, 12}, + {66.7, 25.2, 40}, + {66.8, 23.7, 40}, + {67.1, 22.5, 40}, + }, + [151] = { + {44, 65.9, 12}, + }, + [152] = { + {47.5, 41.6, 12}, + }, + [154] = { + {58.8, 64.2, 40}, + {59.4, 62.7, 40}, + {62.8, 60.7, 40}, + {58.7, 60.4, 40}, + {59.1, 59.3, 40}, + {63.9, 55.1, 40}, + {61.1, 54.8, 40}, + {59.4, 52.3, 40}, + {62.7, 50.6, 40}, + {61.9, 49.4, 40}, + {26.6, 44.1, 40}, + {26.4, 42.6, 40}, + {27.1, 42.4, 40}, + {62.6, 60.6, 40}, + {61.9, 49.2, 40}, + }, + [157] = { + {35.7, 80.8, 40}, + {34.7, 79.4, 40}, + {31.7, 78.1, 40}, + {34.3, 77.8, 40}, + {31.7, 75.9, 40}, + {37.3, 72, 40}, + {35.2, 69.3, 40}, + {38.1, 66.3, 40}, + {53.9, 64.9, 40}, + {42.3, 64.1, 40}, + {56.1, 63.7, 40}, + {37.2, 63.7, 40}, + {47.2, 62.2, 40}, + {43.7, 62.2, 40}, + {45.3, 62.1, 40}, + {39.4, 60.9, 40}, + {51.4, 60.6, 40}, + {37.2, 60.2, 40}, + {57.1, 59.2, 40}, + {44.6, 59.1, 40}, + {54.1, 59, 40}, + {51.2, 59, 40}, + {35.4, 58, 40}, + {41.3, 57.6, 40}, + {49.5, 57.5, 40}, + {45.6, 57.4, 40}, + {44.7, 56.9, 40}, + {46.4, 56, 40}, + {45.6, 55.4, 40}, + {48.5, 52.4, 40}, + {43.8, 52.2, 40}, + {46.3, 51.8, 40}, + {45.1, 50.5, 40}, + {44.8, 49.2, 40}, + {43, 48.9, 40}, + {60.9, 47.7, 40}, + {60.1, 45.5, 40}, + {44.4, 45.1, 40}, + {61.3, 44.9, 40}, + {40.9, 44.8, 40}, + {59.6, 44.5, 40}, + {59, 43.6, 40}, + {42.9, 43.6, 40}, + {38.1, 42.5, 40}, + {59.8, 42.5, 40}, + {37.8, 41, 40}, + {40.8, 39.8, 40}, + {44, 39.6, 40}, + {41.9, 38.8, 40}, + {33.6, 37.9, 40}, + {35.4, 36.6, 40}, + {41.2, 33.2, 40}, + {33.7, 31, 40}, + {39.1, 27.9, 40}, + {48.3, 52.6, 40}, + {43.9, 52.2, 40}, + {61.3, 45, 40}, + {59.7, 44.6, 40}, + {59.1, 43.6, 40}, + {5.7, 76.8, 1581}, + {17.8, 8, 1581}, + }, + [199] = { + {16.8, 76.8, 12}, + {53.7, 43.9, 40}, + {51.4, 42.2, 40}, + {61.3, 40.2, 40}, + {48.4, 40, 40}, + {60, 39.8, 40}, + {54.2, 39.8, 40}, + {57.1, 39.7, 40}, + {49.4, 37.2, 40}, + {48.9, 35.9, 40}, + {52.7, 35.7, 40}, + {62, 35.1, 40}, + {43, 33.7, 40}, + {59.8, 32.3, 40}, + {38.1, 32.1, 40}, + {48.4, 30.2, 40}, + {61, 29.3, 40}, + {46, 29, 40}, + {60.9, 28, 40}, + {62.8, 27.8, 40}, + {48.6, 27.8, 40}, + {51.9, 27.7, 40}, + {36.3, 26.5, 40}, + {42.9, 25.1, 40}, + {49.5, 24.7, 40}, + {56.2, 24.6, 40}, + {60.1, 24.2, 40}, + {44.6, 20.1, 40}, + {38.2, 19.9, 40}, + {43.6, 18.8, 40}, + {52.4, 18.7, 40}, + {47.8, 18.2, 40}, + {55.2, 17.6, 40}, + {50.5, 16.5, 40}, + {41.9, 16.5, 40}, + {59, 13.6, 40}, + {16.6, 77.1, 12}, + {61.8, 35.3, 40}, + {48.8, 30.9, 40}, + {45.8, 29.4, 40}, + {48.4, 27.8, 40}, + {52.4, 27.7, 40}, + {50.6, 16.3, 40}, + {58.8, 13.9, 40}, + }, + [213] = { + {12.6, 66.8, 10}, + {10.7, 47.9, 10}, + {10.6, 29.6, 10}, + {22, 28.6, 10}, + {29.1, 28.3, 10}, + {12.9, 28.3, 10}, + {23.4, 28, 10}, + {14.1, 27.7, 10}, + {31.6, 27.2, 10}, + {33.6, 26.7, 10}, + {27.3, 26.3, 10}, + {27.8, 26.1, 10}, + {25.3, 25.9, 10}, + {22.8, 25.9, 10}, + {30.5, 25.6, 10}, + {35.6, 24.5, 10}, + {33.6, 24.5, 10}, + {31.5, 24.4, 10}, + {19.2, 24.1, 10}, + {17.4, 23.3, 10}, + {16.5, 22.9, 10}, + {32.4, 22.6, 10}, + {36.7, 21.6, 10}, + {35.3, 21.5, 10}, + {35.8, 20.9, 10}, + {41.3, 20.5, 10}, + {43.9, 20.4, 10}, + {39, 19.6, 10}, + {37.7, 19.2, 10}, + {77.1, 19.1, 10}, + {80.4, 18.7, 10}, + {40.1, 18.7, 10}, + {42.5, 18.7, 10}, + {45, 18.5, 10}, + {63.9, 17.1, 10}, + {58.8, 16.9, 10}, + {46.5, 16.8, 10}, + {86.2, 16.1, 10}, + {57.8, 15.6, 10}, + {28.4, 99.8, 12}, + {31.2, 98.3, 12}, + {41.5, 97.3, 12}, + {41.9, 97, 12}, + {45.4, 94.4, 12}, + {48.1, 93.1, 12}, + {51.5, 91.3, 12}, + {87.3, 89.3, 12}, + {10.8, 29.5, 10}, + {22, 29, 10}, + {12.6, 28.3, 10}, + {28.2, 26.1, 10}, + {25.4, 26, 10}, + {22.6, 25.9, 10}, + {19.3, 24, 10}, + {40, 19.1, 10}, + {42.1, 97.1, 12}, + }, + [217] = { + {15.7, 72.6, 10}, + {15.3, 71.3, 10}, + {15, 68.1, 10}, + {17.3, 67.6, 10}, + {14.5, 67.5, 10}, + {15.4, 66.7, 10}, + {10.6, 55.5, 10}, + {11.2, 54.8, 10}, + {13.1, 47.5, 10}, + {10.8, 46.6, 10}, + {12.1, 46, 10}, + {10.5, 44.7, 10}, + {9.3, 40, 10}, + {10.7, 38, 10}, + {22.1, 28.2, 10}, + {18.9, 27.3, 10}, + {31.9, 26.6, 10}, + {16.9, 25.8, 10}, + {78, 24.3, 10}, + {70.8, 21.6, 10}, + {41.8, 20.8, 10}, + {66.7, 20.8, 10}, + {68.6, 20.2, 10}, + {73.8, 20, 10}, + {76, 19.9, 10}, + {66.6, 18.8, 10}, + {60.2, 18.6, 10}, + {57.5, 18.6, 10}, + {62.5, 18.5, 10}, + {49.8, 18.3, 10}, + {66.2, 18.1, 10}, + {47.6, 17.9, 10}, + {52.2, 16.9, 10}, + {54.2, 16.7, 10}, + {56.3, 14.6, 10}, + {15.7, 66.8, 10}, + }, + [227] = { + {73.6, 45.3, 10}, + }, + [228] = { + {73, 44.4, 10}, + }, + [299] = { + {42.7, 62.8, 12}, + {42.6, 62.2, 12}, + {42.7, 60.9, 12}, + {44.5, 58.3, 12}, + {41.5, 58.2, 12}, + {44.8, 57.5, 12}, + {45.4, 55.4, 12}, + {42.9, 55.4, 12}, + {47.5, 46.9, 12}, + {48.3, 46.8, 12}, + {48, 45.9, 12}, + {49.4, 45.3, 12}, + {48.5, 45.2, 12}, + {46.2, 45.1, 12}, + {45.8, 45, 12}, + {46.6, 44.6, 12}, + {50.9, 44.4, 12}, + {45.6, 43.8, 12}, + {46.7, 43.7, 12}, + {45.7, 43.1, 12}, + {45.4, 42.4, 12}, + {46.2, 42.4, 12}, + {45.8, 42.3, 12}, + {51.9, 41.4, 12}, + {46.7, 40.9, 12}, + {45.9, 40.8, 12}, + {45.7, 40.6, 12}, + {47.7, 40.2, 12}, + {46.2, 40.1, 12}, + {46.8, 39.8, 12}, + {47.3, 39.6, 12}, + {48.1, 39.6, 12}, + {45.5, 39.1, 12}, + {48.1, 38.8, 12}, + {46.2, 38.6, 12}, + {47.1, 38.6, 12}, + {49.1, 38.3, 12}, + {45.9, 38.2, 12}, + {46.6, 37.9, 12}, + {47, 37.9, 12}, + {49.5, 37.9, 12}, + {47.6, 37.8, 12}, + {46.9, 37.5, 12}, + {48.4, 37.4, 12}, + {46.2, 37.2, 12}, + {42.6, 61, 12}, + {41.6, 58.3, 12}, + {48.7, 45.5, 12}, + {46.1, 44.9, 12}, + {45.8, 44.6, 12}, + {46.8, 44.5, 12}, + {45.3, 42.6, 12}, + {46.4, 42.3, 12}, + {52.1, 41.6, 12}, + {47.5, 40.5, 12}, + {46.3, 40.1, 12}, + {48, 39.6, 12}, + {47.3, 39.5, 12}, + {46.2, 38.4, 12}, + {47.1, 38.3, 12}, + {48.8, 38.3, 12}, + {46.5, 38.1, 12}, + {46.9, 38, 12}, + {47.4, 37.9, 12}, + {49.4, 37.6, 12}, + {46.3, 37.1, 12}, + }, + [330] = { + {69.7, 79.6, 12}, + {69.7, 79.3, 12}, + }, + [345] = { + {16, 49.3, 44}, + {15.7, 49.3, 44}, + }, + [390] = { + {69.7, 79.4, 12}, + {69.6, 79.1, 12}, + {69.7, 79.2, 12}, + }, + [428] = { + {60.8, 78.3, 44}, + {46, 77.5, 44}, + {55.3, 76.8, 44}, + {63.9, 76.4, 44}, + {61.5, 76.4, 44}, + {57.1, 76.2, 44}, + {59.8, 76.1, 44}, + {50.6, 74.9, 44}, + {55.3, 73.4, 44}, + {35.2, 70.5, 44}, + {55.2, 46.1, 44}, + {57.3, 44.9, 44}, + {57.2, 41.7, 44}, + {54.5, 39.8, 44}, + {52.6, 39, 44}, + {45.8, 35.8, 44}, + {23.9, 35.2, 44}, + {48.6, 35, 44}, + {49.7, 33.8, 44}, + {47.3, 32.5, 44}, + {42.2, 32.4, 44}, + {64.1, 76.4, 44}, + {50.7, 74.7, 44}, + {35.5, 70.2, 44}, + {57.1, 41.6, 44}, + {54.3, 40.3, 44}, + {52.4, 38.8, 44}, + {50.1, 33.9, 44}, + }, + [442] = { + {98.2, 11.6, 10}, + {95.1, 80.7, 12}, + {96.2, 77.9, 12}, + {95, 75.6, 12}, + {95.7, 75.5, 12}, + {11.4, 93.3, 44}, + {13.3, 93.1, 44}, + {10, 86.4, 44}, + {12.9, 85.2, 44}, + {9, 85.1, 44}, + {13.8, 82.1, 44}, + {10.7, 80.6, 44}, + {11.2, 79.6, 44}, + {12.6, 78.2, 44}, + {8.8, 77, 44}, + {12.4, 77, 44}, + {9.9, 76.8, 44}, + {26.6, 75.8, 44}, + {22.9, 75.7, 44}, + {14.1, 75.7, 44}, + {35.2, 75.2, 44}, + {11.7, 72.7, 44}, + {19.4, 72.3, 44}, + {18.1, 71.7, 44}, + {22.2, 67.2, 44}, + {22.4, 66.4, 44}, + {20.1, 65.4, 44}, + {95.6, 79.4, 12}, + {95.6, 75.4, 12}, + {9.8, 83, 44}, + {9.8, 76.7, 44}, + {22.8, 76.3, 44}, + {35, 75.1, 44}, + {19.5, 71.7, 44}, + }, + [454] = { + {20.8, 86.9, 12}, + {16.7, 76.9, 12}, + {51.9, 49, 40}, + {53, 46.2, 40}, + {53.7, 45, 40}, + {52.3, 44.9, 40}, + {55.2, 43.8, 40}, + {54.3, 42.4, 40}, + {56.2, 42.3, 40}, + {49.5, 42.2, 40}, + {49.2, 41.6, 40}, + {52.1, 41.2, 40}, + {53.9, 40.9, 40}, + {52.7, 39.9, 40}, + {57.1, 39.2, 40}, + {63.2, 38.2, 40}, + {49.9, 36.8, 40}, + {61, 36.5, 40}, + {36.2, 35, 40}, + {34.3, 34.8, 40}, + {33.5, 34.5, 40}, + {39.4, 33.8, 40}, + {60, 33.6, 40}, + {60.3, 32.3, 40}, + {43.8, 32.2, 40}, + {37.4, 30.7, 40}, + {44.3, 30.5, 40}, + {40.9, 30.1, 40}, + {60.6, 30, 40}, + {50.4, 29.4, 40}, + {42.8, 28.2, 40}, + {61.9, 27.8, 40}, + {54.2, 26.4, 40}, + {37.8, 25.9, 40}, + {42.2, 23.7, 40}, + {51.4, 23.7, 40}, + {62.9, 23.6, 40}, + {49.7, 23.5, 40}, + {56.1, 20.8, 40}, + {51.1, 20.7, 40}, + {52.1, 20.5, 40}, + {55.2, 19.4, 40}, + {46.6, 19.3, 40}, + {48.6, 19.2, 40}, + {52.3, 17.8, 40}, + {56.9, 16.1, 40}, + {58.9, 13.7, 40}, + {58.4, 12.4, 40}, + {55.2, 43.7, 40}, + {56.2, 42.5, 40}, + {54.2, 42.2, 40}, + {61.2, 36.5, 40}, + {54.2, 26.7, 40}, + {51.3, 23.3, 40}, + {51.2, 20.2, 40}, + {55.3, 19.5, 40}, + {48.7, 19.1, 40}, + {52.4, 17.5, 40}, + }, + [462] = { + {62, 75.5, 40}, + {62.6, 74.2, 40}, + {61.5, 73.9, 40}, + {36.8, 62.5, 40}, + {40.6, 60.5, 40}, + {45.7, 60.1, 40}, + {65.5, 58.7, 40}, + {63.9, 58.2, 40}, + {46.1, 57.7, 40}, + {63.3, 57, 40}, + {44.1, 46.2, 40}, + {46.8, 44.2, 40}, + {45.4, 43.9, 40}, + {50.6, 43.5, 40}, + {51.1, 23.8, 40}, + {54.8, 23.2, 40}, + }, + [471] = { + {62.1, 48.1, 12}, + }, + [491] = { + {57, 47.2, 40}, + }, + [505] = { + {67.2, 76.6, 44}, + {56.9, 46.2, 44}, + {54, 43.3, 44}, + {55.1, 43.2, 44}, + {56.3, 43, 44}, + {52.5, 42, 44}, + {51.9, 41.2, 44}, + {50.6, 40.6, 44}, + {52.9, 39, 44}, + {47.8, 38.5, 44}, + {53.9, 37.9, 44}, + {52.7, 37.4, 44}, + {53.7, 36.4, 44}, + {57.1, 46.6, 44}, + {53.8, 43.6, 44}, + {55.9, 43.4, 44}, + {52.4, 42.1, 44}, + {53.8, 38.2, 44}, + {52.5, 37.6, 44}, + }, + [524] = { + {45, 89.6, 12}, + {45.6, 88, 12}, + {44.6, 87.2, 12}, + {54.9, 85.9, 12}, + {53.6, 85.4, 12}, + {46.6, 85.1, 12}, + {52.7, 84.4, 12}, + {56.4, 83.9, 12}, + {44.5, 83.8, 12}, + {54.4, 83.7, 12}, + {71.4, 83.6, 12}, + {45.9, 82.9, 12}, + {72.9, 82.7, 12}, + {70.7, 82.6, 12}, + {66.9, 82.5, 12}, + {57.2, 82.4, 12}, + {53.2, 82.4, 12}, + {43, 82, 12}, + {74.4, 81.9, 12}, + {52.6, 81.5, 12}, + {60.5, 81.4, 12}, + {65.7, 81.3, 12}, + {46.6, 81, 12}, + {44, 81, 12}, + {48.3, 80.9, 12}, + {54.7, 80.9, 12}, + {73.5, 80.8, 12}, + {58, 80.8, 12}, + {64, 80.6, 12}, + {46.4, 80.2, 12}, + {74.4, 80.1, 12}, + {53.4, 79.9, 12}, + {66.9, 79.9, 12}, + {54.9, 79.9, 12}, + {62.9, 79.8, 12}, + {60.6, 79.7, 12}, + {72.6, 79.7, 12}, + {50.1, 79.7, 12}, + {61, 79.7, 12}, + {64.6, 79.6, 12}, + {47.2, 79.4, 12}, + {44.1, 79.3, 12}, + {44.5, 79.2, 12}, + {50.1, 78.7, 12}, + {47.9, 78.6, 12}, + {46.6, 78.6, 12}, + {73.8, 78.5, 12}, + {64.1, 78.4, 12}, + {61.7, 78.3, 12}, + {61.6, 78.3, 12}, + {60.3, 78.2, 12}, + {60.2, 78.1, 12}, + {52.1, 78, 12}, + {54.2, 77.9, 12}, + {43.3, 77.9, 12}, + {65.8, 77.7, 12}, + {61, 77.2, 12}, + {67.3, 76.9, 12}, + {65, 76.7, 12}, + {55.5, 76.6, 12}, + {59.1, 76.5, 12}, + {73, 76.5, 12}, + {53.4, 76.5, 12}, + {57.2, 76.4, 12}, + {62.9, 76.2, 12}, + {49, 76.1, 12}, + {48, 76, 12}, + {63.9, 75.8, 12}, + {52.5, 75.8, 12}, + {47.3, 75.8, 12}, + {49.5, 75.7, 12}, + {65.5, 75.6, 12}, + {69.5, 75.5, 12}, + {62.1, 75.4, 12}, + {60.1, 75.4, 12}, + {54.6, 75.2, 12}, + {71.8, 75.2, 12}, + {56.4, 75.2, 12}, + {58.6, 74.8, 12}, + {58.5, 74.7, 12}, + {51.9, 74.7, 12}, + {59.3, 74.5, 12}, + {59.1, 74.3, 12}, + {53.3, 73.9, 12}, + {55.6, 73.8, 12}, + {57.1, 73.8, 12}, + {70.8, 73.7, 12}, + {58.4, 72.8, 12}, + {69.7, 72.6, 12}, + {58.3, 72.4, 12}, + {72.7, 79.6, 12}, + {53.1, 74, 12}, + }, + [525] = { + {23.6, 76.8, 12}, + {32.4, 76.7, 12}, + {21.7, 76.4, 12}, + {33.5, 76.1, 12}, + {33.1, 75.7, 12}, + {26.5, 75.6, 12}, + {28.7, 75.5, 12}, + {22.5, 75.5, 12}, + {22.2, 75.5, 12}, + {34.7, 75.5, 12}, + {28.5, 74.8, 12}, + {21.4, 74.3, 12}, + {29.1, 74.3, 12}, + {27.6, 74.2, 12}, + {35.6, 73.6, 12}, + {33.9, 73.6, 12}, + {33.6, 73.4, 12}, + {29.6, 72.9, 12}, + {29.3, 72.8, 12}, + {35.1, 72.6, 12}, + {29.6, 72.2, 12}, + {28.8, 72.2, 12}, + {29.3, 72.2, 12}, + {30.3, 72.1, 12}, + {30.4, 71.9, 12}, + {27.7, 71.3, 12}, + {31.1, 71.3, 12}, + {52.9, 71.3, 12}, + {54.9, 71.1, 12}, + {56.2, 71.1, 12}, + {35.4, 70.7, 12}, + {50.9, 70.5, 12}, + {56.7, 70.1, 12}, + {35, 70, 12}, + {52, 70, 12}, + {55.3, 69.8, 12}, + {57.1, 69.7, 12}, + {53.3, 69.7, 12}, + {51.4, 69.7, 12}, + {29.1, 69.6, 12}, + {49.6, 69.5, 12}, + {36.1, 69.2, 12}, + {30.6, 69.1, 12}, + {48.8, 68.6, 12}, + {45.9, 68.5, 12}, + {46.7, 68.4, 12}, + {47.6, 68.1, 12}, + {30.6, 68.1, 12}, + {32.5, 67.7, 12}, + {34.9, 67.5, 12}, + {28.3, 67.4, 12}, + {35, 66.7, 12}, + {34.4, 65.3, 12}, + {37.5, 64.9, 12}, + {38.5, 63.8, 12}, + {52, 63.8, 12}, + {52.1, 63.7, 12}, + {35.3, 63.4, 12}, + {36.9, 63.2, 12}, + {52.1, 63, 12}, + {52.4, 62.7, 12}, + {35.9, 62.6, 12}, + {34.7, 62.4, 12}, + {42.7, 62.2, 12}, + {38.3, 62.2, 12}, + {52.4, 62, 12}, + {50.8, 61.7, 12}, + {55.2, 61.5, 12}, + {56.5, 61.5, 12}, + {35.4, 61.2, 12}, + {43.1, 61.1, 12}, + {50.4, 61, 12}, + {49.6, 61, 12}, + {36.8, 60.8, 12}, + {58.4, 60.6, 12}, + {53.8, 60.4, 12}, + {56.8, 60.4, 12}, + {57.1, 59.8, 12}, + {55, 59.4, 12}, + {34.2, 59.3, 12}, + {56.4, 59, 12}, + {49.5, 58.3, 12}, + {41.4, 58.2, 12}, + {33.4, 58.2, 12}, + {44.6, 57.8, 12}, + {32.1, 56.2, 12}, + {32.2, 55.5, 12}, + {45.8, 55.4, 12}, + {42.8, 55, 12}, + {23.6, 77, 12}, + {26.4, 75.3, 12}, + {29.2, 72.9, 12}, + {35.1, 72.4, 12}, + {56.2, 71.5, 12}, + {55.3, 70, 12}, + {53.1, 69.7, 12}, + {49.7, 69.4, 12}, + {46, 68.5, 12}, + {46, 65.3, 12}, + {34.4, 65.2, 12}, + {50.5, 61.2, 12}, + {41.6, 58, 12}, + }, + [539] = { + {13.2, 70.3, 10}, + {11.7, 68.7, 10}, + {10.3, 66.6, 10}, + {11.6, 62.7, 10}, + {9.2, 60.8, 10}, + {10.5, 59.3, 10}, + {12, 59.2, 10}, + {9.7, 57.3, 10}, + {9.7, 53.7, 10}, + {10.8, 53, 10}, + {12, 51.2, 10}, + {9.5, 49.7, 10}, + {9, 46.3, 10}, + {7.9, 44.8, 10}, + {9.4, 42.8, 10}, + {8, 40.9, 10}, + {12.5, 28.8, 10}, + {23.8, 27.8, 10}, + {28.8, 27.6, 10}, + {34, 24.6, 10}, + {40.1, 21.8, 10}, + {37.1, 21.3, 10}, + {72.1, 19, 10}, + {69.7, 18.7, 10}, + {84.7, 18.4, 10}, + {66.4, 18, 10}, + {66.7, 17.9, 10}, + {74.8, 17.5, 10}, + {66.3, 17.1, 10}, + {46.5, 16.7, 10}, + {60.8, 16.6, 10}, + {68.3, 15.9, 10}, + {47.6, 15.9, 10}, + {54.5, 14.2, 10}, + {53.9, 13.3, 10}, + {73.3, 89.2, 12}, + {71.4, 66.5, 40}, + {70.3, 64.9, 40}, + {69.5, 60.5, 40}, + {69.9, 57.8, 40}, + {69.3, 49.3, 40}, + {68.5, 48.1, 40}, + {10.6, 55.5, 10}, + {11.9, 68.9, 10}, + {10.1, 66.1, 10}, + {11.4, 65.1, 10}, + {10.4, 59.1, 10}, + {9.4, 57.2, 10}, + {10.7, 55.4, 10}, + {9.5, 53.8, 10}, + {9, 46.2, 10}, + {7.9, 40.8, 10}, + {69.6, 18.6, 10}, + {70.2, 64.6, 40}, + {70.4, 59.1, 40}, + {69.6, 57.7, 40}, + {69.3, 49.2, 40}, + {68.4, 45.1, 40}, + }, + [547] = { + {67.5, 70.9, 40}, + {49.7, 68.7, 40}, + {54.6, 68.3, 40}, + {66.7, 68.1, 40}, + {58.6, 68, 40}, + {57.1, 67.7, 40}, + {51.7, 67.6, 40}, + {60.8, 67.6, 40}, + {64.3, 66.7, 40}, + {58.2, 62.2, 40}, + {58.8, 59, 40}, + {59, 56.9, 40}, + {63.9, 55.1, 40}, + {62.4, 51.5, 40}, + {60.2, 50.5, 40}, + {59, 49.1, 40}, + {28.4, 79.4, 44}, + {30.4, 78.6, 44}, + {24.7, 78.5, 44}, + {38.4, 73.6, 44}, + {29, 73.3, 44}, + {19.4, 72.8, 44}, + {31.3, 72, 44}, + {34, 71.3, 44}, + {32.1, 71.1, 44}, + {17.9, 71.1, 44}, + {31.2, 67.8, 44}, + {33.6, 65.9, 44}, + {33.9, 61.1, 44}, + {25.4, 60.5, 44}, + {17.7, 55.3, 44}, + {15.3, 54, 44}, + {18.3, 54, 44}, + {16.8, 52.3, 44}, + {14.5, 49.9, 44}, + {17.4, 47.1, 44}, + {18.5, 43.6, 44}, + {19.8, 40, 44}, + {48.5, 25.3, 44}, + {45.2, 20.5, 44}, + {46.2, 17.1, 44}, + {42.3, 16.3, 44}, + {46.9, 14.3, 44}, + {46.4, 10.4, 44}, + {27, 79.8, 44}, + {27.8, 75.6, 44}, + {18.1, 71.7, 44}, + {57.2, 41.7, 44}, + {54.5, 68.1, 40}, + {58.1, 62.3, 40}, + {26.8, 79.4, 44}, + {28.3, 79.3, 44}, + {30.3, 78.3, 44}, + {27.7, 75.4, 44}, + {38.4, 73.3, 44}, + {31.4, 72.2, 44}, + {33.9, 71.3, 44}, + {31.2, 67.9, 44}, + {33.3, 65.8, 44}, + {15.6, 54, 44}, + {14.7, 49.5, 44}, + {17.2, 46.9, 44}, + }, + [565] = { + {12.9, 69.2, 10}, + {10.6, 61, 10}, + {11.6, 57.3, 10}, + {10.7, 51.6, 10}, + {10, 46.4, 10}, + {9.6, 40.5, 10}, + {11.6, 33.6, 10}, + {11, 32.3, 10}, + {13.2, 30.5, 10}, + {21.5, 30.5, 10}, + {16.1, 29.7, 10}, + {33.1, 29.4, 10}, + {20.3, 28.9, 10}, + {34, 28.1, 10}, + {20.4, 28.1, 10}, + {19.2, 27.8, 10}, + {16.3, 27.2, 10}, + {14.3, 27.2, 10}, + {35.2, 26.1, 10}, + {15.6, 26.1, 10}, + {37.6, 25.7, 10}, + {22.6, 25.6, 10}, + {20.4, 25.4, 10}, + {18.9, 25.3, 10}, + {39, 23.8, 10}, + {67.1, 22.6, 10}, + {40.1, 22.1, 10}, + {67.3, 20.1, 10}, + {56.2, 19.6, 10}, + {61.5, 19.5, 10}, + {54.8, 18.4, 10}, + {66, 18, 10}, + {31.3, 98, 12}, + {14.1, 27.7, 10}, + {31.2, 98.3, 12}, + {25.2, 29.9, 10}, + {19.3, 28.1, 10}, + {13.8, 27.6, 10}, + {35.1, 26, 10}, + {31, 98.2, 12}, + }, + [569] = { + {13.3, 69.2, 10}, + {13.9, 64.8, 10}, + {13.2, 58.4, 10}, + {12.4, 55.1, 10}, + {13, 51, 10}, + {11, 41.9, 10}, + {10.3, 41.5, 10}, + {71.3, 23.7, 10}, + {61.9, 23.2, 10}, + {75.5, 22.7, 10}, + {68.1, 22.7, 10}, + {69, 22.6, 10}, + {66.8, 22.5, 10}, + {64.8, 22.4, 10}, + {57.3, 22.3, 10}, + {54.9, 22, 10}, + {68.6, 21.9, 10}, + {59.9, 21.9, 10}, + {72.8, 21.5, 10}, + {58.1, 21, 10}, + {62.5, 20.4, 10}, + {56.1, 20.4, 10}, + {54.2, 20.4, 10}, + {50.8, 20.3, 10}, + {51.3, 19.3, 10}, + {16.5, 69.5, 10}, + {74.6, 21.2, 10}, + }, + [574] = { + {86.4, 47.6, 10}, + }, + [616] = { + {53.1, 37.1, 44}, + }, + [628] = { + {52.7, 63.2, 10}, + {48.6, 61.5, 10}, + {59.3, 58.8, 10}, + {59.7, 57.8, 10}, + {69.7, 35.3, 10}, + {68.4, 33.3, 10}, + {67.4, 31.8, 10}, + {66.1, 28.7, 10}, + {63.7, 27.8, 10}, + {64.1, 25.6, 10}, + {70, 35.4, 10}, + {67.3, 31.8, 10}, + }, + [681] = { + {34.7, 17.5, 33}, + {35.1, 16.5, 33}, + {38.7, 15.4, 33}, + {36.1, 15.3, 33}, + {39.2, 14.4, 33}, + {37.4, 14.3, 33}, + {34.5, 14, 33}, + {38.8, 13.6, 33}, + {36, 12.9, 33}, + {34, 12.9, 33}, + {35.6, 12.3, 33}, + {35.1, 12.1, 33}, + {37.3, 11.6, 33}, + {34, 11.3, 33}, + {33.6, 11.1, 33}, + {40.2, 10, 33}, + {31.5, 9.9, 33}, + {33.4, 9.9, 33}, + {33, 9.7, 33}, + {32.4, 9.6, 33}, + {31.4, 8.5, 33}, + {40.3, 8.2, 33}, + {39.3, 8.1, 33}, + {30.6, 7.6, 33}, + {39, 6.7, 33}, + {35, 16.3, 33}, + {33.9, 13, 33}, + {35.7, 12.1, 33}, + {34.1, 11.3, 33}, + {40.2, 9.9, 33}, + {33.1, 9.6, 33}, + {31.4, 8.4, 33}, + {40.4, 8.3, 33}, + }, + [682] = { + {47.2, 18.5, 33}, + {46.6, 17.5, 33}, + {46, 16.9, 33}, + {47.1, 16.8, 33}, + {46.6, 16.1, 33}, + {30.7, 16, 33}, + {46.1, 15.3, 33}, + {30.4, 15.2, 33}, + {47.1, 15.2, 33}, + {42.9, 15.2, 33}, + {45.5, 14.5, 33}, + {30.9, 14.5, 33}, + {46.6, 14.4, 33}, + {44, 13.7, 33}, + {48.1, 13.7, 33}, + {30.2, 13.6, 33}, + {46.5, 12.9, 33}, + {47.6, 12.9, 33}, + {48.2, 12.2, 33}, + {47.4, 11.4, 33}, + {29.7, 11.3, 33}, + {29.4, 10.5, 33}, + {28.3, 10.4, 33}, + {29.9, 9.8, 33}, + {29, 9.5, 33}, + {30.4, 9, 33}, + {42.9, 15.3, 33}, + {30.3, 15.2, 33}, + {29.4, 10.4, 33}, + }, + [683] = { + {35.1, 15.1, 33}, + {35, 14.2, 33}, + {36.5, 13.8, 33}, + {42.9, 13.6, 33}, + {38.3, 13.4, 33}, + {41.3, 13.4, 33}, + {42.4, 13.2, 33}, + {42.3, 13.2, 33}, + {41.8, 13, 33}, + {41.3, 12.9, 33}, + {37.7, 12.8, 33}, + {41.8, 12.7, 33}, + {43.1, 12.3, 33}, + {40.5, 11.4, 33}, + {40.6, 11.2, 33}, + {42.9, 10.5, 33}, + {41.4, 10.5, 33}, + {42.1, 10.1, 33}, + {42.4, 9.7, 33}, + {37.3, 8.2, 33}, + {41.3, 8.1, 33}, + {36.1, 7.2, 33}, + {40.3, 8.2, 33}, + {39.3, 8.1, 33}, + {39, 6.7, 33}, + {36.6, 13.8, 33}, + {41.4, 12.8, 33}, + {37.6, 12.5, 33}, + {42.5, 9.6, 33}, + {39.2, 8.1, 33}, + {39, 6.6, 33}, + }, + [684] = { + {39.3, 45.5, 33}, + {37.3, 45.2, 33}, + {39.6, 44.2, 33}, + {39.6, 42.6, 33}, + {39.5, 41.7, 33}, + {39.2, 41.1, 33}, + {40.3, 41, 33}, + {41.2, 40.9, 33}, + {39.8, 40.7, 33}, + {40.7, 40.2, 33}, + {39.1, 39.9, 33}, + {41.3, 39.8, 33}, + {40.3, 39.6, 33}, + {38.1, 39.3, 33}, + {36.5, 39.1, 33}, + {42, 38.9, 33}, + {37.2, 38.9, 33}, + {38.7, 38.6, 33}, + {41.4, 38.2, 33}, + {42.2, 38.1, 33}, + {35.5, 38, 33}, + {39.2, 37.9, 33}, + {36.6, 37.8, 33}, + {40.5, 37.7, 33}, + {39.9, 37.6, 33}, + {38.7, 37.2, 33}, + {36.2, 37.2, 33}, + {38.2, 37.1, 33}, + {35.2, 37, 33}, + {37.6, 36.4, 33}, + {41.2, 36.3, 33}, + {35.6, 36.2, 33}, + {36.5, 36.1, 33}, + {38.6, 35.7, 33}, + {36.1, 35.6, 33}, + {37.2, 35.6, 33}, + {39.6, 35.5, 33}, + {39.2, 34.8, 33}, + {36.6, 34.8, 33}, + {41.3, 34.8, 33}, + {38.3, 34.7, 33}, + {36, 33.9, 33}, + {39.4, 33.3, 33}, + {37.7, 33.2, 33}, + {38.7, 32.9, 33}, + {40.3, 31.7, 33}, + {47, 29.6, 33}, + {46.5, 28.4, 33}, + {48, 27.8, 33}, + {47, 27.7, 33}, + {45.9, 27.7, 33}, + {47.6, 27.4, 33}, + {46.4, 26.9, 33}, + {46.5, 26.9, 33}, + {45.5, 26.8, 33}, + {46.1, 26.2, 33}, + {49.7, 25.5, 33}, + {45.5, 25.3, 33}, + {50.2, 24.7, 33}, + {49.2, 24.6, 33}, + {49.7, 24.1, 33}, + {48.9, 23.9, 33}, + {49.2, 23.1, 33}, + {47.2, 23.1, 33}, + {48.1, 23.1, 33}, + {50.2, 23, 33}, + {46.4, 22.5, 33}, + {47.7, 22.3, 33}, + {48.7, 22.2, 33}, + {50.3, 21.8, 33}, + {49.2, 21.6, 33}, + {48.1, 21.5, 33}, + {49.7, 20.7, 33}, + {48.6, 20.7, 33}, + {50.7, 20.6, 33}, + {49.3, 20.4, 33}, + {48.1, 20.2, 33}, + {38.7, 39.7, 33}, + {49.7, 20.8, 33}, + {48.7, 20.7, 33}, + }, + [685] = { + {27.5, 17.7, 33}, + {26.7, 17.6, 33}, + {26, 17.1, 33}, + {27.2, 16.8, 33}, + {24.5, 16.2, 33}, + {24.2, 16.2, 33}, + {24.8, 16.1, 33}, + {26.7, 16, 33}, + {25.7, 15.8, 33}, + {24.5, 15.7, 33}, + {24.1, 15.7, 33}, + {27.4, 15.5, 33}, + {23.7, 15.4, 33}, + {27.8, 14.4, 33}, + {27.3, 16.6, 33}, + {26.6, 16, 33}, + {27.8, 14.3, 33}, + }, + [686] = { + {37.6, 27.8, 33}, + {35.9, 27.1, 33}, + {38.1, 27, 33}, + {35.6, 26.3, 33}, + {38.8, 26.2, 33}, + {34, 25.5, 33}, + {38.4, 25.2, 33}, + {30.3, 24.7, 33}, + {38.9, 24.6, 33}, + {32.4, 24.6, 33}, + {33.4, 24.6, 33}, + {30.6, 24, 33}, + {33, 23.9, 33}, + {32.1, 23.8, 33}, + {30.7, 23.7, 33}, + {30.6, 23.5, 33}, + {30.8, 23.2, 33}, + {32.4, 23.1, 33}, + {30.6, 22.5, 33}, + {39.1, 22.1, 33}, + {38.9, 21.6, 33}, + {37.7, 21.5, 33}, + {32.5, 21.5, 33}, + {32, 20.7, 33}, + {38.2, 20.7, 33}, + {38.5, 19.8, 33}, + {37.8, 19.7, 33}, + {38.3, 19.2, 33}, + {39.3, 19.1, 33}, + {37.6, 27.9, 33}, + {30.7, 23.5, 33}, + {32.4, 23.2, 33}, + {32.5, 21.4, 33}, + {38.2, 20.5, 33}, + }, + [687] = { + {23.2, 51.5, 33}, + {26.1, 51.4, 33}, + {24.9, 51.2, 33}, + {26.6, 50.5, 33}, + {23.4, 50.3, 33}, + {27.6, 50.3, 33}, + {26, 49.9, 33}, + {27.2, 49.8, 33}, + {23, 49.7, 33}, + {24.1, 49.6, 33}, + {23.7, 48.9, 33}, + {27.7, 48.9, 33}, + {26.9, 48.7, 33}, + {23.4, 48.2, 33}, + {27.4, 48.1, 33}, + {28, 47.2, 33}, + {28.3, 46.5, 33}, + {28.8, 45.6, 33}, + {28.2, 45, 33}, + {31.2, 44.6, 33}, + {28.8, 44.2, 33}, + {30.6, 44.1, 33}, + {30, 44.1, 33}, + {30.1, 43.6, 33}, + {29.3, 43.6, 33}, + {28.3, 43.4, 33}, + {27.3, 43.4, 33}, + {31.5, 43.3, 33}, + {32.1, 43.1, 33}, + {30.9, 42.6, 33}, + {28.9, 42.6, 33}, + {32, 42.4, 33}, + {30.4, 41.9, 33}, + {32.5, 41.8, 33}, + {31.4, 41.8, 33}, + {31.9, 40.8, 33}, + {31.5, 40.7, 33}, + {34.6, 40.1, 33}, + {32.7, 39.2, 33}, + {34.6, 38.9, 33}, + {33.5, 37.2, 33}, + {32.5, 37.1, 33}, + {32.9, 36.4, 33}, + {27.3, 49.7, 33}, + {27.8, 48.9, 33}, + {28.9, 44.2, 33}, + {28.3, 43.5, 33}, + {31.5, 43.4, 33}, + {30.9, 42.5, 33}, + {31.4, 42, 33}, + {30.5, 41.8, 33}, + {32.4, 41.7, 33}, + {33.4, 37.2, 33}, + {32.6, 37.2, 33}, + {32.9, 36.5, 33}, + }, + [704] = { + {24, 79.7, 1}, + {25.5, 79.5, 1}, + {23.3, 78.7, 1}, + {28.1, 78.3, 1}, + {24.7, 78.2, 1}, + {26.9, 78, 1}, + {23.3, 77.9, 1}, + {27.7, 77, 1}, + {28.8, 76.9, 1}, + {26.8, 76.3, 1}, + {28, 76.1, 1}, + {26.1, 75.5, 1}, + {27.4, 75.4, 1}, + {26.8, 75.2, 1}, + {29.7, 74.8, 1}, + {29.8, 74.7, 1}, + {24.8, 78.2, 1}, + {28.1, 78.1, 1}, + {23.2, 77.7, 1}, + {26.9, 76.2, 1}, + {26, 75.3, 1}, + {26.8, 75.1, 1}, + {50.6, 7.8, 5581}, + {52.8, 7.5, 5581}, + {49.4, 6.3, 5581}, + {51.8, 5.5, 5581}, + {56.8, 5.3, 5581}, + {54.9, 5.2, 5581}, + {49.4, 4.7, 5581}, + {56.1, 3.7, 5581}, + {57.8, 3.5, 5581}, + {54.9, 2.4, 5581}, + {56.6, 2.2, 5581}, + {55.7, 1.2, 5581}, + {53.5, 1, 5581}, + {54.8, 0.7, 5581}, + {59.1, 0.3, 5581}, + {59.4, 0.2, 5581}, + }, + [705] = { + {22.5, 78.2, 1}, + {29.2, 78, 1}, + {23.8, 78, 1}, + {25.4, 77.9, 1}, + {29.5, 77.1, 1}, + {29.7, 76.1, 1}, + {29.2, 75.9, 1}, + {28.5, 75.8, 1}, + {30.9, 75.8, 1}, + {28.1, 75.4, 1}, + {30.3, 75, 1}, + {28.1, 75, 1}, + {28.5, 74.8, 1}, + {29, 74.8, 1}, + {26.4, 74.7, 1}, + {29.3, 74.7, 1}, + {30.7, 74.7, 1}, + {29, 74.5, 1}, + {29.9, 74.5, 1}, + {28.4, 74.4, 1}, + {29.7, 74.3, 1}, + {27.5, 74.2, 1}, + {29.3, 74.1, 1}, + {28.6, 74.1, 1}, + {30.4, 74.1, 1}, + {31.3, 73.9, 1}, + {28.4, 73.9, 1}, + {31.4, 73.9, 1}, + {28.8, 73.8, 1}, + {30.5, 73.7, 1}, + {30.1, 73.6, 1}, + {28.8, 73.6, 1}, + {29.9, 73.4, 1}, + {29.4, 73.4, 1}, + {28, 73.2, 1}, + {27.8, 73.2, 1}, + {31, 73.1, 1}, + {30.8, 72.9, 1}, + {28.7, 72.7, 1}, + {28.7, 72.4, 1}, + {28.6, 72.1, 1}, + {27.5, 72.1, 1}, + {28.3, 71.8, 1}, + {27.5, 71.3, 1}, + {26.1, 71, 1}, + {32.2, 70.9, 1}, + {26.5, 70.8, 1}, + {31.5, 70.2, 1}, + {26.8, 70.1, 1}, + {26.1, 69.2, 1}, + {31.1, 69, 1}, + {22.6, 78.2, 1}, + {25.6, 78.1, 1}, + {23.9, 77.9, 1}, + {29.8, 75.9, 1}, + {28.2, 75.5, 1}, + {28.5, 75.1, 1}, + {29, 74.7, 1}, + {30.8, 74.6, 1}, + {27.6, 74.2, 1}, + {30.3, 74.2, 1}, + {28.8, 74.2, 1}, + {31.3, 74, 1}, + {29.2, 73.9, 1}, + {30, 73.7, 1}, + {29.3, 73.4, 1}, + {30.9, 73.2, 1}, + {27.6, 72.4, 1}, + {27.4, 71.1, 1}, + {26, 70.9, 1}, + {32.3, 70.8, 1}, + {31.4, 70, 1}, + {26.7, 69.9, 1}, + {48.4, 5.5, 5581}, + {52.9, 5.3, 5581}, + {58.5, 5.1, 5581}, + {50.3, 4.9, 5581}, + {58.9, 3.8, 5581}, + {59.3, 2, 5581}, + {58.5, 1.9, 5581}, + {57.4, 1.9, 5581}, + {61, 1.8, 5581}, + {56.9, 1.3, 5581}, + {57.3, 0.7, 5581}, + {60.1, 0.6, 5581}, + {56.7, 0.5, 5581}, + {54.2, 0.2, 5581}, + {58.6, 0.1, 5581}, + {58.2, 0.1, 5581}, + }, + [708] = { + {24.7, 79.3, 1}, + {24, 78.8, 1}, + {23.5, 78.8, 1}, + {21.5, 78, 1}, + {19.7, 76.4, 1}, + {24.1, 76.3, 1}, + {20.7, 75.3, 1}, + {19.5, 75.2, 1}, + {20.1, 74.3, 1}, + {20.4, 74.2, 1}, + {20.7, 74.1, 1}, + {19.9, 73.4, 1}, + {20.8, 73.4, 1}, + {20.1, 73.3, 1}, + {20.7, 73.3, 1}, + {28.1, 73.2, 1}, + {28, 73.1, 1}, + {22.8, 72.5, 1}, + {22, 72.3, 1}, + {19.9, 72.3, 1}, + {24.1, 72.3, 1}, + {22.1, 72.3, 1}, + {22.7, 72.3, 1}, + {21.3, 72.2, 1}, + {32.2, 71.7, 1}, + {27.4, 71.6, 1}, + {22.4, 71.6, 1}, + {21.8, 71.4, 1}, + {20.1, 71.4, 1}, + {21.4, 71.3, 1}, + {26.1, 71.3, 1}, + {22, 71.2, 1}, + {20.1, 71.1, 1}, + {24.7, 70.9, 1}, + {20.7, 70.3, 1}, + {21.4, 70.2, 1}, + {25.5, 70.2, 1}, + {22.6, 70.1, 1}, + {22.7, 70.1, 1}, + {21.9, 70, 1}, + {26.6, 69.7, 1}, + {22.7, 69.6, 1}, + {22.2, 69.5, 1}, + {31.4, 69.3, 1}, + {22.6, 69.3, 1}, + {22, 69.2, 1}, + {23.4, 69.2, 1}, + {26, 69.2, 1}, + {20.7, 69.2, 1}, + {24.7, 69.2, 1}, + {21.3, 69.1, 1}, + {20.7, 69.1, 1}, + {26.6, 68.6, 1}, + {23.5, 68.5, 1}, + {25.4, 68.4, 1}, + {24.4, 68.1, 1}, + {24, 76.5, 1}, + {20.2, 74.5, 1}, + {28.2, 73, 1}, + {24, 72.5, 1}, + {22.7, 72.4, 1}, + {22.1, 72.2, 1}, + {32.3, 71.7, 1}, + {21.9, 71.3, 1}, + {26.2, 71.2, 1}, + {25.4, 70.2, 1}, + {22.7, 70.2, 1}, + {21.9, 70.1, 1}, + {22, 69.3, 1}, + {31.2, 69.2, 1}, + {24.6, 69.2, 1}, + {23.4, 69.1, 1}, + {23.6, 68.6, 1}, + {25.5, 68.4, 1}, + {51.6, 7.1, 5581}, + {50.6, 6.4, 5581}, + {49.7, 6.3, 5581}, + {46.7, 5.2, 5581}, + {50.5, 2.8, 5581}, + {44.1, 2.7, 5581}, + {45.6, 1, 5581}, + {43.7, 0.8, 5581}, + }, + [730] = { + {28.7, 44.8, 33}, + {31.8, 43.4, 33}, + {28.3, 42.5, 33}, + }, + [731] = { + {38.2, 35.6, 33}, + }, + [734] = { + {38, 3, 33}, + }, + [736] = { + {28.2, 16.6, 33}, + {29.8, 16.1, 33}, + {28.9, 16, 33}, + {28.7, 14.6, 33}, + {29.5, 13.6, 33}, + {28.4, 13.4, 33}, + {28.4, 12.3, 33}, + {30.9, 11.3, 33}, + {30.4, 10.5, 33}, + {45.7, 10.3, 33}, + {45.8, 10.3, 33}, + {29.3, 8.9, 33}, + {30.4, 15.2, 33}, + {30.9, 14.5, 33}, + {28.3, 10.4, 33}, + {29.9, 9.8, 33}, + {28.9, 15.9, 33}, + {28.8, 14.6, 33}, + {31, 14.6, 33}, + {30.5, 10.7, 33}, + {28.4, 10.5, 33}, + {30, 9.8, 33}, + {29.4, 9.1, 33}, + }, + [767] = { + {27.2, 64, 8}, + {26, 60.8, 8}, + {16.7, 60.1, 8}, + {23.5, 60.1, 8}, + {12.1, 59.5, 8}, + {22.4, 57.9, 8}, + {35.6, 54.3, 8}, + {33.7, 54, 8}, + {30, 52.5, 8}, + {31.9, 50, 8}, + {36.5, 48.4, 8}, + {26, 47.5, 8}, + {39.1, 47.3, 8}, + {14.6, 46.1, 8}, + {40.4, 45.3, 8}, + {55.5, 45.2, 8}, + {37.2, 45.2, 8}, + {43.3, 45.1, 8}, + {56.9, 43.8, 8}, + {28.7, 43.6, 8}, + {44.7, 43.4, 8}, + {41.9, 43.4, 8}, + {33.2, 43.2, 8}, + {20.2, 42.8, 8}, + {23.1, 42.4, 8}, + {53.8, 41.6, 8}, + {43.8, 41.4, 8}, + {25.3, 41.3, 8}, + {40.1, 41, 8}, + {47.5, 40.5, 8}, + {36.9, 39.9, 8}, + {27, 39.6, 8}, + {39, 39.2, 8}, + {36.4, 39.2, 8}, + {41, 38.8, 8}, + {23.5, 38.7, 8}, + {53.2, 38.4, 8}, + {37.6, 37.1, 8}, + {40.3, 36.8, 8}, + {33.2, 35, 8}, + {26.4, 34.3, 8}, + {29.1, 34.1, 8}, + {39.9, 31.3, 8}, + {37.4, 30.9, 8}, + {34.7, 30.3, 8}, + {31.7, 29.4, 8}, + {26.3, 60.7, 8}, + {12, 59.4, 8}, + {33.6, 54.4, 8}, + {35.9, 53.9, 8}, + {32, 50, 8}, + {26.4, 47.4, 8}, + {43.5, 41.2, 8}, + {39.9, 41, 8}, + {47.7, 40.7, 8}, + {36.3, 39.6, 8}, + {39.2, 39.3, 8}, + {37.5, 37.6, 8}, + {40.4, 37.3, 8}, + {33.4, 35.5, 8}, + {29.1, 34.3, 8}, + {39.9, 31.2, 8}, + }, + [768] = { + {65.4, 67.9, 8}, + {83.8, 59, 8}, + {90.6, 49, 8}, + {93, 44.6, 8}, + {88.6, 36.3, 8}, + {87.8, 31.4, 8}, + {87.3, 28.6, 8}, + {69.9, 26.3, 8}, + {74.1, 25.3, 8}, + {69.3, 11.6, 8}, + {70.6, 11, 8}, + {71, 10.5, 8}, + }, + [769] = { + {81.1, 89.1, 8}, + {76.2, 88.9, 8}, + {76.9, 86.7, 8}, + {77.4, 86, 8}, + {80.2, 83.9, 8}, + {75, 82.9, 8}, + {72.8, 82.8, 8}, + {73.2, 80.2, 8}, + {70.2, 79.9, 8}, + {72.8, 79.9, 8}, + {67.4, 78.9, 8}, + {76.5, 77.4, 8}, + {65.3, 69.9, 8}, + {66, 69.6, 8}, + {60.6, 68.9, 8}, + {71.3, 68.8, 8}, + {60.9, 68.7, 8}, + {67.8, 68.4, 8}, + {62.9, 67.6, 8}, + {69.4, 67.5, 8}, + {63.7, 66.4, 8}, + {59.6, 65.9, 8}, + {56.5, 65.7, 8}, + {62.1, 65.5, 8}, + {61.9, 65.3, 8}, + {65.5, 65.1, 8}, + {56.9, 64.4, 8}, + {59.3, 63.6, 8}, + {58, 62.5, 8}, + {55.4, 62.3, 8}, + {56.9, 62.1, 8}, + {55.9, 62.1, 8}, + {60.6, 62.1, 8}, + {60.5, 61.9, 8}, + {56.4, 61.8, 8}, + {56.4, 61, 8}, + {60.5, 60.3, 8}, + {59.4, 60.2, 8}, + {69.4, 67.3, 8}, + }, + [772] = { + {39.3, 45.5, 33}, + {37.3, 45.2, 33}, + {39.6, 44.2, 33}, + {39.6, 42.6, 33}, + {39.5, 41.7, 33}, + {39.2, 41.1, 33}, + {40.3, 41, 33}, + {41.2, 40.9, 33}, + {39.8, 40.7, 33}, + {40.7, 40.2, 33}, + {39.1, 39.9, 33}, + {41.3, 39.8, 33}, + {40.3, 39.6, 33}, + {38.1, 39.3, 33}, + {36.5, 39.1, 33}, + {42, 38.9, 33}, + {37.2, 38.9, 33}, + {38.7, 38.6, 33}, + {41.4, 38.2, 33}, + {42.2, 38.1, 33}, + {35.5, 38, 33}, + {39.2, 37.9, 33}, + {36.6, 37.8, 33}, + {40.5, 37.7, 33}, + {39.9, 37.6, 33}, + {38.7, 37.2, 33}, + {36.2, 37.2, 33}, + {38.2, 37.1, 33}, + {35.2, 37, 33}, + {37.6, 36.4, 33}, + {41.2, 36.3, 33}, + {35.6, 36.2, 33}, + {36.5, 36.1, 33}, + {38.6, 35.7, 33}, + {36.1, 35.6, 33}, + {37.2, 35.6, 33}, + {39.6, 35.5, 33}, + {39.2, 34.8, 33}, + {36.6, 34.8, 33}, + {41.3, 34.8, 33}, + {38.3, 34.7, 33}, + {36, 33.9, 33}, + {39.4, 33.3, 33}, + {37.7, 33.2, 33}, + {38.7, 32.9, 33}, + {40.3, 31.7, 33}, + {47, 29.6, 33}, + {46.5, 28.4, 33}, + {48, 27.8, 33}, + {47, 27.7, 33}, + {45.9, 27.7, 33}, + {47.6, 27.4, 33}, + {46.4, 26.9, 33}, + {46.5, 26.9, 33}, + {45.5, 26.8, 33}, + {46.1, 26.2, 33}, + {49.7, 25.5, 33}, + {45.5, 25.3, 33}, + {50.2, 24.7, 33}, + {49.2, 24.6, 33}, + {49.7, 24.1, 33}, + {48.9, 23.9, 33}, + {49.2, 23.1, 33}, + {47.2, 23.1, 33}, + {48.1, 23.1, 33}, + {50.2, 23, 33}, + {46.4, 22.5, 33}, + {47.7, 22.3, 33}, + {48.7, 22.2, 33}, + {50.3, 21.8, 33}, + {49.2, 21.6, 33}, + {48.1, 21.5, 33}, + {49.7, 20.7, 33}, + {48.6, 20.7, 33}, + {50.7, 20.6, 33}, + {49.3, 20.4, 33}, + {48.1, 20.2, 33}, + {39.2, 41, 33}, + {40.7, 38.4, 33}, + {38.7, 37.1, 33}, + {38.2, 36.9, 33}, + {36.2, 35.7, 33}, + {37.2, 35.5, 33}, + {37.6, 33, 33}, + {49.3, 24.5, 33}, + {49.3, 23.2, 33}, + {48.1, 23, 33}, + {48.2, 21.6, 33}, + }, + [789] = { + {27.1, 45.5, 44}, + }, + [791] = { + {28.8, 47.3, 44}, + }, + [822] = { + {25.5, 90.7, 12}, + {38.3, 89.4, 12}, + {36.3, 89.2, 12}, + {45.9, 85.1, 12}, + {53.5, 84.7, 12}, + {83.5, 84.7, 12}, + {24.5, 83.4, 12}, + {79.8, 83.3, 12}, + {49.2, 82.8, 12}, + {28.8, 81.6, 12}, + {43.3, 81.2, 12}, + {87.8, 81.1, 12}, + {72.9, 80.9, 12}, + {47, 79.7, 12}, + {60.2, 79.5, 12}, + {63.2, 79.2, 12}, + {78.1, 79.2, 12}, + {60.8, 79.1, 12}, + {66.1, 78.8, 12}, + {86.4, 78.5, 12}, + {45, 78.1, 12}, + {88.9, 77.5, 12}, + {81.1, 77.2, 12}, + {63.1, 77.1, 12}, + {48.3, 76.8, 12}, + {77.2, 76.7, 12}, + {56.9, 75.9, 12}, + {49.2, 75.9, 12}, + {46.3, 75.8, 12}, + {22.4, 75.8, 12}, + {53.9, 75.5, 12}, + {70.6, 74.9, 12}, + {87.2, 69.3, 12}, + {75.8, 67.5, 12}, + {59.1, 66.9, 12}, + {74.6, 66.8, 12}, + {68.6, 66.3, 12}, + {62.9, 65.9, 12}, + {88.3, 65.7, 12}, + {86.5, 65.2, 12}, + {86.8, 64.5, 12}, + {68.9, 63.9, 12}, + {86.2, 63.2, 12}, + {64.7, 63, 12}, + {77.9, 61.7, 12}, + {78.7, 61.4, 12}, + {71.3, 61.3, 12}, + {82.2, 59.2, 12}, + {43.4, 81.2, 12}, + {63.3, 77, 12}, + {86.6, 65.2, 12}, + {23, 12.4, 406}, + {23.1, 11.6, 406}, + }, + [829] = { + {30.1, 71.6, 1}, + }, + [830] = { + {39.6, 12.9, 40}, + {40, 12.2, 40}, + {39.6, 12.2, 40}, + {40.9, 12, 40}, + {49.8, 11.8, 40}, + {40.1, 11.6, 40}, + {40.7, 11.5, 40}, + {41.2, 11.3, 40}, + {50.7, 11, 40}, + {50.3, 11, 40}, + {46.7, 10.6, 40}, + {47.5, 10.6, 40}, + {49.9, 10.4, 40}, + {47.9, 10.3, 40}, + {50.5, 10.3, 40}, + {46.4, 10.2, 40}, + {46.1, 9.6, 40}, + {39.6, 12.3, 40}, + {41, 12.2, 40}, + {40.1, 11.7, 40}, + {40.8, 11.5, 40}, + {41.1, 11.2, 40}, + }, + [831] = { + {31.1, 24.2, 40}, + {31.6, 24.1, 40}, + {31.9, 23.3, 40}, + {31.4, 23.3, 40}, + {32.3, 22.7, 40}, + {31.8, 22.6, 40}, + {32.6, 22, 40}, + {33, 21.8, 40}, + {32.5, 21.4, 40}, + {34.9, 18.5, 40}, + {36, 16.7, 40}, + {36.6, 16.2, 40}, + {31.8, 23.5, 40}, + {31.7, 22.7, 40}, + }, + [833] = { + {35.2, 42.7, 40}, + {62.3, 42, 40}, + {32.6, 40.2, 40}, + {35, 39.1, 40}, + {30.5, 38.2, 40}, + {36.5, 36.5, 40}, + {60.1, 36.4, 40}, + {30.5, 36.4, 40}, + {52, 35.1, 40}, + {36, 33.6, 40}, + {61.9, 32.2, 40}, + {33.3, 31.6, 40}, + {33, 30.3, 40}, + {35.9, 29.8, 40}, + {41.7, 26.7, 40}, + {39.7, 26.5, 40}, + {41.1, 26.2, 40}, + {37.3, 25.1, 40}, + {61.8, 23.4, 40}, + {46.4, 20.5, 40}, + {41.1, 18.1, 40}, + {47.5, 14.8, 40}, + {62.1, 32.5, 40}, + }, + [834] = { + {19.7, 84, 12}, + {17.6, 78.5, 12}, + {17.5, 75.5, 12}, + {16.8, 74, 12}, + {51.2, 46.1, 40}, + {50.5, 45, 40}, + {62.7, 42.3, 40}, + {35.4, 42.2, 40}, + {55.2, 40.8, 40}, + {61.1, 39.7, 40}, + {30.8, 39.3, 40}, + {53.3, 37.9, 40}, + {60.2, 37.7, 40}, + {62.7, 36.5, 40}, + {41.5, 35, 40}, + {41.9, 32, 40}, + {46.2, 31.1, 40}, + {59.4, 30.5, 40}, + {55.2, 29.3, 40}, + {39.9, 29.3, 40}, + {50.7, 29.2, 40}, + {43.4, 29.1, 40}, + {61.7, 29.1, 40}, + {36, 28.9, 40}, + {58.8, 28.1, 40}, + {40.9, 27.7, 40}, + {54.6, 27.7, 40}, + {36.9, 27.1, 40}, + {56.2, 26.8, 40}, + {47.5, 26.5, 40}, + {41.2, 26.4, 40}, + {61.5, 26.3, 40}, + {36.4, 26, 40}, + {38.1, 23.6, 40}, + {58.8, 22.5, 40}, + {56, 22.4, 40}, + {53.4, 22.1, 40}, + {39.6, 20.8, 40}, + {61.8, 20.8, 40}, + {41.6, 19, 40}, + {57, 18.2, 40}, + {58, 16.7, 40}, + {47.4, 15.3, 40}, + {59.8, 15.3, 40}, + {58.4, 14.7, 40}, + {59.6, 12.3, 40}, + {59, 10.8, 40}, + {61.9, 32.2, 40}, + {51.3, 46.1, 40}, + {50.5, 45.1, 40}, + {55, 40.7, 40}, + {53.3, 38.2, 40}, + {62.7, 36.4, 40}, + {55.4, 30.2, 40}, + {55.1, 29.5, 40}, + {38.2, 23.8, 40}, + {53.3, 22.5, 40}, + }, + [854] = { + {31.3, 44.7, 33}, + {30.7, 44, 33}, + {32.1, 43.1, 33}, + {32, 42.5, 33}, + }, + [855] = { + {24.2, 16.3, 33}, + {24.7, 16, 33}, + {24, 15.7, 33}, + }, + [856] = { + {30.5, 23.5, 33}, + {30.6, 23.5, 33}, + }, + [858] = { + {24.9, 64, 8}, + {22.7, 62.2, 8}, + {26.1, 62, 8}, + {28.3, 61.9, 8}, + {25.1, 59.2, 8}, + {22.3, 59, 8}, + {34.4, 55.6, 8}, + {20, 53.3, 8}, + {29.3, 53.3, 8}, + {17.7, 53.3, 8}, + {31.6, 50.5, 8}, + {26, 49.5, 8}, + {26.8, 48.6, 8}, + {26.4, 45.9, 8}, + {27.7, 43.4, 8}, + {21.3, 42.5, 8}, + {22.5, 37.7, 8}, + {38.8, 37.5, 8}, + {47.9, 34.3, 8}, + {37.5, 32.1, 8}, + {36.5, 30.9, 8}, + {39.5, 30.8, 8}, + {71.4, 30, 8}, + {40.8, 29.8, 8}, + {37.1, 29.2, 8}, + {43.2, 29, 8}, + {33.2, 28.9, 8}, + {28.3, 62, 8}, + {25.4, 59.3, 8}, + {34.5, 55.2, 8}, + {17.6, 53.4, 8}, + {19.8, 52.9, 8}, + {47.8, 33.8, 8}, + {37.1, 28.6, 8}, + }, + [922] = { + {80.5, 93.9, 8}, + {86.5, 86.4, 8}, + {83.2, 84.7, 8}, + {84.5, 83.1, 8}, + {85.6, 82.5, 8}, + {89.8, 80.4, 8}, + {88, 78.3, 8}, + {90.2, 23.8, 8}, + {88, 20.5, 8}, + {89.1, 18.3, 8}, + {87, 18.2, 8}, + {83, 15.5, 8}, + {84.4, 14.8, 8}, + {84.7, 13.9, 8}, + {82.7, 12.6, 8}, + {84.4, 11.9, 8}, + {81.8, 9.6, 8}, + {81.2, 9.3, 8}, + {78.7, 8, 8}, + {80.3, 7.4, 8}, + {73.7, 5.3, 8}, + {78.9, 5.3, 8}, + {77.5, 5.2, 8}, + {76, 5.2, 8}, + {76, 3, 8}, + }, + [923] = { + {22, 78.3, 10}, + {24.4, 78, 10}, + {20.1, 77.7, 10}, + {43.5, 77.4, 10}, + {52.3, 76.2, 10}, + {25.7, 74.8, 10}, + {27.6, 74.3, 10}, + {54.1, 74.1, 10}, + {47.8, 73.9, 10}, + {53.4, 73.1, 10}, + {52.1, 72.9, 10}, + {18.2, 72, 10}, + {26.3, 71, 10}, + {42.9, 70.8, 10}, + {47.8, 70.3, 10}, + {50, 69.1, 10}, + {22.1, 68.8, 10}, + {26.7, 67.6, 10}, + {55.5, 65.7, 10}, + {37.5, 65.4, 10}, + {28.4, 65.4, 10}, + {53.5, 65.1, 10}, + {48.8, 65.1, 10}, + {17.8, 65.1, 10}, + {40.4, 64.9, 10}, + {56, 64.4, 10}, + {51.2, 64.1, 10}, + {24.8, 64, 10}, + {54.6, 63.7, 10}, + {46.9, 63.3, 10}, + {28.3, 62.8, 10}, + {30.7, 62.6, 10}, + {32.5, 62.4, 10}, + {56.4, 62.1, 10}, + {59.8, 60.8, 10}, + {61, 59.3, 10}, + {69.5, 30.8, 10}, + {69.3, 30.1, 10}, + {67.2, 28.1, 10}, + {66.1, 24, 10}, + {49, 65.2, 10}, + {55.9, 64.3, 10}, + }, + [930] = { + {80.7, 62.8, 10}, + {34.8, 59.5, 10}, + {37.3, 59.5, 10}, + {77.8, 58.4, 10}, + {35.1, 58.2, 10}, + {83.8, 57.6, 10}, + {31.4, 57.5, 10}, + {37.2, 57.5, 10}, + {31.3, 56.5, 10}, + {35.7, 55.8, 10}, + {30.1, 55.2, 10}, + {30.4, 55.2, 10}, + {31.2, 54.9, 10}, + {32.4, 54.7, 10}, + {35.6, 54.7, 10}, + {34.3, 54.1, 10}, + {80.4, 54.1, 10}, + {82.1, 53.3, 10}, + {29.2, 53.3, 10}, + {86.9, 53.1, 10}, + {26.2, 52.3, 10}, + {87.1, 52.1, 10}, + {86, 52.1, 10}, + {81.6, 52.1, 10}, + {26.7, 52, 10}, + {85.5, 51.7, 10}, + {32.8, 51.7, 10}, + {79.3, 51.4, 10}, + {30.1, 51.4, 10}, + {30.1, 51.2, 10}, + {86.3, 50.3, 10}, + {26.9, 50.3, 10}, + {31.5, 49.9, 10}, + {33.8, 49.7, 10}, + {29.6, 49.4, 10}, + {30.1, 48.4, 10}, + {32.6, 48.2, 10}, + {27.4, 47.3, 10}, + {26.4, 47, 10}, + {29.6, 46.6, 10}, + {31.5, 46.5, 10}, + {34.9, 46, 10}, + {33.4, 45.9, 10}, + {28.1, 44.7, 10}, + {31.6, 42.4, 10}, + {27.7, 40.8, 10}, + {32.9, 39.5, 10}, + {32.3, 39.4, 10}, + {30.8, 36, 10}, + {32.8, 36, 10}, + {31.9, 34.3, 10}, + {33.6, 34.1, 10}, + {32, 34.1, 10}, + {30.9, 32.5, 10}, + {35, 32.4, 10}, + {34.6, 59.1, 10}, + {31.4, 57.1, 10}, + {29.5, 53.5, 10}, + {31.3, 49.5, 10}, + {31.8, 46.6, 10}, + {32.8, 32.7, 10}, + }, + [949] = { + {21.5, 46.3, 10}, + {18.1, 43.7, 10}, + {18.6, 41.9, 10}, + {17, 39.6, 10}, + {21, 39.1, 10}, + {23.4, 36.7, 10}, + {18.9, 42.1, 10}, + }, + [976] = { + {45.9, 10, 33}, + {45.9, 9.6, 33}, + }, + [977] = { + {45.7, 9.7, 33}, + {45.8, 9.4, 33}, + }, + [1015] = { + {57.8, 37.6, 11}, + {57, 37.1, 11}, + {57.5, 33.5, 11}, + {53, 30.3, 11}, + {55.7, 30, 11}, + {51.4, 28.9, 11}, + {56.2, 28.7, 11}, + {56.8, 28.7, 11}, + {54.7, 28, 11}, + {49.9, 27.6, 11}, + {55.6, 26.1, 11}, + {54.6, 25.5, 11}, + {52.4, 25.5, 11}, + {55.5, 24.7, 11}, + {40, 24.5, 11}, + {41.6, 24.1, 11}, + {38.5, 24.1, 11}, + {52.5, 23.4, 11}, + {54.4, 23.2, 11}, + {41, 23.2, 11}, + {40.1, 21.9, 11}, + {41.7, 21.8, 11}, + {53.1, 30.4, 11}, + {54.8, 27.7, 11}, + {54.7, 25.7, 11}, + {13.2, 7.9, 5602}, + {12.6, 7.4, 5602}, + {12.9, 4.7, 5602}, + {9.5, 2.3, 5602}, + {11.5, 2, 5602}, + {8.2, 1.2, 5602}, + {11.9, 1, 5602}, + {12.4, 1, 5602}, + {10.9, 0.2, 5602}, + {7.1, 0.1, 5602}, + }, + [1016] = { + {57.8, 35.4, 11}, + {59.5, 30.3, 11}, + {56.3, 27.3, 11}, + {57.3, 25.4, 11}, + {35.6, 25, 11}, + {39, 23.6, 11}, + {57.4, 23.4, 11}, + {38.5, 23.1, 11}, + {61.7, 23.1, 11}, + {56.2, 22.5, 11}, + {61.4, 22.1, 11}, + {30.2, 21.6, 11}, + {30.3, 21.2, 11}, + {39.6, 21, 11}, + {30.1, 20.8, 11}, + {30.6, 20.7, 11}, + {30.7, 20.3, 11}, + {32.2, 19.5, 11}, + {13.1, 6.2, 5602}, + {14.5, 2.2, 5602}, + }, + [1017] = { + {56.4, 31.4, 11}, + {57.5, 27.7, 11}, + {56.9, 27.6, 11}, + {58.2, 26.8, 11}, + {37.6, 23.4, 11}, + {37.9, 22.8, 11}, + {37.3, 22.8, 11}, + {36.6, 22.5, 11}, + {37.7, 22.4, 11}, + {35.4, 21.9, 11}, + {58.7, 21.5, 11}, + {59.6, 21.3, 11}, + {58.9, 21.2, 11}, + {34.5, 20.7, 11}, + {37.4, 20.2, 11}, + {38.2, 19.6, 11}, + {37.6, 19.4, 11}, + {33, 18.1, 11}, + {30.4, 16.8, 11}, + {31.2, 16.2, 11}, + {12, 3.1, 5602}, + {12.9, 0.2, 5602}, + {12.4, 0.1, 5602}, + }, + [1018] = { + {72, 39.9, 11}, + {71.7, 39.1, 11}, + {70, 38.7, 11}, + {67.2, 38, 11}, + {70.2, 37.9, 11}, + {69.8, 37.4, 11}, + {68, 36.3, 11}, + {67.3, 36.3, 11}, + {68, 35.8, 11}, + {69.3, 35.1, 11}, + {69.5, 34.4, 11}, + {69.6, 33.5, 11}, + {68.4, 33.1, 11}, + {68.1, 32.9, 11}, + {66.8, 32.9, 11}, + {66.4, 32.8, 11}, + {69.4, 32.1, 11}, + {66.5, 31.9, 11}, + {65.8, 31.3, 11}, + {67.1, 30.9, 11}, + {67.8, 30.7, 11}, + {69.7, 30.4, 11}, + {65.3, 30.2, 11}, + {70.3, 30, 11}, + {67.3, 29.5, 11}, + {70.6, 29.2, 11}, + {69.1, 28.8, 11}, + {65.1, 28.7, 11}, + {67.4, 28.6, 11}, + {65.9, 28.6, 11}, + {68.7, 27.8, 11}, + {35.2, 20.4, 11}, + {35.5, 19.5, 11}, + {35.4, 18.7, 11}, + {33.6, 17, 11}, + {32.7, 15.3, 11}, + {32.1, 14.6, 11}, + {69, 35.3, 11}, + {24.1, 9.6, 5602}, + {23.8, 9, 5602}, + {22.5, 8.7, 5602}, + {20.3, 8.1, 5602}, + {22.6, 8, 5602}, + {22.3, 7.6, 5602}, + {21, 6.8, 5602}, + {20.4, 6.8, 5602}, + {21, 6.4, 5602}, + {21.7, 6.1, 5602}, + {22.1, 5.3, 5602}, + {22.2, 4.7, 5602}, + {21.3, 4.4, 5602}, + {21, 4.2, 5602}, + {20.1, 4.2, 5602}, + {19.7, 4.1, 5602}, + {22, 3.6, 5602}, + {19.8, 3.4, 5602}, + {19.3, 3, 5602}, + {20.3, 2.7, 5602}, + {20.8, 2.5, 5602}, + {22.3, 2.3, 5602}, + {18.9, 2.1, 5602}, + {22.7, 2, 5602}, + {20.5, 1.6, 5602}, + {23, 1.4, 5602}, + {21.8, 1.1, 5602}, + {18.8, 1, 5602}, + {20.5, 0.9, 5602}, + {19.4, 0.9, 5602}, + {21.5, 0.3, 5602}, + }, + [1019] = { + {69.3, 32.9, 11}, + {69.9, 32.8, 11}, + {67.9, 32.4, 11}, + {69.9, 32, 11}, + {70.2, 31.8, 11}, + {69.1, 31.6, 11}, + {67.6, 31.6, 11}, + {68.7, 31.3, 11}, + {70.7, 30.8, 11}, + {69.3, 29.8, 11}, + {69.7, 29.1, 11}, + {70.2, 28.4, 11}, + {69.1, 28.2, 11}, + {67.9, 27.5, 11}, + {40.1, 18.4, 11}, + {39.7, 17.1, 11}, + {38.5, 16.9, 11}, + {38.2, 15.9, 11}, + {38.1, 15.7, 11}, + {33.7, 14.9, 11}, + {33, 13, 11}, + {34, 12.4, 11}, + {24.7, 98.9, 45}, + {25.8, 98.2, 45}, + {22, 4.2, 5602}, + {22.4, 4.1, 5602}, + {20.9, 3.8, 5602}, + {22.4, 3.5, 5602}, + {22.7, 3.4, 5602}, + {21.8, 3.2, 5602}, + {20.6, 3.2, 5602}, + {21.5, 3, 5602}, + {23, 2.6, 5602}, + {22, 1.8, 5602}, + {22.3, 1.3, 5602}, + {22.7, 0.7, 5602}, + {21.8, 0.6, 5602}, + {20.9, 0.1, 5602}, + }, + [1020] = { + {23, 59.8, 11}, + {23.1, 59.6, 11}, + {22.5, 59, 11}, + {23.2, 54.1, 11}, + {22.4, 53.4, 11}, + {19.8, 52.5, 11}, + {21.4, 51.9, 11}, + {23.3, 51.2, 11}, + {24.1, 50.4, 11}, + {23, 50.1, 11}, + {25.2, 49.8, 11}, + {20.7, 48.8, 11}, + {24.2, 48.3, 11}, + {26, 47.5, 11}, + {23.9, 47.2, 11}, + {24.6, 46.8, 11}, + {27.2, 46.8, 11}, + {29.5, 46.5, 11}, + {26.4, 46.3, 11}, + {24.3, 46.3, 11}, + {29.7, 46.2, 11}, + {27.3, 43.7, 11}, + {28.2, 42.7, 11}, + {29.4, 42.5, 11}, + {28.8, 42.4, 11}, + {37.1, 42.3, 11}, + {31.5, 42.1, 11}, + {22.2, 53.5, 11}, + {27.2, 43.5, 11}, + {28.1, 42.8, 11}, + {29.5, 42.4, 11}, + {37.1, 42.2, 11}, + }, + [1021] = { + {23.7, 60.4, 11}, + {23, 56.5, 11}, + {23.5, 56, 11}, + {22.6, 55.9, 11}, + {23, 55.6, 11}, + {24.1, 53, 11}, + {24.2, 52.5, 11}, + {23.9, 52.4, 11}, + {22.8, 50.7, 11}, + {26.2, 49.1, 11}, + {25.8, 48.8, 11}, + {26.1, 48.6, 11}, + {29.3, 45.4, 11}, + {29.8, 45.2, 11}, + {29.9, 44.4, 11}, + {30.2, 44, 11}, + {29.4, 45.4, 11}, + {30.2, 44.1, 11}, + }, + [1022] = { + {35.6, 51.9, 11}, + {32.8, 51, 11}, + {34.5, 50.9, 11}, + {35.3, 50.8, 11}, + {35.9, 49.6, 11}, + {35.2, 49.5, 11}, + {30.5, 48.7, 11}, + {31.3, 48.5, 11}, + {36.1, 48.4, 11}, + {31.7, 47, 11}, + {34.4, 45.9, 11}, + {33.7, 44.8, 11}, + {35.8, 43.8, 11}, + {34.4, 43.7, 11}, + {34.4, 50.9, 11}, + {35.1, 50.8, 11}, + {35.3, 49.5, 11}, + {35.9, 49.4, 11}, + {30.6, 48.7, 11}, + {31.8, 46.9, 11}, + {34.5, 45.8, 11}, + {33.6, 44.6, 11}, + {35.7, 44.1, 11}, + }, + [1023] = { + {33.7, 49.8, 11}, + {34.4, 49.6, 11}, + {32.2, 48.6, 11}, + {33.7, 48.5, 11}, + {32.8, 48.4, 11}, + {34.6, 47.3, 11}, + {33.3, 47, 11}, + {35.2, 46.2, 11}, + {33.6, 46.1, 11}, + {34.3, 49.5, 11}, + {32.2, 48.7, 11}, + {33.8, 48.3, 11}, + {32.9, 48.3, 11}, + {34.7, 47.1, 11}, + {35.2, 46.3, 11}, + {33.6, 46.2, 11}, + }, + [1082] = { + {56.1, 56.8, 8}, + {56.1, 56.5, 8}, + {53.6, 56, 8}, + {53.6, 55.6, 8}, + {56.5, 52.3, 8}, + {55, 49.6, 8}, + {56.6, 48, 8}, + {53.4, 47.3, 8}, + {57.7, 46.1, 8}, + {47.7, 43.3, 8}, + {53.5, 42.4, 8}, + {55.2, 41.3, 8}, + {52.2, 41, 8}, + {48.4, 39.1, 8}, + {56.5, 39.1, 8}, + {54.7, 36.7, 8}, + {59.4, 34.6, 8}, + {56.5, 34.6, 8}, + {60.7, 32.4, 8}, + {63.3, 32, 8}, + {62.3, 30.3, 8}, + {72, 19.8, 8}, + {69.8, 18.6, 8}, + {72.1, 15.3, 8}, + {69.5, 15.1, 8}, + {47.5, 43, 8}, + {52.2, 41.5, 8}, + {56.9, 39, 8}, + {54.5, 36.4, 8}, + {59.6, 35.1, 8}, + {60.5, 32.2, 8}, + {69.6, 15.2, 8}, + }, + [1084] = { + {28.9, 56.1, 8}, + {27.1, 55.1, 8}, + {30.5, 54.7, 8}, + {25.9, 52.2, 8}, + {34.4, 51.8, 8}, + {37.4, 51.1, 8}, + {24.1, 50.3, 8}, + {32, 47.2, 8}, + {23.3, 44.9, 8}, + {24.6, 42.6, 8}, + {24.5, 39, 8}, + {30.4, 38.9, 8}, + {32.7, 38.9, 8}, + {35.7, 36.9, 8}, + {29.3, 36.4, 8}, + {25.5, 35.9, 8}, + {38.6, 35.3, 8}, + {42.1, 34.4, 8}, + {31.1, 33.8, 8}, + {34.1, 33, 8}, + {28.6, 56, 8}, + {30.3, 54.8, 8}, + {37.6, 51.2, 8}, + {24.6, 39.2, 8}, + }, + [1087] = { + {82.6, 99.5, 8}, + {76.3, 97.4, 8}, + {80.6, 96.8, 8}, + {83.8, 95.4, 8}, + {84, 64.8, 8}, + {85.3, 63.2, 8}, + {86.8, 56.6, 8}, + {87.3, 38.6, 8}, + {83.4, 36.3, 8}, + {86.5, 34.1, 8}, + {84.1, 33.3, 8}, + {81.2, 32.6, 8}, + {82.7, 30.9, 8}, + {79.7, 30.6, 8}, + {78, 28.4, 8}, + {79.4, 26.7, 8}, + {82.6, 25.9, 8}, + {81.3, 23.7, 8}, + {78.2, 23.7, 8}, + {86.2, 20, 8}, + {81, 19.8, 8}, + {78.2, 19.7, 8}, + {75.5, 19.4, 8}, + {84.2, 15.3, 8}, + {78, 14.6, 8}, + {75.1, 14.6, 8}, + {77, 13.2, 8}, + {82.8, 12.3, 8}, + {79.7, 7.4, 8}, + {76.7, 5.8, 8}, + {85.4, 63.4, 8}, + {86.7, 56.2, 8}, + {81.3, 32.7, 8}, + {82.5, 26.1, 8}, + {81.2, 23.7, 8}, + {78, 23.3, 8}, + {78, 19.9, 8}, + }, + [1088] = { + {78.9, 98.1, 8}, + {89.3, 86.1, 8}, + {95.1, 77.1, 8}, + {97, 71.8, 8}, + {94.9, 70.6, 8}, + {93.1, 69.9, 8}, + {93.4, 68.4, 8}, + {97.2, 62.9, 8}, + {93.5, 61.9, 8}, + {95, 56.1, 8}, + {97.2, 54.3, 8}, + {94.9, 46.6, 8}, + {96, 46.3, 8}, + {93.5, 42.2, 8}, + {94.9, 40.1, 8}, + {93.4, 37.9, 8}, + {93.4, 35.7, 8}, + {94.3, 32.4, 8}, + {92, 31.4, 8}, + {92.8, 30.1, 8}, + {91.4, 19.7, 8}, + {79.3, 2.4, 8}, + {93.7, 62.3, 8}, + {93.6, 35.5, 8}, + {94.1, 32.7, 8}, + }, + [1108] = { + {38.7, 18.3, 33}, + {36.6, 18.3, 33}, + {37.7, 18.3, 33}, + {37.2, 17.8, 33}, + {38, 17.6, 33}, + {39.2, 17.6, 33}, + {38.7, 16.8, 33}, + {37.5, 16.8, 33}, + {36.6, 16.7, 33}, + {37.2, 16, 33}, + {38.3, 15.7, 33}, + {36.7, 18.3, 33}, + {39.2, 17.7, 33}, + {37.3, 16.2, 33}, + }, + [1109] = { + {37, 69.1, 40}, + {39.8, 68.2, 40}, + {40.5, 68.1, 40}, + {40.2, 67.6, 40}, + {39.7, 67, 40}, + {40.4, 66.9, 40}, + {37.5, 66, 40}, + {35.2, 65, 40}, + {45.7, 63.5, 40}, + {39, 62.3, 40}, + {39.6, 62.1, 40}, + {50.2, 61.9, 40}, + {55.7, 60.9, 40}, + {40.9, 59.7, 40}, + {52.4, 59.3, 40}, + {50.5, 59.3, 40}, + {40.1, 59.2, 40}, + {49.4, 56.4, 40}, + {48.4, 54.8, 40}, + {44.7, 53.7, 40}, + {46.9, 51.3, 40}, + {48.7, 50.8, 40}, + {42.8, 50.8, 40}, + {43.2, 49.1, 40}, + {59.9, 46.4, 40}, + {43.8, 46.4, 40}, + {61.1, 46.2, 40}, + {45.9, 45.4, 40}, + {43.6, 43.8, 40}, + {60.7, 43.6, 40}, + {39.9, 42.9, 40}, + {43.9, 42, 40}, + {39.3, 42, 40}, + {35.5, 41.2, 40}, + {39.7, 39.8, 40}, + {33.3, 39.2, 40}, + {41.9, 39.2, 40}, + {39.5, 38.5, 40}, + {29.9, 38.3, 40}, + {36.2, 38, 40}, + {33.1, 36.8, 40}, + {38.2, 34.5, 40}, + {39.2, 29.3, 40}, + {39.1, 25, 40}, + {51.2, 24.9, 40}, + {39.9, 24, 40}, + {61.3, 44.9, 40}, + {59.6, 44.5, 40}, + {59, 43.6, 40}, + {45.7, 63.7, 40}, + {45.8, 63.5, 40}, + {50.5, 62.3, 40}, + {50.5, 59.2, 40}, + {52.3, 59.1, 40}, + {59.8, 46.7, 40}, + {43.6, 46.4, 40}, + {36, 42, 40}, + }, + [1111] = { + {56.1, 67.9, 11}, + {54.8, 66.7, 11}, + {54.8, 66.6, 11}, + {57.3, 66.5, 11}, + {56.3, 65.4, 11}, + {53, 65.4, 11}, + {57.3, 65.3, 11}, + {53.8, 65.2, 11}, + {54.7, 64.7, 11}, + {55.3, 63.7, 11}, + {46.8, 63.6, 11}, + {54, 63.3, 11}, + {51.7, 63, 11}, + {52.7, 63, 11}, + {55.5, 63, 11}, + {46.9, 62.9, 11}, + {50.4, 62.9, 11}, + {52.4, 62.8, 11}, + {57, 62.8, 11}, + {54.8, 62.8, 11}, + {49, 62.7, 11}, + {46.1, 62.3, 11}, + {51.6, 62.1, 11}, + {46.5, 62, 11}, + {46.8, 62, 11}, + {51.2, 61.8, 11}, + {56.7, 61.7, 11}, + {56, 61.6, 11}, + {49, 61.4, 11}, + {50.3, 61, 11}, + {56.2, 60.9, 11}, + {47.9, 59.8, 11}, + {50.3, 59.6, 11}, + {46.2, 59.4, 11}, + {50, 58.8, 11}, + {11.9, 31.2, 5602}, + {10.8, 30.2, 5602}, + {10.8, 30.1, 5602}, + {12.8, 30.1, 5602}, + {12, 29.2, 5602}, + {9.5, 29.2, 5602}, + {12.7, 29.2, 5602}, + {10.1, 29.1, 5602}, + {10.8, 28.7, 5602}, + {11.2, 27.9, 5602}, + {4.7, 27.8, 5602}, + {10.3, 27.6, 5602}, + {8.5, 27.4, 5602}, + {9.2, 27.4, 5602}, + {11.4, 27.3, 5602}, + {4.8, 27.3, 5602}, + {7.4, 27.3, 5602}, + {9, 27.2, 5602}, + {12.5, 27.2, 5602}, + {10.8, 27.2, 5602}, + {6.4, 27.1, 5602}, + {4.2, 26.8, 5602}, + {8.4, 26.7, 5602}, + {4.4, 26.6, 5602}, + {4.7, 26.6, 5602}, + {8, 26.5, 5602}, + {12.3, 26.3, 5602}, + {11.7, 26.3, 5602}, + {6.4, 26.1, 5602}, + {7.4, 25.8, 5602}, + {11.9, 25.8, 5602}, + {5.6, 24.9, 5602}, + {7.4, 24.7, 5602}, + {4.2, 24.6, 5602}, + {7.2, 24.1, 5602}, + }, + [1112] = { + {47.1, 61.5, 11}, + {47.3, 59.2, 11}, + {5.1, 24.4, 5602}, + }, + [1114] = { + {47.7, 38, 33}, + {48.6, 37.9, 33}, + {44.4, 37.8, 33}, + {47.1, 37.2, 33}, + {48.1, 37.2, 33}, + {45.5, 36.5, 33}, + {46.5, 36.5, 33}, + {44.7, 36.4, 33}, + {48.6, 36.2, 33}, + {48.2, 35.7, 33}, + {49.1, 35.4, 33}, + {45.6, 34.9, 33}, + {46.6, 34.7, 33}, + {46.1, 34.2, 33}, + {49.2, 34, 33}, + {48.1, 34, 33}, + {48.2, 32.7, 33}, + {41.9, 32.6, 33}, + {49.2, 32.4, 33}, + {44.1, 32.3, 33}, + {42.3, 31.9, 33}, + {43.5, 31.7, 33}, + {41.9, 30.9, 33}, + {42.9, 30.9, 33}, + {43.9, 30.9, 33}, + {46.1, 29.6, 33}, + {45, 29.3, 33}, + {43.4, 28.7, 33}, + {44.5, 28.7, 33}, + {45.5, 28.6, 33}, + {41.2, 28.5, 33}, + {42.3, 28.5, 33}, + {40.8, 28.1, 33}, + {45, 27.8, 33}, + {43, 27.7, 33}, + {41.9, 27.6, 33}, + {44.5, 27.2, 33}, + {42.3, 27, 33}, + {43, 26.2, 33}, + {40.8, 26.2, 33}, + {41.8, 26.2, 33}, + {41.9, 24.6, 33}, + {42.8, 31, 33}, + {41.8, 26, 33}, + {40.8, 26, 33}, + {41.8, 24.4, 33}, + }, + [1125] = { + {42.2, 67.6, 1}, + {44.4, 67.1, 1}, + {43.2, 66.1, 1}, + {45.7, 66, 1}, + {43.3, 65.8, 1}, + {42.2, 65.7, 1}, + {44.8, 65.6, 1}, + {46.2, 65.4, 1}, + {45.1, 65.4, 1}, + {41.9, 65.2, 1}, + {42.3, 65.1, 1}, + {38.6, 64.6, 1}, + {46.1, 64.5, 1}, + {45.4, 64.1, 1}, + {47.7, 64.1, 1}, + {45.4, 64, 1}, + {46.2, 63.8, 1}, + {39.4, 63.8, 1}, + {39.4, 63.4, 1}, + {44.3, 63.2, 1}, + {36.5, 63.1, 1}, + {36.4, 62.9, 1}, + {48.7, 62.9, 1}, + {36.2, 62.1, 1}, + {44.9, 62.1, 1}, + {46.2, 61.9, 1}, + {49.7, 61.8, 1}, + {45.9, 61.7, 1}, + {47.1, 61.5, 1}, + {47.4, 61.1, 1}, + {40.5, 60.5, 1}, + {41.9, 60.4, 1}, + {48.5, 60.1, 1}, + {36.6, 60, 1}, + {42.3, 59.9, 1}, + {37.8, 59.8, 1}, + {35, 59.6, 1}, + {39.2, 59.5, 1}, + {33.7, 59.5, 1}, + {44.2, 59.4, 1}, + {46.6, 59.4, 1}, + {49.2, 59.3, 1}, + {46, 59, 1}, + {48.6, 58.6, 1}, + {51.8, 58.5, 1}, + {34.7, 58.4, 1}, + {41.2, 58.3, 1}, + {31.4, 57.9, 1}, + {39.5, 57.8, 1}, + {44.8, 57.6, 1}, + {32.7, 57.1, 1}, + {34.4, 57.1, 1}, + {45.8, 57, 1}, + {42.3, 56.8, 1}, + {43.1, 56.4, 1}, + {43.4, 55.9, 1}, + {43.9, 55.8, 1}, + {44, 55.5, 1}, + {49.1, 54.8, 1}, + {30, 54.3, 1}, + {31, 53.7, 1}, + {51.5, 53.3, 1}, + {44.2, 52.9, 1}, + {51, 52.3, 1}, + {50.1, 51.9, 1}, + {51.3, 51.1, 1}, + {45.5, 50.6, 1}, + {48.5, 50.5, 1}, + {49.4, 50.5, 1}, + {49.3, 50.3, 1}, + {45.7, 50.2, 1}, + {45.4, 49.9, 1}, + {48.1, 49.1, 1}, + {43.3, 66.3, 1}, + {42.4, 65.4, 1}, + {36.5, 62.9, 1}, + {47.4, 61.2, 1}, + {34.4, 57.2, 1}, + {44, 55.6, 1}, + {45.4, 50, 1}, + }, + [1126] = { + {47.3, 63.9, 1}, + {49.4, 62.6, 1}, + {50.4, 60, 1}, + {49.7, 58.6, 1}, + {48, 56.7, 1}, + {48.2, 56.6, 1}, + {34.3, 55.6, 1}, + {59.8, 55.3, 1}, + {62.4, 55.1, 1}, + {49.2, 54.8, 1}, + {35.6, 54.7, 1}, + {58.7, 54.1, 1}, + {36.3, 54.1, 1}, + {64.2, 53.9, 1}, + {37.3, 52.7, 1}, + {56.8, 52.7, 1}, + {36.3, 52.2, 1}, + {50.5, 52.1, 1}, + {38.1, 51.1, 1}, + {37, 51.1, 1}, + {38.1, 51, 1}, + {67, 50.3, 1}, + {65.3, 50.2, 1}, + {38.7, 48.6, 1}, + {39.1, 48.1, 1}, + {55.9, 47.8, 1}, + {56.1, 47.8, 1}, + {48.5, 47.3, 1}, + {48.1, 47.2, 1}, + {41.4, 47.2, 1}, + {48.1, 47.1, 1}, + {39.6, 47, 1}, + {38.5, 46.6, 1}, + {45.2, 45.3, 1}, + {50.4, 43.2, 1}, + {45.4, 41.6, 1}, + {45.1, 41.6, 1}, + {41.7, 39.5, 1}, + {43.5, 38, 1}, + {43.5, 34.3, 1}, + {41.2, 30.5, 1}, + {43.9, 29.7, 1}, + {50.5, 60, 1}, + {49.5, 58.4, 1}, + {48.4, 56.8, 1}, + {36.4, 54.2, 1}, + {56.2, 47.7, 1}, + {41.2, 47.2, 1}, + {48.1, 47, 1}, + {39.6, 46.9, 1}, + {38.6, 46.7, 1}, + {45.4, 41.7, 1}, + }, + [1127] = { + {70.7, 61.6, 1}, + {74.7, 61.3, 1}, + {72.4, 61, 1}, + {63.9, 60.5, 1}, + {60.9, 60.3, 1}, + {59.6, 60.1, 1}, + {56.6, 60, 1}, + {58.5, 59.7, 1}, + {63.1, 59.2, 1}, + {55.8, 59, 1}, + {58.7, 58.9, 1}, + {59.9, 58.6, 1}, + {61.4, 58.4, 1}, + {53.4, 58.2, 1}, + {59, 57.9, 1}, + {55.5, 57.4, 1}, + {55.6, 57.4, 1}, + {79.2, 57.2, 1}, + {61.3, 56.7, 1}, + {57.3, 56.7, 1}, + {62.8, 56.2, 1}, + {55.7, 55.8, 1}, + {71.5, 55.6, 1}, + {26.6, 55.1, 1}, + {55.3, 54.3, 1}, + {56.7, 54.3, 1}, + {27.4, 54, 1}, + {60.8, 53.9, 1}, + {28.3, 53.3, 1}, + {66.1, 53.3, 1}, + {66.2, 52.7, 1}, + {73.5, 52.1, 1}, + {30.1, 52, 1}, + {70.5, 52, 1}, + {68.7, 51.9, 1}, + {37.7, 51.9, 1}, + {67.4, 51.7, 1}, + {70.8, 51.6, 1}, + {58.2, 51.3, 1}, + {57.9, 50.4, 1}, + {69.7, 50.2, 1}, + {59.2, 50.1, 1}, + {32, 49.8, 1}, + {29.6, 49.7, 1}, + {66.8, 49.3, 1}, + {32.1, 48.8, 1}, + {73.4, 48.7, 1}, + {75.8, 48.2, 1}, + {33.5, 47.9, 1}, + {27.7, 47.4, 1}, + {40.5, 47.2, 1}, + {32.2, 47.1, 1}, + {25.9, 46.4, 1}, + {80.2, 46.3, 1}, + {37.8, 46, 1}, + {27.5, 45.8, 1}, + {26.2, 44.7, 1}, + {28.4, 44.3, 1}, + {79.8, 43.3, 1}, + {26.7, 42.9, 1}, + {28, 42.7, 1}, + {45.3, 42.6, 1}, + {29.6, 42.5, 1}, + {45.2, 42.2, 1}, + {29.2, 40, 1}, + {27.5, 39.3, 1}, + {42.4, 38.9, 1}, + {31.7, 38.7, 1}, + {30.1, 38.4, 1}, + {33.1, 37.5, 1}, + {37.5, 37.4, 1}, + {35.6, 36.8, 1}, + {31, 35.7, 1}, + {37.4, 35.6, 1}, + {81.5, 35.6, 1}, + {34.1, 35.2, 1}, + {35.5, 34.9, 1}, + {37, 34.3, 1}, + {32.4, 34.3, 1}, + {39.5, 34, 1}, + {44.2, 32, 1}, + {34.6, 31.5, 1}, + {36.8, 31.2, 1}, + {33.9, 31.1, 1}, + {40.1, 30.5, 1}, + {42.1, 30, 1}, + {43.6, 29.6, 1}, + {27.5, 53.9, 1}, + {30.1, 51.9, 1}, + {68.7, 51.7, 1}, + {33.5, 48, 1}, + {27.5, 45.7, 1}, + {32.4, 34.4, 1}, + {0.5, 79.6, 5602}, + {1.4, 69.5, 5602}, + {1.1, 66.8, 5602}, + {2.6, 59.8, 5602}, + }, + [1128] = { + {41.9, 66.8, 1}, + {38.9, 62, 1}, + {38.7, 61.1, 1}, + {41.7, 60.9, 1}, + {39.9, 60.9, 1}, + {40.5, 60.8, 1}, + {39, 60.5, 1}, + {35.5, 60, 1}, + {41.6, 59.9, 1}, + {34.4, 59.5, 1}, + {39.6, 59.1, 1}, + {43.5, 59, 1}, + {45.6, 58.2, 1}, + {45.3, 58.1, 1}, + {32.5, 57.9, 1}, + {30.8, 57.2, 1}, + {32.3, 56, 1}, + {30.4, 55.6, 1}, + {44.7, 55.4, 1}, + {45, 55.2, 1}, + {43.7, 51.9, 1}, + {50.5, 51.8, 1}, + {49.2, 51, 1}, + {44.2, 51, 1}, + {44.9, 50.2, 1}, + {48.1, 49.9, 1}, + {45.4, 49.3, 1}, + {44.3, 47.8, 1}, + {43.1, 45.9, 1}, + {43.7, 44.9, 1}, + {44.7, 55.3, 1}, + }, + [1130] = { + {55.6, 58.5, 1}, + }, + [1131] = { + {50.7, 52.5, 1}, + {51.5, 49.6, 1}, + {52.1, 48.7, 1}, + {45.8, 47.2, 1}, + {43.2, 47.1, 1}, + {45.9, 47, 1}, + {51.6, 46.4, 1}, + {51, 45, 1}, + {49, 44.6, 1}, + {48.9, 44.6, 1}, + {33.8, 43.7, 1}, + {33.5, 43.6, 1}, + {45.1, 43.3, 1}, + {34, 42.8, 1}, + {44.9, 42.4, 1}, + {45.7, 42.3, 1}, + {34.6, 42.2, 1}, + {34, 42.2, 1}, + {45.1, 41.8, 1}, + {44.9, 41.4, 1}, + {34.6, 41.3, 1}, + {34, 41.3, 1}, + {33.9, 41, 1}, + {46, 47.2, 1}, + {34.5, 41.3, 1}, + }, + [1132] = { + {34.2, 41.8, 1}, + }, + [1133] = { + {34.2, 41.9, 1}, + {34.2, 41.8, 1}, + }, + [1138] = { + {44.6, 47.1, 1}, + {45.7, 46.7, 1}, + {43.3, 46.5, 1}, + {45.6, 46.2, 1}, + {45.9, 46.2, 1}, + {45.7, 45.9, 1}, + {51.3, 45.7, 1}, + {51.3, 45.4, 1}, + {44.4, 45.3, 1}, + {49.7, 44.5, 1}, + {45.7, 46, 1}, + }, + [1140] = { + {69.9, 29.2, 11}, + {22.4, 1.4, 5602}, + }, + [1149] = { + {31.6, 28, 33}, + }, + [1150] = { + {39.9, 14.4, 33}, + {41.2, 14.1, 33}, + {41, 13.7, 33}, + {40.4, 12.3, 33}, + {39.9, 11.5, 33}, + {38.7, 11.3, 33}, + {36.9, 10.5, 33}, + {34.5, 10.5, 33}, + {34.1, 9.6, 33}, + {32.5, 8.9, 33}, + {35.7, 8.2, 33}, + {34.5, 8.1, 33}, + {35.6, 8.1, 33}, + {34.6, 10.5, 33}, + {34, 9.6, 33}, + {34.6, 8.1, 33}, + }, + [1152] = { + {37.9, 32.6, 33}, + {39.2, 31.9, 33}, + {38.4, 31.4, 33}, + {39.9, 30.8, 33}, + {38.6, 29.9, 33}, + {40.1, 29.5, 33}, + {39.5, 28.7, 33}, + {38.8, 28.5, 33}, + {40.1, 28.4, 33}, + {40.2, 27.3, 33}, + {39.4, 25, 33}, + {40.8, 24.7, 33}, + {40.1, 22.9, 33}, + {40.7, 22.2, 33}, + {41.3, 21.5, 33}, + {39.6, 21.5, 33}, + {42.1, 21.2, 33}, + {41.4, 20.7, 33}, + {40.4, 19.4, 33}, + {41.4, 19.1, 33}, + {40.9, 17.7, 33}, + {39.7, 17.5, 33}, + {41.8, 17.5, 33}, + {42.4, 16.6, 33}, + {39.8, 16, 33}, + {41.9, 15.9, 33}, + {41.3, 15.3, 33}, + {39.9, 30.7, 33}, + {38.6, 29.8, 33}, + {40.2, 27.2, 33}, + {39.7, 21.5, 33}, + {42.2, 21.2, 33}, + {42.5, 16.7, 33}, + }, + [1184] = { + {55.3, 78.3, 38}, + {60.2, 77.5, 38}, + {58.1, 77.3, 38}, + {50.5, 73.8, 38}, + {49, 73, 38}, + {48.7, 72.4, 38}, + {46.3, 72.4, 38}, + {56.6, 65.9, 38}, + {57.1, 65.2, 38}, + {62.5, 55.7, 38}, + {62.6, 54.4, 38}, + {64.7, 53.3, 38}, + {67.5, 52.4, 38}, + {69.7, 51.6, 38}, + {65.6, 50.9, 38}, + {71.2, 50.9, 38}, + {26.8, 84.4, 5602}, + {24.4, 82, 5602}, + {23.7, 81.6, 5602}, + {23.5, 81.3, 5602}, + {22.3, 81.3, 5602}, + {27.5, 78, 5602}, + {27.8, 77.6, 5602}, + {30.5, 72.8, 5602}, + {30.6, 72.1, 5602}, + {31.7, 71.6, 5602}, + {33.1, 71.1, 5602}, + {34.3, 70.7, 5602}, + {32.2, 70.3, 5602}, + {35, 70.3, 5602}, + }, + [1185] = { + {78.9, 40.9, 38}, + {80.4, 40.3, 38}, + {80.8, 40, 38}, + {75.8, 38, 38}, + {76.2, 38, 38}, + {74.3, 37.8, 38}, + {77.8, 36.4, 38}, + {75.6, 35.9, 38}, + {74.3, 34.7, 38}, + {77.5, 34.3, 38}, + {59.3, 23.3, 38}, + {39, 65.2, 5602}, + {39.7, 64.9, 5602}, + {39.9, 64.7, 5602}, + {37.4, 63.7, 5602}, + {37.5, 63.7, 5602}, + {36.6, 63.6, 5602}, + {38.4, 62.9, 5602}, + {37.3, 62.6, 5602}, + {36.6, 62, 5602}, + {38.2, 61.8, 5602}, + {28.9, 56.2, 5602}, + }, + [1186] = { + {20.8, 77.5, 38}, + {18.9, 75.5, 38}, + {39.5, 74.5, 38}, + {40.4, 71.7, 38}, + {37.8, 55.8, 38}, + {30.6, 53.9, 38}, + {37.2, 52.8, 38}, + {40.7, 52.4, 38}, + {29.4, 40.3, 38}, + {26.3, 40.3, 38}, + {24.8, 36.5, 38}, + {39.7, 33.3, 38}, + {37.1, 32.4, 38}, + {36, 32.1, 38}, + {33.4, 30.1, 38}, + {34.1, 29.8, 38}, + {32.5, 29.8, 38}, + {27.4, 28.7, 38}, + {23.8, 25.4, 38}, + {30.1, 24.7, 38}, + {27.5, 14.3, 38}, + {29.1, 13.4, 38}, + {27.2, 10.5, 38}, + {30.5, 10.4, 38}, + {20.7, 77.4, 38}, + {38.1, 56, 38}, + {40.8, 52.5, 38}, + {37.2, 32.8, 38}, + {27.5, 28.7, 38}, + {27.4, 14.2, 38}, + {9.2, 83.9, 5602}, + {8.2, 82.9, 5602}, + {18.7, 82.4, 5602}, + {19.3, 81, 5602}, + {18.1, 72.9, 5602}, + {14.2, 71.8, 5602}, + {17.6, 71.3, 5602}, + {19.5, 71.1, 5602}, + {13.6, 64.9, 5602}, + {12, 64.9, 5602}, + {11.2, 62.9, 5602}, + {18.9, 61.2, 5602}, + {17.6, 61, 5602}, + {17, 60.7, 5602}, + {15.7, 59.6, 5602}, + {16, 59.5, 5602}, + {15.2, 59.5, 5602}, + {12.6, 58.9, 5602}, + {10.7, 57.2, 5602}, + {14, 56.8, 5602}, + {12.6, 51.5, 5602}, + {13.4, 51.1, 5602}, + {12.5, 49.6, 5602}, + {14.2, 49.5, 5602}, + }, + [1188] = { + {63.2, 80.2, 38}, + {66.2, 76, 38}, + {58.6, 75, 38}, + {65.9, 74.2, 38}, + {64.4, 73.5, 38}, + {67.9, 73.3, 38}, + {68.4, 72.6, 38}, + {65.5, 69.7, 38}, + {58.4, 69.1, 38}, + {78.1, 68.7, 38}, + {40.8, 66.7, 38}, + {43.1, 63.7, 38}, + {40.9, 62.3, 38}, + {43.9, 61.3, 38}, + {73.9, 55.3, 38}, + {75.8, 54.9, 38}, + {76.1, 51.7, 38}, + {74.6, 50.6, 38}, + {67.9, 49.3, 38}, + {65.8, 47.3, 38}, + {66.3, 45.5, 38}, + {65, 45.3, 38}, + {62.5, 43.3, 38}, + {66.2, 42.7, 38}, + {75.7, 71.3, 38}, + {79.5, 71.1, 38}, + {78.2, 70.8, 38}, + {75.4, 68.2, 38}, + {78.2, 67.5, 38}, + {80.8, 64.9, 38}, + {80.7, 61.6, 38}, + {78.9, 59.1, 38}, + {76, 56.8, 38}, + {30.9, 85.4, 5602}, + {32.4, 83.2, 5602}, + {32.3, 82.3, 5602}, + {31.5, 81.9, 5602}, + {33.3, 81.8, 5602}, + {33.6, 81.5, 5602}, + {37.3, 80.8, 5602}, + {39.3, 80.7, 5602}, + {38.6, 80.5, 5602}, + {32.1, 80, 5602}, + {28.5, 79.6, 5602}, + {37.1, 79.2, 5602}, + {38.6, 78.8, 5602}, + {19.4, 78.4, 5602}, + {39.9, 77.5, 5602}, + {20.6, 76.9, 5602}, + {19.5, 76.1, 5602}, + {39.9, 75.8, 5602}, + {21, 75.6, 5602}, + {39, 74.5, 5602}, + {37.5, 73.4, 5602}, + {36.4, 72.6, 5602}, + {37.4, 72.4, 5602}, + {37.5, 70.7, 5602}, + {36.8, 70.2, 5602}, + {33.3, 69.5, 5602}, + {32.2, 68.5, 5602}, + {32.5, 67.5, 5602}, + {31.8, 67.4, 5602}, + {30.6, 66.4, 5602}, + {32.4, 66.1, 5602}, + }, + [1189] = { + {37.9, 63.4, 38}, + {72.8, 44.7, 38}, + {74.2, 44.7, 38}, + {75.8, 44.3, 38}, + {68.3, 42.8, 38}, + {76.4, 41.8, 38}, + {78, 41.6, 38}, + {68.6, 41.5, 38}, + {71.8, 40.5, 38}, + {71.7, 40.3, 38}, + {72.1, 37.9, 38}, + {71.9, 36.7, 38}, + {68.5, 36.4, 38}, + {73.2, 35.5, 38}, + {64.9, 34.5, 38}, + {63.6, 33.6, 38}, + {62.1, 33, 38}, + {58.8, 32.3, 38}, + {64.4, 29.5, 38}, + {58.9, 28.7, 38}, + {60, 26.9, 38}, + {59, 25.2, 38}, + {72.2, 37.7, 38}, + {18, 76.7, 5602}, + {35.8, 67.1, 5602}, + {36.5, 67.1, 5602}, + {37.3, 66.9, 5602}, + {33.5, 66.1, 5602}, + {37.7, 65.6, 5602}, + {38.5, 65.5, 5602}, + {33.7, 65.5, 5602}, + {35.3, 65, 5602}, + {35.3, 64.9, 5602}, + {35.5, 63.6, 5602}, + {35.4, 63, 5602}, + {33.6, 62.9, 5602}, + {36, 62.4, 5602}, + {31.8, 61.9, 5602}, + {31.1, 61.4, 5602}, + {30.4, 61.1, 5602}, + {28.7, 60.8, 5602}, + {31.5, 59.3, 5602}, + {28.7, 58.9, 5602}, + {29.3, 58, 5602}, + {28.8, 57.1, 5602}, + }, + [1190] = { + {29.9, 46.8, 38}, + {37.2, 39.9, 38}, + {27.2, 39.1, 38}, + {37.4, 38.4, 38}, + {38.3, 37.9, 38}, + {38.5, 36.1, 38}, + {37.2, 36.1, 38}, + {34.7, 36.1, 38}, + {29.9, 36, 38}, + {32.9, 35.1, 38}, + {37, 34.5, 38}, + {38.3, 34.3, 38}, + {37.1, 34.3, 38}, + {35.5, 34, 38}, + {33.3, 33.4, 38}, + {29.1, 32.8, 38}, + {33.9, 31.8, 38}, + {34.3, 30.5, 38}, + {39.6, 28.8, 38}, + {27.7, 26.6, 38}, + {31.8, 24.1, 38}, + {28.6, 23.3, 38}, + {24.3, 21.4, 38}, + {30.6, 16.6, 38}, + {30.5, 15.4, 38}, + {25.3, 13.9, 38}, + {38.2, 37.9, 38}, + {29.9, 36.5, 38}, + {34.8, 36.4, 38}, + {37.2, 36.2, 38}, + {37.2, 34.4, 38}, + {38.2, 34.2, 38}, + {39.5, 29.2, 38}, + {28.6, 23.2, 38}, + {13.9, 68.2, 5602}, + {17.6, 64.7, 5602}, + {12.5, 64.3, 5602}, + {17.7, 63.9, 5602}, + {18.1, 63.7, 5602}, + {13.9, 62.9, 5602}, + {16.4, 62.9, 5602}, + {17.6, 62.8, 5602}, + {18.3, 62.7, 5602}, + {15.4, 62.2, 5602}, + {17.5, 61.9, 5602}, + {17.6, 61.8, 5602}, + {18.1, 61.8, 5602}, + {16.7, 61.6, 5602}, + {15.6, 61.3, 5602}, + {13.5, 61, 5602}, + {15.9, 60.5, 5602}, + {16.1, 59.8, 5602}, + {18.8, 59.2, 5602}, + {12.8, 57.9, 5602}, + {14.8, 56.6, 5602}, + {13.2, 56.1, 5602}, + {11, 55.2, 5602}, + {14.2, 52.7, 5602}, + {14.2, 52.1, 5602}, + {11.5, 51.3, 5602}, + }, + [1191] = { + {52.5, 74.7, 38}, + {77.9, 73.7, 38}, + {52.2, 73.4, 38}, + {74.7, 72.5, 38}, + {50.8, 71.6, 38}, + {66.4, 68.9, 38}, + {49.1, 68.8, 38}, + {45.5, 66.6, 38}, + {58.7, 65.8, 38}, + {63.8, 65.7, 38}, + {60.2, 65.6, 38}, + {59.8, 63.7, 38}, + {56.5, 63.6, 38}, + {65, 63.4, 38}, + {44.2, 62.8, 38}, + {34.2, 62.6, 38}, + {60.1, 61.8, 38}, + {58.5, 61.3, 38}, + {62.6, 60.3, 38}, + {62.7, 59.9, 38}, + {59.9, 59.5, 38}, + {64.2, 59.3, 38}, + {65.6, 58, 38}, + {63, 57.5, 38}, + {61.6, 57.3, 38}, + {64.3, 56.9, 38}, + {41.8, 55.3, 38}, + {74.5, 52.4, 38}, + {39, 51.3, 38}, + {63.4, 50.7, 38}, + {65.2, 48.4, 38}, + {63.6, 47, 38}, + {78.4, 72.1, 38}, + {78.6, 69.1, 38}, + {49.1, 68.6, 38}, + {79.7, 67.9, 38}, + {45.6, 66.4, 38}, + {79.6, 65.4, 38}, + {85, 62.2, 38}, + {79.4, 61.1, 38}, + {83.9, 60.2, 38}, + {81.3, 59.8, 38}, + {82.8, 58.4, 38}, + {81.8, 58.3, 38}, + {63.5, 47.1, 38}, + {25.4, 82.5, 5602}, + {25.3, 81.9, 5602}, + {36.8, 81.4, 5602}, + {38.7, 81.2, 5602}, + {24.6, 80.9, 5602}, + {38.8, 79.7, 5602}, + {32.6, 79.5, 5602}, + {23.7, 79.4, 5602}, + {39.4, 79.1, 5602}, + {21.9, 78.3, 5602}, + {28.6, 78, 5602}, + {31.2, 77.9, 5602}, + {29.4, 77.8, 5602}, + {39.3, 77.7, 5602}, + {29.2, 76.9, 5602}, + {27.5, 76.8, 5602}, + {31.8, 76.7, 5602}, + {21.2, 76.4, 5602}, + {16, 76.3, 5602}, + {42.1, 76.1, 5602}, + {29.3, 75.9, 5602}, + {28.5, 75.6, 5602}, + {39.2, 75.6, 5602}, + {30.6, 75.1, 5602}, + {41.5, 75.1, 5602}, + {30.6, 75, 5602}, + {40.2, 74.9, 5602}, + {29.2, 74.7, 5602}, + {31.4, 74.6, 5602}, + {40.9, 74.1, 5602}, + {40.4, 74.1, 5602}, + {32.2, 74, 5602}, + {30.8, 73.7, 5602}, + {30.1, 73.6, 5602}, + {31.5, 73.4, 5602}, + {20, 72.6, 5602}, + {36.7, 71.1, 5602}, + {18.5, 70.5, 5602}, + {31, 70.2, 5602}, + {31.9, 69, 5602}, + {31.1, 68.3, 5602}, + }, + [1192] = { + {74.8, 50.7, 38}, + {77.3, 47.1, 38}, + {75.7, 46.6, 38}, + {74.8, 42.1, 38}, + {65.4, 41.9, 38}, + {61.3, 41.4, 38}, + {67.4, 41.3, 38}, + {66.3, 40.1, 38}, + {63, 39.9, 38}, + {61.2, 39.6, 38}, + {67.1, 39.4, 38}, + {64.9, 38.8, 38}, + {62.4, 38.4, 38}, + {66.1, 38.3, 38}, + {60.1, 37.9, 38}, + {70.1, 37.8, 38}, + {60.4, 36.2, 38}, + {63.2, 35.1, 38}, + {60.2, 34.3, 38}, + {60, 37.9, 38}, + {60.2, 33.9, 38}, + {36.8, 70.2, 5602}, + {38.1, 68.3, 5602}, + {37.3, 68.1, 5602}, + {36.8, 65.8, 5602}, + {32, 65.7, 5602}, + {30, 65.4, 5602}, + {33, 65.4, 5602}, + {32.5, 64.8, 5602}, + {30.8, 64.7, 5602}, + {29.9, 64.5, 5602}, + {32.9, 64.4, 5602}, + {31.8, 64.1, 5602}, + {30.5, 63.9, 5602}, + {32.4, 63.8, 5602}, + {29.3, 63.6, 5602}, + {34.5, 63.6, 5602}, + {29.4, 62.8, 5602}, + {30.9, 62.2, 5602}, + {29.4, 61.6, 5602}, + }, + [1194] = { + {78.6, 76.9, 38}, + {78.2, 76, 38}, + {68.5, 75.8, 38}, + {77.3, 75.3, 38}, + {71.1, 74.8, 38}, + {76.3, 74.8, 38}, + {70.6, 74.1, 38}, + {77, 74, 38}, + {74.4, 72.5, 38}, + {78, 72.4, 38}, + {71.1, 72.4, 38}, + {75.9, 72.3, 38}, + {75.8, 70.5, 38}, + {74.7, 67.7, 38}, + {79.4, 66.8, 38}, + {74.5, 64.3, 38}, + {76.8, 63.7, 38}, + {79.3, 63.3, 38}, + {78.9, 59.2, 38}, + {77.1, 58.3, 38}, + {78.5, 58, 38}, + {77.2, 57.3, 38}, + {76.9, 55.6, 38}, + {70.9, 52.6, 38}, + {68.4, 50.5, 38}, + {78.7, 70.3, 38}, + {77.9, 68.9, 38}, + {77.7, 66.6, 38}, + {80.2, 66.2, 38}, + {77, 64.1, 38}, + {74.7, 63, 38}, + {76.4, 62.8, 38}, + {76.6, 61.8, 38}, + {74, 61.6, 38}, + {77.2, 61.3, 38}, + {76.7, 60.2, 38}, + {73.7, 60, 38}, + {83, 59.4, 38}, + {74.1, 58.7, 38}, + {80.5, 58.6, 38}, + {75.7, 58, 38}, + {76.8, 57.6, 38}, + {77.9, 56.8, 38}, + {38.8, 80.2, 5602}, + {38.4, 79.6, 5602}, + {38.3, 78.4, 5602}, + {39.6, 78.1, 5602}, + {38, 77.1, 5602}, + {36.8, 76.5, 5602}, + {37.6, 76.4, 5602}, + {37.8, 75.9, 5602}, + {36.4, 75.8, 5602}, + {38.1, 75.7, 5602}, + {37.8, 75.1, 5602}, + {36.3, 75, 5602}, + {41, 74.7, 5602}, + {36.5, 74.3, 5602}, + {39.8, 74.3, 5602}, + {37.3, 74, 5602}, + {37.9, 73.8, 5602}, + {38.4, 73.3, 5602}, + }, + [1195] = { + {19.3, 83.5, 38}, + {39.5, 74.7, 38}, + {36.9, 73.9, 38}, + {39.5, 73, 38}, + {21, 71.8, 38}, + {28.6, 57, 38}, + {34.6, 54.2, 38}, + {35.7, 52.8, 38}, + {31, 52.3, 38}, + {28.8, 51.9, 38}, + {29.8, 50.6, 38}, + {28.7, 48.8, 38}, + {29.8, 47, 38}, + {30.5, 45.1, 38}, + {30, 44.6, 38}, + {27.7, 41.9, 38}, + {31.1, 41.6, 38}, + {33.9, 40.9, 38}, + {33.5, 40.5, 38}, + {25.3, 37.8, 38}, + {36, 34.4, 38}, + {32.3, 32.5, 38}, + {38.5, 31.6, 38}, + {36.6, 31.1, 38}, + {36.6, 30.8, 38}, + {31.1, 30.6, 38}, + {28.7, 30.6, 38}, + {32.8, 30.6, 38}, + {29.9, 22.6, 38}, + {27.5, 21.5, 38}, + {30, 19.8, 38}, + {29.7, 18.5, 38}, + {27.5, 18, 38}, + {32.1, 17.1, 38}, + {28.7, 16.8, 38}, + {34.7, 36.1, 38}, + {21.1, 71.6, 38}, + {34.3, 54.2, 38}, + {35.6, 52.8, 38}, + {31.1, 52.2, 38}, + {29.9, 50.7, 38}, + {28.6, 49.1, 38}, + {29.6, 46.8, 38}, + {33.6, 40.5, 38}, + {32.4, 32.6, 38}, + {28.7, 30.8, 38}, + {27.7, 21.3, 38}, + {27.4, 18.3, 38}, + {8.4, 87, 5602}, + {18.7, 82.5, 5602}, + {17.4, 82.1, 5602}, + {18.8, 81.6, 5602}, + {9.4, 80.9, 5602}, + {13.2, 73.4, 5602}, + {16.1, 72, 5602}, + {16.8, 71.3, 5602}, + {14.5, 71, 5602}, + {13.3, 70.8, 5602}, + {13.9, 70.2, 5602}, + {13.2, 69.4, 5602}, + {13.7, 68.2, 5602}, + {14.2, 67.3, 5602}, + {13.9, 67.1, 5602}, + {12.8, 65.7, 5602}, + {14.5, 65.5, 5602}, + {15.9, 65.2, 5602}, + {15.7, 65, 5602}, + {11.5, 63.6, 5602}, + {17, 61.9, 5602}, + {15.1, 60.9, 5602}, + {18.3, 60.4, 5602}, + {17.3, 60.2, 5602}, + {17.3, 60, 5602}, + {13.2, 60, 5602}, + {15.3, 59.9, 5602}, + {14.5, 59.9, 5602}, + {13.9, 55.8, 5602}, + {12.7, 55.1, 5602}, + {13.9, 54.4, 5602}, + {13.8, 53.7, 5602}, + {12.6, 53.6, 5602}, + {15, 52.9, 5602}, + {13.2, 52.8, 5602}, + }, + [1196] = { + {46.3, 63.3, 1}, + {46.7, 63.3, 1}, + {49.3, 62.3, 1}, + {58.7, 61.3, 1}, + {64.9, 59.9, 1}, + {30.5, 59.6, 1}, + {50.2, 59.4, 1}, + {61, 56.9, 1}, + {26.7, 56.2, 1}, + {25.3, 55.8, 1}, + {26, 55.2, 1}, + {28.1, 54.5, 1}, + {50.3, 53.8, 1}, + {28, 52, 1}, + {59.3, 52, 1}, + {29.7, 51.7, 1}, + {66.5, 50.5, 1}, + {32.3, 49.9, 1}, + {80.1, 48.6, 1}, + {74.8, 48.1, 1}, + {28.3, 47.6, 1}, + {27.6, 47.6, 1}, + {32.4, 47.2, 1}, + {35.9, 47.2, 1}, + {26.8, 46.9, 1}, + {25.5, 46.4, 1}, + {37.1, 44, 1}, + {38.5, 43.5, 1}, + {56.3, 43.3, 1}, + {51.2, 43.1, 1}, + {28, 43.1, 1}, + {38.1, 42.9, 1}, + {29.1, 42.7, 1}, + {30.2, 42.3, 1}, + {37.7, 41.6, 1}, + {29.3, 41.4, 1}, + {30.8, 40.8, 1}, + {38.9, 40.3, 1}, + {31.8, 38.6, 1}, + {33.4, 36.8, 1}, + {35.2, 35.9, 1}, + {30.6, 35.1, 1}, + {37.8, 33.9, 1}, + {34.2, 32.1, 1}, + {36.2, 30.5, 1}, + {34.6, 30.3, 1}, + {41.2, 29.2, 1}, + {43.8, 28.8, 1}, + {28.1, 52.2, 1}, + {28.3, 47.5, 1}, + {26.9, 46.9, 1}, + {37.1, 43.9, 1}, + {29.3, 41.5, 1}, + {30.9, 40.8, 1}, + {1.3, 71.7, 5602}, + }, + [1198] = { + {83.3, 66.1, 12}, + }, + [1201] = { + {47.3, 63.3, 1}, + {47.9, 63.2, 1}, + {48.5, 61.9, 1}, + {49.5, 61.8, 1}, + {59.5, 61, 1}, + {59.4, 60.8, 1}, + {49.3, 60.6, 1}, + {55.8, 60.4, 1}, + {50.1, 60.4, 1}, + {63.6, 60.2, 1}, + {60.3, 60, 1}, + {58.5, 59.6, 1}, + {61.2, 59.6, 1}, + {62.7, 59.2, 1}, + {48.5, 59.1, 1}, + {55.2, 59, 1}, + {60.1, 58.8, 1}, + {59.1, 58.6, 1}, + {60, 58, 1}, + {56.5, 58, 1}, + {53.8, 57.8, 1}, + {63, 57.3, 1}, + {54.7, 57.2, 1}, + {61.8, 57, 1}, + {62.3, 56.6, 1}, + {53.9, 56.5, 1}, + {48.3, 56.5, 1}, + {57.1, 56.3, 1}, + {59.5, 55.9, 1}, + {57.8, 55.8, 1}, + {27.5, 55, 1}, + {56.5, 54.9, 1}, + {29.1, 54.2, 1}, + {28.3, 53.6, 1}, + {28.6, 53.6, 1}, + {56.1, 52.7, 1}, + {56.3, 52.2, 1}, + {29.5, 50.8, 1}, + {29.1, 49.7, 1}, + {28.2, 48.9, 1}, + {32.5, 48.4, 1}, + {26.8, 47.9, 1}, + {34.5, 47.5, 1}, + {25.7, 46.9, 1}, + {28.1, 44.9, 1}, + {27.6, 42.5, 1}, + {28.2, 40, 1}, + {30.2, 39.8, 1}, + {29.3, 39.6, 1}, + {31.2, 39.6, 1}, + {31.2, 39.4, 1}, + {38.4, 38.4, 1}, + {33.2, 37.8, 1}, + {36.8, 37.7, 1}, + {82.2, 37.6, 1}, + {79.1, 37.2, 1}, + {37.6, 36.8, 1}, + {35.4, 36.4, 1}, + {79, 36.3, 1}, + {80.4, 35.9, 1}, + {34.9, 35.7, 1}, + {79.2, 35.4, 1}, + {34.1, 35.3, 1}, + {35.2, 35, 1}, + {38.3, 33.5, 1}, + {36.7, 31.9, 1}, + {42.7, 31, 1}, + {37.6, 30.9, 1}, + {40.1, 30.8, 1}, + {44, 30.3, 1}, + {49.9, 60.2, 1}, + {27.6, 54.8, 1}, + {32.6, 48.5, 1}, + {26.8, 47.8, 1}, + {34.4, 47.7, 1}, + {28.1, 45, 1}, + {30.1, 40.1, 1}, + {36.7, 37.6, 1}, + {3.3, 61.6, 5602}, + {0.4, 61.2, 5602}, + {0.3, 60.4, 5602}, + {1.6, 60.1, 5602}, + {0.5, 59.5, 5602}, + }, + [1216] = { + {36.7, 88.4, 40}, + {35.8, 87.9, 40}, + {30.6, 83, 40}, + {32, 82.7, 40}, + {30.7, 81.9, 40}, + {29.9, 81.5, 40}, + {30.1, 80.5, 40}, + {29.3, 80.4, 40}, + {29.5, 79.3, 40}, + {29, 78.7, 40}, + {29.5, 78.4, 40}, + {28.8, 77.8, 40}, + {25.9, 67.4, 40}, + {26.3, 67, 40}, + {25.7, 66.8, 40}, + {25.9, 66.1, 40}, + {25.5, 65.7, 40}, + {25.7, 65.5, 40}, + {25.3, 64.7, 40}, + {25.6, 64.7, 40}, + {25.3, 63.9, 40}, + {25.8, 63.7, 40}, + {29.2, 80.5, 40}, + {25.7, 67.3, 40}, + {25.9, 66.2, 40}, + {25.4, 65.7, 40}, + }, + [1243] = { + {40.7, 65.1, 1}, + }, + [1250] = { + {83.3, 66.7, 12}, + }, + [1258] = { + {50.8, 60.5, 10}, + {69.5, 38.2, 10}, + {67.3, 35.1, 10}, + {66, 33.2, 10}, + {65.5, 31.3, 10}, + {63.3, 27.7, 10}, + {67.2, 35.1, 10}, + {66, 32.8, 10}, + }, + [1285] = { + {58.4, 61.7, 1519}, + {64.8, 72.2, 1519}, + }, + [1287] = { + {57.4, 56.8, 1519}, + {64.1, 68.4, 1519}, + }, + [1297] = { + {50.5, 57.2, 1519}, + {58.7, 68.7, 1519}, + }, + [1298] = { + {50, 57.6, 1519}, + {58.3, 69, 1519}, + }, + [1400] = { + {19.3, 58.2, 11}, + {21.3, 58.1, 11}, + {20.6, 56.7, 11}, + {19.1, 56.7, 11}, + {17.5, 55.8, 11}, + {19, 55.6, 11}, + {17.2, 54.4, 11}, + {18.4, 53.7, 11}, + {17.8, 53.4, 11}, + {18.2, 51.4, 11}, + {25.4, 44.6, 11}, + {46.2, 36.8, 11}, + {41.8, 36.6, 11}, + {44.6, 35.9, 11}, + {42.2, 35.8, 11}, + {48.6, 34.7, 11}, + {41, 34.6, 11}, + {46.6, 33.5, 11}, + {38.8, 33.2, 11}, + {43.8, 32.9, 11}, + {41.9, 32.7, 11}, + {29.5, 32.6, 11}, + {32.6, 31.6, 11}, + {36.2, 31.6, 11}, + {37.5, 31.5, 11}, + {34.3, 31.3, 11}, + {47.5, 31.3, 11}, + {39.7, 31.3, 11}, + {40.5, 30.8, 11}, + {45, 30.4, 11}, + {35.3, 30.2, 11}, + {24, 30.2, 11}, + {39.3, 29.5, 11}, + {33.5, 29.5, 11}, + {36.5, 29.3, 11}, + {24.8, 29.3, 11}, + {34.6, 29, 11}, + {41, 28.4, 11}, + {37, 28, 11}, + {33.6, 28, 11}, + {38.6, 27.7, 11}, + {36.1, 27.5, 11}, + {28.6, 27.5, 11}, + {20.1, 27.2, 11}, + {34.7, 26.9, 11}, + {24.8, 26.9, 11}, + {33.7, 26.8, 11}, + {37.9, 26.6, 11}, + {29.8, 26.1, 11}, + {21.7, 25.6, 11}, + {28.1, 25.4, 11}, + {32, 25.3, 11}, + {43.8, 33, 11}, + {41.7, 32.8, 11}, + {4.3, 7.2, 5602}, + {0.8, 7.1, 5602}, + {3, 6.5, 5602}, + {1.2, 6.5, 5602}, + {6.1, 5.6, 5602}, + {0.2, 5.5, 5602}, + {4.5, 4.7, 5602}, + {2.4, 4.2, 5602}, + {0.7, 4.1, 5602}, + {5.2, 3, 5602}, + {3.3, 2.3, 5602}, + {0.2, 0.7, 5602}, + }, + [1417] = { + {63.4, 75.3, 11}, + {64.1, 75.3, 11}, + {62.2, 74.9, 11}, + {64, 73.6, 11}, + {66.3, 72.7, 11}, + {63.9, 72.6, 11}, + {66, 72.5, 11}, + {64, 72.2, 11}, + {65.9, 72.1, 11}, + {65.4, 71, 11}, + {65.5, 68.9, 11}, + {64.7, 68.9, 11}, + {63.4, 66.4, 11}, + {63.5, 65.3, 11}, + {60.5, 63.6, 11}, + {61.9, 61.8, 11}, + {61, 61.7, 11}, + {62.1, 61.4, 11}, + {14.4, 59.9, 11}, + {62.6, 59.4, 11}, + {61.8, 56.8, 11}, + {60.6, 54.7, 11}, + {13.4, 54.7, 11}, + {61, 54.7, 11}, + {59.7, 53.3, 11}, + {14.6, 53.2, 11}, + {61.4, 53.1, 11}, + {15.4, 52.2, 11}, + {16.9, 52.1, 11}, + {18.5, 51.6, 11}, + {14.4, 51.6, 11}, + {58.9, 51.1, 11}, + {58.5, 50.8, 11}, + {59.4, 49.4, 11}, + {18.7, 49.3, 11}, + {57, 48.3, 11}, + {15.1, 47.7, 11}, + {57.2, 47.2, 11}, + {18.6, 47.2, 11}, + {20.2, 47.2, 11}, + {19.1, 47.1, 11}, + {15.8, 46.4, 11}, + {55.1, 45.7, 11}, + {19.8, 45.1, 11}, + {21.4, 44.6, 11}, + {53.8, 42.5, 11}, + {53.1, 42.3, 11}, + {53, 41.3, 11}, + {26.5, 41.2, 11}, + {27.8, 39.6, 11}, + {52.4, 39.6, 11}, + {28.9, 38.9, 11}, + {30.5, 38.8, 11}, + {51.5, 38.7, 11}, + {34.5, 37.6, 11}, + {53.1, 37.6, 11}, + {37.3, 37.1, 11}, + {50.9, 37.1, 11}, + {40.2, 36.3, 11}, + {52.2, 36.2, 11}, + {28.7, 36.2, 11}, + {38.6, 36.2, 11}, + {35.1, 35.9, 11}, + {33.7, 35.4, 11}, + {51.9, 35.4, 11}, + {37.3, 34.6, 11}, + {28, 34.4, 11}, + {50.5, 33.8, 11}, + {28.7, 33.8, 11}, + {53.1, 33.3, 11}, + {53.2, 32.7, 11}, + {52.4, 32.7, 11}, + {28, 32.6, 11}, + {53.8, 32.6, 11}, + {26.7, 32.5, 11}, + {50.3, 32, 11}, + {54.7, 31.2, 11}, + {63.6, 65.6, 11}, + {57.3, 47.1, 11}, + {53.2, 41.3, 11}, + {30.4, 39, 11}, + {29, 38.8, 11}, + {51.5, 38.4, 11}, + {53.1, 37.4, 11}, + {34.5, 37.2, 11}, + {36.9, 36.6, 11}, + {40.3, 36.1, 11}, + {38.6, 35.9, 11}, + {17.5, 36.8, 5602}, + {18, 36.8, 5602}, + {16.5, 36.5, 5602}, + {19.7, 34.8, 5602}, + {17.9, 34.7, 5602}, + {19.4, 34.7, 5602}, + {17.9, 34.4, 5602}, + {19.4, 34.4, 5602}, + {19, 33.5, 5602}, + {19.1, 31.9, 5602}, + {18.4, 31.9, 5602}, + {17.5, 30, 5602}, + {17.6, 29.3, 5602}, + {15.2, 27.8, 5602}, + {16.3, 26.4, 5602}, + {15.6, 26.3, 5602}, + {16.4, 26.1, 5602}, + {16.8, 24.6, 5602}, + {16.2, 22.6, 5602}, + {15.3, 21, 5602}, + {15.6, 21, 5602}, + {14.6, 19.9, 5602}, + {15.9, 19.7, 5602}, + {14, 18.2, 5602}, + {13.7, 18, 5602}, + {14.4, 16.9, 5602}, + {12.5, 16.1, 5602}, + {12.8, 15.1, 5602}, + {11.1, 14, 5602}, + {10.1, 11.6, 5602}, + {9.5, 11.5, 5602}, + {9.6, 10.6, 5602}, + {9, 9.3, 5602}, + {8.3, 8.5, 5602}, + {9.6, 7.7, 5602}, + {7.8, 7.4, 5602}, + {8.8, 6.8, 5602}, + {8.6, 6.1, 5602}, + {7.5, 4.9, 5602}, + {9.5, 4.5, 5602}, + {9.6, 4.1, 5602}, + {9, 4, 5602}, + {10.1, 3.9, 5602}, + {7.4, 3.5, 5602}, + {10.8, 2.9, 5602}, + }, + [1448] = { + {10.7, 56.8, 11}, + }, + [1452] = { + {12.1, 58, 11}, + }, + [1459] = { + {11.3, 58.4, 11}, + }, + [1461] = { + {11.3, 59.6, 11}, + }, + [1462] = { + {11.1, 58.3, 11}, + }, + [1469] = { + {35.8, 43.5, 38}, + {16.9, 66.5, 5602}, + }, + [1504] = { + {27.9, 60.5, 85}, + {28.2, 59.9, 85}, + {27.7, 59.6, 85}, + {28.9, 59.4, 85}, + {29.3, 59.4, 85}, + {28.1, 59.3, 85}, + {27.2, 59.2, 85}, + {28.5, 59, 85}, + {27.5, 58.8, 85}, + {29.7, 58.5, 85}, + {28.4, 58.3, 85}, + {29.1, 58.1, 85}, + {27.4, 58, 85}, + {29.2, 57.7, 85}, + {28.3, 57.4, 85}, + {29.9, 57.4, 85}, + {28.2, 57.1, 85}, + {29, 57.1, 85}, + {27.8, 57, 85}, + {29.8, 57, 85}, + {27.1, 56.6, 85}, + {28.5, 56.5, 85}, + {30.6, 56.5, 85}, + {26.8, 56.2, 85}, + {29.1, 56.1, 85}, + {29.9, 56, 85}, + {27.4, 55.9, 85}, + {28.2, 55.6, 85}, + {29.3, 55.6, 85}, + {27.9, 55.2, 85}, + {28.2, 59.8, 85}, + {28.9, 59.5, 85}, + {29.4, 59.4, 85}, + {27.1, 59.2, 85}, + {28.1, 59.2, 85}, + {28.5, 59.1, 85}, + {27.5, 58.9, 85}, + {27.4, 57.9, 85}, + {29.8, 55.9, 85}, + {29.4, 55.6, 85}, + }, + [1505] = { + {24.2, 60.7, 85}, + {24.5, 60.6, 85}, + {24, 60.6, 85}, + {26.1, 60.4, 85}, + {25.1, 60.3, 85}, + {26.2, 60.3, 85}, + {24.8, 60.3, 85}, + {23.5, 60.2, 85}, + {24.6, 60.1, 85}, + {23.3, 59.9, 85}, + {25.4, 59.9, 85}, + {24.5, 59.7, 85}, + {24.8, 59.7, 85}, + {26.2, 59.7, 85}, + {23.2, 59.6, 85}, + {25.2, 59.6, 85}, + {25.9, 59.5, 85}, + {25.6, 59.5, 85}, + {26.6, 59.5, 85}, + {24.8, 59.4, 85}, + {25, 59.2, 85}, + {23.5, 59.1, 85}, + {23.4, 58.9, 85}, + {24, 58.4, 85}, + {23.7, 58.4, 85}, + {23.6, 58.3, 85}, + {25.2, 60.4, 85}, + {26.2, 60.2, 85}, + {25.4, 59.8, 85}, + {25.2, 59.5, 85}, + {25.6, 59.4, 85}, + }, + [1508] = { + {33.6, 68.6, 85}, + {28.9, 68.2, 85}, + {33.1, 67.4, 85}, + {29.4, 67.2, 85}, + {34.2, 67.1, 85}, + {29.4, 65.7, 85}, + {28.8, 64.7, 85}, + {30.3, 64.1, 85}, + {30.1, 62.9, 85}, + {30.8, 62.1, 85}, + {29.5, 61.9, 85}, + {30.7, 60.8, 85}, + {31.9, 59.6, 85}, + {31.6, 58.6, 85}, + {34.9, 57.9, 85}, + {32, 57.5, 85}, + {34.3, 56.6, 85}, + {31, 55.6, 85}, + {33, 67.4, 85}, + {34.3, 67.2, 85}, + {30.2, 64, 85}, + {30.9, 62.1, 85}, + {30.8, 60.8, 85}, + {31.4, 58.4, 85}, + {34.2, 57, 85}, + }, + [1509] = { + {35.6, 65.8, 85}, + {34.7, 65.4, 85}, + {38.2, 63.9, 85}, + {36.5, 63.6, 85}, + {36.9, 63.5, 85}, + {37.8, 63, 85}, + {34.6, 62.5, 85}, + {35.7, 62.3, 85}, + {36.6, 60.8, 85}, + {37.7, 60.6, 85}, + {35.7, 59.5, 85}, + {34.6, 58.9, 85}, + {37.4, 58.7, 85}, + {35.1, 57.4, 85}, + {35.7, 57.3, 85}, + {36.9, 56.9, 85}, + {34.5, 56.7, 85}, + {35.9, 56, 85}, + {37.6, 56, 85}, + {34.6, 65.2, 85}, + {34.5, 56.8, 85}, + }, + [1512] = { + {31.5, 72.5, 85}, + {29.4, 72.5, 85}, + {32, 71.6, 85}, + {35.7, 71.4, 85}, + {35.1, 71.4, 85}, + {35.8, 70.8, 85}, + {34.3, 70.7, 85}, + {35.1, 70.4, 85}, + {29.9, 70.3, 85}, + {35, 69.6, 85}, + {32.4, 69.5, 85}, + {34.3, 69.5, 85}, + {30.7, 69.3, 85}, + {35.7, 69.3, 85}, + {29.3, 69.2, 85}, + {28.6, 69.1, 85}, + {34.2, 68.2, 85}, + {29, 67.8, 85}, + {27.9, 67.4, 85}, + {33.5, 66.8, 85}, + {29.1, 66.7, 85}, + {29.9, 66.3, 85}, + {30.1, 65.4, 85}, + {27.9, 64.8, 85}, + {29.7, 64.1, 85}, + {28.3, 63.9, 85}, + {28.3, 63.6, 85}, + {29.8, 63.6, 85}, + {27.8, 63.6, 85}, + {28.4, 62.6, 85}, + {29.7, 62.1, 85}, + {30.1, 61.5, 85}, + {29.5, 61, 85}, + {30.9, 59.7, 85}, + {34, 58.2, 85}, + {32.8, 58.2, 85}, + {31.1, 58, 85}, + {33.8, 57.3, 85}, + {31.5, 56.2, 85}, + {32.5, 56.2, 85}, + {33.4, 66.8, 85}, + {29.9, 66.2, 85}, + {30.2, 65.5, 85}, + {29.6, 62.2, 85}, + {32.8, 58.1, 85}, + }, + [1513] = { + {34.9, 66.8, 85}, + {35.1, 66.2, 85}, + {34.1, 65.4, 85}, + {35.3, 64.7, 85}, + {36.9, 64.4, 85}, + {35.4, 63.2, 85}, + {37.8, 62, 85}, + {37.1, 62, 85}, + {33.9, 61.7, 85}, + {35.7, 61.5, 85}, + {34.9, 61.4, 85}, + {37.2, 59.8, 85}, + {34.4, 59.6, 85}, + {37.6, 59.4, 85}, + {35.1, 59, 85}, + {36.5, 58.6, 85}, + {35.7, 58.2, 85}, + {37.7, 58, 85}, + {34.1, 57.3, 85}, + {35.2, 56.7, 85}, + {36.8, 56.5, 85}, + {35.2, 64.7, 85}, + {35.4, 63, 85}, + {35.6, 61.8, 85}, + {35.6, 58.3, 85}, + }, + [1547] = { + {70.6, 65.1, 85}, + {69.6, 63.8, 85}, + {67.2, 63.5, 85}, + {64.8, 61.8, 85}, + {64.3, 59, 85}, + {49, 58.9, 85}, + {48, 58.6, 85}, + {66.2, 57.9, 85}, + {68.4, 57.8, 85}, + {46.1, 57.4, 85}, + {44.5, 57, 85}, + {59.5, 56.9, 85}, + {66.5, 56.9, 85}, + {64.1, 56.6, 85}, + {40.2, 56.3, 85}, + {41.8, 56.2, 85}, + {41, 55.7, 85}, + {66.1, 55.6, 85}, + {61.2, 55.6, 85}, + {59.9, 55.6, 85}, + {67.5, 55, 85}, + {62.6, 54.8, 85}, + {68.6, 54.4, 85}, + {49.3, 54.3, 85}, + {45, 54.2, 85}, + {63.1, 54.1, 85}, + {48.6, 53.7, 85}, + {56.6, 53.6, 85}, + {64.5, 53.5, 85}, + {44.4, 53.3, 85}, + {55.2, 53.3, 85}, + {57.8, 52.7, 85}, + {40.7, 52.7, 85}, + {67.5, 52.6, 85}, + {39.4, 52, 85}, + {63.3, 50.8, 85}, + {51.1, 50.5, 85}, + {66.1, 50.3, 85}, + {42.7, 50.1, 85}, + {50, 49.8, 85}, + {41.3, 49.6, 85}, + {65.2, 49.2, 85}, + {41.5, 48.8, 85}, + {64.4, 48.3, 85}, + {62.9, 47.9, 85}, + {38.6, 47.5, 85}, + {50, 47.4, 85}, + {62.4, 47, 85}, + {63.5, 46.3, 85}, + {39.9, 46.2, 85}, + {61.8, 45.6, 85}, + {38.7, 45.5, 85}, + {37.5, 45.1, 85}, + {40.8, 45, 85}, + {38.8, 44.7, 85}, + {61, 44.4, 85}, + {61.5, 43.9, 85}, + {40, 43.7, 85}, + {38.7, 43.7, 85}, + {63.7, 43.5, 85}, + {64.6, 43.1, 85}, + {40.7, 42.9, 85}, + {39.1, 42.6, 85}, + {41.9, 42.4, 85}, + {38.6, 41.8, 85}, + {63.6, 41.7, 85}, + {38, 41.5, 85}, + {40.4, 41.2, 85}, + {39, 40.7, 85}, + {42.6, 40.6, 85}, + {62.8, 40.5, 85}, + {60.5, 40.4, 85}, + {61.6, 40.4, 85}, + {38.6, 40.3, 85}, + {64.4, 40.1, 85}, + {65.9, 40, 85}, + {40.3, 39.7, 85}, + {62.6, 39.4, 85}, + {62.8, 38.1, 85}, + {65.1, 37.4, 85}, + {64.1, 58.9, 85}, + {61.1, 55.6, 85}, + {62.6, 54.9, 85}, + {63.1, 54, 85}, + {55.3, 53.4, 85}, + {38.5, 47.3, 85}, + }, + [1548] = { + {20.3, 61.2, 28}, + {18.8, 60.9, 28}, + {17.9, 59.9, 28}, + {21, 59.8, 28}, + {23.7, 59.8, 28}, + {24.6, 59.3, 28}, + {20.6, 58.6, 28}, + {19.6, 57.4, 28}, + {17.8, 57, 28}, + {23.1, 56.4, 28}, + {18, 55.8, 28}, + {20.6, 55.6, 28}, + {20, 55.1, 28}, + {23.3, 54.2, 28}, + {21.7, 53.5, 28}, + {22.9, 53, 28}, + {20, 51.7, 28}, + {24.6, 51.6, 28}, + {18.4, 51.4, 28}, + {24.1, 50.8, 28}, + {22.2, 50.8, 28}, + {20.9, 50.2, 28}, + {22.4, 48.3, 28}, + {23.8, 47.3, 28}, + {52.5, 74.6, 85}, + {77.2, 73.9, 85}, + {55.7, 73.6, 85}, + {75.8, 73.6, 85}, + {75, 72.6, 85}, + {77.8, 72.5, 85}, + {80.5, 72.5, 85}, + {52.8, 72.5, 85}, + {52, 72.3, 85}, + {81.4, 72.1, 85}, + {54.9, 71.4, 85}, + {77.5, 71.4, 85}, + {52.4, 71.3, 85}, + {51.2, 70.2, 85}, + {76.5, 70.2, 85}, + {74.8, 69.9, 85}, + {55.3, 69.8, 85}, + {79.9, 69.3, 85}, + {75, 68.8, 85}, + {77.5, 68.5, 85}, + {45.3, 68.5, 85}, + {43.2, 68.1, 85}, + {76.9, 68, 85}, + {46.4, 67.7, 85}, + {42, 67.5, 85}, + {80.1, 67.2, 85}, + {44.7, 67, 85}, + {54.1, 66.9, 85}, + {78.6, 66.5, 85}, + {55.6, 66.1, 85}, + {79.7, 66.1, 85}, + {49.7, 65.8, 85}, + {54, 65.7, 85}, + {55.6, 65.5, 85}, + {41.5, 65.5, 85}, + {53, 65.2, 85}, + {76.9, 64.9, 85}, + {81.4, 64.7, 85}, + {75.4, 64.6, 85}, + {41.5, 64.5, 85}, + {56.6, 64.4, 85}, + {45.8, 64.2, 85}, + {80.9, 64, 85}, + {79, 64, 85}, + {48.5, 63.9, 85}, + {54.3, 63.7, 85}, + {50.9, 63.4, 85}, + {77.7, 63.4, 85}, + {47.7, 63.3, 85}, + {53.4, 63.3, 85}, + {57.5, 63, 85}, + {49.2, 62.9, 85}, + {58.4, 62.7, 85}, + {42.8, 62.4, 85}, + {57.4, 62.4, 85}, + {50.6, 62.3, 85}, + {60.2, 62.2, 85}, + {43.8, 62, 85}, + {44.2, 61.9, 85}, + {71.9, 61.8, 85}, + {41, 61.6, 85}, + {79.2, 61.6, 85}, + {72.8, 61.5, 85}, + {50.1, 60.9, 85}, + {40.6, 60.9, 85}, + {45.1, 60.8, 85}, + {59.4, 60.7, 85}, + {46.7, 60.6, 85}, + {57.9, 60.6, 85}, + {80.6, 60.6, 85}, + {43.9, 60.6, 85}, + {51.4, 60, 85}, + {69, 59.9, 85}, + {58.3, 59.8, 85}, + {57.1, 59.6, 85}, + {71.1, 59.5, 85}, + {78.9, 59.1, 85}, + {48.3, 59, 85}, + {80.2, 58.6, 85}, + {40.6, 58.4, 85}, + {58.3, 58.3, 85}, + {44.3, 58.2, 85}, + {42.8, 58.1, 85}, + {72.9, 56.9, 85}, + {48.6, 56.7, 85}, + {44.8, 54.4, 85}, + {74.6, 54.3, 85}, + {75.8, 54.3, 85}, + {76.2, 52.4, 85}, + {72, 52.1, 85}, + {47.3, 51, 85}, + {46.1, 50.9, 85}, + {46.9, 50.1, 85}, + {48.3, 50, 85}, + {50, 49.9, 85}, + {72.6, 49.2, 85}, + {47.4, 49.1, 85}, + {48, 48.4, 85}, + {45.1, 47.8, 85}, + {45.1, 47.7, 85}, + {72.6, 47.6, 85}, + {71.8, 47.2, 85}, + {42.3, 47, 85}, + {45.9, 46.9, 85}, + {72.7, 46.5, 85}, + {46.3, 46.1, 85}, + {74.9, 46, 85}, + {47, 45.4, 85}, + {75.6, 45.4, 85}, + {43.3, 45.2, 85}, + {44.5, 45.2, 85}, + {41.9, 45, 85}, + {41.3, 44.4, 85}, + {74.6, 44.2, 85}, + {45, 43.9, 85}, + {72.6, 43.7, 85}, + {43.7, 42.9, 85}, + {42.2, 42.8, 85}, + {40.3, 42.5, 85}, + {73, 41.4, 85}, + {44.6, 40.4, 85}, + {72.2, 40.2, 85}, + {73.2, 38.1, 85}, + {71.8, 38, 85}, + {74.5, 37.6, 85}, + {68.1, 37, 85}, + {68.2, 36.2, 85}, + {71.5, 36.1, 85}, + {70.4, 36.1, 85}, + {74.4, 36, 85}, + {69.1, 35.6, 85}, + {74.1, 35.3, 85}, + {66.1, 35, 85}, + {76.7, 34.8, 85}, + {66.7, 34.8, 85}, + {68, 34.6, 85}, + {68.9, 33.9, 85}, + {63.4, 33.7, 85}, + {75.6, 33, 85}, + {65, 32.8, 85}, + {78, 32.7, 85}, + {71.1, 32.6, 85}, + {72.6, 32.6, 85}, + {68.9, 31.7, 85}, + {73.3, 31.7, 85}, + {67.6, 31.6, 85}, + {70.5, 31.5, 85}, + {76.5, 31, 85}, + {68.3, 30.7, 85}, + {69, 29.6, 85}, + {76.4, 29.6, 85}, + {70.4, 29.5, 85}, + {72.1, 29.5, 85}, + {74, 28.7, 85}, + {69.7, 28.5, 85}, + {72.8, 28.5, 85}, + {66.4, 2.7, 130}, + {22, 45, 1497}, + {23.7, 35, 1497}, + {19.8, 34.3, 1497}, + {33.5, 30.1, 1497}, + {21.7, 29.7, 1497}, + {55.7, 65.7, 85}, + {58.4, 60, 85}, + }, + [1549] = { + {32.5, 30.8, 28}, + {33.2, 29.7, 28}, + {31.8, 29.6, 28}, + {23.4, 28.2, 28}, + {33.5, 27.5, 28}, + {31.9, 27.4, 28}, + {34.1, 26.3, 28}, + {29.4, 25.1, 28}, + {30.4, 24.7, 28}, + {29.6, 23.4, 28}, + {83, 50.4, 85}, + {79.6, 49.5, 85}, + {83, 48.8, 85}, + {88.2, 48.5, 85}, + {82.2, 48.3, 85}, + {89.4, 47.9, 85}, + {83.2, 47.4, 85}, + {90.3, 47.1, 85}, + {82, 47, 85}, + {88.9, 46.7, 85}, + {88.2, 46.4, 85}, + {83.6, 46, 85}, + {87.4, 46, 85}, + {85.1, 46, 85}, + {87, 45.3, 85}, + {81.5, 45.2, 85}, + {86, 45.1, 85}, + {88.9, 44.9, 85}, + {83.9, 44.2, 85}, + {85.1, 44, 85}, + {89.5, 43.9, 85}, + {88.2, 43.8, 85}, + {82.1, 43.6, 85}, + {80.2, 42.4, 85}, + {89.8, 41.8, 85}, + {83.3, 41.7, 85}, + {88.3, 41.7, 85}, + {84.4, 41.5, 85}, + {83.9, 41.5, 85}, + {90.4, 40.6, 85}, + {85.9, 39.5, 85}, + {86.8, 39.1, 85}, + {86.1, 37.9, 85}, + }, + [1553] = { + {54.2, 65.9, 85}, + {53.6, 65, 85}, + {53.4, 64.1, 85}, + {55.1, 64, 85}, + {71, 63.8, 85}, + {52.5, 63.5, 85}, + {55.5, 63.4, 85}, + {56.6, 62.9, 85}, + {68.3, 62.9, 85}, + {55, 62.6, 85}, + {66.7, 62.3, 85}, + {54.1, 61.7, 85}, + {59.3, 61.6, 85}, + {63.8, 61.4, 85}, + {55.7, 61, 85}, + {60.3, 60.8, 85}, + {46.7, 60.2, 85}, + {57.4, 59.7, 85}, + {49.2, 59.6, 85}, + {50.9, 59.3, 85}, + {45.5, 58.9, 85}, + {64.7, 58.5, 85}, + {58.8, 58.4, 85}, + {46.6, 58, 85}, + {48.8, 57.7, 85}, + {56.3, 57.5, 85}, + {64.3, 57.5, 85}, + {41.7, 57.4, 85}, + {43, 57.3, 85}, + {45.4, 57.3, 85}, + {58, 57.1, 85}, + {58.4, 57, 85}, + {41.4, 56.9, 85}, + {65.5, 56.6, 85}, + {62.2, 56.6, 85}, + {67, 56.6, 85}, + {45.9, 56.4, 85}, + {61.1, 56.3, 85}, + {47.4, 56.3, 85}, + {43.3, 56.2, 85}, + {58.5, 56.2, 85}, + {59.3, 56.1, 85}, + {66.8, 55.9, 85}, + {43.8, 55.9, 85}, + {56.9, 55.7, 85}, + {40.1, 55.6, 85}, + {47.4, 55.3, 85}, + {58, 55.2, 85}, + {48.9, 55, 85}, + {64, 54.9, 85}, + {45.8, 54.8, 85}, + {45.3, 54.6, 85}, + {65.1, 54.6, 85}, + {66.3, 54.5, 85}, + {45.8, 54.4, 85}, + {57.1, 54.2, 85}, + {68, 54.1, 85}, + {69, 53.9, 85}, + {41.9, 53.7, 85}, + {66.5, 53.6, 85}, + {65.9, 53.2, 85}, + {40.1, 52.7, 85}, + {45.7, 52.5, 85}, + {46.8, 52.5, 85}, + {67.1, 51.8, 85}, + {45.2, 51.2, 85}, + {46.8, 50.8, 85}, + {46.8, 50.3, 85}, + {64.8, 50.1, 85}, + {44.5, 50.1, 85}, + {63.2, 49.7, 85}, + {49.5, 49.7, 85}, + {51.4, 49.5, 85}, + {44.6, 49.4, 85}, + {63.7, 49.2, 85}, + {40.3, 48.6, 85}, + {50, 48.5, 85}, + {51.9, 48.4, 85}, + {39.1, 47.8, 85}, + {42.8, 47.7, 85}, + {63.3, 47.6, 85}, + {41.1, 47.4, 85}, + {36.5, 47.3, 85}, + {63.3, 47.1, 85}, + {47.2, 46.4, 85}, + {43.1, 46.2, 85}, + {37.4, 46.1, 85}, + {64.3, 46, 85}, + {63, 46, 85}, + {47.9, 45.1, 85}, + {42.3, 44.2, 85}, + {64.2, 44.1, 85}, + {62.9, 44, 85}, + {37.9, 43.9, 85}, + {62.3, 43.9, 85}, + {62.5, 43.5, 85}, + {37.3, 43.5, 85}, + {60.4, 43.1, 85}, + {42.5, 42.4, 85}, + {39.3, 42.2, 85}, + {41.1, 42.2, 85}, + {60, 41.8, 85}, + {62.5, 41.8, 85}, + {65.2, 41.7, 85}, + {63.3, 41.7, 85}, + {43.3, 41.5, 85}, + {61.8, 41.4, 85}, + {64.7, 40.4, 85}, + {41.8, 40.3, 85}, + {61.7, 38.8, 85}, + {37.5, 38.8, 85}, + {63.2, 38.7, 85}, + {65.4, 38.6, 85}, + {38.5, 38.6, 85}, + {60.9, 38.5, 85}, + {64.9, 38.2, 85}, + {64.1, 37.8, 85}, + {62.2, 36.8, 85}, + {64.3, 36.6, 85}, + {62.9, 35.8, 85}, + {64.5, 57.3, 85}, + {37.4, 46.4, 85}, + }, + [1554] = { + {25.3, 61.6, 28}, + {21.7, 60.7, 28}, + {23.3, 60.1, 28}, + {18.5, 59.7, 28}, + {19.7, 59.6, 28}, + {21.8, 58.9, 28}, + {17.1, 58.7, 28}, + {20, 58.5, 28}, + {22.5, 57.9, 28}, + {24, 57.7, 28}, + {21.3, 57.4, 28}, + {21.8, 56.9, 28}, + {22.8, 56.3, 28}, + {23.6, 54.8, 28}, + {16.9, 54.3, 28}, + {24.6, 53.5, 28}, + {23.9, 53.4, 28}, + {27.1, 53.3, 28}, + {19.4, 53.1, 28}, + {25.6, 52.9, 28}, + {25.4, 51.5, 28}, + {21.9, 51.5, 28}, + {23.4, 51.1, 28}, + {17.8, 50.9, 28}, + {19.2, 50.5, 28}, + {22, 49.2, 28}, + {23.3, 49.1, 28}, + {34.1, 30.7, 28}, + {32.4, 28.6, 28}, + {34.1, 28.5, 28}, + {30.9, 26.3, 28}, + {28.3, 24.7, 28}, + {30.9, 24, 28}, + {81.9, 74.3, 85}, + {78.6, 73.4, 85}, + {80.1, 72.8, 85}, + {75.5, 72.4, 85}, + {76.6, 72.3, 85}, + {78.6, 71.7, 85}, + {74.2, 71.5, 85}, + {76.9, 71.3, 85}, + {79.3, 70.7, 85}, + {80.7, 70.5, 85}, + {78.2, 70.2, 85}, + {78.7, 69.7, 85}, + {79.6, 69.2, 85}, + {44.9, 69.1, 85}, + {44, 68, 85}, + {47.9, 67.8, 85}, + {80.4, 67.7, 85}, + {74, 67.3, 85}, + {45.9, 67.3, 85}, + {43.1, 67.1, 85}, + {81.3, 66.5, 85}, + {80.7, 66.4, 85}, + {46.9, 66.4, 85}, + {83.7, 66.3, 85}, + {45.3, 66.3, 85}, + {76.4, 66.2, 85}, + {82.2, 66, 85}, + {72.7, 65.3, 85}, + {82, 64.6, 85}, + {78.7, 64.6, 85}, + {80.2, 64.3, 85}, + {74.9, 64, 85}, + {76.1, 63.7, 85}, + {52.9, 63.6, 85}, + {48.6, 63.6, 85}, + {51.9, 63.4, 85}, + {41.6, 63.4, 85}, + {48.4, 63.2, 85}, + {44.9, 63.2, 85}, + {43.2, 63, 85}, + {45.5, 62.8, 85}, + {72.8, 62.6, 85}, + {44.4, 62.6, 85}, + {78.8, 62.4, 85}, + {80.1, 62.3, 85}, + {47.5, 62.2, 85}, + {48.7, 62, 85}, + {51.4, 61.8, 85}, + {50.7, 61.7, 85}, + {46.3, 61.5, 85}, + {70.5, 61.2, 85}, + {41.6, 60.9, 85}, + {69.8, 60.7, 85}, + {44.7, 60.3, 85}, + {40.6, 60, 85}, + {72.1, 59.8, 85}, + {49.1, 59.8, 85}, + {42.1, 59.4, 85}, + {73.4, 59.3, 85}, + {80.5, 59.3, 85}, + {79.2, 59, 85}, + {74.6, 58.7, 85}, + {42.2, 58.6, 85}, + {71, 58.4, 85}, + {72.8, 58.3, 85}, + {73.7, 57.1, 85}, + {75.6, 55.8, 85}, + {70.2, 53.7, 85}, + {75, 53, 85}, + {82.2, 52.8, 85}, + {83.7, 51.6, 85}, + {75.8, 51.3, 85}, + {71.3, 50.1, 85}, + {71.7, 49, 85}, + {79.3, 48.7, 85}, + {83.6, 48.4, 85}, + {76.8, 48.3, 85}, + {71.2, 48.2, 85}, + {81.1, 48.2, 85}, + {75.7, 47.9, 85}, + {74.5, 47.3, 85}, + {75.5, 47.1, 85}, + {72.7, 47.1, 85}, + {71.2, 46.8, 85}, + {82.8, 46.3, 85}, + {80.7, 46.3, 85}, + {75.4, 46.1, 85}, + {77.8, 46.1, 85}, + {79, 45.9, 85}, + {78.6, 44.9, 85}, + {84.4, 44.9, 85}, + {90.4, 44.8, 85}, + {80, 44.5, 85}, + {83.1, 44.3, 85}, + {73.2, 44.3, 85}, + {74.8, 44.2, 85}, + {84.1, 43.1, 85}, + {88.7, 42.8, 85}, + {90.4, 42.8, 85}, + {82.5, 42.8, 85}, + {72.9, 42.6, 85}, + {82.9, 42.6, 85}, + {84.6, 40.8, 85}, + {87.3, 40.6, 85}, + {73.3, 40.6, 85}, + {73, 39.3, 85}, + {84.8, 39.1, 85}, + {87.3, 38.4, 85}, + {72.6, 37.2, 85}, + {71.2, 36.9, 85}, + {64.5, 36.6, 85}, + {73.4, 36.1, 85}, + {75.4, 35.1, 85}, + {65.8, 34.9, 85}, + {69.7, 34.8, 85}, + {71.1, 34.6, 85}, + {67.5, 34, 85}, + {73.7, 33.9, 85}, + {65.5, 33.8, 85}, + {71.9, 33.7, 85}, + {70.4, 33.7, 85}, + {74.9, 33.7, 85}, + {69.7, 32.8, 85}, + {68.2, 32.8, 85}, + {74.2, 32.6, 85}, + {66.2, 31.7, 85}, + {76.8, 31.6, 85}, + {71.8, 31.6, 85}, + {72.8, 30.6, 85}, + {71.2, 30.5, 85}, + {69.7, 30.5, 85}, + {76.4, 30.3, 85}, + {73.3, 29.2, 85}, + {68.6, 28.3, 85}, + {77.7, 46.2, 85}, + }, + [1555] = { + {85.9, 57.3, 85}, + {86.8, 57, 85}, + {87.4, 56.1, 85}, + {83.4, 55.6, 85}, + {83.8, 55.4, 85}, + {85.4, 55.1, 85}, + {88.2, 55, 85}, + {82.4, 54.8, 85}, + {87.5, 53.8, 85}, + {86.6, 53.8, 85}, + {88.9, 53.7, 85}, + {85.9, 53.7, 85}, + {88, 53.7, 85}, + {86.7, 53.2, 85}, + {84.7, 52.8, 85}, + {85.7, 52.5, 85}, + {89.5, 52.5, 85}, + {86, 52.5, 85}, + {83.7, 52.4, 85}, + {89.7, 51.7, 85}, + {87.3, 51.2, 85}, + {84.5, 51, 85}, + {85.1, 51, 85}, + {91.1, 50.5, 85}, + {86.7, 50.5, 85}, + {89.8, 50.4, 85}, + {88.1, 50.4, 85}, + {84.5, 50.1, 85}, + {89.1, 49.8, 85}, + {91.8, 49.5, 85}, + {87.4, 49.3, 85}, + {90.3, 49.3, 85}, + {85.9, 49.3, 85}, + {86.7, 48.4, 85}, + {85.2, 48.3, 85}, + {84.5, 47.2, 85}, + }, + [1557] = { + {32.4, 68.6, 33}, + {31.2, 67.9, 33}, + {31.9, 67.7, 33}, + {32.9, 67.7, 33}, + {33.9, 67.6, 33}, + {33.6, 67, 33}, + {34.5, 66.7, 33}, + {30.9, 66.7, 33}, + {32.6, 66.6, 33}, + {34.1, 66.4, 33}, + {33, 66.1, 33}, + {32.1, 66.1, 33}, + {34.6, 65.4, 33}, + {32.6, 65.4, 33}, + {33.5, 65.4, 33}, + {31.4, 65.4, 33}, + {33, 64.8, 33}, + {34, 64.6, 33}, + {35, 64.4, 33}, + {33.5, 63.7, 33}, + {33.2, 63.4, 33}, + {34.4, 62.5, 33}, + {33.5, 62.3, 33}, + {31.3, 60.7, 33}, + {30.9, 60, 33}, + {32, 59.8, 33}, + {32.5, 59.1, 33}, + {32.4, 58, 33}, + {32.3, 68.4, 33}, + {33, 67.6, 33}, + {31.9, 67.6, 33}, + {33.9, 67.5, 33}, + {33.5, 67, 33}, + {32.6, 65.5, 33}, + {33.5, 65.5, 33}, + {34, 64.5, 33}, + {33.5, 63.6, 33}, + {33.6, 62.2, 33}, + }, + [1558] = { + {36.3, 63.9, 33}, + {35.2, 63.4, 33}, + {35.5, 63.3, 33}, + {35.5, 62.7, 33}, + {36.4, 62.4, 33}, + {37.1, 62.3, 33}, + {37.8, 62.2, 33}, + {37.9, 60.8, 33}, + {37.9, 60, 33}, + }, + [1668] = { + {57.7, 53.9, 40}, + }, + [1682] = { + {34.8, 48.6, 38}, + {16.3, 69.1, 5602}, + }, + [1685] = { + {82.5, 63.4, 38}, + {82.3, 63.3, 38}, + {40.7, 76.7, 5602}, + }, + [1686] = { + {83.2, 63.4, 38}, + {36.1, 45.1, 38}, + {17, 67.3, 5602}, + }, + [1687] = { + {83, 63, 38}, + {37.3, 46.8, 38}, + {17.6, 68.2, 5602}, + }, + [1688] = { + {24, 58.2, 85}, + }, + [1689] = { + {70.9, 61.3, 1}, + {74.9, 60.8, 1}, + {73, 59.9, 1}, + {78.7, 59.3, 1}, + {75.7, 57.8, 1}, + {79.7, 57.6, 1}, + {79.4, 55.8, 1}, + {81.6, 55.4, 1}, + {73.5, 55.2, 1}, + {71.2, 54, 1}, + {81.4, 53.1, 1}, + {74, 53, 1}, + {79.6, 51.9, 1}, + {84.9, 50.7, 1}, + {73.5, 50.6, 1}, + {86.7, 50.2, 1}, + {75.7, 49.9, 1}, + {78.9, 49.5, 1}, + {74.1, 49, 1}, + {74.9, 48.9, 1}, + {84.7, 48.3, 1}, + {80.2, 47.5, 1}, + {86.3, 47.5, 1}, + {79.8, 46.4, 1}, + {81, 43.9, 1}, + {80.1, 43.9, 1}, + {83.2, 38.1, 1}, + {82.5, 37.8, 1}, + {82.7, 35.5, 1}, + {80.8, 35.3, 1}, + {80.2, 34.7, 1}, + {83.1, 33.1, 1}, + {17.1, 56.5, 38}, + {0.1, 81.5, 5602}, + {1, 79.9, 5602}, + {0.7, 78.3, 5602}, + {2.7, 77.9, 5602}, + {2.5, 75.8, 5602}, + {0.9, 74.7, 5602}, + {5.7, 73.6, 5602}, + {7.3, 73.2, 5602}, + {0.2, 72.5, 5602}, + {5.5, 71.4, 5602}, + {1.4, 70.7, 5602}, + {7, 70.7, 5602}, + {1, 69.6, 5602}, + {2.2, 67.4, 5602}, + {1.3, 67.3, 5602}, + {4.2, 62.1, 5602}, + {3.5, 61.8, 5602}, + {3.7, 59.7, 5602}, + {2, 59.5, 5602}, + {1.4, 58.9, 5602}, + {4.1, 57.5, 5602}, + }, + [1691] = { + {47.2, 52.4, 1}, + }, + [1693] = { + {58.8, 57.8, 38}, + {54.2, 56.2, 38}, + {54, 55.2, 38}, + {55.5, 53.5, 38}, + {55.1, 52.9, 38}, + {55.3, 52.7, 38}, + {56.5, 52.6, 38}, + {62.4, 51.5, 38}, + {61.9, 45.4, 38}, + {61.3, 43.3, 38}, + {54.5, 41.7, 38}, + {60.1, 41.6, 38}, + {53.8, 40.4, 38}, + {53.9, 39.6, 38}, + {53.1, 39.2, 38}, + {56.9, 39.1, 38}, + {56.4, 37.8, 38}, + {56.1, 37.8, 38}, + {53.6, 37.4, 38}, + {58.8, 36.8, 38}, + {55.7, 36, 38}, + {56.4, 35.9, 38}, + {51.5, 35, 38}, + {55.5, 33.8, 38}, + {57.4, 31.6, 38}, + {58.4, 24.1, 38}, + {57.4, 21.9, 38}, + {58.8, 57.6, 38}, + {61.3, 43.7, 38}, + {60, 41.8, 38}, + {54, 39.3, 38}, + {56.3, 37.6, 38}, + {56.5, 36.4, 38}, + {51.2, 34.9, 38}, + {58.3, 24, 38}, + {57.3, 21.7, 38}, + {28.6, 73.7, 5602}, + {26.3, 73, 5602}, + {26.2, 72.5, 5602}, + {27, 71.6, 5602}, + {26.8, 71.3, 5602}, + {26.9, 71.2, 5602}, + {27.5, 71.2, 5602}, + {30.5, 70.6, 5602}, + {30.2, 67.5, 5602}, + {29.9, 66.6, 5602}, + {29.3, 65.6, 5602}, + {26.5, 65.6, 5602}, + {26.1, 64.9, 5602}, + {26.2, 64.4, 5602}, + {25.8, 64.3, 5602}, + {27.7, 64.3, 5602}, + {27.3, 63.6, 5602}, + {27.4, 63.5, 5602}, + {26, 63.4, 5602}, + {28.6, 63.1, 5602}, + {27.5, 62.9, 5602}, + {27.1, 62.6, 5602}, + {24.8, 62.1, 5602}, + {27, 61.6, 5602}, + {28, 60.4, 5602}, + {28.4, 56.5, 5602}, + {27.9, 55.3, 5602}, + }, + [1713] = { + {31.2, 58.5, 33}, + {36.1, 58.4, 33}, + {29.8, 58, 33}, + {35.6, 57.8, 33}, + {36.1, 56.8, 33}, + {36.7, 55.9, 33}, + {35.4, 55.7, 33}, + {36.3, 55.3, 33}, + {36.6, 55.2, 33}, + {36.2, 56.7, 33}, + }, + [1765] = { + {51.8, 77.2, 85}, + {51, 77.1, 85}, + {51.9, 77, 85}, + {53.2, 74.7, 85}, + {43.2, 52.4, 130}, + {45.8, 50.2, 130}, + {47.9, 48.1, 130}, + {44.2, 47.9, 130}, + {46.5, 45.9, 130}, + {65, 11.2, 130}, + {63.8, 11.1, 130}, + {66, 10.4, 130}, + {66.4, 9, 130}, + {66.1, 8.8, 130}, + {67.6, 8.4, 130}, + {58, 8.3, 130}, + {64.4, 8, 130}, + {69.1, 7.8, 130}, + {63.7, 7.4, 130}, + {69.3, 7.4, 130}, + {64.8, 7.3, 130}, + {70.2, 7.1, 130}, + {69.8, 6.9, 130}, + {68.3, 6.6, 130}, + {66.6, 6.3, 130}, + {68.3, 6.3, 130}, + {64.8, 5.9, 130}, + {65.7, 5.5, 130}, + {68.9, 5.5, 130}, + {64.8, 5.5, 130}, + {65.8, 5.3, 130}, + {68.8, 4.9, 130}, + {70, 4.7, 130}, + {69.9, 4.3, 130}, + {67.1, 2.9, 130}, + {25.4, 45.6, 1497}, + }, + [1766] = { + {44.5, 26.8, 130}, + {47.6, 25.7, 130}, + {46.4, 25.6, 130}, + {44.5, 25.4, 130}, + {47.7, 24.7, 130}, + {47, 22.8, 130}, + {49.2, 22.5, 130}, + {47.6, 21.9, 130}, + {50.9, 21.8, 130}, + {48.9, 20.1, 130}, + {50.9, 19.5, 130}, + {50.5, 17.9, 130}, + {52.3, 17.5, 130}, + {55.5, 17.3, 130}, + {56.4, 16.2, 130}, + {59.5, 16, 130}, + {57.4, 15.8, 130}, + {49.4, 15.4, 130}, + {57.9, 14.1, 130}, + {54.9, 13.6, 130}, + {50.1, 13.6, 130}, + {59.2, 13.6, 130}, + {54.5, 12.8, 130}, + {55.3, 12.7, 130}, + {56.2, 12.5, 130}, + {61.2, 11.9, 130}, + {63.1, 10.9, 130}, + {57.1, 10.8, 130}, + {58.9, 10.5, 130}, + {59.8, 10.4, 130}, + {56.2, 10.2, 130}, + {57.8, 10, 130}, + {57.1, 10, 130}, + {61, 9, 130}, + {59.7, 8.9, 130}, + {57.7, 8.8, 130}, + {61.8, 8.8, 130}, + {62.1, 8.5, 130}, + {56.4, 8, 130}, + {58.5, 6.8, 130}, + {47.5, 21.8, 130}, + {57, 10.1, 130}, + {57.9, 9.9, 130}, + {59.5, 8.9, 130}, + }, + [1778] = { + {44.1, 52, 130}, + {45, 51.9, 130}, + {44.6, 50.4, 130}, + {46.3, 49.6, 130}, + {44.2, 48.2, 130}, + {44.9, 47.3, 130}, + {47, 46.5, 130}, + {51.3, 45.4, 130}, + {50.8, 44.9, 130}, + {55.2, 44.2, 130}, + {54.5, 43.9, 130}, + {50.2, 43.4, 130}, + {51.1, 42.5, 130}, + {50.6, 40.8, 130}, + {53.6, 39.8, 130}, + {51.3, 38.7, 130}, + {51, 38.4, 130}, + {54.3, 38.2, 130}, + {50.1, 36.3, 130}, + {46.9, 33.5, 130}, + {47.3, 33, 130}, + {48.8, 32.9, 130}, + {48.8, 26.3, 130}, + {46, 26, 130}, + {44.5, 24.7, 130}, + {48, 23.3, 130}, + {49.7, 22.7, 130}, + {51.9, 21.6, 130}, + {48.2, 20.3, 130}, + {50, 19.9, 130}, + {52.2, 19.6, 130}, + {47.6, 19.6, 130}, + {48.3, 19.3, 130}, + {51.2, 18.6, 130}, + {50.4, 18.5, 130}, + {49.4, 17.4, 130}, + {51.3, 16.7, 130}, + {51, 14.9, 130}, + {49.1, 14.2, 130}, + {53.5, 39.5, 130}, + {50.1, 36.2, 130}, + {47.5, 19.7, 130}, + }, + [1780] = { + {34.2, 17.3, 130}, + {35, 16.2, 130}, + {35.7, 16, 130}, + {37.3, 16, 130}, + {34.2, 15.2, 130}, + {35.7, 14.9, 130}, + {36.5, 14.9, 130}, + {35, 14.4, 130}, + {36.3, 14.2, 130}, + {35.5, 14, 130}, + {35.9, 13.7, 130}, + {35.5, 13.1, 130}, + {34.8, 12.7, 130}, + {34.9, 12.6, 130}, + {35.4, 12.5, 130}, + {35.7, 16.1, 130}, + {37.2, 15.9, 130}, + {35.7, 15, 130}, + {36.4, 15, 130}, + {35.1, 14.5, 130}, + {35.6, 14.1, 130}, + {36.1, 14.1, 130}, + }, + [1781] = { + {35.5, 11.6, 130}, + {35.1, 11.4, 130}, + {35.4, 10.9, 130}, + {35.5, 10.5, 130}, + {35.3, 10.4, 130}, + {35.1, 10.3, 130}, + {34.7, 10.2, 130}, + {35.3, 10.1, 130}, + {34.5, 10, 130}, + {35.1, 9.9, 130}, + {34.2, 9.7, 130}, + {35.2, 9.6, 130}, + {34.3, 9.2, 130}, + {36, 9, 130}, + {35.4, 8.9, 130}, + {35.9, 8.6, 130}, + {34.5, 8.3, 130}, + {35.3, 8.3, 130}, + {34.8, 7.8, 130}, + {23.6, 81.4, 85}, + {22.9, 81.3, 85}, + {23.4, 81.2, 85}, + {22.6, 81.1, 85}, + {23.5, 81, 85}, + {22.7, 80.6, 85}, + {24.2, 80.4, 85}, + {23.7, 80.3, 85}, + {24.1, 80.1, 85}, + {22.8, 79.8, 85}, + {23.6, 79.8, 85}, + {24.1, 79.6, 85}, + {23.1, 79.4, 85}, + {35.1, 11.5, 130}, + {35.4, 10.8, 130}, + {34.5, 9.9, 130}, + }, + [1797] = { + {55.5, 71, 130}, + {49.4, 70.3, 130}, + {54.7, 69.5, 130}, + {50.2, 69.4, 130}, + {50.9, 67.9, 130}, + {50.5, 67.2, 130}, + {56.1, 67.1, 130}, + {52.9, 67.1, 130}, + {51.6, 66.7, 130}, + {55.4, 65, 130}, + {53.4, 65, 130}, + {51.6, 62.5, 130}, + {52.5, 61.3, 130}, + {53.1, 60.6, 130}, + {51.6, 59.1, 130}, + {51.8, 58.1, 130}, + {54.2, 56.2, 130}, + {51.3, 55.3, 130}, + {53.2, 54, 130}, + {51.5, 53.9, 130}, + {51.5, 52.8, 130}, + {52.9, 51.8, 130}, + {55.6, 51.7, 130}, + {42.1, 21.7, 130}, + {36.7, 21.5, 130}, + {35.2, 19.6, 130}, + {42.5, 19.5, 130}, + {43.6, 18.8, 130}, + {46, 18.3, 130}, + {41.2, 18.2, 130}, + {42.5, 18.1, 130}, + {47, 18.1, 130}, + {41.6, 18, 130}, + {37.1, 17.7, 130}, + {37.4, 17.2, 130}, + {35.4, 17.2, 130}, + {44.4, 17, 130}, + {37.7, 16.6, 130}, + {39.9, 16.5, 130}, + {45.3, 16.1, 130}, + {38.9, 16, 130}, + {37.9, 15.6, 130}, + {34.5, 15.3, 130}, + {39.7, 14.9, 130}, + {39, 13.5, 130}, + {39.1, 15.9, 130}, + }, + [1809] = { + {32.3, 62.4, 28}, + {29.9, 54.7, 28}, + {86.4, 67.7, 85}, + }, + [1815] = { + {36.3, 68, 28}, + {37.2, 67, 28}, + {36.4, 65.8, 28}, + {33.4, 65.8, 28}, + {37.9, 65.7, 28}, + {37.2, 64.6, 28}, + {36.5, 63.6, 28}, + {33.4, 63.6, 28}, + {34.9, 63.5, 28}, + {37.9, 63.2, 28}, + {39.4, 62.7, 28}, + {39, 62.3, 28}, + {37.2, 62.3, 28}, + {34.2, 62.1, 28}, + {31.9, 61.7, 28}, + {29.3, 61.6, 28}, + {31.3, 60.1, 28}, + {28.7, 58.9, 28}, + {34.8, 58.8, 28}, + {32.6, 57.6, 28}, + {31.8, 56.6, 28}, + {33.4, 56.2, 28}, + {32.7, 55.2, 28}, + {31, 55.2, 28}, + {30.2, 54.4, 28}, + {34.8, 54.2, 28}, + {31.7, 54.1, 28}, + {33.9, 52.8, 28}, + {88.2, 74.3, 85}, + {85.8, 74.2, 85}, + {87.7, 72.9, 85}, + {85.2, 71.7, 85}, + {89, 70.4, 85}, + {88.2, 69.5, 85}, + {89.7, 69.1, 85}, + {89, 68.2, 85}, + {87.4, 68.1, 85}, + {86.7, 67.4, 85}, + {91, 67.2, 85}, + {88, 67.1, 85}, + {90.2, 65.8, 85}, + {37.1, 66.9, 28}, + {36.3, 65.6, 28}, + {37.9, 65.5, 28}, + {86.6, 67.4, 85}, + }, + [1816] = { + {55.2, 64.5, 28}, + {55.8, 63.6, 28}, + {58.2, 62.1, 28}, + {56.7, 62.1, 28}, + {59, 61, 28}, + {58, 60.1, 28}, + {53.4, 60.1, 28}, + {55.8, 58.6, 28}, + {57.7, 58.4, 28}, + {52.5, 58.4, 28}, + {66.6, 56.4, 28}, + {67.4, 55.3, 28}, + {65.5, 55.1, 28}, + {52.2, 54.3, 28}, + {60.4, 54.1, 28}, + {65.7, 53.6, 28}, + {57.1, 53.5, 28}, + {66.7, 53.4, 28}, + {65, 53, 28}, + {55.4, 53, 28}, + {57.9, 53, 28}, + {60.5, 51.9, 28}, + {54.4, 51.9, 28}, + {61.9, 51.8, 28}, + {59, 51.6, 28}, + {57.1, 51, 28}, + {56.6, 50.6, 28}, + {60.2, 50.5, 28}, + {65.3, 50.3, 28}, + {62.9, 49.9, 28}, + {63.7, 49.8, 28}, + {52.9, 49.5, 28}, + {60.5, 49.4, 28}, + {68.1, 48.5, 28}, + {53.4, 48.3, 28}, + {66.7, 47.8, 28}, + {66.3, 47.8, 28}, + {64.5, 47.7, 28}, + {61.9, 47, 28}, + {64.4, 47, 28}, + {67.6, 46.1, 28}, + {55, 45.8, 28}, + {65.9, 44, 28}, + {56.6, 50.7, 28}, + }, + [1817] = { + {52.5, 70.4, 28}, + {52, 61, 28}, + {49.4, 59.6, 28}, + {42.6, 56.3, 28}, + {43.5, 55.3, 28}, + {42.5, 54.1, 28}, + {51.1, 53.1, 28}, + {44.9, 49.5, 28}, + {39.6, 48.5, 28}, + {45.7, 48.3, 28}, + {47.3, 48.3, 28}, + {48, 47.3, 28}, + {44.8, 47, 28}, + {51.8, 47, 28}, + {50.3, 46.9, 28}, + {45.8, 46, 28}, + {47.3, 46, 28}, + {44.3, 45.8, 28}, + {47.4, 43.7, 28}, + {45.7, 43.7, 28}, + {45.7, 41.3, 28}, + {43.5, 40.2, 28}, + {45, 40.2, 28}, + {46.6, 40.1, 28}, + {47.3, 39, 28}, + {46.5, 37.8, 28}, + {49.7, 37.8, 28}, + {45.9, 36.7, 28}, + {50.3, 34.5, 28}, + {45, 32.1, 28}, + {46.6, 29.7, 28}, + {47.3, 28.3, 28}, + {49.6, 27.3, 28}, + {48.1, 27.2, 28}, + {48.8, 26.1, 28}, + {95.6, 61.7, 85}, + {43.5, 55.2, 28}, + {47.2, 48.3, 28}, + {44.4, 45.9, 28}, + {45.7, 43.9, 28}, + {44.9, 40.3, 28}, + {46.6, 40.2, 28}, + {47.4, 39, 28}, + }, + [1822] = { + {35.5, 66.8, 28}, + {34.2, 66.8, 28}, + {35.6, 64.6, 28}, + {32.6, 64.6, 28}, + {34.1, 64.5, 28}, + {38.7, 64.5, 28}, + {32.1, 63.5, 28}, + {32.4, 62.3, 28}, + {35.8, 62.3, 28}, + {31, 62.2, 28}, + {39.9, 62.1, 28}, + {33.4, 61.4, 28}, + {37.9, 61.3, 28}, + {30, 61, 28}, + {29.1, 59.2, 28}, + {33.4, 59, 28}, + {34.3, 57.6, 28}, + {29.8, 56.7, 28}, + {28.9, 56, 28}, + {34, 55.4, 28}, + {33.2, 54.1, 28}, + {87.4, 74.8, 85}, + {86.4, 73.6, 85}, + {85.6, 72, 85}, + {86.2, 69.6, 85}, + {85.4, 68.9, 85}, + {90.3, 68.3, 85}, + {89.5, 67.1, 85}, + {37.2, 64.6, 28}, + {37.1, 64.7, 28}, + {38.6, 64.5, 28}, + {34.2, 64.5, 28}, + {31.1, 62.4, 28}, + {34.1, 55.4, 28}, + {87.5, 75, 85}, + }, + [1824] = { + {54.9, 62.2, 28}, + {53.4, 62, 28}, + {54.2, 61.1, 28}, + {55.9, 61, 28}, + {56.6, 60, 28}, + {59.1, 59.6, 28}, + {56.6, 57.5, 28}, + {66, 57.4, 28}, + {66.4, 54.5, 28}, + {58.9, 54.1, 28}, + {55.6, 54, 28}, + {60.8, 53.5, 28}, + {56, 53.4, 28}, + {52.7, 51.7, 28}, + {56, 51.4, 28}, + {60.8, 51, 28}, + {55, 50.7, 28}, + {62.8, 50.6, 28}, + {53.4, 50.6, 28}, + {58.8, 50.5, 28}, + {66.8, 49.9, 28}, + {64.4, 49.7, 28}, + {55.8, 49.6, 28}, + {54.3, 49.4, 28}, + {62.2, 48.5, 28}, + {67.2, 48.2, 28}, + {54.3, 47.1, 28}, + {65.1, 46.4, 28}, + {63.3, 46.4, 28}, + {66.1, 45.8, 28}, + {68.1, 45.5, 28}, + {63.6, 45.2, 28}, + {55.7, 54, 28}, + {58.9, 53.9, 28}, + {62.7, 50.3, 28}, + }, + [1922] = { + {60.4, 71.3, 12}, + {62.2, 70.9, 12}, + {61.1, 70.1, 12}, + {69.9, 69.5, 12}, + {59.2, 69.4, 12}, + {63.1, 69, 12}, + {74, 68.9, 12}, + {59.9, 68.6, 12}, + {66.2, 67.8, 12}, + {72.8, 67.6, 12}, + {59.4, 66.9, 12}, + {61.3, 66.8, 12}, + {60.2, 66.7, 12}, + {71.6, 66.5, 12}, + {61.4, 66.4, 12}, + {73.8, 66.2, 12}, + {59.7, 65.9, 12}, + {68.2, 65.8, 12}, + {60.1, 65.8, 12}, + {66.2, 65.6, 12}, + {59.7, 65.4, 12}, + {68.6, 65.1, 12}, + {74.2, 64.9, 12}, + {59.1, 64.7, 12}, + {65.1, 64.4, 12}, + {60.9, 64.3, 12}, + {60.1, 64.3, 12}, + {69.3, 64.1, 12}, + {67.6, 64.1, 12}, + {60.6, 64, 12}, + {58.7, 63.9, 12}, + {73.6, 63.7, 12}, + {60.6, 63.7, 12}, + {63.2, 63.7, 12}, + {65.6, 63.6, 12}, + {70.4, 63.6, 12}, + {60.9, 63.6, 12}, + {61, 63.4, 12}, + {72.8, 63.2, 12}, + {71.8, 63.2, 12}, + {69, 62.9, 12}, + {66.9, 62.8, 12}, + {67.6, 62.2, 12}, + {58.4, 62.1, 12}, + {69.8, 61.8, 12}, + {70.5, 61.8, 12}, + {60.9, 61.5, 12}, + {69.8, 61.1, 12}, + {61.8, 61.1, 12}, + {71.4, 61.1, 12}, + {64.6, 60.3, 12}, + {60.4, 59.7, 12}, + {68.4, 59.5, 12}, + {66.4, 58.2, 12}, + {68.1, 57.9, 12}, + {69, 57.4, 12}, + {59.8, 65.9, 12}, + {73.8, 63.6, 12}, + }, + [1923] = { + {50.2, 84.9, 130}, + {49.7, 84.2, 130}, + {49.4, 82.6, 130}, + {48.9, 82, 130}, + {52.5, 81.8, 130}, + {48.7, 80.8, 130}, + {47.5, 80.8, 130}, + {44.6, 80.7, 130}, + {48.4, 80.3, 130}, + {55, 80.3, 130}, + {51.7, 79.5, 130}, + {45.9, 79.3, 130}, + {52.2, 78.2, 130}, + {51.5, 77.9, 130}, + {49.8, 77.7, 130}, + {48.3, 77.3, 130}, + {46.7, 77.1, 130}, + {51.8, 76.7, 130}, + {50, 76.4, 130}, + {51.6, 76.4, 130}, + {49.7, 75.4, 130}, + {49.8, 74, 130}, + {52.2, 14.3, 5179}, + {51.5, 13.6, 5179}, + {51.3, 11.7, 5179}, + {50.7, 11, 5179}, + {54.7, 10.9, 5179}, + {50.5, 9.7, 5179}, + {49, 9.7, 5179}, + {45.7, 9.6, 5179}, + {50.1, 9.2, 5179}, + {57.7, 9.1, 5179}, + {53.8, 8.2, 5179}, + {47.2, 8, 5179}, + {54.4, 6.7, 5179}, + {53.6, 6.4, 5179}, + {51.7, 6.1, 5179}, + {49.9, 5.7, 5179}, + {48.1, 5.5, 5179}, + {54, 5, 5179}, + {51.9, 4.7, 5179}, + {53.7, 4.7, 5179}, + {51.5, 3.5, 5179}, + {51.7, 1.9, 5179}, + }, + [1961] = { + {78.3, 37.8, 1}, + }, + [1984] = { + {60.6, 46.3, 141}, + {56.9, 45.7, 141}, + {58.5, 45.7, 141}, + {61.2, 45.7, 141}, + {59.5, 45.4, 141}, + {57.5, 45.2, 141}, + {59.1, 44.6, 141}, + {56.7, 44.3, 141}, + {61.3, 43.7, 141}, + {55.5, 43.4, 141}, + {55.1, 43.4, 141}, + {59.1, 43.3, 141}, + {61.5, 42.9, 141}, + {57.8, 42.6, 141}, + {62, 42.4, 141}, + {63.3, 42.4, 141}, + {55.5, 42.3, 141}, + {63.2, 42.2, 141}, + {56.6, 42.2, 141}, + {56.7, 42.2, 141}, + {55.6, 41.9, 141}, + {55.9, 41.2, 141}, + {62.1, 41.2, 141}, + {57.6, 39.5, 141}, + {57, 45.6, 141}, + {59.5, 45.3, 141}, + {57.4, 45, 141}, + {59, 44.4, 141}, + {57.8, 42.4, 141}, + }, + [1985] = { + {55.5, 39.4, 141}, + {60.1, 39.2, 141}, + {61.8, 38.7, 141}, + {56.3, 38.4, 141}, + {56.2, 37.8, 141}, + {59.8, 37.6, 141}, + {55.3, 37.1, 141}, + {56.3, 36.8, 141}, + {59.5, 36.8, 141}, + {61.4, 36, 141}, + {62.2, 35.9, 141}, + {57.2, 35.7, 141}, + {61.2, 35.3, 141}, + {58.2, 34.8, 141}, + {60.2, 34.8, 141}, + {59.4, 34.6, 141}, + {60.5, 34.3, 141}, + {61.8, 34.2, 141}, + {60.3, 34, 141}, + {60.2, 34, 141}, + {60, 31.6, 141}, + {60.2, 39.1, 141}, + {56.3, 37.9, 141}, + {56.4, 36.9, 141}, + {59.6, 36.9, 141}, + {61.4, 35.3, 141}, + {60.3, 34.1, 141}, + }, + [1986] = { + {56.6, 34.3, 141}, + {56.9, 33.9, 141}, + {57.6, 33.9, 141}, + {56.4, 33.2, 141}, + {58.2, 32.9, 141}, + {58.9, 32.8, 141}, + {57.7, 32.7, 141}, + {56.2, 32.4, 141}, + {57.2, 32.3, 141}, + {56.9, 31.8, 141}, + {56.8, 31.3, 141}, + {56.6, 30.8, 141}, + {56.4, 30.3, 141}, + {57.2, 29.7, 141}, + {56.6, 29.4, 141}, + {57.2, 29.3, 141}, + {57.5, 29.2, 141}, + {56.1, 29.1, 141}, + {58, 28.6, 141}, + {55.7, 28.5, 141}, + {56.9, 28.4, 141}, + {57.9, 28.4, 141}, + {57.7, 28, 141}, + {56.9, 27.8, 141}, + {55.5, 27.6, 141}, + {55.8, 27.5, 141}, + {57.3, 27.5, 141}, + {57.7, 27.4, 141}, + {58.1, 27.2, 141}, + {55.9, 27.1, 141}, + {57.9, 27, 141}, + {56.1, 27, 141}, + {56.5, 26.8, 141}, + {57.9, 26.5, 141}, + {56.3, 26.4, 141}, + {56, 26.2, 141}, + {57, 26.2, 141}, + {57.4, 26, 141}, + {57.9, 26, 141}, + {56.5, 25.9, 141}, + {56.7, 25.6, 141}, + {56.5, 25.3, 141}, + {55.7, 25.1, 141}, + {56.4, 24.8, 141}, + {56.9, 34.5, 141}, + {58.8, 33.4, 141}, + {57.8, 33.1, 141}, + {57.6, 32.7, 141}, + {56.2, 32.5, 141}, + {57.8, 32.5, 141}, + {56.8, 31.8, 141}, + {56.7, 31.3, 141}, + {56.6, 30.7, 141}, + {56.4, 30.2, 141}, + {57.2, 29.6, 141}, + {56.5, 29.5, 141}, + {56.2, 29.2, 141}, + {57, 29.2, 141}, + {57.6, 29.1, 141}, + {55.9, 28.8, 141}, + {57.8, 28, 141}, + {58, 27.3, 141}, + {55.9, 27.3, 141}, + {56.1, 26.9, 141}, + {56.6, 26.8, 141}, + {57.9, 26.4, 141}, + {56, 26.4, 141}, + {56.9, 26.3, 141}, + {57.9, 26.1, 141}, + {55.9, 25.9, 141}, + {56.4, 25.9, 141}, + {56.8, 25.6, 141}, + {56.1, 24.9, 141}, + }, + [1994] = { + {56.6, 26.3, 141}, + }, + [1995] = { + {55.4, 66, 141}, + {64.1, 64.3, 141}, + {63, 64.2, 141}, + {54.8, 64, 141}, + {62.7, 63.1, 141}, + {52.4, 62.7, 141}, + {68.7, 62.4, 141}, + {68.1, 62.1, 141}, + {64.9, 61.5, 141}, + {67.2, 61.4, 141}, + {57.9, 60.8, 141}, + {54.3, 60.3, 141}, + {58.9, 60.3, 141}, + {54.1, 60.1, 141}, + {53.5, 59.4, 141}, + {53.7, 59.4, 141}, + {52.2, 59.3, 141}, + {50, 58.7, 141}, + {49.8, 58.1, 141}, + {52.6, 57.1, 141}, + {54.2, 57, 141}, + {51.9, 56.7, 141}, + {58.7, 56.4, 141}, + {57.6, 56.3, 141}, + {65.7, 54.5, 141}, + {53, 54.5, 141}, + {64.1, 54.4, 141}, + {63.4, 53.6, 141}, + {67.5, 53.5, 141}, + {65.4, 52.6, 141}, + {63.5, 51.7, 141}, + {64.7, 51.5, 141}, + {62, 51.4, 141}, + {61.2, 50.6, 141}, + {46.4, 48.6, 141}, + {45.9, 47.5, 141}, + {64, 64.1, 141}, + {54.1, 60.4, 141}, + {58.7, 56.2, 141}, + {64.2, 54.4, 141}, + {67.5, 53.6, 141}, + {63.4, 51.7, 141}, + {46.3, 48.6, 141}, + }, + [1996] = { + {53.1, 77.9, 141}, + {52.5, 77.8, 141}, + {43.8, 77.4, 141}, + {51.9, 77.4, 141}, + {42.9, 77.1, 141}, + {53.7, 77.1, 141}, + {58.9, 77, 141}, + {53.3, 76.6, 141}, + {45.6, 76.5, 141}, + {58.1, 76.2, 141}, + {46.2, 75.5, 141}, + {42.2, 75.3, 141}, + {57.6, 75, 141}, + {58.5, 75, 141}, + {48.2, 74.2, 141}, + {42.4, 72.3, 141}, + {41.9, 71.5, 141}, + {64.3, 69.2, 141}, + {41.1, 68.4, 141}, + {64.2, 67.2, 141}, + {65.9, 67, 141}, + {65.5, 66.5, 141}, + {39.3, 65.3, 141}, + {38.7, 63.4, 141}, + {38.4, 62.5, 141}, + {38.2, 55.6, 141}, + {39.5, 54.5, 141}, + {39.7, 53.7, 141}, + {39.5, 53, 141}, + {42.7, 52.1, 141}, + {38.8, 51.5, 141}, + {39.1, 51.4, 141}, + {37.6, 51.3, 141}, + {43.9, 50.6, 141}, + {41.3, 49.7, 141}, + {42.8, 48.9, 141}, + {98.1, 20.9, 1657}, + }, + [1997] = { + {38.5, 47.8, 141}, + {38.5, 46.2, 141}, + {39.3, 46.1, 141}, + {40.8, 45, 141}, + {45.6, 40.6, 141}, + {44.4, 39.9, 141}, + {46.1, 39.7, 141}, + {38.1, 39.5, 141}, + {38, 38.3, 141}, + {37.7, 37.9, 141}, + {46.2, 31.4, 141}, + {39, 30.7, 141}, + {37.6, 29.3, 141}, + }, + [1998] = { + {52.6, 67.4, 141}, + {53.3, 66.7, 141}, + {51.7, 66.3, 141}, + {67.1, 66.3, 141}, + {51, 65.2, 141}, + {52.9, 64.3, 141}, + {54.3, 64.2, 141}, + {68, 64.1, 141}, + {68.5, 63.6, 141}, + {63.3, 63.2, 141}, + {54.2, 62.5, 141}, + {67.2, 62, 141}, + {53.5, 61.9, 141}, + {64.1, 61.8, 141}, + {53.6, 61.4, 141}, + {61.1, 61.3, 141}, + {63.9, 60.8, 141}, + {69.5, 60.3, 141}, + {60.1, 60.3, 141}, + {70.1, 60.2, 141}, + {59.6, 59.7, 141}, + {60.2, 59.5, 141}, + {51.6, 59.4, 141}, + {51.8, 58.5, 141}, + {60.2, 58.4, 141}, + {69.5, 57.2, 141}, + {60.8, 57.1, 141}, + {58.3, 56, 141}, + {52.2, 55.7, 141}, + {52.3, 55.6, 141}, + {60.9, 55.2, 141}, + {60.8, 54.6, 141}, + {60.8, 54.4, 141}, + {61.6, 54.4, 141}, + {51, 53.8, 141}, + {47.1, 47, 141}, + {45.9, 46.7, 141}, + {60.1, 58.2, 141}, + {58.2, 55.8, 141}, + {61.4, 54.6, 141}, + {60.9, 54.5, 141}, + }, + [1999] = { + {45.8, 78.9, 141}, + {45.1, 77.9, 141}, + {44.8, 77.5, 141}, + {54.5, 77.4, 141}, + {54.3, 76.1, 141}, + {59.6, 76.1, 141}, + {49, 76, 141}, + {41.2, 76, 141}, + {48.5, 75.1, 141}, + {61.5, 75, 141}, + {60.2, 75, 141}, + {41.8, 75, 141}, + {42.9, 74.7, 141}, + {44.7, 74.5, 141}, + {54.3, 74.2, 141}, + {61, 73.4, 141}, + {55, 73.2, 141}, + {45.3, 73.1, 141}, + {46.5, 71.6, 141}, + {45.4, 70.2, 141}, + {65.2, 69.1, 141}, + {38.9, 61.7, 141}, + {39.8, 60, 141}, + {38.8, 59.6, 141}, + {43.8, 54.4, 141}, + {42.8, 52.6, 141}, + {40.7, 48.8, 141}, + {40.4, 48.6, 141}, + {38.7, 47.9, 141}, + {37.2, 47.4, 141}, + {96, 2, 1657}, + {43.7, 54.3, 141}, + }, + [2000] = { + {43.7, 47.3, 141}, + {42.4, 46.3, 141}, + {43.2, 45.5, 141}, + {48.6, 44.9, 141}, + {47.8, 44.5, 141}, + {43.8, 44.5, 141}, + {43.1, 43.5, 141}, + {45.8, 43.4, 141}, + {45.1, 35.1, 141}, + {47.4, 34.1, 141}, + {34.6, 32.6, 141}, + {33.7, 32.3, 141}, + {36.3, 32.3, 141}, + {47.9, 32.1, 141}, + {39.9, 31.4, 141}, + {33.3, 30.9, 141}, + {33.6, 32.3, 141}, + {33.3, 31, 141}, + }, + [2001] = { + {41.2, 28.9, 141}, + {38, 27.9, 141}, + {45.7, 27.5, 141}, + {40.2, 25.4, 141}, + }, + [2031] = { + {58.3, 46.7, 141}, + {60.3, 46, 141}, + {58.9, 45.9, 141}, + {58.4, 45.6, 141}, + {62.2, 45.4, 141}, + {61.1, 45.1, 141}, + {60.5, 45.1, 141}, + {57.9, 44.7, 141}, + {61.5, 44.7, 141}, + {56.6, 44.5, 141}, + {62.3, 44.3, 141}, + {61.6, 44.2, 141}, + {61.8, 44.1, 141}, + {62.8, 43.9, 141}, + {62.1, 43.6, 141}, + {62.1, 42.6, 141}, + {62.5, 42.2, 141}, + {63.3, 42.1, 141}, + {64.3, 42, 141}, + {62.6, 41.7, 141}, + {61.5, 41.7, 141}, + {61.6, 40.9, 141}, + {64.4, 40.5, 141}, + {61.5, 39.3, 141}, + {57.9, 38.9, 141}, + {58.4, 46.7, 141}, + {58.8, 46, 141}, + {60.6, 45, 141}, + {61.4, 44.5, 141}, + {61.9, 44.2, 141}, + {62, 43.6, 141}, + {62, 42.8, 141}, + {62.6, 42.3, 141}, + {61.5, 41.6, 141}, + }, + [2032] = { + {61.2, 39.1, 141}, + {62.4, 39, 141}, + {63.4, 38.8, 141}, + {62.2, 38.7, 141}, + {62.5, 38.2, 141}, + {62.3, 38, 141}, + {62.7, 37.8, 141}, + {62.5, 37.2, 141}, + {63, 37, 141}, + {62.1, 36.8, 141}, + {62.9, 36.5, 141}, + {60.3, 36.1, 141}, + {60, 36, 141}, + {58.9, 35.8, 141}, + {59.5, 35.8, 141}, + {60.8, 35.7, 141}, + {60.7, 35.5, 141}, + {61.8, 35.3, 141}, + {60.9, 34.8, 141}, + {62.6, 34.4, 141}, + {59.9, 34.3, 141}, + {59.2, 31.5, 141}, + {59.5, 36.8, 141}, + {60.2, 34.8, 141}, + {60.5, 34.3, 141}, + {62.2, 39, 141}, + {62.7, 37.9, 141}, + {62.4, 37, 141}, + {62, 36.9, 141}, + {58.9, 35.9, 141}, + {61.7, 35.2, 141}, + {61, 34.7, 141}, + {60.6, 34.3, 141}, + {59.8, 34.2, 141}, + }, + [2033] = { + {46.9, 45.6, 141}, + {46.3, 44.5, 141}, + {39.8, 44.5, 141}, + {45.2, 42.6, 141}, + {45.7, 41, 141}, + {46, 40.9, 141}, + {45.8, 37.8, 141}, + {44.6, 37.7, 141}, + {45.1, 36.7, 141}, + {39.6, 35.4, 141}, + {39.5, 35.2, 141}, + {45.8, 33.8, 141}, + {34.7, 30.9, 141}, + {35.3, 29.8, 141}, + {35.9, 29.2, 141}, + {34.7, 31, 141}, + }, + [2034] = { + {38, 29.3, 141}, + {39.9, 28.7, 141}, + {40.7, 27.4, 141}, + {38.3, 26.8, 141}, + {41.3, 25.1, 141}, + {37.4, 24.8, 141}, + }, + [2042] = { + {54.9, 65.3, 141}, + {54.3, 63.3, 141}, + {59.6, 63.3, 141}, + {53.7, 63.2, 141}, + {59.5, 63, 141}, + {53, 62.6, 141}, + {51.8, 62.3, 141}, + {52.8, 62.3, 141}, + {51.5, 62.3, 141}, + {59.4, 61.7, 141}, + {62.2, 61.7, 141}, + {50.9, 61.5, 141}, + {51, 61.4, 141}, + {59.5, 61.3, 141}, + {62.8, 61, 141}, + {62.8, 60.4, 141}, + {51.6, 60.2, 141}, + {51.5, 60.2, 141}, + {62.1, 60.1, 141}, + {51.7, 59.3, 141}, + {51, 59.2, 141}, + {61.5, 59, 141}, + {52.9, 58.5, 141}, + {58.8, 58.4, 141}, + {51, 57.4, 141}, + {50.8, 57.3, 141}, + {64.1, 56, 141}, + {49.8, 55.8, 141}, + {53.6, 55.6, 141}, + {49.7, 55.4, 141}, + {68.7, 55.4, 141}, + {57.6, 54.7, 141}, + {67, 54.3, 141}, + {68, 54, 141}, + {65.6, 53.5, 141}, + {49.7, 52.3, 141}, + {67.3, 51.3, 141}, + {66.2, 51.2, 141}, + {49.3, 51.1, 141}, + {48.7, 50.4, 141}, + {48.5, 50.1, 141}, + {54.8, 65.1, 141}, + {59.5, 61.4, 141}, + {62.8, 60.3, 141}, + {58.7, 58.3, 141}, + {68.8, 55.4, 141}, + {49.7, 55.3, 141}, + {49.7, 52.4, 141}, + {67.3, 51.2, 141}, + {48.5, 50, 141}, + }, + [2043] = { + {50, 79.2, 141}, + {50.6, 79, 141}, + {50.9, 77.2, 141}, + {49.7, 77.1, 141}, + {50.3, 76.1, 141}, + {45.8, 75.7, 141}, + {47, 75.1, 141}, + {45, 74.6, 141}, + {43.2, 74.3, 141}, + {56.9, 73.9, 141}, + {61.5, 73.3, 141}, + {62.6, 72.8, 141}, + {53.9, 72.5, 141}, + {55.8, 72.3, 141}, + {63.4, 72.2, 141}, + {62.2, 72.1, 141}, + {45.2, 72.1, 141}, + {46.6, 71.3, 141}, + {63.5, 71.3, 141}, + {47.1, 70.9, 141}, + {63.4, 70.2, 141}, + {41.2, 70.2, 141}, + {40.4, 69.9, 141}, + {39.9, 69.3, 141}, + {40.4, 69.1, 141}, + {40.7, 59.5, 141}, + {38.2, 58.6, 141}, + {38.9, 58.1, 141}, + {40.8, 57, 141}, + {38.6, 56.5, 141}, + {40.1, 55.8, 141}, + {40.5, 55.6, 141}, + {41.2, 70.1, 141}, + {38.5, 56.2, 141}, + }, + [2069] = { + {37.5, 79, 148}, + {38.5, 77.5, 148}, + {38, 77.5, 148}, + {37.5, 77.3, 148}, + {39, 72.4, 148}, + {42.6, 71.8, 148}, + {41.8, 71.5, 148}, + {39, 70.6, 148}, + {39.9, 70.6, 148}, + {40.6, 70, 148}, + {43.6, 69.9, 148}, + {40, 69.3, 148}, + {39.7, 69.3, 148}, + {39.2, 69.2, 148}, + {44.6, 68.7, 148}, + {43.5, 67.8, 148}, + {39.4, 67.8, 148}, + {41.9, 67.7, 148}, + {43.1, 67.5, 148}, + {40.1, 67.5, 148}, + {44.4, 67.4, 148}, + {42.2, 67.4, 148}, + {41, 67.4, 148}, + {39, 66.8, 148}, + {44.1, 66.1, 148}, + {40, 66, 148}, + {43.8, 65.7, 148}, + {38.6, 64.5, 148}, + {42.4, 64.2, 148}, + {39.4, 63.8, 148}, + {37.5, 63.4, 148}, + {38, 59.7, 148}, + {49.2, 32.5, 148}, + {54.9, 30.1, 148}, + {52.7, 29.4, 148}, + {50.8, 28.6, 148}, + {53.8, 27.8, 148}, + {49, 27.7, 148}, + {53.3, 27.2, 148}, + {48.7, 27.1, 148}, + {50.5, 27, 148}, + {49.4, 26.7, 148}, + {51.7, 25.6, 148}, + {48.9, 25.5, 148}, + {44.6, 24.8, 148}, + {55.5, 23.3, 148}, + {54.7, 21.9, 148}, + {55.5, 21.6, 148}, + {55.1, 21.4, 148}, + {55.4, 20.8, 148}, + }, + [2070] = { + {37.5, 93, 148}, + {39.8, 91.8, 148}, + {36.6, 90.8, 148}, + {39.5, 90.5, 148}, + {42.9, 90.3, 148}, + {37, 89.7, 148}, + {35.2, 89.7, 148}, + {45.2, 58.8, 148}, + {44.5, 58.2, 148}, + {45.3, 57.6, 148}, + {45, 57.2, 148}, + {45.2, 56.1, 148}, + {44, 55.7, 148}, + {44.6, 55.7, 148}, + {43.2, 54.6, 148}, + {42, 54.5, 148}, + {46.5, 54.2, 148}, + {46.4, 52.4, 148}, + {38.8, 50.7, 148}, + {40.1, 50.1, 148}, + {38, 50.1, 148}, + {38.9, 48.8, 148}, + {42, 48.2, 148}, + {42.1, 45.3, 148}, + {47.2, 44.5, 148}, + {44.1, 43.8, 148}, + {42.6, 43.2, 148}, + {41, 43, 148}, + {44.8, 42.7, 148}, + {46.4, 42.1, 148}, + {40.5, 41.2, 148}, + {44.8, 41.2, 148}, + {40.3, 41.1, 148}, + {47.2, 40.8, 148}, + {39.7, 40.4, 148}, + {47.9, 40.1, 148}, + {39.4, 39.7, 148}, + {39.4, 39.1, 148}, + {48.3, 38.8, 148}, + {39.6, 37.8, 148}, + {38.1, 36.9, 148}, + {39.2, 34.3, 148}, + {39.6, 33.4, 148}, + {45.5, 33.3, 148}, + {40.1, 33.2, 148}, + {45.7, 31.7, 148}, + {44.9, 29.3, 148}, + {43.6, 29, 148}, + {45.2, 28, 148}, + {44.3, 27.7, 148}, + {43, 26.1, 148}, + {42.7, 25.9, 148}, + {45.1, 24.8, 148}, + {44.7, 24.3, 148}, + {48.7, 24.3, 148}, + {43.5, 24.2, 148}, + {46.3, 24, 148}, + {46.2, 23.7, 148}, + {61, 13, 148}, + {61, 11.9, 148}, + {59.6, 11.8, 148}, + {62.6, 7, 148}, + }, + [2071] = { + {37.2, 93.4, 148}, + {39.4, 91.8, 148}, + {43, 90.8, 148}, + {36.5, 90.5, 148}, + {39.9, 90.3, 148}, + {36.9, 89.7, 148}, + {35, 88.8, 148}, + {60.9, 13.4, 148}, + {61, 11.9, 148}, + {59.9, 11.8, 148}, + {62.9, 7.3, 148}, + {62.5, 6.7, 148}, + {61.2, 13.7, 148}, + }, + [2084] = { + {51.5, 30.8, 618}, + }, + [2089] = { + {15.3, 29.2, 11}, + {17.6, 29.1, 11}, + {18.4, 27, 11}, + {17, 26.9, 11}, + {16.2, 26.2, 11}, + {19, 24.8, 11}, + {20.8, 24, 11}, + {17.5, 23.5, 11}, + {19.2, 23, 11}, + {26.1, 22.6, 11}, + {22.4, 20.9, 11}, + {21.7, 20, 11}, + {27, 19.4, 11}, + {20.6, 24.2, 11}, + }, + [2115] = { + {32.3, 65.4, 85}, + }, + [2134] = { + {61.1, 52.6, 85}, + {61.2, 52.6, 85}, + }, + [2140] = { + {44, 39.9, 130}, + }, + [2156] = { + {36.2, 86.3, 148}, + {36.3, 85.8, 148}, + {35.7, 85.6, 148}, + {36.1, 85.6, 148}, + {35.5, 85.1, 148}, + {35.1, 84.9, 148}, + {35.4, 84.4, 148}, + {35.6, 84.1, 148}, + {35.7, 85.7, 148}, + {36, 85.5, 148}, + {35.3, 84.5, 148}, + {35.6, 84.3, 148}, + }, + [2157] = { + {36.2, 86.3, 148}, + {36.3, 85.8, 148}, + {35.7, 85.6, 148}, + {36.1, 85.6, 148}, + {35.5, 85.1, 148}, + {35.1, 84.9, 148}, + {36.2, 86.4, 148}, + {36.2, 85.9, 148}, + {35.5, 85, 148}, + }, + [2163] = { + {44.7, 60.2, 148}, + {41.6, 56.9, 148}, + {43.2, 56.4, 148}, + {42.6, 55.8, 148}, + {45.7, 55.3, 148}, + {43.6, 54.9, 148}, + {42.9, 54.8, 148}, + {46.3, 54.8, 148}, + {38, 48.6, 148}, + {41, 48.5, 148}, + {41.6, 48, 148}, + {42.6, 47.9, 148}, + {42, 47.1, 148}, + {38.1, 47, 148}, + {43.2, 44.2, 148}, + {45.1, 43.8, 148}, + {45.6, 43.5, 148}, + {44.6, 43.2, 148}, + {43.7, 42.7, 148}, + {43.2, 42.6, 148}, + {43.5, 41.9, 148}, + {41.1, 41.9, 148}, + {47, 41.9, 148}, + {45.8, 41.7, 148}, + {45.7, 41.5, 148}, + {48.2, 40.8, 148}, + {40.4, 40.3, 148}, + {39.9, 39.5, 148}, + {38.4, 38.8, 148}, + {38.4, 38.6, 148}, + {40.5, 38.4, 148}, + {49, 38.3, 148}, + {38.6, 38.3, 148}, + {40.5, 38, 148}, + {39.2, 37.2, 148}, + {40.2, 36.8, 148}, + {40.2, 36.5, 148}, + {39.5, 36.3, 148}, + {40.4, 35, 148}, + {41.6, 34.8, 148}, + {43.4, 34.7, 148}, + {41.1, 34.1, 148}, + {44.9, 33.9, 148}, + {41.6, 33.9, 148}, + {44.6, 33.4, 148}, + {43.4, 33.4, 148}, + {43.7, 32.4, 148}, + {45.1, 32.3, 148}, + {44.1, 32.2, 148}, + {44.4, 31.6, 148}, + {44.3, 31, 148}, + {44.8, 30.8, 148}, + {43.5, 30, 148}, + {45.5, 29.9, 148}, + {44.6, 29.9, 148}, + {43.7, 29.4, 148}, + {44.1, 29, 148}, + {45.7, 28.9, 148}, + {44.8, 27.2, 148}, + {43.4, 26.9, 148}, + {46, 26.6, 148}, + {45.9, 26.4, 148}, + {45.3, 26.4, 148}, + {45.6, 26, 148}, + {44.4, 25.9, 148}, + {43.5, 25.8, 148}, + {46.7, 25.4, 148}, + {42.9, 24.9, 148}, + {46.2, 24.9, 148}, + {44.5, 24.7, 148}, + {45.6, 23.9, 148}, + {47.6, 23.8, 148}, + {47.3, 23.7, 148}, + {45.2, 23.6, 148}, + {48, 23.4, 148}, + {45.8, 23, 148}, + {43.1, 22.9, 148}, + {46.8, 22.6, 148}, + {43.5, 22.6, 148}, + {48.7, 22.2, 148}, + {30.1, 31.1, 361}, + {45.1, 24.8, 148}, + {44.6, 28.6, 148}, + }, + [2165] = { + {40.1, 82.9, 148}, + {43, 82.4, 148}, + {38.6, 82.4, 148}, + {38, 82, 148}, + {37.5, 82, 148}, + {39.6, 82, 148}, + {40.5, 81.8, 148}, + {45.1, 81.4, 148}, + {39.3, 81.4, 148}, + {41.1, 81.3, 148}, + {40.4, 81.1, 148}, + {39.8, 80.8, 148}, + {38.6, 80.5, 148}, + {37.7, 79.8, 148}, + {40.1, 79.8, 148}, + {37.8, 78.6, 148}, + {39.1, 76.3, 148}, + {40.3, 75.9, 148}, + {38.2, 71.1, 148}, + {39.1, 70, 148}, + {28.7, 61.4, 361}, + {39.7, 82.1, 148}, + }, + [2172] = { + {34.8, 87.3, 148}, + }, + [2175] = { + {40.3, 40.5, 148}, + }, + [2231] = { + {35.4, 51.2, 148}, + {35.6, 50.7, 148}, + {35.7, 50, 148}, + {36.5, 50, 148}, + {36, 49.9, 148}, + {35.4, 49.4, 148}, + {35.1, 49.4, 148}, + {36.5, 49.1, 148}, + {36.4, 49.1, 148}, + {35.1, 48.5, 148}, + {36.7, 48.4, 148}, + {35.9, 48.3, 148}, + {35.6, 48.1, 148}, + {34.7, 48, 148}, + {36.4, 47.8, 148}, + {35.4, 47.5, 148}, + {35.6, 47.1, 148}, + {36, 46.9, 148}, + {35, 46.7, 148}, + {35.3, 46.2, 148}, + {35.9, 45.9, 148}, + {35.5, 44.7, 148}, + {36.2, 44.5, 148}, + {35, 44.4, 148}, + {35.2, 43, 148}, + {36.8, 41.8, 148}, + {35.9, 41.3, 148}, + {35.5, 41.2, 148}, + {37.2, 40.6, 148}, + {36.9, 40.5, 148}, + {36.3, 40.4, 148}, + {37.4, 40.3, 148}, + {36.1, 39.8, 148}, + {37.4, 39.7, 148}, + {36.5, 39.4, 148}, + {36.4, 38.8, 148}, + {37.2, 38.7, 148}, + {36.4, 37.7, 148}, + {36.5, 37.5, 148}, + {37.1, 37.1, 148}, + {36.6, 36.8, 148}, + {37, 36.6, 148}, + {36.7, 36.3, 148}, + {36.8, 35.9, 148}, + {37.7, 35.8, 148}, + {37, 35.5, 148}, + {36, 50.1, 148}, + {36.6, 49.8, 148}, + {36, 47, 148}, + {35.4, 44.6, 148}, + }, + [2232] = { + {36.6, 69.8, 148}, + {35.5, 68.9, 148}, + {36.7, 68.2, 148}, + {37.1, 67.2, 148}, + {36.9, 66.6, 148}, + {36.4, 65.9, 148}, + {36.7, 65.8, 148}, + {35.6, 65.8, 148}, + {35.5, 65.2, 148}, + {35.8, 64.6, 148}, + {36.3, 63.2, 148}, + {36, 62.7, 148}, + {41.6, 30.1, 148}, + {42.1, 29.4, 148}, + {41.2, 28.6, 148}, + {42.3, 28.3, 148}, + {41.5, 26.9, 148}, + {41.9, 26.8, 148}, + {41.6, 25.3, 148}, + {42.1, 24.8, 148}, + {41.6, 24.2, 148}, + {42.6, 23.2, 148}, + {41.7, 23, 148}, + {42.3, 22.6, 148}, + {42.2, 21, 148}, + {42.8, 20.6, 148}, + {43.1, 19.8, 148}, + }, + [2233] = { + {32.3, 83.7, 148}, + {32.9, 82.9, 148}, + {31.7, 81.9, 148}, + {32.7, 81.4, 148}, + {32, 80.7, 148}, + {52.3, 23.3, 148}, + {51.3, 23.2, 148}, + {51.8, 22.5, 148}, + {51.7, 21, 148}, + {52.2, 20.9, 148}, + {52.8, 19.5, 148}, + {54, 18.9, 148}, + {52.2, 18.6, 148}, + {53.3, 18.6, 148}, + {52.8, 17.8, 148}, + {51.7, 22.5, 148}, + }, + [2234] = { + {35.1, 56.1, 148}, + {35.2, 54.4, 148}, + {34.9, 54.4, 148}, + {34.5, 53.7, 148}, + {35.9, 53.7, 148}, + {35.2, 52.7, 148}, + {36.5, 52.2, 148}, + {37, 34.3, 148}, + {37.8, 33.9, 148}, + {37.4, 32.9, 148}, + {38.1, 32.4, 148}, + {40.6, 31.8, 148}, + {39.5, 31.5, 148}, + {38.5, 31.5, 148}, + {40.2, 31.4, 148}, + {40.9, 31.1, 148}, + {38.9, 30.7, 148}, + }, + [2235] = { + {35.5, 77.5, 148}, + {36, 75.2, 148}, + {35.7, 73.6, 148}, + {35.8, 72.8, 148}, + {35.2, 72.4, 148}, + {34.9, 72.3, 148}, + {50.8, 22.5, 148}, + {50.6, 22.4, 148}, + {44.8, 21.8, 148}, + {47.6, 21.5, 148}, + {46.5, 21, 148}, + {48.2, 21, 148}, + {45.5, 20.7, 148}, + {48.3, 20.7, 148}, + {46.1, 20.6, 148}, + {46.8, 20.3, 148}, + {48.9, 20.1, 148}, + {45.4, 20, 148}, + {50.7, 22.5, 148}, + }, + [2236] = { + {29.8, 91.1, 148}, + {29.7, 91.1, 148}, + {29.5, 90.8, 148}, + {28.6, 90.8, 148}, + {30.6, 90.5, 148}, + {29.6, 90.4, 148}, + {30.6, 89.7, 148}, + {29.7, 89.6, 148}, + {31.4, 89.6, 148}, + {32.9, 89, 148}, + {31, 89, 148}, + {55.5, 19.2, 148}, + {54.8, 18.3, 148}, + {56, 17.5, 148}, + {55.3, 17.1, 148}, + {56.4, 17, 148}, + {55.8, 16.6, 148}, + {56.4, 16.3, 148}, + {56, 14.9, 148}, + {57.5, 14.5, 148}, + {56.3, 14.5, 148}, + {56.9, 13.1, 148}, + {56.4, 12.5, 148}, + {56.6, 11.4, 148}, + {12.3, 8.3, 331}, + {12.2, 8.2, 331}, + {12, 8, 331}, + {11, 7.9, 331}, + {13.2, 7.6, 331}, + {12, 7.4, 331}, + {13.2, 6.6, 331}, + {12.2, 6.6, 331}, + {14.2, 6.5, 331}, + }, + [2237] = { + {39.3, 94.5, 148}, + {44.7, 92, 148}, + {37.5, 92, 148}, + {36.6, 91.5, 148}, + {40.4, 90.8, 148}, + {38.5, 90.5, 148}, + {42.6, 88.2, 148}, + {37.6, 83.9, 148}, + {41.7, 83.3, 148}, + {43.8, 83.1, 148}, + {43.2, 82.8, 148}, + {36.2, 82.1, 148}, + {43.2, 82, 148}, + {43.8, 81.8, 148}, + {39.9, 81.4, 148}, + {44.1, 81.3, 148}, + {44, 80, 148}, + {38.3, 77.2, 148}, + {39.8, 70.7, 148}, + {60, 13.4, 148}, + {61.4, 11.2, 148}, + {23.1, 12.1, 331}, + {40.1, 82.9, 148}, + {40.1, 79.8, 148}, + {37.5, 91.9, 148}, + {42.5, 88.1, 148}, + {43.2, 82.9, 148}, + {60.1, 13.4, 148}, + }, + [2322] = { + {37.6, 78.8, 148}, + {40.7, 77.3, 148}, + {38.4, 76.6, 148}, + {39.9, 76.4, 148}, + {39.1, 75, 148}, + {38.2, 73.7, 148}, + {37.9, 72.4, 148}, + {42.2, 70.8, 148}, + {40.5, 70.8, 148}, + {43.7, 70.5, 148}, + {38.7, 70.5, 148}, + {39.3, 70.2, 148}, + {43.8, 69.7, 148}, + {42.5, 69, 148}, + {49.1, 36.6, 148}, + {49.5, 36.5, 148}, + {52.6, 35.4, 148}, + {49.4, 34.9, 148}, + {51.8, 34.7, 148}, + {48.6, 34.7, 148}, + {49.9, 33.9, 148}, + {51.1, 33.8, 148}, + {48, 33.7, 148}, + {46.9, 33.6, 148}, + {49, 33.6, 148}, + {51.1, 32.5, 148}, + {49.9, 32.2, 148}, + {48.5, 32.2, 148}, + {47.2, 31.9, 148}, + {50.5, 31.9, 148}, + {48.9, 31.1, 148}, + {46.9, 31.1, 148}, + {48.3, 30.6, 148}, + {49.8, 30.5, 148}, + {54.1, 30.5, 148}, + {50.4, 30.3, 148}, + {50.6, 29.3, 148}, + {48.8, 29.1, 148}, + {53.4, 28.9, 148}, + {47.4, 28.5, 148}, + {49.6, 28.4, 148}, + {52.7, 27.9, 148}, + {49.6, 27.7, 148}, + {48.9, 27.3, 148}, + {48, 26.8, 148}, + {52.7, 26.2, 148}, + {53.9, 26.1, 148}, + {49.7, 26, 148}, + }, + [2323] = { + {37.1, 95.6, 148}, + {38.2, 95.6, 148}, + {38.8, 94.8, 148}, + {39.6, 94.6, 148}, + {37, 94.6, 148}, + {36.5, 94.3, 148}, + {36.7, 93.6, 148}, + {40.5, 93.6, 148}, + {39.6, 93.6, 148}, + {37.6, 92.9, 148}, + {36.9, 92.8, 148}, + {40.9, 92.6, 148}, + {38.5, 92.3, 148}, + {40.6, 92, 148}, + {40.1, 91.9, 148}, + {44.9, 91.8, 148}, + {36.5, 91.6, 148}, + {44.6, 91.6, 148}, + {36.3, 91.5, 148}, + {38, 91.4, 148}, + {46, 91.4, 148}, + {39.1, 91.3, 148}, + {38, 90.3, 148}, + {38.9, 90.1, 148}, + {37.6, 90, 148}, + {42.3, 89.7, 148}, + {36, 89.6, 148}, + {40.5, 89.5, 148}, + {42.3, 89.3, 148}, + {37.8, 89.1, 148}, + {35.9, 89.1, 148}, + {37, 88.2, 148}, + {41.5, 88.1, 148}, + {34.8, 88, 148}, + {34.4, 86.8, 148}, + {37, 83.6, 148}, + {41.2, 83.3, 148}, + {38.1, 83, 148}, + {36, 82.4, 148}, + {35, 82.3, 148}, + {40.9, 82.3, 148}, + {43.3, 82.2, 148}, + {39.5, 81.9, 148}, + {37, 81.6, 148}, + {38.1, 81.2, 148}, + {40.6, 81.1, 148}, + {37.2, 80.6, 148}, + {42.3, 80.5, 148}, + {39, 80.4, 148}, + {43.2, 80.2, 148}, + {35.8, 80, 148}, + {61.2, 14.9, 148}, + {61.6, 14.2, 148}, + {58.4, 14.2, 148}, + {58.9, 13.3, 148}, + {59.5, 12.6, 148}, + {61.6, 12.3, 148}, + {61.8, 12, 148}, + {60.9, 10.3, 148}, + {62.6, 9.2, 148}, + {61.6, 8.9, 148}, + {20.7, 13.4, 331}, + {21.8, 13.4, 331}, + {22.6, 12.5, 331}, + {23.4, 12.3, 331}, + {24.4, 11.1, 331}, + {28.6, 73.2, 361}, + {29.8, 72.8, 361}, + }, + [2348] = { + {12.2, 40.3, 45}, + {10.8, 39.5, 45}, + {11.4, 38.1, 45}, + {12.9, 38, 45}, + {66.4, 77.6, 267}, + {63.9, 73.6, 267}, + {66.6, 72.8, 267}, + {66, 72.6, 267}, + {62.8, 72.2, 267}, + {65.8, 70.9, 267}, + {63.4, 70.8, 267}, + {74.2, 70.4, 267}, + {68.9, 69.9, 267}, + {72.5, 69.5, 267}, + {60.7, 68.6, 267}, + {68.3, 67.9, 267}, + {73.2, 67.9, 267}, + {75, 67.8, 267}, + {66.5, 67.8, 267}, + {63.9, 66.4, 267}, + {67.3, 65.9, 267}, + {61.1, 63.2, 267}, + {58.6, 61.8, 267}, + {69.2, 60.5, 267}, + {60.4, 57.9, 267}, + {59.4, 55.5, 267}, + {61.2, 55.4, 267}, + {62, 54, 267}, + {60.2, 52.7, 267}, + {57, 52.4, 267}, + {65.7, 71.2, 267}, + {56.7, 52.4, 267}, + }, + [2349] = { + {19.7, 99.2, 36}, + {20.2, 97, 36}, + {32.2, 96.8, 36}, + {23.2, 95.8, 36}, + {69.3, 95.7, 36}, + {21.3, 94.6, 36}, + {75.6, 92.8, 36}, + {29.4, 92.7, 36}, + {32.1, 92.1, 36}, + {22.4, 91.8, 36}, + {18.4, 91.8, 36}, + {22.5, 91.7, 36}, + {69.7, 90.5, 36}, + {15.8, 88.8, 36}, + {24.1, 88, 36}, + {75.3, 87, 36}, + {14.6, 86.3, 36}, + {26.6, 83.6, 36}, + {72.2, 83, 36}, + {71.3, 82.6, 36}, + {22.6, 82, 36}, + {14.6, 81.7, 36}, + {24.9, 79.6, 36}, + {28.8, 79.5, 36}, + {24.2, 79.1, 36}, + {30.1, 78.2, 36}, + {23.4, 77.7, 36}, + {26.2, 76.2, 36}, + {25.9, 74.3, 36}, + {28.5, 73.5, 36}, + {22.4, 73.4, 36}, + {27.7, 69.9, 36}, + {25.6, 66, 36}, + {25, 63.3, 36}, + {23.7, 56.6, 36}, + {15.1, 35.1, 45}, + {11.6, 33.9, 45}, + {15.7, 33.4, 45}, + {13.6, 32.8, 45}, + {16, 31.7, 45}, + {14.2, 31, 45}, + {14.5, 27.3, 45}, + {19.6, 25, 45}, + {17.9, 24.1, 45}, + {21.4, 22.6, 45}, + {20.2, 21.3, 45}, + {21.4, 21.3, 45}, + {23.5, 18.3, 45}, + {21, 18.3, 45}, + {26.4, 14.6, 45}, + {24.6, 14.5, 45}, + {26, 13.5, 45}, + {27.6, 67.8, 267}, + {29.6, 66.4, 267}, + {33, 66, 267}, + {38.6, 65.5, 267}, + {41.3, 65.2, 267}, + {27.8, 65.1, 267}, + {33.3, 64.9, 267}, + {32, 64.7, 267}, + {77.4, 64.5, 267}, + {40.9, 64.4, 267}, + {31.8, 63.6, 267}, + {36.4, 63.5, 267}, + {29, 63.3, 267}, + {33.8, 63.2, 267}, + {73.5, 63.1, 267}, + {32.2, 62.7, 267}, + {78.1, 62.5, 267}, + {75.7, 61.9, 267}, + {71.4, 61.9, 267}, + {44.6, 61.6, 267}, + {31, 61.3, 267}, + {42.7, 61.1, 267}, + {78.4, 60.6, 267}, + {70.3, 60.3, 267}, + {76.4, 59.9, 267}, + {39.6, 59.9, 267}, + {42.5, 59.4, 267}, + {36.1, 59, 267}, + {31.7, 58.9, 267}, + {66.7, 58.8, 267}, + {24.7, 58.6, 267}, + {40.3, 58.3, 267}, + {64.9, 57.8, 267}, + {34.1, 57.4, 267}, + {23.9, 57.1, 267}, + {66.1, 57, 267}, + {24.5, 57, 267}, + {35.7, 56.7, 267}, + {29.2, 56.5, 267}, + {36.8, 55.9, 267}, + {73.3, 55.8, 267}, + {32.7, 55.8, 267}, + {76.7, 55.7, 267}, + {62.9, 55.6, 267}, + {67.2, 55.6, 267}, + {38.4, 55.5, 267}, + {29.7, 54.2, 267}, + {36.9, 54.2, 267}, + {28.7, 54.1, 267}, + {34.8, 54.1, 267}, + {27.5, 54.1, 267}, + {75.8, 54.1, 267}, + {23.5, 54, 267}, + {73.5, 53.8, 267}, + {82.5, 53.1, 267}, + {32.7, 52.9, 267}, + {69, 52.5, 267}, + {65.1, 52.2, 267}, + {41.3, 52.2, 267}, + {80.6, 52.1, 267}, + {70.3, 52, 267}, + {40.3, 51.7, 267}, + {38.1, 51.6, 267}, + {77.6, 51.3, 267}, + {23.5, 51, 267}, + {27.7, 50.7, 267}, + {73, 50.5, 267}, + {84.5, 50.4, 267}, + {71.4, 50.3, 267}, + {40.2, 49.6, 267}, + {64, 49.2, 267}, + {26.6, 49, 267}, + {83.1, 49, 267}, + {84.5, 49, 267}, + {68.7, 48.9, 267}, + {60.9, 47.8, 267}, + {61.9, 46.5, 267}, + {86.9, 45.6, 267}, + {84, 45.5, 267}, + {63.7, 44.8, 267}, + {90.2, 41.4, 267}, + {88.1, 41.3, 267}, + {89.6, 40.2, 267}, + {67.3, 38.2, 267}, + {26.2, 36.9, 267}, + {68.2, 36.7, 267}, + {85.6, 36.1, 267}, + {84, 35.4, 267}, + {26.1, 35.3, 267}, + {75.3, 34, 267}, + {26.6, 33.3, 267}, + {37, 33.2, 267}, + {29.1, 32.3, 267}, + {69.5, 32.1, 267}, + {81.5, 32, 267}, + {79, 31.7, 267}, + {27.5, 31.2, 267}, + {75, 29.6, 267}, + {34.6, 29.6, 267}, + {36.9, 29, 267}, + {28.5, 28.8, 267}, + {24.9, 28.8, 267}, + {28.5, 28.7, 267}, + {69.8, 27.6, 267}, + {22.7, 26.1, 267}, + {30, 25.4, 267}, + {74.8, 24.6, 267}, + {21.6, 23.9, 267}, + {32.1, 21.6, 267}, + {72.1, 21.1, 267}, + {71.3, 20.7, 267}, + {28.6, 20.2, 267}, + {21.6, 19.9, 267}, + {30.7, 18.1, 267}, + {34.1, 18, 267}, + {30, 17.7, 267}, + {35.2, 16.9, 267}, + {29.3, 16.4, 267}, + {31.8, 15.1, 267}, + {31.5, 13.4, 267}, + {33.8, 12.8, 267}, + {28.4, 12.7, 267}, + {32.9, 52.7, 267}, + {65, 51.8, 267}, + {64.1, 49.1, 267}, + {68.3, 36.7, 267}, + {83.7, 28.1, 5179}, + {85.5, 27, 5179}, + {88.5, 26.6, 5179}, + {93.3, 26.1, 5179}, + {95.6, 25.9, 5179}, + {83.9, 25.8, 5179}, + {88.7, 25.7, 5179}, + {87.6, 25.4, 5179}, + {95.3, 25.2, 5179}, + {87.4, 24.5, 5179}, + {91.4, 24.4, 5179}, + {84.9, 24.2, 5179}, + {89.1, 24.1, 5179}, + {87.7, 23.7, 5179}, + {98.6, 22.7, 5179}, + {86.7, 22.4, 5179}, + {96.9, 22.3, 5179}, + {94.2, 21.2, 5179}, + {96.7, 20.8, 5179}, + {91.2, 20.5, 5179}, + {87.3, 20.3, 5179}, + {81.2, 20.1, 5179}, + {94.8, 19.9, 5179}, + {89.4, 19.1, 5179}, + {80.5, 18.8, 5179}, + {81, 18.7, 5179}, + {90.8, 18.4, 5179}, + {85.1, 18.3, 5179}, + {91.8, 17.8, 5179}, + {88.2, 17.7, 5179}, + {93.2, 17.4, 5179}, + {85.6, 16.3, 5179}, + {91.8, 16.3, 5179}, + {84.7, 16.2, 5179}, + {90, 16.2, 5179}, + {83.7, 16.1, 5179}, + {80.1, 16.1, 5179}, + {88.4, 15, 5179}, + {95.7, 14.5, 5179}, + {94.8, 14.1, 5179}, + {92.9, 14, 5179}, + {80.2, 13.5, 5179}, + {83.8, 13.2, 5179}, + {94.7, 12.3, 5179}, + {82.9, 11.8, 5179}, + {82.5, 1.1, 5179}, + }, + [2350] = { + {58, 99.1, 36}, + {57.2, 98.9, 36}, + {72, 98, 36}, + {63.6, 95.5, 36}, + {60.5, 94.9, 36}, + {54.5, 93.3, 36}, + {56.2, 92, 36}, + {64.9, 90.1, 36}, + {64.4, 88.2, 36}, + {55.9, 86.6, 36}, + {65.6, 83.4, 36}, + {54.8, 77.8, 36}, + {59.4, 75.9, 36}, + {51.6, 75.6, 36}, + {62.7, 75.3, 36}, + {61.4, 74.6, 36}, + {57.7, 74.6, 36}, + {51.3, 74.5, 36}, + {55, 74.4, 36}, + {68, 74.2, 36}, + {62.9, 71.1, 36}, + {67.1, 70.6, 36}, + {54.5, 70.3, 36}, + {66.7, 69.9, 36}, + {69.9, 69.2, 36}, + {23, 53.5, 267}, + {19.7, 53.4, 267}, + {18.1, 52.9, 267}, + {21.2, 51.8, 267}, + {78.5, 50.6, 267}, + {19.1, 47.8, 267}, + {70.4, 44.6, 267}, + {20.4, 43.3, 267}, + {23.5, 43.1, 267}, + {71.8, 41.2, 267}, + {62.2, 39.8, 267}, + {63.2, 39.3, 267}, + {25.6, 39.3, 267}, + {26.6, 38.9, 267}, + {27.8, 38.9, 267}, + {60.9, 38.2, 267}, + {70.8, 38.2, 267}, + {71.4, 36.6, 267}, + {63.3, 36.3, 267}, + {59.6, 35.2, 267}, + {58.9, 35, 267}, + {71.9, 34.2, 267}, + {64.5, 32, 267}, + {61.8, 31.5, 267}, + {56.5, 30.1, 267}, + {58.1, 28.9, 267}, + {65.7, 27.2, 267}, + {65.2, 25.6, 267}, + {57.8, 24.2, 267}, + {66.2, 21.4, 267}, + {56.8, 16.5, 267}, + {60.8, 14.8, 267}, + {54, 14.6, 267}, + {63.7, 14.3, 267}, + {62.6, 13.7, 267}, + {59.4, 13.7, 267}, + {53.8, 13.6, 267}, + {57, 13.5, 267}, + {68.3, 13.3, 267}, + {63.9, 10.7, 267}, + {67.5, 10.2, 267}, + {56.5, 10, 267}, + {67.2, 9.6, 267}, + {70.1, 9, 267}, + {57.3, 99.3, 36}, + {65.5, 83.4, 36}, + {54.6, 78.2, 36}, + {51.4, 75.4, 36}, + {55.2, 74.4, 36}, + {20.5, 43.4, 267}, + {61, 38.2, 267}, + {59, 35.3, 267}, + {66.1, 21.4, 267}, + {56.6, 16.9, 267}, + {53.8, 14.4, 267}, + {57.1, 13.5, 267}, + {79.7, 15.7, 5179}, + {76.8, 15.6, 5179}, + {75.5, 15.1, 5179}, + {78.1, 14.2, 5179}, + {76.3, 10.6, 5179}, + {77.5, 6.9, 5179}, + {80.2, 6.5, 5179}, + {82, 3.2, 5179}, + {82.8, 2.9, 5179}, + {83.9, 2.9, 5179}, + }, + [2351] = { + {66, 98.5, 36}, + {63.4, 97.7, 36}, + {56, 97.4, 36}, + {57.4, 96.4, 36}, + {58.2, 94.3, 36}, + {62, 93.4, 36}, + {54.9, 92, 36}, + {66.3, 91.9, 36}, + {63.4, 91.8, 36}, + {55.9, 91.3, 36}, + {54.9, 91.1, 36}, + {66.7, 88.5, 36}, + {55.7, 85.9, 36}, + {65.4, 85.3, 36}, + {66.5, 85.2, 36}, + {66.6, 81.3, 36}, + {53.2, 77.2, 36}, + {66.2, 76.9, 36}, + {55.7, 76.2, 36}, + {52.5, 74.2, 36}, + {67.9, 72.6, 36}, + {62.1, 72, 36}, + {68.5, 69.8, 36}, + {51.9, 69.1, 36}, + {54.1, 68.8, 36}, + {63.1, 68.7, 36}, + {62.1, 66.2, 36}, + {69.9, 79.4, 130}, + {71.1, 78.4, 130}, + {22, 56.5, 267}, + {20.2, 53.6, 267}, + {20, 52.2, 267}, + {19, 49.8, 267}, + {23.4, 48.2, 267}, + {30.7, 48.1, 267}, + {17.3, 44.8, 267}, + {23.4, 43.9, 267}, + {18.9, 43.6, 267}, + {21.5, 42.8, 267}, + {26.4, 41.8, 267}, + {26.7, 41.6, 267}, + {24.4, 40.1, 267}, + {26.3, 38.2, 267}, + {64.4, 37.8, 267}, + {61.9, 36.7, 267}, + {59.5, 36.6, 267}, + {61.5, 36, 267}, + {65.4, 35.9, 267}, + {66.6, 34.6, 267}, + {64.4, 34, 267}, + {57.9, 33.7, 267}, + {59.1, 32.7, 267}, + {59.8, 30.9, 267}, + {63.1, 30.1, 267}, + {56.9, 28.9, 267}, + {66.9, 28.8, 267}, + {64.4, 28.7, 267}, + {57.8, 28.3, 267}, + {56.9, 28.1, 267}, + {67.2, 25.9, 267}, + {57.6, 23.6, 267}, + {66.1, 23.1, 267}, + {67, 23, 267}, + {67.1, 19.6, 267}, + {55.4, 16, 267}, + {66.8, 15.7, 267}, + {57.6, 15.1, 267}, + {54.8, 13.4, 267}, + {68.2, 12, 267}, + {63.2, 11.5, 267}, + {68.8, 9.5, 267}, + {54.2, 8.9, 267}, + {56.2, 8.6, 267}, + {64.1, 8.6, 267}, + {63.2, 6.4, 267}, + {65.8, 98.1, 36}, + {56.3, 97.9, 36}, + {55.1, 92.1, 36}, + {66.7, 88.7, 36}, + {66.5, 81.2, 36}, + {52.5, 77.1, 36}, + {50.4, 68.8, 36}, + {62, 36.3, 267}, + {66.5, 34.2, 267}, + {58.1, 34.1, 267}, + {57, 29, 267}, + {66.9, 28.9, 267}, + {67.2, 26, 267}, + {67.1, 19.5, 267}, + {54.8, 15.9, 267}, + {53, 8.6, 267}, + {78.8, 18.2, 5179}, + {77.3, 15.7, 5179}, + {77.1, 14.5, 5179}, + {76.2, 12.5, 5179}, + {80, 11, 5179}, + {86.4, 10.9, 5179}, + {74.7, 8, 5179}, + {80, 7.3, 5179}, + {76.1, 7, 5179}, + {78.4, 6.3, 5179}, + {82.7, 5.5, 5179}, + {82.9, 5.3, 5179}, + {80.9, 3.9, 5179}, + {82.6, 2.3, 5179}, + }, + [2354] = { + {71.1, 98.8, 36}, + {41.7, 98.8, 36}, + {36.8, 98.3, 36}, + {70.4, 97.2, 36}, + {47, 97.2, 36}, + {49.3, 96.8, 36}, + {48.7, 95, 36}, + {50.4, 94.2, 36}, + {54.8, 52.3, 267}, + {44.5, 50.7, 267}, + {76.5, 49.1, 267}, + {53.1, 47.9, 267}, + {46.6, 47.2, 267}, + {48.1, 47.2, 267}, + {45.7, 46.8, 267}, + {54.8, 46.4, 267}, + {48.6, 46.2, 267}, + {52.3, 46, 267}, + {40.4, 44.8, 267}, + {49.9, 44.7, 267}, + {45.6, 43.6, 267}, + {56.3, 43.2, 267}, + {49.5, 43.1, 267}, + {40.6, 42.6, 267}, + {53.3, 42.5, 267}, + {54.4, 42.2, 267}, + {38.8, 41.8, 267}, + {44.3, 41.4, 267}, + {45.4, 40.3, 267}, + {56.9, 40.3, 267}, + {56.6, 39.6, 267}, + {54.9, 39.5, 267}, + {66.5, 39.5, 267}, + {40.9, 39.5, 267}, + {46.5, 38.8, 267}, + {44.6, 38.6, 267}, + {51.5, 37.8, 267}, + {38.5, 37.7, 267}, + {44.7, 37.1, 267}, + {49.1, 37, 267}, + {41.2, 36.8, 267}, + {40.1, 36.8, 267}, + {47.8, 36.4, 267}, + {70.1, 35.5, 267}, + {71.1, 34.9, 267}, + {45.4, 34.9, 267}, + {74, 34.5, 267}, + {41, 34.5, 267}, + {70.5, 33.5, 267}, + {49.9, 33.5, 267}, + {52, 33.2, 267}, + {51.5, 31.5, 267}, + {52.9, 30.9, 267}, + {54.8, 52.5, 267}, + {45.5, 43.6, 267}, + {49.2, 43, 267}, + {44.2, 41.4, 267}, + {41.1, 37.2, 267}, + {98.5, 13.2, 5179}, + {99.5, 9.8, 5179}, + {94.9, 8, 5179}, + {99.4, 7, 5179}, + {95.1, 6.1, 5179}, + {93.5, 5.5, 5179}, + {98.3, 5.1, 5179}, + {99.2, 4.2, 5179}, + {95.3, 3.4, 5179}, + {98.5, 2.6, 5179}, + {93.2, 1.9, 5179}, + {95.5, 1.4, 5179}, + {98.6, 1.3, 5179}, + {94.6, 1, 5179}, + }, + [2356] = { + {20.7, 98.1, 36}, + {30.9, 97.3, 36}, + {23.5, 95.8, 36}, + {32.4, 94.2, 36}, + {22.1, 94.2, 36}, + {22.6, 92.8, 36}, + {74.2, 92.7, 36}, + {69.1, 92, 36}, + {75.4, 90.3, 36}, + {70.4, 88.4, 36}, + {73.8, 84.8, 36}, + {70.7, 84.5, 36}, + {70.2, 84, 36}, + {15.4, 83.2, 36}, + {23.8, 82.8, 36}, + {26.9, 81.9, 36}, + {24.7, 81.1, 36}, + {26.3, 78, 36}, + {24.8, 77.1, 36}, + {23.3, 76, 36}, + {21.3, 75.7, 36}, + {21.6, 73, 36}, + {21.2, 69.1, 36}, + {28.3, 69.1, 36}, + {25, 64.7, 36}, + {24.8, 59.4, 36}, + {12.8, 37, 45}, + {14.8, 36.9, 45}, + {16.7, 30.1, 45}, + {15.5, 26.8, 45}, + {20.5, 25.8, 45}, + {19, 24.4, 45}, + {16.2, 24.3, 45}, + {19.4, 24, 45}, + {22.4, 20, 45}, + {20.5, 19.4, 45}, + {26.3, 16.2, 45}, + {26, 15.7, 45}, + {24.9, 14.7, 45}, + {24.2, 14.2, 45}, + {71.9, 66.9, 267}, + {74.9, 66.7, 267}, + {77.1, 66.5, 267}, + {28.9, 66.1, 267}, + {37.7, 65.7, 267}, + {72.2, 65.6, 267}, + {29.6, 65.6, 267}, + {30.6, 65.2, 267}, + {33.1, 64.5, 267}, + {35, 64.4, 267}, + {43, 63.5, 267}, + {29.4, 62.9, 267}, + {38.3, 62.8, 267}, + {71, 62.4, 267}, + {73, 61.9, 267}, + {35.3, 61.6, 267}, + {68.1, 61.4, 267}, + {40.1, 61, 267}, + {34.9, 60.2, 267}, + {45.2, 60.1, 267}, + {73.7, 59.9, 267}, + {79.3, 58.8, 267}, + {37.6, 58.4, 267}, + {41.6, 58.3, 267}, + {24.2, 57.7, 267}, + {45, 57.6, 267}, + {75.1, 57.5, 267}, + {40.1, 57.1, 267}, + {39.8, 56.7, 267}, + {34.5, 56.6, 267}, + {30.5, 56.5, 267}, + {46.7, 56.4, 267}, + {29.4, 55.4, 267}, + {32.4, 55.3, 267}, + {77.8, 55.1, 267}, + {34.9, 54.3, 267}, + {25.7, 54.2, 267}, + {83.5, 54, 267}, + {39, 53.2, 267}, + {72.1, 52.7, 267}, + {30.6, 52.6, 267}, + {28.7, 52.4, 267}, + {81.8, 52.4, 267}, + {74.8, 52.4, 267}, + {26.5, 52.4, 267}, + {43, 52.4, 267}, + {78.6, 52.3, 267}, + {24.6, 52.1, 267}, + {82.2, 52, 267}, + {25.2, 51.3, 267}, + {39.1, 50.9, 267}, + {36.1, 50.9, 267}, + {70.3, 50.7, 267}, + {78.5, 50.5, 267}, + {63.1, 50.3, 267}, + {38.1, 49.4, 267}, + {62.4, 48.9, 267}, + {63.4, 48.3, 267}, + {41.2, 47.7, 267}, + {65.3, 47.6, 267}, + {85.6, 47.6, 267}, + {42.3, 47.5, 267}, + {83.5, 46.8, 267}, + {63, 46.1, 267}, + {90, 43.2, 267}, + {89.7, 42.7, 267}, + {88.4, 41.5, 267}, + {87.7, 40.9, 267}, + {87.8, 39.8, 267}, + {85.8, 39.1, 267}, + {87, 37.9, 267}, + {82.9, 36.6, 267}, + {26.7, 36.3, 267}, + {79.3, 36.1, 267}, + {87.1, 35.9, 267}, + {80.2, 35.7, 267}, + {81.5, 35.6, 267}, + {85, 35.2, 267}, + {27, 34.3, 267}, + {35.9, 33.5, 267}, + {74.5, 33.5, 267}, + {84.7, 33.3, 267}, + {78.6, 32.9, 267}, + {29.4, 32.2, 267}, + {76.6, 31.8, 267}, + {37.2, 30.8, 267}, + {28.2, 30.8, 267}, + {77.3, 30.7, 267}, + {28.6, 29.6, 267}, + {73.8, 29.5, 267}, + {69.3, 29, 267}, + {74.8, 27.5, 267}, + {70.5, 25.7, 267}, + {73.4, 22.6, 267}, + {70.7, 22.4, 267}, + {70.3, 22, 267}, + {22.4, 21.3, 267}, + {29.6, 20.9, 267}, + {32.4, 20.1, 267}, + {30.5, 19.4, 267}, + {31.9, 16.7, 267}, + {30.6, 15.9, 267}, + {29.3, 14.9, 267}, + {27.5, 14.7, 267}, + {28.8, 52.3, 267}, + {39, 50.6, 267}, + {69.4, 29, 267}, + {84.8, 26.7, 5179}, + {92.6, 26.3, 5179}, + {85.4, 26.2, 5179}, + {86.3, 25.9, 5179}, + {88.5, 25.2, 5179}, + {90.2, 25.2, 5179}, + {97.2, 24.4, 5179}, + {85.3, 23.9, 5179}, + {93, 23.8, 5179}, + {90.4, 22.7, 5179}, + {94.7, 22.2, 5179}, + {90.1, 21.5, 5179}, + {99.1, 21.4, 5179}, + {92.5, 19.9, 5179}, + {95.9, 19.9, 5179}, + {80.8, 19.3, 5179}, + {98.9, 19.3, 5179}, + {94.6, 18.8, 5179}, + {94.4, 18.4, 5179}, + {89.7, 18.4, 5179}, + {86.2, 18.3, 5179}, + {85.3, 17.3, 5179}, + {87.9, 17.3, 5179}, + {90.1, 16.4, 5179}, + {82.1, 16.2, 5179}, + {93.7, 15.4, 5179}, + {86.4, 14.9, 5179}, + {82.8, 14.7, 5179}, + {84.8, 14.6, 5179}, + {81.1, 14.5, 5179}, + {81.6, 13.8, 5179}, + {91.2, 13.4, 5179}, + {93.6, 13.2, 5179}, + {92.9, 12.1, 5179}, + {95.6, 10.6, 5179}, + {96.6, 10.4, 5179}, + {82.9, 0.6, 5179}, + }, + [2384] = { + {39, 99.4, 36}, + {35.1, 98.8, 36}, + {45.5, 98.3, 36}, + {50.3, 95.7, 36}, + {52.2, 95.7, 36}, + {50.1, 95.4, 36}, + {47.3, 95.2, 36}, + {52.4, 94.6, 36}, + {46.7, 94.2, 36}, + {52.4, 92, 36}, + {52.8, 89, 36}, + {44.3, 88.7, 36}, + {53.8, 88.4, 36}, + {56, 50.4, 267}, + {52.9, 48.9, 267}, + {44.5, 48.8, 267}, + {39, 46.7, 267}, + {44.6, 45.8, 267}, + {39, 45.6, 267}, + {38, 44.5, 267}, + {55.7, 44.4, 267}, + {53.9, 44.3, 267}, + {51.8, 44.2, 267}, + {54.3, 42.9, 267}, + {55.7, 42.1, 267}, + {51.6, 41.7, 267}, + {57.8, 41.6, 267}, + {50.2, 41.3, 267}, + {39.8, 40.6, 267}, + {58.9, 40.4, 267}, + {42.5, 40.4, 267}, + {53.6, 39.8, 267}, + {54.3, 39.7, 267}, + {53.4, 39.3, 267}, + {39, 38.2, 267}, + {39.3, 37.8, 267}, + {53.3, 36.5, 267}, + {53.6, 36.4, 267}, + {37.8, 36.2, 267}, + {53.4, 36.1, 267}, + {52.4, 35.9, 267}, + {51, 35.5, 267}, + {42.9, 35.4, 267}, + {39.6, 34.9, 267}, + {48.6, 34.5, 267}, + {52.8, 32.2, 267}, + {54.5, 32.2, 267}, + {52.7, 31.9, 267}, + {50.2, 31.8, 267}, + {54.7, 31.2, 267}, + {49.7, 30.9, 267}, + {54.7, 28.9, 267}, + {55.1, 26.3, 267}, + {47.6, 26.1, 267}, + {56, 25.7, 267}, + {50.1, 95.3, 36}, + {52.2, 92.1, 36}, + {52.7, 89, 36}, + {38.1, 46.6, 267}, + {51.9, 44.7, 267}, + {55.6, 44.4, 267}, + {50, 41.2, 267}, + {53.5, 39.9, 267}, + {53.3, 36.7, 267}, + {52.6, 31.9, 267}, + {54.6, 29, 267}, + {55, 26.3, 267}, + {98.5, 11.6, 5179}, + {92.9, 9.7, 5179}, + {98.6, 9, 5179}, + {93.7, 8.8, 5179}, + {92.8, 7.8, 5179}, + {94.3, 4.3, 5179}, + {96.8, 4.2, 5179}, + {93.7, 2.3, 5179}, + {93.9, 2, 5179}, + {92.6, 0.5, 5179}, + }, + [2385] = { + {6.7, 49.7, 45}, + {68, 80.9, 267}, + {66.3, 75.3, 267}, + {66.2, 74.2, 267}, + {65.1, 73, 267}, + {62.6, 71.4, 267}, + {65, 69.4, 267}, + {61.8, 69, 267}, + {70.4, 68, 267}, + {63.6, 68, 267}, + {61.1, 66.9, 267}, + {65.1, 66.4, 267}, + {56.7, 60.1, 267}, + {57.7, 59.3, 267}, + {60.2, 58.5, 267}, + {59, 56.5, 267}, + {58.5, 55.6, 267}, + {61, 50.9, 267}, + {57.6, 50, 267}, + {66.3, 74.1, 267}, + {64.9, 69.3, 267}, + {65, 66.4, 267}, + }, + [2401] = { + {61.4, 81.7, 36}, + {62.6, 19.9, 267}, + }, + [2406] = { + {41.4, 98.5, 28}, + {42.7, 97.7, 28}, + {38.2, 92.1, 36}, + {40.4, 91.9, 36}, + {37.1, 89.9, 36}, + {37.5, 88.7, 36}, + {30.5, 88.3, 36}, + {39.4, 88.1, 36}, + {34.2, 87.1, 36}, + {29.7, 86.6, 36}, + {36.9, 86.5, 36}, + {35.8, 84.7, 36}, + {29.7, 84.7, 36}, + {35.3, 84.2, 36}, + {33.6, 83.9, 36}, + {34.5, 82.9, 36}, + {32.1, 82.9, 36}, + {31.7, 81.1, 36}, + {33, 80.1, 36}, + {32.3, 79.6, 36}, + {30.6, 78.9, 36}, + {42.8, 78.5, 36}, + {29.9, 77.1, 36}, + {49.1, 75.2, 36}, + {29.7, 74.6, 36}, + {31.1, 72.2, 36}, + {29.4, 70.4, 36}, + {74.1, 59.8, 36}, + {83.1, 58.2, 36}, + {76.7, 51.3, 36}, + {78.7, 50.1, 36}, + {64.7, 48.6, 36}, + {42.3, 29, 267}, + {44.2, 28.8, 267}, + {41.3, 27.1, 267}, + {41.7, 26, 267}, + {35.6, 25.7, 267}, + {43.3, 25.5, 267}, + {38.8, 24.6, 267}, + {34.9, 24.2, 267}, + {41.1, 24.2, 267}, + {40.2, 22.6, 267}, + {34.8, 22.6, 267}, + {39.7, 22.1, 267}, + {38.3, 21.8, 267}, + {39, 21, 267}, + {36.9, 21, 267}, + {36.6, 19.4, 267}, + {37.7, 18.5, 267}, + {37.1, 18.1, 267}, + {35.6, 17.5, 267}, + {46.3, 17.2, 267}, + {35, 15.9, 267}, + {51.8, 14.2, 267}, + {34.8, 13.7, 267}, + {36.1, 11.6, 267}, + {34.6, 10, 267}, + {73.7, 0.8, 267}, + {38.2, 91.9, 36}, + {40.3, 91.9, 36}, + {37, 86.4, 36}, + {35.9, 84.8, 36}, + {34.4, 82.9, 36}, + {32.1, 82.8, 36}, + {32.5, 79.5, 36}, + {42.3, 28.9, 267}, + {41.2, 24, 267}, + {40.3, 22.6, 267}, + {38.9, 21, 267}, + {37, 20.9, 267}, + {37.3, 18, 267}, + }, + [2407] = { + {42.5, 99.6, 28}, + {40.7, 98.8, 28}, + {44.5, 96.9, 28}, + {39.6, 92.3, 36}, + {30.9, 84.7, 36}, + {39.8, 84.7, 36}, + {48.9, 83.1, 36}, + {43.7, 80.7, 36}, + {45.7, 77.7, 36}, + {44.2, 76.7, 36}, + {83.2, 64.9, 36}, + {83.1, 61.1, 36}, + {82.6, 58.6, 36}, + {74.4, 54.7, 36}, + {78.4, 52.9, 36}, + {75.6, 51.7, 36}, + {66.7, 51.1, 36}, + {81.4, 48.8, 36}, + {56.2, 32.7, 36}, + {53.6, 31.6, 36}, + {52.8, 30.7, 36}, + {53.5, 30.6, 36}, + {39.6, 25.8, 36}, + {38.1, 24.8, 36}, + {52.5, 24.7, 36}, + {51.7, 23.7, 36}, + {45.3, 23.7, 36}, + {50.4, 22.7, 36}, + {45.5, 19.4, 36}, + {43.5, 29.2, 267}, + {35.9, 22.6, 267}, + {43.7, 22.5, 267}, + {51.6, 21.1, 267}, + {47.1, 19.1, 267}, + {48.8, 16.4, 267}, + {47.5, 15.5, 267}, + {81.7, 5.2, 267}, + {81.6, 1.9, 267}, + {37.1, 89.9, 36}, + {36.9, 86.5, 36}, + {32.1, 82.9, 36}, + {41.3, 27.1, 267}, + {41.1, 24.2, 267}, + {36.9, 21, 267}, + {37.1, 90.1, 36}, + {41.3, 27.3, 267}, + }, + [2408] = { + {45.5, 99.3, 28}, + {46.7, 98.5, 28}, + {48.1, 96.9, 28}, + {49.6, 95.7, 28}, + {50.6, 95.3, 28}, + {67.3, 95.5, 36}, + {67.6, 91.9, 36}, + {68.1, 88.3, 36}, + {67.5, 85.2, 36}, + {67.8, 81.2, 36}, + {68.1, 78, 36}, + {68.9, 74.5, 36}, + {72.3, 68.3, 36}, + {72.8, 68, 36}, + {73.6, 66.2, 36}, + {75, 65, 36}, + {76.4, 64.1, 36}, + {77.2, 63.3, 36}, + {78.8, 62.6, 36}, + {79.6, 59.6, 36}, + {80.3, 58.7, 36}, + {80.3, 57.9, 36}, + {80.9, 55.9, 36}, + {14, 55, 36}, + {12.4, 54.7, 36}, + {12.9, 54.6, 36}, + {10.6, 54.5, 36}, + {82.7, 53.9, 36}, + {14.2, 53, 36}, + {17.7, 52.8, 36}, + {15.3, 52.6, 36}, + {82.9, 52.4, 36}, + {16.5, 52.2, 36}, + {84.8, 51.3, 36}, + {16.7, 50.7, 36}, + {19, 50.6, 36}, + {17.6, 50.5, 36}, + {17.7, 49.7, 36}, + {19.3, 49.6, 36}, + {20.2, 49.1, 36}, + {86.9, 48.8, 36}, + {21.2, 47.3, 36}, + {89.2, 47, 36}, + {19.9, 46.8, 36}, + {90.9, 46.4, 36}, + {21.2, 45.8, 36}, + {22.9, 45.8, 36}, + {23.6, 45.4, 36}, + {24.2, 43.8, 36}, + {24.8, 43.7, 36}, + {26.7, 42.6, 36}, + {25.2, 42.3, 36}, + {26.3, 40.5, 36}, + {27.9, 40.3, 36}, + {28.8, 39.7, 36}, + {29.1, 38.4, 36}, + {29.7, 38, 36}, + {29.7, 37.1, 36}, + {30.8, 36.4, 36}, + {31.1, 35.4, 36}, + {29.7, 34.8, 36}, + {31.2, 33.7, 36}, + {30.7, 32, 36}, + {31.9, 31.4, 36}, + {31.9, 30, 36}, + {32.9, 29.3, 36}, + {34.2, 27.5, 36}, + {33.4, 26.9, 36}, + {34.3, 25.9, 36}, + {35.5, 25.8, 36}, + {35.8, 23.3, 36}, + {35.9, 21.7, 36}, + {36.6, 20.1, 36}, + {36.8, 19, 36}, + {35.8, 13.5, 36}, + {31.7, 13.2, 36}, + {33.4, 11.9, 36}, + {72.8, 42.6, 130}, + {71.7, 42.4, 130}, + {72.1, 42.4, 130}, + {70.5, 42.3, 130}, + {73, 41.3, 130}, + {75.3, 41.1, 130}, + {73.7, 41, 130}, + {74.5, 40.7, 130}, + {74.6, 39.8, 130}, + {76.1, 39.7, 130}, + {75.2, 39.6, 130}, + {75.3, 39.1, 130}, + {76.4, 39, 130}, + {77, 38.7, 130}, + {77.7, 37.5, 130}, + {76.7, 37.1, 130}, + {77.6, 36.5, 130}, + {78.7, 36.5, 130}, + {79.2, 36.2, 130}, + {79.6, 35.1, 130}, + {80.1, 35.1, 130}, + {81.3, 34.3, 130}, + {80.3, 34.1, 130}, + {81, 32.9, 130}, + {82.1, 32.8, 130}, + {82.7, 32.4, 130}, + {82.9, 31.6, 130}, + {83.3, 31.3, 130}, + {83.3, 30.7, 130}, + {83.3, 29.1, 130}, + {55.7, 57.2, 267}, + {55.9, 53.9, 267}, + {56.5, 50.5, 267}, + {56.8, 47.2, 267}, + {59.1, 45, 267}, + {61.7, 42.8, 267}, + {64, 40, 267}, + {66.1, 38.3, 267}, + {67.5, 35.5, 267}, + {67.7, 32, 267}, + {68, 28.9, 267}, + {68.4, 25.7, 267}, + {68, 23, 267}, + {68.2, 19.5, 267}, + {68.4, 16.7, 267}, + {69.1, 13.6, 267}, + {72.1, 8.2, 267}, + {72.5, 8, 267}, + {73.3, 6.3, 267}, + {74.5, 5.3, 267}, + {75.7, 4.5, 267}, + {76.4, 3.9, 267}, + {77.8, 3.2, 267}, + {78.5, 0.6, 267}, + {51, 95.5, 28}, + {67.3, 99.3, 36}, + {67.7, 92, 36}, + {67.7, 80.9, 36}, + {67.9, 78.3, 36}, + {72.5, 68.6, 36}, + {77.2, 63.2, 36}, + {79.6, 59.3, 36}, + {91.5, 46.7, 36}, + {55.9, 57.1, 267}, + {56.4, 50.6, 267}, + {59.2, 45.1, 267}, + {63.9, 39.7, 267}, + {67.8, 35.3, 267}, + {67.8, 32, 267}, + {68.1, 28.9, 267}, + {68.1, 19.2, 267}, + {68.3, 16.9, 267}, + {72.3, 8.4, 267}, + {76.4, 3.7, 267}, + }, + [2476] = { + {58.8, 30.9, 38}, + {58.9, 31.1, 38}, + {28.7, 60.1, 5602}, + }, + [2505] = { + {77.4, 86.5, 47}, + {76.2, 84, 47}, + {80.6, 77.3, 47}, + {80.5, 74.8, 47}, + {79.9, 73.6, 47}, + {80.3, 71.9, 47}, + {75.8, 71.4, 47}, + {74.7, 70.9, 47}, + {78.3, 70.6, 47}, + {75.4, 69.9, 47}, + {78.6, 69.7, 47}, + {77.3, 69.5, 47}, + {80.7, 69.4, 47}, + {78.2, 68.5, 47}, + {76.7, 68.3, 47}, + {74.6, 68.3, 47}, + {77.3, 66.9, 47}, + {79.7, 65.8, 47}, + {76.1, 65.2, 47}, + {78.5, 65.2, 47}, + {86, 64.8, 47}, + {84, 64.6, 47}, + {77.3, 64.2, 47}, + {78.1, 63, 47}, + {84.4, 61.9, 47}, + {79.1, 61.9, 47}, + {80.7, 61.8, 47}, + {77.3, 61.8, 47}, + {86, 61.8, 47}, + {78.2, 60.7, 47}, + {81.5, 60.5, 47}, + {79.9, 60.4, 47}, + {82.6, 59.2, 47}, + {79, 59.2, 47}, + {84.2, 59.1, 47}, + {79.8, 57.8, 47}, + {83.6, 57.8, 47}, + {85.1, 57.7, 47}, + {83.3, 55.3, 47}, + {81.5, 55.1, 47}, + {85.1, 55, 47}, + {81.7, 54.6, 47}, + {82.7, 53.8, 47}, + {81.5, 53, 47}, + {81.9, 53, 47}, + {83, 52.9, 47}, + {82.3, 51.3, 47}, + {83.1, 50.4, 47}, + {81.4, 50.3, 47}, + {82.4, 48.8, 47}, + {83.1, 47.6, 47}, + {82.6, 46.1, 47}, + {84.9, 45.4, 47}, + {83.2, 44.8, 47}, + {81.3, 44.8, 47}, + {82.1, 43.1, 47}, + {83.4, 42.1, 47}, + {81.8, 42, 47}, + {85.3, 41.8, 47}, + {82.7, 41.3, 47}, + {74.7, 71, 47}, + {77.3, 69.6, 47}, + {77.4, 67, 47}, + {78, 63, 47}, + {77.3, 61.7, 47}, + {79.7, 60.4, 47}, + {79.2, 59.3, 47}, + {80, 58, 47}, + {83.5, 55.3, 47}, + }, + [2521] = { + {39.1, 86.6, 33}, + {40.3, 86.5, 33}, + {38.7, 85.9, 33}, + {41.3, 85, 33}, + {41.9, 84.1, 33}, + {40.5, 83.8, 33}, + {41.9, 82.5, 33}, + {40.3, 82, 33}, + {40.9, 81.9, 33}, + {41.9, 81.1, 33}, + {40.9, 80.5, 33}, + {40.2, 80.5, 33}, + {41.9, 79.5, 33}, + {40.3, 79.5, 33}, + {40.7, 79.1, 33}, + {39.2, 78.7, 33}, + {41.9, 77.9, 33}, + {41.3, 77.8, 33}, + {40.4, 77.3, 33}, + {40.4, 83.9, 33}, + }, + [2522] = { + {38.6, 86.6, 33}, + {37.6, 86.1, 33}, + {36.6, 85.7, 33}, + {36.5, 84, 33}, + {37.6, 83.6, 33}, + {37.1, 83.3, 33}, + {37.5, 82.8, 33}, + {37.7, 81.6, 33}, + {38, 81.3, 33}, + {36.5, 80.9, 33}, + {37.1, 80.3, 33}, + {37.8, 80, 33}, + {37.3, 78.6, 33}, + {38.9, 77.8, 33}, + {37.1, 80.4, 33}, + }, + [2544] = { + {31.2, 88.1, 33}, + {35.3, 87.9, 33}, + {35.1, 87.8, 33}, + {29.9, 87.7, 33}, + {28.7, 87.6, 33}, + {28.8, 87.5, 33}, + {29.9, 87.3, 33}, + {31.7, 87, 33}, + {26.7, 87, 33}, + {31.3, 85.9, 33}, + {33.9, 85.9, 33}, + {25.8, 85.9, 33}, + {27.5, 85.9, 33}, + {30.1, 85.8, 33}, + {29.9, 85.7, 33}, + {27.8, 85.6, 33}, + {30.8, 85.3, 33}, + {26.9, 84.4, 33}, + {26.3, 84.1, 33}, + {32.2, 82.5, 33}, + {29.3, 82.3, 33}, + {31.3, 81.9, 33}, + {30.8, 81.7, 33}, + {33.8, 76.5, 33}, + }, + [2559] = { + {42.8, 79.6, 45}, + {43.2, 77.8, 45}, + {41.1, 74.2, 45}, + {44.9, 65.5, 45}, + {46.6, 64.3, 45}, + {40, 61.9, 45}, + {40.1, 58.9, 45}, + {67.1, 56.3, 45}, + {47.8, 56.2, 45}, + {44.8, 55, 45}, + {41.2, 53.4, 45}, + {46.8, 53, 45}, + {38.5, 52.1, 45}, + {69.1, 52, 45}, + {49.5, 51.9, 45}, + {70.6, 51, 45}, + {42, 50.8, 45}, + {46.9, 50.8, 45}, + {39.4, 50.7, 45}, + {64, 50.5, 45}, + {37.3, 50.3, 45}, + {41.3, 49.8, 45}, + {41.8, 49.4, 45}, + {49.6, 49.4, 45}, + {40.3, 49.3, 45}, + {55.1, 49.2, 45}, + {68.7, 49.1, 45}, + {38.4, 48.7, 45}, + {74, 48.3, 45}, + {56, 48, 45}, + {39.3, 47.9, 45}, + {54.1, 47.6, 45}, + {51.1, 47.4, 45}, + {42.2, 46.9, 45}, + {54.3, 46.7, 45}, + {56.9, 46.6, 45}, + {62.4, 46.5, 45}, + {64.3, 46.4, 45}, + {39.8, 46.4, 45}, + {53.3, 45.9, 45}, + {53.8, 45.6, 45}, + {61.6, 45.2, 45}, + {63.3, 45.2, 45}, + {59.7, 45.2, 45}, + {65.1, 45, 45}, + {20.6, 45, 45}, + {52.1, 44.9, 45}, + {41, 44.8, 45}, + {53.1, 44, 45}, + {42.5, 43.8, 45}, + {66.2, 43.8, 45}, + {60.6, 43.7, 45}, + {40.3, 43.7, 45}, + {58.7, 43.7, 45}, + {17.2, 42.7, 45}, + {61.5, 42.3, 45}, + {67.6, 42.3, 45}, + {60.8, 42.2, 45}, + {63.4, 42.2, 45}, + {65.2, 42.2, 45}, + {19.6, 42.2, 45}, + {17.1, 42.1, 45}, + {41.8, 41.9, 45}, + {41.3, 41.4, 45}, + {43.2, 41.2, 45}, + {68.5, 41.2, 45}, + {58.7, 41.1, 45}, + {46.8, 41.1, 45}, + {60.7, 41, 45}, + {44.1, 41, 45}, + {19, 41, 45}, + {64.3, 40.9, 45}, + {51.4, 40.9, 45}, + {49.5, 40.7, 45}, + {24.5, 40.7, 45}, + {65.9, 40.5, 45}, + {18.1, 40.2, 45}, + {25.2, 40.1, 45}, + {65.2, 39.7, 45}, + {23.7, 39.6, 45}, + {51.4, 39.5, 45}, + {46.7, 39.5, 45}, + {43.1, 39.2, 45}, + {21.9, 39.2, 45}, + {66.9, 39.1, 45}, + {69.1, 38.6, 45}, + {20.5, 38.2, 45}, + {18.9, 38.1, 45}, + {65.6, 38, 45}, + {20, 38, 45}, + {60.3, 37.2, 45}, + {26.9, 37, 45}, + {67.6, 36.9, 45}, + {68, 36.9, 45}, + {24.9, 36.8, 45}, + {69, 36.8, 45}, + {64.3, 36.8, 45}, + {21.4, 36.4, 45}, + {32.7, 36.1, 45}, + {69.8, 35.8, 45}, + {23.7, 35.4, 45}, + {65.9, 35.1, 45}, + {33.6, 35.1, 45}, + {26.4, 35, 45}, + {19.5, 34.2, 45}, + {67.2, 34, 45}, + {48.2, 33.7, 45}, + {52.3, 32.4, 45}, + {69.8, 31.6, 45}, + {76.3, 31.1, 45}, + {70, 30.4, 45}, + {69.8, 27, 45}, + {26.7, 24, 45}, + {30.3, 21.1, 45}, + {27.2, 20.7, 45}, + {30.8, 20.4, 45}, + {29.9, 19.9, 45}, + {28.3, 17.3, 45}, + {41.4, 53.4, 45}, + {49.5, 52.1, 45}, + {38.4, 51.7, 45}, + {39.6, 50.6, 45}, + {46.7, 50.5, 45}, + {49.3, 49.4, 45}, + {40.5, 49.3, 45}, + {55.1, 49.1, 45}, + {39.6, 47.8, 45}, + {62.5, 46.5, 45}, + {65.3, 45.2, 45}, + {59.6, 45.2, 45}, + {63.3, 45.1, 45}, + {66.1, 43.7, 45}, + {60.6, 43.6, 45}, + {65.4, 42.4, 45}, + {61.6, 42.2, 45}, + {64.4, 40.9, 45}, + {51.3, 40.6, 45}, + {51.4, 39.6, 45}, + {69, 36.6, 45}, + {67.2, 34.1, 45}, + }, + [2561] = { + {48, 82.4, 45}, + {47.5, 80.7, 45}, + {45.6, 79.5, 45}, + {46.8, 78.5, 45}, + {45.7, 76.6, 45}, + {46.7, 75.8, 45}, + {47.6, 75.2, 45}, + {50, 70.4, 45}, + {49.5, 68.9, 45}, + {53.2, 68.8, 45}, + {51.1, 68.7, 45}, + {52.3, 68.1, 45}, + {53.7, 67.8, 45}, + {55.1, 67.5, 45}, + {50.4, 67.4, 45}, + {50.4, 64.7, 45}, + {54.1, 64.7, 45}, + {52.2, 64.6, 45}, + {50.2, 63.9, 45}, + {46.8, 78.7, 45}, + {46.5, 76, 45}, + {49.6, 69.1, 45}, + {53.1, 68.8, 45}, + {50.4, 67.5, 45}, + {54, 64.6, 45}, + {50.5, 64.6, 45}, + {52.3, 64.6, 45}, + }, + [2565] = { + {47.8, 77.2, 45}, + {46, 74.5, 45}, + {61.7, 69, 45}, + {57, 69, 45}, + {59.9, 68.5, 45}, + {56.9, 67.5, 45}, + {60.7, 67.2, 45}, + {51.2, 66.6, 45}, + {53.5, 66, 45}, + {61.4, 65.9, 45}, + {57.5, 65.8, 45}, + {59.7, 65.8, 45}, + {57.4, 65, 45}, + {58.8, 64.6, 45}, + {60.5, 64.5, 45}, + {60.4, 64.4, 45}, + {56.9, 62, 45}, + {52.6, 61, 45}, + {56.7, 60.5, 45}, + {56, 59.1, 45}, + {53.2, 58.9, 45}, + {56.5, 57.8, 45}, + {53.4, 56.4, 45}, + {54.5, 56.3, 45}, + {54.4, 54.9, 45}, + {55.8, 54.6, 45}, + {44.9, 36.8, 45}, + {46.6, 36.7, 45}, + {45.7, 34.8, 45}, + {42, 34.1, 45}, + {42.9, 32.6, 45}, + {42.5, 32.3, 45}, + {41, 31.9, 45}, + {40.3, 31.7, 45}, + {44.6, 31.6, 45}, + {44.1, 31.1, 45}, + {42.7, 29.5, 45}, + {38.4, 29.3, 45}, + {40.2, 29.1, 45}, + {39.3, 27.8, 45}, + {37.9, 26.9, 45}, + {38.4, 25.4, 45}, + {37.3, 24.6, 45}, + {59.8, 65.8, 45}, + }, + [2579] = { + {50.3, 70.9, 45}, + {61.4, 69.8, 45}, + {56.7, 68.7, 45}, + {40.4, 66.9, 45}, + {36.5, 66.3, 45}, + {33.9, 66.1, 45}, + {54.3, 65.4, 45}, + {34.1, 61.9, 45}, + {56, 55.7, 45}, + {19.8, 52.1, 45}, + {43.3, 38.7, 45}, + {45.2, 35.4, 45}, + {39.2, 30.5, 45}, + {37.7, 26.7, 45}, + {57.2, 71.7, 45}, + {49.9, 69.9, 45}, + {38.6, 69.6, 45}, + {49.4, 67.7, 45}, + {61.7, 65.6, 45}, + {56.7, 64, 45}, + {37.4, 62.3, 45}, + {38.6, 62.2, 45}, + {32.8, 59.3, 45}, + {48.2, 40.1, 45}, + {43, 35.6, 45}, + {38.8, 29.8, 45}, + {37.8, 25.1, 45}, + }, + [2580] = { + {46.9, 81.7, 45}, + {21.5, 72.6, 45}, + {18.6, 71.3, 45}, + {23.8, 71.2, 45}, + {15.3, 69.7, 45}, + {24.3, 68.9, 45}, + {27.4, 67.9, 45}, + {16, 64.5, 45}, + {49.1, 82.5, 45}, + {20.9, 75.2, 45}, + {17, 72, 45}, + {23.5, 70.6, 45}, + {31.6, 69.4, 45}, + }, + [2635] = { + {33.4, 32.5, 33}, + {29.8, 25.5, 33}, + {29.3, 22.3, 33}, + {25.3, 19.2, 33}, + }, + [2680] = { + {66.7, 72.4, 47}, + {68, 70.6, 47}, + {68.4, 70.6, 47}, + {65.1, 70.3, 47}, + {65.8, 70.3, 47}, + {46.5, 69.7, 47}, + {63.5, 68.9, 47}, + {68.2, 68.7, 47}, + {43.6, 67.8, 47}, + {47.3, 66, 47}, + {44.6, 65.4, 47}, + {63.5, 65.2, 47}, + {46.4, 65.1, 47}, + {48.4, 64, 47}, + }, + [2681] = { + {86, 22.2, 45}, + {85.7, 18.7, 45}, + {84.7, 18.7, 45}, + {84.5, 17.7, 45}, + {84.7, 16.1, 45}, + {83.9, 16, 45}, + {82.2, 14.6, 45}, + {62, 83.1, 47}, + {61.7, 79.9, 47}, + {60.8, 79.8, 47}, + {60.6, 78.9, 47}, + {60.8, 77.4, 47}, + {60.1, 77.3, 47}, + {60, 76, 47}, + {58.4, 76, 47}, + {58.9, 75.5, 47}, + {58.8, 64.9, 47}, + {58.6, 64.5, 47}, + {85.7, 18.8, 45}, + {61.8, 79.9, 47}, + }, + [2685] = { + {28.5, 75.1, 33}, + }, + [2686] = { + {35.5, 74.4, 47}, + {31.3, 74.1, 47}, + {35.1, 73.7, 47}, + {30.6, 73.6, 47}, + {33.2, 73.4, 47}, + {32.1, 73.3, 47}, + {31, 72.5, 47}, + {32, 71.4, 47}, + {35.3, 71, 47}, + {31, 70.7, 47}, + {29.5, 70.7, 47}, + {30.7, 69.7, 47}, + {33.3, 69.3, 47}, + {36.5, 68.3, 47}, + {34.8, 68.3, 47}, + {37.7, 67.7, 47}, + {34.1, 66.9, 47}, + {35.8, 64.7, 47}, + {37, 63.8, 47}, + {34.8, 63.2, 47}, + {53.2, 12.6, 45}, + {52.4, 12.1, 45}, + {33.3, 73.6, 47}, + {35.3, 71.4, 47}, + {31, 70.6, 47}, + {34.4, 66.8, 47}, + }, + [2727] = { + {53.2, 52, 3}, + {51.2, 50.9, 3}, + {55, 49.9, 3}, + {48.4, 49.8, 3}, + {53.7, 48, 3}, + {47.1, 48, 3}, + {55.1, 48, 3}, + {58.1, 46.5, 3}, + {49.8, 46.2, 3}, + {49.6, 46.1, 3}, + {46.7, 44.1, 3}, + {48.9, 42.4, 3}, + {49.9, 41.9, 3}, + {49.8, 41.5, 3}, + {50.3, 40, 3}, + {47.8, 39.8, 3}, + {52.5, 39.7, 3}, + {48.3, 38, 3}, + {51.1, 37.9, 3}, + {47, 35.8, 3}, + {50.2, 35.2, 3}, + {48.5, 34.5, 3}, + {54.9, 24.8, 3}, + {52.4, 24.7, 3}, + {53.7, 21.7, 3}, + {52.3, 20.5, 3}, + {53.7, 17.7, 3}, + {52.9, 14.6, 3}, + {46.9, 48.3, 3}, + {55, 48.1, 3}, + {52.8, 39.2, 3}, + {51, 38.1, 3}, + {46.9, 36, 3}, + {53.7, 22.2, 3}, + {53.8, 17.5, 3}, + {21.8, 99.9, 5602}, + {23.3, 99.6, 5602}, + {22.5, 99.3, 5602}, + {25.5, 94.8, 5602}, + {24.3, 94.7, 5602}, + {24.9, 93.6, 5602}, + {24.3, 92.8, 5602}, + {25, 91.4, 5602}, + {24.5, 90.1, 5602}, + }, + [2728] = { + {48.6, 76.2, 3}, + {54.2, 74.7, 3}, + {45.9, 73.5, 3}, + {44.4, 72, 3}, + {60.4, 71.9, 3}, + {59.2, 70.8, 3}, + {55.3, 68.5, 3}, + {56.4, 68, 3}, + {57.8, 68, 3}, + {59.5, 67.8, 3}, + {55.5, 67.2, 3}, + {61.6, 65.7, 3}, + {57.8, 64.1, 3}, + {55.1, 63.8, 3}, + {60.7, 63.3, 3}, + {53.5, 61.3, 3}, + {56.7, 61, 3}, + {52.4, 60.6, 3}, + {60.2, 60.5, 3}, + {59.3, 60.5, 3}, + {57.9, 59.9, 3}, + {56.2, 59, 3}, + {55.2, 58.3, 3}, + {53.5, 58.2, 3}, + {55.5, 56.7, 3}, + {53, 56.6, 3}, + {58, 56.5, 3}, + {60.6, 55.7, 3}, + {58.9, 55.5, 3}, + {58.8, 54.3, 3}, + {55.6, 53.8, 3}, + {55, 52, 3}, + {53.4, 51.9, 3}, + {59.1, 51.9, 3}, + {63.1, 51.3, 3}, + {58.8, 50.4, 3}, + {60.9, 50.2, 3}, + {61.7, 49.8, 3}, + {60.8, 48.1, 3}, + {60.4, 71.7, 3}, + {57.8, 68.2, 3}, + {58, 63.9, 3}, + {55, 63.8, 3}, + {61.7, 49.7, 3}, + }, + [2729] = { + {93.1, 85.4, 1}, + {30.7, 74.2, 3}, + {31.8, 73.6, 3}, + {35.7, 73.5, 3}, + {28.3, 72.1, 3}, + {41.7, 71.9, 3}, + {33.6, 71.1, 3}, + {31, 70.9, 3}, + {34.3, 70.2, 3}, + {38.7, 69.9, 3}, + {37.9, 69.1, 3}, + {40.1, 68.6, 3}, + {14.1, 68.1, 3}, + {29.4, 68, 3}, + {39.2, 67.4, 3}, + {12.9, 67.4, 3}, + {10.4, 67.1, 3}, + {37.9, 66.8, 3}, + {32.3, 66, 3}, + {12.9, 65.8, 3}, + {16.9, 65.7, 3}, + {35, 64.3, 3}, + {29.7, 64.1, 3}, + {30.1, 63.8, 3}, + {13.5, 63.8, 3}, + {10.5, 63.7, 3}, + {11.2, 63.1, 3}, + {37.6, 63, 3}, + {30.7, 62.3, 3}, + {27.1, 62, 3}, + {34.6, 61.6, 3}, + {35.9, 61.1, 3}, + {27.5, 60.4, 3}, + {30.9, 60, 3}, + {32.6, 59.6, 3}, + {24.3, 58.6, 3}, + {22.8, 58.2, 3}, + {32.4, 57.9, 3}, + {37.3, 57.8, 3}, + {20.9, 56.8, 3}, + {21.4, 56.3, 3}, + {25.6, 55.9, 3}, + {33.4, 54.5, 3}, + {18.8, 53.9, 3}, + {26.4, 53, 3}, + {23.3, 51.6, 3}, + {28.4, 47.7, 3}, + {93.1, 85.3, 1}, + {41.6, 71.4, 3}, + {32.4, 66.2, 3}, + {27.4, 62.2, 3}, + {31.1, 60.1, 3}, + {32.5, 57.8, 3}, + {25.5, 56, 3}, + {28.3, 47.6, 3}, + }, + [2730] = { + {69.4, 36.2, 3}, + {71.5, 34.8, 3}, + {70.3, 33.7, 3}, + {72.7, 33.4, 3}, + {70.3, 32.8, 3}, + {69, 30.8, 3}, + {33.1, 99.4, 5602}, + {32.6, 98.9, 5602}, + {33.7, 98.8, 5602}, + {32.6, 98.5, 5602}, + {32, 97.5, 5602}, + }, + [2731] = { + {38.1, 46.5, 3}, + {62.6, 46.3, 3}, + {60.6, 43.8, 3}, + {36.1, 43.3, 3}, + {33.5, 43.3, 3}, + {34.7, 41.7, 3}, + {33.6, 40.9, 3}, + {45.2, 40.5, 3}, + {35.1, 40.4, 3}, + {42.9, 40.2, 3}, + {61.8, 40.2, 3}, + {61.8, 39.3, 3}, + {43.6, 38.3, 3}, + {40.1, 37.7, 3}, + {46.2, 37.6, 3}, + {65.8, 37.5, 3}, + {65.3, 37.1, 3}, + {44.8, 35.7, 3}, + {60.6, 30.5, 3}, + {62, 29.9, 3}, + {63.1, 29.6, 3}, + {57.1, 28.3, 3}, + {59.1, 25.6, 3}, + {53.8, 15.5, 3}, + {55.2, 15.1, 3}, + {20.8, 99.8, 5602}, + {28.1, 97.4, 5602}, + {28.8, 97.2, 5602}, + {29.3, 97, 5602}, + {26.5, 96.4, 5602}, + {27.4, 95.1, 5602}, + {25, 90.5, 5602}, + {25.6, 90.3, 5602}, + }, + [2732] = { + {46.9, 76.7, 3}, + {43.5, 75, 3}, + {56.7, 75, 3}, + {63.1, 73.7, 3}, + {39.9, 73.1, 3}, + {44.4, 72.8, 3}, + {43.2, 69.5, 3}, + {65, 68.4, 3}, + {41.5, 67, 3}, + {40.4, 64.1, 3}, + {63.1, 64, 3}, + {41.1, 63.3, 3}, + {60.7, 62.3, 3}, + {40.3, 61.5, 3}, + {49.6, 60.1, 3}, + {41.7, 59.9, 3}, + {39.1, 59.9, 3}, + {49.5, 58.4, 3}, + {51.8, 58.1, 3}, + {40.3, 58, 3}, + {50.2, 57.1, 3}, + {35.5, 56, 3}, + {48.3, 52.5, 3}, + {47.6, 51.8, 3}, + {31.5, 51.6, 3}, + {34.8, 51.2, 3}, + {41.4, 51, 3}, + {32.2, 49.9, 3}, + {30.1, 49.9, 3}, + {36.9, 49.9, 3}, + {46.7, 49.7, 3}, + {34.1, 49.1, 3}, + {37.6, 48.6, 3}, + {30.6, 47.9, 3}, + {44.5, 47.6, 3}, + {31.8, 46.9, 3}, + {45.9, 46.6, 3}, + {56.7, 46.6, 3}, + {53.8, 45.9, 3}, + {29.7, 45.5, 3}, + {59.3, 44.6, 3}, + {53.6, 39.5, 3}, + {63.1, 64.4, 3}, + {39.4, 60, 3}, + {40.5, 58.3, 3}, + {36.9, 49.4, 3}, + {59.2, 45.9, 3}, + {30, 45.5, 3}, + }, + [2734] = { + {25.4, 79.3, 3}, + {26.1, 74.7, 3}, + {19.9, 72.5, 3}, + {25.7, 71.4, 3}, + {19.5, 70.4, 3}, + {10.8, 70.1, 3}, + {21.3, 68.9, 3}, + {18, 67.2, 3}, + {28.3, 66.5, 3}, + {18.5, 65.9, 3}, + {25.9, 63.8, 3}, + {20.3, 62, 3}, + {22.9, 60.2, 3}, + {20.4, 60.2, 3}, + {10.4, 58.8, 3}, + {8.4, 57.8, 3}, + {14.6, 57.1, 3}, + {14.9, 56, 3}, + {12.7, 55.4, 3}, + {16.2, 53.8, 3}, + {15.2, 51.8, 3}, + {16.5, 49.9, 3}, + {25.9, 64, 3}, + {20.4, 60.1, 3}, + }, + [2753] = { + {46.3, 74.3, 3}, + }, + [2803] = { + {62.3, 25.6, 361}, + }, + [2806] = { + {50.4, 74.2, 148}, + {34.8, 53.2, 361}, + }, + [2808] = { + {46.4, 47.6, 45}, + }, + [2820] = { + {74.1, 32.4, 45}, + }, + [2829] = { + {60.7, 39.6, 3}, + {60.2, 30.1, 3}, + {57.5, 28.6, 3}, + {48.9, 27.4, 3}, + {58.8, 24.3, 3}, + {55, 13.8, 3}, + {63.9, 46, 3}, + {65.3, 37.7, 3}, + {56.6, 28.6, 3}, + {60.7, 28.3, 3}, + {52.7, 26.1, 3}, + {54, 23.5, 3}, + {26.3, 96.6, 5602}, + {28.2, 96.4, 5602}, + {24.5, 95.4, 5602}, + {25.1, 94.2, 5602}, + }, + [2830] = { + {50.1, 77.9, 3}, + {58, 73.8, 3}, + {49.5, 63.8, 3}, + {50.3, 60, 3}, + {61.8, 55.6, 3}, + {48.6, 55.5, 3}, + {63.3, 54.1, 3}, + {67.2, 86.3, 3}, + {68.3, 86.3, 3}, + {69.4, 84, 3}, + {62.7, 79.1, 3}, + {51.4, 74.4, 3}, + {60.8, 72.8, 3}, + {59, 70.9, 3}, + {50.2, 59.7, 3}, + {62.8, 57.3, 3}, + {52.7, 50.3, 3}, + {56.5, 45.9, 3}, + }, + [2831] = { + {18.9, 77.6, 3}, + {24.4, 76.2, 3}, + {25.3, 73.9, 3}, + {28.8, 72, 3}, + {12.3, 71, 3}, + {12.2, 67.9, 3}, + {25.8, 61.9, 3}, + {15, 61.8, 3}, + {16.2, 61.7, 3}, + {17.1, 61.3, 3}, + {15.1, 60, 3}, + {17.1, 59.9, 3}, + {33.4, 59.8, 3}, + {16, 58.7, 3}, + {15.4, 58.6, 3}, + {17.2, 58.4, 3}, + {17.5, 49.1, 3}, + {25.4, 78.8, 3}, + {17.6, 73.6, 3}, + {22.7, 72.5, 3}, + {35.1, 72.3, 3}, + {16, 70.1, 3}, + {18.8, 64.5, 3}, + {17.2, 61.3, 3}, + {15, 60.1, 3}, + {17.2, 60, 3}, + {33.5, 59.6, 3}, + {15.5, 58.5, 3}, + {17.1, 58.3, 3}, + {33.5, 53.5, 3}, + {15.3, 49.6, 3}, + }, + [2839] = { + {28.3, 74.6, 33}, + }, + [2850] = { + {44, 38.4, 3}, + {60.4, 30.1, 3}, + {54.7, 14.4, 3}, + {28, 97.2, 5602}, + {25.4, 90, 5602}, + }, + [2908] = { + {3.1, 45.9, 3}, + {82.2, 37.1, 51}, + }, + [2923] = { + {20.3, 57.2, 47}, + {20.1, 56.6, 47}, + {15.2, 55.6, 47}, + {21, 55.4, 47}, + {21.8, 55, 47}, + {16.4, 54.7, 47}, + {19.3, 54.3, 47}, + {15.1, 54.2, 47}, + {21.8, 53, 47}, + {18.2, 52.5, 47}, + {19.6, 52.3, 47}, + {23.6, 51.7, 47}, + {19.7, 51.5, 47}, + {22.6, 51.2, 47}, + {20.9, 50.3, 47}, + {20.1, 50, 47}, + {17.2, 49.5, 47}, + {18.8, 49.1, 47}, + {18.3, 48.9, 47}, + {17.6, 48.2, 47}, + {19, 47.6, 47}, + {21.7, 47.6, 47}, + {16, 51.6, 47}, + {22.5, 51.2, 47}, + {18.2, 51, 47}, + {20, 50.2, 47}, + }, + [2924] = { + {27.9, 69.4, 47}, + {31, 64.6, 47}, + {32.7, 63.5, 47}, + {32.1, 63.4, 47}, + {28.8, 63.2, 47}, + {35, 61.6, 47}, + {27.9, 61.6, 47}, + {34.2, 61.1, 47}, + {31.8, 60.7, 47}, + {34.4, 58.8, 47}, + {26.9, 58.1, 47}, + {26.6, 55.3, 47}, + {32.2, 55.2, 47}, + {37.5, 55.1, 47}, + {29.6, 54.2, 47}, + {33.7, 53.6, 47}, + {27.8, 53.4, 47}, + {38.1, 53.1, 47}, + {35.2, 52.6, 47}, + {36.7, 52.6, 47}, + {35.7, 52.6, 47}, + {37.6, 52.1, 47}, + {36.8, 51.7, 47}, + {24.7, 51.1, 47}, + {37.3, 49.7, 47}, + {36.2, 49.1, 47}, + {35.8, 49, 47}, + {35.5, 47.7, 47}, + {34.1, 46.1, 47}, + {35.8, 45, 47}, + {35, 44.1, 47}, + {36.1, 44, 47}, + {32.1, 55.3, 47}, + {37.6, 55, 47}, + {35.7, 53, 47}, + }, + [2925] = { + {41.3, 67.1, 47}, + {40.6, 64.2, 47}, + {39.4, 62.9, 47}, + {42.7, 62.7, 47}, + {44, 61.8, 47}, + {48, 61.5, 47}, + {37.4, 61, 47}, + {51.8, 60, 47}, + {46.8, 59.5, 47}, + {47.8, 59, 47}, + {50.5, 58.9, 47}, + {49.8, 57.6, 47}, + {41, 55.1, 47}, + {43, 53.7, 47}, + {39.9, 50.6, 47}, + {46.7, 47.8, 47}, + {42.5, 46.3, 47}, + {41.5, 46.2, 47}, + {39.3, 46, 47}, + {51.1, 40.6, 47}, + }, + [2926] = { + {72.1, 62.9, 47}, + {71.2, 61.6, 47}, + {50.8, 61.4, 47}, + {66.6, 60.6, 47}, + {68.6, 60.4, 47}, + {71.6, 59.5, 47}, + {72, 59.3, 47}, + {70.5, 58, 47}, + {73.4, 57.9, 47}, + {72.9, 57.8, 47}, + {66.8, 57.8, 47}, + {70.1, 57.1, 47}, + {62.7, 56.6, 47}, + {72.4, 56.4, 47}, + {69.8, 56.4, 47}, + {73.5, 56.1, 47}, + {53.5, 56, 47}, + {74, 55.9, 47}, + {61.2, 55.8, 47}, + {60.2, 55.3, 47}, + {75.5, 55.2, 47}, + {64, 55.1, 47}, + {69.6, 54.8, 47}, + {71.5, 54.8, 47}, + {58.3, 54.7, 47}, + {51.5, 54.3, 47}, + {59.7, 54.1, 47}, + {60.7, 54, 47}, + {69.5, 54, 47}, + {76.4, 53.5, 47}, + {65.4, 53.3, 47}, + {68, 52.9, 47}, + {68.6, 52.8, 47}, + {63.1, 52.4, 47}, + {78, 52.2, 47}, + {73.1, 52, 47}, + {58.4, 51.9, 47}, + {67.4, 51.5, 47}, + {59.6, 50.8, 47}, + {67, 50.4, 47}, + {62.5, 50.3, 47}, + {69.3, 49.8, 47}, + {63.6, 49.3, 47}, + {52.2, 49.1, 47}, + {63.2, 49.1, 47}, + {59.4, 49, 47}, + {64.2, 48.9, 47}, + {76.6, 48.8, 47}, + {67.6, 48.6, 47}, + {74.7, 48.6, 47}, + {66.7, 48.6, 47}, + {61.2, 48, 47}, + {58.8, 47.5, 47}, + {62, 46.3, 47}, + {56.4, 46.2, 47}, + {69.4, 45, 47}, + {54.3, 43.8, 47}, + {55.8, 43.8, 47}, + {63, 42.9, 47}, + {54.2, 42.8, 47}, + {72, 63, 47}, + {71.3, 61.5, 47}, + {51.1, 61, 47}, + {68.7, 60.3, 47}, + {69.2, 53.9, 47}, + }, + [2954] = { + {61, 86.5, 215}, + {61, 85.3, 215}, + {63.7, 81.7, 215}, + {61.1, 80.6, 215}, + {62.3, 79.2, 215}, + {63.6, 79.1, 215}, + {64.2, 78.6, 215}, + {63.7, 78.4, 215}, + {59.8, 78.3, 215}, + {65.6, 77.9, 215}, + {63.9, 77.7, 215}, + {59, 77.2, 215}, + {62.9, 76.1, 215}, + {61.5, 75.7, 215}, + {64.3, 78.6, 215}, + {63.9, 77.6, 215}, + }, + [2955] = { + {53.1, 88.9, 215}, + {50.5, 88.7, 215}, + {47.8, 88.2, 215}, + {51.6, 88, 215}, + {45.2, 87.9, 215}, + {48.6, 87.7, 215}, + {44.2, 87.7, 215}, + {46.7, 87.6, 215}, + {52.6, 87.5, 215}, + {50.1, 87, 215}, + {50.7, 87, 215}, + {53.9, 86.9, 215}, + {46.2, 86.9, 215}, + {47.9, 86.6, 215}, + {47.2, 86.5, 215}, + {53.3, 86.3, 215}, + {44, 86.2, 215}, + {49.6, 86, 215}, + {50.6, 85.9, 215}, + {51.4, 85.7, 215}, + {54.4, 85.6, 215}, + {45.3, 85.5, 215}, + {52.4, 85.5, 215}, + {46, 85.2, 215}, + {46.6, 85.2, 215}, + {43.4, 85, 215}, + {44.1, 85, 215}, + {46.6, 85, 215}, + {53.9, 84.3, 215}, + {45.4, 84.1, 215}, + {47.8, 84.1, 215}, + {53.4, 84.1, 215}, + {44.2, 83.9, 215}, + {50.9, 83.8, 215}, + {44.6, 83.7, 215}, + {47.1, 83.7, 215}, + {46.1, 83.5, 215}, + {49.9, 83.4, 215}, + {49.4, 83.4, 215}, + {51.2, 83.1, 215}, + {51.9, 83.1, 215}, + {48.7, 83, 215}, + {53.9, 82.8, 215}, + {52.8, 82.7, 215}, + {42.3, 82.3, 215}, + {46.6, 82.2, 215}, + {53.2, 82.1, 215}, + {45.3, 81.9, 215}, + {52, 81.9, 215}, + {48.1, 81.8, 215}, + {49.6, 81.7, 215}, + {43.3, 81.7, 215}, + {42.3, 81.4, 215}, + {52.2, 81.3, 215}, + {50.7, 81.2, 215}, + {46.7, 81, 215}, + {46, 80.6, 215}, + {48.3, 80.4, 215}, + {49.3, 80.4, 215}, + {45.7, 80.3, 215}, + {44.1, 80.1, 215}, + {51.9, 79.8, 215}, + {48.6, 79.6, 215}, + {45.4, 79.2, 215}, + {50.6, 79.1, 215}, + {47.5, 79.1, 215}, + {43.6, 79.1, 215}, + {51.4, 79, 215}, + {49.7, 78.6, 215}, + {46.7, 78.2, 215}, + {50.9, 78.1, 215}, + {49.6, 77.8, 215}, + {47.9, 77.4, 215}, + {42.1, 77.3, 215}, + {52, 77.2, 215}, + {48, 77.1, 215}, + {50.6, 77, 215}, + {48.9, 76.9, 215}, + {42.9, 76.5, 215}, + {47.9, 76.4, 215}, + {48.5, 76.3, 215}, + {51.2, 76.2, 215}, + {51, 76.2, 215}, + {49.2, 75.7, 215}, + {47, 75.6, 215}, + {43.3, 75.5, 215}, + {49.9, 75.3, 215}, + {47.7, 75.3, 215}, + {51.9, 75.1, 215}, + {46.5, 75, 215}, + {51.5, 74.9, 215}, + {52.2, 74.8, 215}, + {44.9, 74.2, 215}, + {50.1, 74.2, 215}, + {44.3, 74, 215}, + {46.5, 73.6, 215}, + {45.8, 73.3, 215}, + {53.2, 82, 215}, + {44.1, 79.8, 215}, + }, + [2956] = { + {37.4, 55.7, 17}, + {34.9, 79.9, 215}, + {35.7, 79.1, 215}, + {36.9, 77.2, 215}, + {34.8, 77, 215}, + {37.8, 75.2, 215}, + {35, 74.4, 215}, + {36.9, 73.2, 215}, + {40.9, 73.2, 215}, + {42.2, 72.3, 215}, + {58.4, 72.3, 215}, + {35.6, 71.4, 215}, + {46, 71.2, 215}, + {45.1, 71.1, 215}, + {39.4, 70.2, 215}, + {40.2, 70.2, 215}, + {34.2, 69.9, 215}, + {37, 69.4, 215}, + {41.1, 69, 215}, + {54.6, 68.6, 215}, + {36.4, 68.4, 215}, + {46.6, 68.4, 215}, + {47.9, 68.3, 215}, + {40.8, 68.3, 215}, + {43.5, 67.5, 215}, + {42.8, 67.4, 215}, + {57.8, 67.4, 215}, + {38, 67.1, 215}, + {50.2, 67, 215}, + {58.1, 66.6, 215}, + {39.1, 66.6, 215}, + {42, 66.4, 215}, + {35.1, 66.4, 215}, + {49.1, 66.4, 215}, + {39.6, 66.3, 215}, + {36.8, 65.8, 215}, + {38.2, 63.5, 215}, + {41.6, 63.4, 215}, + {40.4, 62.6, 215}, + {39.5, 61.9, 215}, + {53.9, 61.6, 215}, + {36.3, 61.6, 215}, + {37.6, 61.3, 215}, + {52.5, 60.6, 215}, + {40.8, 59.7, 215}, + {42.1, 59.7, 215}, + {61.7, 59.6, 215}, + {36.8, 58, 215}, + {55.1, 57.8, 215}, + {56.5, 57.7, 215}, + {59.5, 57.1, 215}, + {40, 56.9, 215}, + {35.9, 56.7, 215}, + {39.3, 56.1, 215}, + {35.6, 55.3, 215}, + {43, 55, 215}, + {62.6, 54.8, 215}, + {38.8, 54.3, 215}, + {36.4, 53.9, 215}, + {39.2, 53, 215}, + {35.8, 52.3, 215}, + {39.5, 52.2, 215}, + {43.7, 51.4, 215}, + {40.5, 51.4, 215}, + {37, 51, 215}, + {36.6, 50, 215}, + {35.3, 48.3, 215}, + {58.4, 46.8, 215}, + {55.7, 46.1, 215}, + {48.8, 45.8, 215}, + {45.4, 45.1, 215}, + {52.5, 45.1, 215}, + {35.7, 44.2, 215}, + {43.7, 43.9, 215}, + {42.2, 43.3, 215}, + {57.2, 43, 215}, + {54.7, 42, 215}, + {36.7, 41.2, 215}, + {56, 40.9, 215}, + {44.9, 40.8, 215}, + {49, 40.1, 215}, + {54.5, 39.2, 215}, + {35.1, 39.2, 215}, + {43.5, 38.9, 215}, + {35.6, 38.1, 215}, + {45.8, 37.2, 215}, + {57.4, 36.9, 215}, + {55.1, 36.9, 215}, + {44.8, 36.3, 215}, + {37.6, 35.3, 215}, + {34.3, 34.1, 215}, + {46.5, 31.5, 215}, + {33.3, 31.1, 215}, + {33.7, 30.7, 215}, + {87.9, 63.1, 405}, + {86.6, 59.7, 405}, + {87.1, 59.2, 405}, + {42.1, 59.6, 215}, + }, + [2957] = { + {39.3, 58.7, 17}, + {40.3, 58.1, 17}, + {34.5, 44.4, 17}, + {35, 43.3, 17}, + {34.6, 42.1, 17}, + {35, 41.6, 17}, + {36.2, 41.4, 17}, + {37.1, 41.2, 17}, + {36.4, 40.9, 17}, + {36.2, 40.4, 17}, + {35.7, 39.5, 17}, + {35.2, 39, 17}, + {35, 38.4, 17}, + {33.6, 37.8, 17}, + {34.6, 37, 17}, + {32.6, 35, 17}, + {33, 34.3, 17}, + {31.7, 33, 17}, + {31.3, 32, 17}, + {61, 64.5, 215}, + {62.2, 64.4, 215}, + {64.2, 63.5, 215}, + {62.4, 62.8, 215}, + {64.2, 61.7, 215}, + {66.2, 60.7, 215}, + {68.2, 59.6, 215}, + {55.9, 56.4, 215}, + {57.7, 53.8, 215}, + {60.5, 53.6, 215}, + {55.7, 50.6, 215}, + {40.8, 50.1, 215}, + {43.3, 47, 215}, + {38.1, 47, 215}, + {50.6, 46.1, 215}, + {56.8, 44.8, 215}, + {46.1, 44.3, 215}, + {53.9, 44.1, 215}, + {37.1, 43, 215}, + {51.2, 42.2, 215}, + {47.4, 41.9, 215}, + {38.2, 39.8, 215}, + {52.5, 39.7, 215}, + {52, 39.4, 215}, + {49.6, 38.1, 215}, + {47.9, 36.5, 215}, + {52.5, 35.2, 215}, + {52.5, 34.4, 215}, + {53.6, 34.1, 215}, + {55.7, 33.7, 215}, + {50.1, 33.5, 215}, + {56.8, 32.5, 215}, + {52.6, 32.5, 215}, + {46.3, 32.2, 215}, + {44.6, 31.5, 215}, + {54, 31.3, 215}, + {57.8, 30.3, 215}, + {49.2, 29.6, 215}, + {56, 29.5, 215}, + {49.8, 29.3, 215}, + {54, 28.9, 215}, + {44.6, 28.4, 215}, + {57, 27.9, 215}, + {51.5, 27.9, 215}, + {47.3, 27, 215}, + {57.8, 27, 215}, + {60.2, 26.6, 215}, + {51.2, 26.6, 215}, + {33.3, 26.5, 215}, + {62, 26.3, 215}, + {60.7, 25.6, 215}, + {49.3, 25.6, 215}, + {55.7, 25.6, 215}, + {47.7, 25.3, 215}, + {52.5, 24.9, 215}, + {60.3, 24.7, 215}, + {54.5, 24.6, 215}, + {51, 23.6, 215}, + {54.3, 23, 215}, + {59.2, 23, 215}, + {52, 22.1, 215}, + {58.2, 21.9, 215}, + {42.9, 21.5, 215}, + {54.8, 21, 215}, + {57.9, 20.6, 215}, + {53.7, 19.7, 215}, + {55.1, 19.6, 215}, + {38.2, 18.9, 215}, + {39.5, 18.8, 215}, + {47.3, 18.6, 215}, + {51.2, 18.6, 215}, + {42.7, 18.6, 215}, + {45.8, 18.5, 215}, + {53.1, 18.4, 215}, + {34.3, 18.1, 215}, + {57, 17.9, 215}, + {44.2, 17.9, 215}, + {51.1, 17.7, 215}, + {38.4, 17.1, 215}, + {46, 17, 215}, + {48.6, 16.1, 215}, + {37.4, 14.8, 215}, + {48.6, 14.7, 215}, + {53.2, 14.1, 215}, + {46.8, 14, 215}, + {50.5, 14, 215}, + {47.6, 13.7, 215}, + {49.7, 13.5, 215}, + {41.1, 13.2, 215}, + {53.9, 12.6, 215}, + {38.5, 12.5, 215}, + {41.6, 11.5, 215}, + {42.2, 11.2, 215}, + {46.1, 10.7, 215}, + {43.7, 10.2, 215}, + {47.3, 10.2, 215}, + {51.3, 10.1, 215}, + {48.1, 10, 215}, + {39.7, 8.8, 215}, + {44, 8.6, 215}, + {50.6, 8.1, 215}, + {47.1, 7.6, 215}, + {86.6, 54.5, 405}, + {87.9, 44.9, 405}, + {57.7, 53.9, 215}, + {69.1, 100, 406}, + {74.8, 99.5, 406}, + {71.8, 99.1, 406}, + }, + [2958] = { + {34.1, 79.3, 215}, + {35, 77.9, 215}, + {33.9, 77.5, 215}, + {33.7, 76.3, 215}, + {34.9, 76.1, 215}, + {34.3, 76.1, 215}, + {37.8, 75.6, 215}, + {35.7, 75.2, 215}, + {36.9, 75.2, 215}, + {39, 74, 215}, + {41.9, 73.7, 215}, + {34.3, 73.5, 215}, + {40.2, 73.4, 215}, + {35.6, 73.4, 215}, + {39, 72.4, 215}, + {36.3, 72.4, 215}, + {35.1, 72.4, 215}, + {36.5, 71.5, 215}, + {43.1, 71.3, 215}, + {34.9, 71.3, 215}, + {39.5, 71.2, 215}, + {40.9, 71.2, 215}, + {38.2, 71.2, 215}, + {45.6, 70.4, 215}, + {46.2, 70.4, 215}, + {54.4, 70.4, 215}, + {55.4, 70.4, 215}, + {36.3, 70.4, 215}, + {43.6, 70.4, 215}, + {38.1, 70.3, 215}, + {41.6, 70.3, 215}, + {53.4, 69.8, 215}, + {48.3, 69.8, 215}, + {51.2, 69.5, 215}, + {37.8, 69.5, 215}, + {35.7, 69.4, 215}, + {39.9, 69.4, 215}, + {56.6, 69.3, 215}, + {45.9, 69.3, 215}, + {47.6, 69.1, 215}, + {40, 68.6, 215}, + {35, 68.4, 215}, + {39.3, 68.4, 215}, + {50.6, 68.4, 215}, + {53.4, 68.4, 215}, + {41.6, 68.2, 215}, + {39.5, 67.5, 215}, + {37, 67.5, 215}, + {48.6, 67.5, 215}, + {51.3, 67.5, 215}, + {55.1, 67.4, 215}, + {36.3, 67.4, 215}, + {44.8, 67.3, 215}, + {43, 66.6, 215}, + {51.8, 66.5, 215}, + {40.7, 66.4, 215}, + {37.6, 66.4, 215}, + {56.7, 66.3, 215}, + {40.9, 66.1, 215}, + {55, 65.8, 215}, + {38.3, 65.7, 215}, + {56.4, 65.6, 215}, + {41.5, 65.6, 215}, + {47.5, 65.4, 215}, + {35.7, 65.4, 215}, + {49.8, 65.3, 215}, + {36.6, 65.2, 215}, + {36.2, 64.7, 215}, + {50.7, 64.6, 215}, + {37.5, 64.4, 215}, + {39, 64.2, 215}, + {41.4, 63.9, 215}, + {42, 63.8, 215}, + {56.4, 63.7, 215}, + {39.8, 63.7, 215}, + {37.2, 63.6, 215}, + {38.7, 63, 215}, + {55.8, 62.7, 215}, + {37.3, 62.5, 215}, + {40.8, 62, 215}, + {38.1, 61.8, 215}, + {42.2, 61.7, 215}, + {36.4, 61, 215}, + {40.8, 60.7, 215}, + {41.5, 60.6, 215}, + {36.4, 59.7, 215}, + {36.8, 59.4, 215}, + {35.6, 59.3, 215}, + {54.3, 59.3, 215}, + {40, 58.7, 215}, + {37.8, 58.6, 215}, + {38.2, 58.1, 215}, + {53.9, 57.7, 215}, + {40.2, 57.7, 215}, + {36, 57.7, 215}, + {39.6, 57.3, 215}, + {37.8, 56.6, 215}, + {36.9, 56.4, 215}, + {35.8, 56, 215}, + {42.2, 55.7, 215}, + {38.2, 55.7, 215}, + {39.6, 55.6, 215}, + {42.2, 55, 215}, + {37.6, 54.8, 215}, + {40.2, 54.7, 215}, + {40.8, 54, 215}, + {35.1, 53.7, 215}, + {39.3, 53.6, 215}, + {38.2, 53.5, 215}, + {39.9, 52.6, 215}, + {34.9, 52.6, 215}, + {36.3, 52.2, 215}, + {38.1, 52.2, 215}, + {36.5, 51.9, 215}, + {40.8, 51.8, 215}, + {41.6, 51.8, 215}, + {39.2, 50.9, 215}, + {39, 49.9, 215}, + {51.9, 66.7, 215}, + {56.3, 65.8, 215}, + }, + [2959] = { + {36.6, 56.3, 17}, + {37.6, 54.9, 17}, + {35.8, 51, 17}, + {57.8, 73, 215}, + {56.5, 71.3, 215}, + {59.1, 71.1, 215}, + {57.7, 70.4, 215}, + {61.8, 69.3, 215}, + {57.9, 68.2, 215}, + {61.1, 67.7, 215}, + {59, 67.2, 215}, + {60.9, 66.5, 215}, + {59.7, 65.9, 215}, + {57.2, 65.6, 215}, + {58.9, 64.6, 215}, + {60.5, 63.3, 215}, + {58.6, 62.5, 215}, + {57, 62.5, 215}, + {60.3, 61.7, 215}, + {60.6, 60.7, 215}, + {56.4, 59.6, 215}, + {60.2, 59.4, 215}, + {60.9, 58.8, 215}, + {56.9, 58.6, 215}, + {57.6, 58, 215}, + {55.8, 57.9, 215}, + {60.9, 56, 215}, + {57.2, 56, 215}, + {56, 55.8, 215}, + {60.3, 55.4, 215}, + {58.8, 55, 215}, + {63, 53.2, 215}, + {42.8, 53, 215}, + {58.8, 52.7, 215}, + {58.8, 52.1, 215}, + {41.7, 52, 215}, + {35.4, 51.7, 215}, + {57.5, 51.6, 215}, + {41.8, 51, 215}, + {35.5, 51, 215}, + {58.2, 50.8, 215}, + {42.8, 50.4, 215}, + {55.2, 50.2, 215}, + {35.4, 50, 215}, + {47.4, 50, 215}, + {40.1, 49.7, 215}, + {41.5, 49.2, 215}, + {45, 49.1, 215}, + {49.3, 49, 215}, + {46.6, 48.9, 215}, + {55.4, 48.2, 215}, + {49.9, 48, 215}, + {51.4, 48, 215}, + {46.5, 47.9, 215}, + {43.2, 47.9, 215}, + {47.9, 47.8, 215}, + {44.6, 47.7, 215}, + {57.4, 47.6, 215}, + {42.7, 47.3, 215}, + {57.6, 47.2, 215}, + {49.3, 47.1, 215}, + {46.8, 47, 215}, + {47.8, 46.9, 215}, + {51, 46.6, 215}, + {43.4, 46.6, 215}, + {57.6, 46.4, 215}, + {51.9, 46.3, 215}, + {57.1, 46.1, 215}, + {42.2, 46, 215}, + {59.4, 45.5, 215}, + {55.9, 45.2, 215}, + {41.4, 45.2, 215}, + {53.8, 45.1, 215}, + {46.7, 45.1, 215}, + {50.8, 45.1, 215}, + {53.5, 45, 215}, + {36.3, 45, 215}, + {52.4, 45, 215}, + {58, 44.6, 215}, + {57.1, 44.5, 215}, + {52.6, 44.4, 215}, + {55, 44.3, 215}, + {54.5, 44.2, 215}, + {51.2, 44.2, 215}, + {55.8, 44, 215}, + {41.1, 44, 215}, + {49.9, 43.2, 215}, + {52.6, 43.2, 215}, + {51.8, 43.2, 215}, + {42.2, 43.2, 215}, + {49, 43.1, 215}, + {35, 43, 215}, + {58.1, 42.8, 215}, + {54.4, 42.6, 215}, + {39.5, 42.4, 215}, + {40.9, 42.2, 215}, + {48, 42.2, 215}, + {43.4, 42.1, 215}, + {42.1, 41.7, 215}, + {42.8, 41.5, 215}, + {54.1, 41.3, 215}, + {42, 41.3, 215}, + {44.1, 41.1, 215}, + {53.8, 40.5, 215}, + {39.5, 40.3, 215}, + {41.7, 40.2, 215}, + {44.8, 40.1, 215}, + {51.9, 40, 215}, + {38.3, 40, 215}, + {45.4, 40, 215}, + {50.5, 39.9, 215}, + {49.5, 39.9, 215}, + {41.6, 39.8, 215}, + {38.9, 39.6, 215}, + {48, 39.2, 215}, + {51.5, 38.9, 215}, + {44.8, 38.3, 215}, + {41.4, 37.9, 215}, + {47, 37.8, 215}, + {48.6, 37.8, 215}, + {54.5, 37.3, 215}, + {50.2, 36.9, 215}, + {35.7, 36.3, 215}, + {49.3, 36.2, 215}, + {37.1, 35.9, 215}, + {34.7, 35.8, 215}, + {45.2, 35.5, 215}, + {37.5, 35.4, 215}, + {46.7, 35.4, 215}, + {37.1, 34.3, 215}, + {45.6, 34.2, 215}, + {51.2, 34.1, 215}, + {46.1, 33.6, 215}, + {35.9, 33.4, 215}, + {45.4, 33.2, 215}, + {47.4, 32.6, 215}, + {35.6, 32.5, 215}, + {48.7, 31.1, 215}, + {34.2, 31.1, 215}, + {33.7, 28.7, 215}, + {34.3, 27.7, 215}, + {35.1, 25.2, 215}, + {35.2, 24.9, 215}, + {88.3, 65.1, 405}, + {89.3, 61.4, 405}, + {87.7, 59.7, 405}, + {87.2, 57, 405}, + {87.9, 55.9, 405}, + {88.7, 53, 405}, + {88.8, 52.6, 405}, + }, + [2960] = { + {38.5, 62.3, 17}, + {38.7, 62.2, 17}, + {38.5, 62, 17}, + {39.5, 61.6, 17}, + {39.2, 61.3, 17}, + {38.9, 61.1, 17}, + {39.6, 60.8, 17}, + {39.2, 60.7, 17}, + {39.5, 60.1, 17}, + {38.8, 59.9, 17}, + {39.6, 58.5, 17}, + {39.1, 58.2, 17}, + {38.2, 58.2, 17}, + {37.5, 58.2, 17}, + {38.4, 57.8, 17}, + {39.2, 57.7, 17}, + {39.5, 57.7, 17}, + {37.7, 57.7, 17}, + {39.6, 57.4, 17}, + {38.9, 56.9, 17}, + {39.3, 56.6, 17}, + {37.1, 56.4, 17}, + {37.8, 56.4, 17}, + {34.8, 43.8, 17}, + {35.6, 42.6, 17}, + {34.7, 42.5, 17}, + {34.5, 42.3, 17}, + {35.2, 42.1, 17}, + {36.6, 42, 17}, + {34.6, 40.9, 17}, + {34.8, 40.5, 17}, + {34.9, 39.3, 17}, + {34.6, 38.4, 17}, + {32.6, 34.5, 17}, + {31.7, 32.5, 17}, + {30.6, 31.8, 17}, + {63.5, 69.2, 215}, + {61.7, 68.3, 215}, + {62.9, 68.1, 215}, + {64.7, 67.8, 215}, + {61.5, 67.8, 215}, + {65.2, 67.7, 215}, + {64.8, 67.2, 215}, + {66.8, 66.5, 215}, + {62.2, 66.3, 215}, + {66.1, 65.8, 215}, + {62.3, 65.6, 215}, + {62.9, 65.5, 215}, + {65.5, 65.4, 215}, + {64.2, 65.4, 215}, + {67, 64.8, 215}, + {66.1, 64.7, 215}, + {66.8, 63.6, 215}, + {65.3, 63.2, 215}, + {61.6, 61.6, 215}, + {62.9, 61.6, 215}, + {64.6, 61, 215}, + {63.5, 60.6, 215}, + {66.9, 60.4, 215}, + {65.9, 59.7, 215}, + {64.2, 59.7, 215}, + {62.8, 59.7, 215}, + {64.5, 59, 215}, + {66.2, 58.8, 215}, + {66.7, 58.8, 215}, + {63.2, 58.7, 215}, + {62.3, 58.7, 215}, + {66.9, 58.1, 215}, + {62.3, 57.7, 215}, + {61.6, 57.7, 215}, + {65.6, 57.3, 215}, + {66.2, 56.7, 215}, + {62.1, 56.2, 215}, + {63.4, 56.2, 215}, + {54.1, 33.9, 215}, + {53.1, 32.5, 215}, + {50.5, 32.2, 215}, + {56, 31.3, 215}, + {57.4, 31.3, 215}, + {49.1, 30.7, 215}, + {44.4, 30.3, 215}, + {44.1, 30.2, 215}, + {50.5, 29.6, 215}, + {53.3, 29.4, 215}, + {45.4, 29.1, 215}, + {59, 29, 215}, + {46.5, 28.8, 215}, + {57.3, 28.7, 215}, + {48, 28.5, 215}, + {56.8, 28.4, 215}, + {45.9, 28, 215}, + {58.2, 27.9, 215}, + {61, 27.8, 215}, + {45.5, 27.3, 215}, + {49.1, 27.2, 215}, + {46.2, 26.6, 215}, + {45.4, 26.6, 215}, + {57.1, 25.6, 215}, + {57.4, 24.8, 215}, + {43.5, 24.7, 215}, + {56.3, 24.6, 215}, + {53.9, 24.1, 215}, + {48.8, 23.7, 215}, + {52.2, 23.2, 215}, + {57.6, 22.4, 215}, + {51, 21.9, 215}, + {54.6, 21.5, 215}, + {56.2, 20.9, 215}, + {57.1, 20.8, 215}, + {52.7, 20.7, 215}, + {32.3, 20.1, 215}, + {52, 19.9, 215}, + {50.5, 19.7, 215}, + {45.3, 18.8, 215}, + {40.7, 18.6, 215}, + {38.9, 17.8, 215}, + {45.3, 17.7, 215}, + {52.6, 17.4, 215}, + {50.9, 16.6, 215}, + {45.3, 15.9, 215}, + {36.3, 15.8, 215}, + {49.4, 15.8, 215}, + {37.2, 15.5, 215}, + {52.3, 15.3, 215}, + {42.9, 14.7, 215}, + {50, 14.2, 215}, + {51.1, 14.2, 215}, + {49.2, 13.9, 215}, + {53.1, 13, 215}, + {50.6, 12.9, 215}, + {49.2, 12.3, 215}, + {49.9, 11.8, 215}, + {40.8, 11.1, 215}, + {42.7, 10.1, 215}, + {40.1, 10, 215}, + {51.3, 9.1, 215}, + {43.4, 9, 215}, + {41.5, 8.1, 215}, + {49.2, 7.7, 215}, + {85.5, 47.2, 405}, + {67.5, 38.8, 1638}, + {38.1, 58.2, 17}, + {62.8, 61.7, 215}, + {61.5, 61.6, 215}, + {63.7, 60.7, 215}, + {64, 59.7, 215}, + {61.5, 57.6, 215}, + {49.3, 14.1, 215}, + {40.2, 10.1, 215}, + {67, 99.5, 406}, + {73.6, 99.2, 406}, + }, + [2961] = { + {56.1, 94.7, 215}, + {44.2, 94.5, 215}, + {45, 94.2, 215}, + {55.8, 94, 215}, + {56.5, 93.8, 215}, + {57.7, 93.8, 215}, + {44.8, 92.8, 215}, + {47.4, 92.7, 215}, + {56.4, 92.7, 215}, + {53.2, 92.5, 215}, + {52, 92.4, 215}, + {57.9, 92.3, 215}, + {43.7, 92.1, 215}, + {48.5, 92.1, 215}, + {46.8, 92.1, 215}, + {45.1, 91.9, 215}, + {44.9, 91.8, 215}, + {55.8, 91.8, 215}, + {47.9, 91.8, 215}, + {54.6, 91.6, 215}, + {49.2, 91.6, 215}, + {44.8, 91.5, 215}, + {48.6, 91.4, 215}, + {43.2, 91.3, 215}, + {53.1, 91.3, 215}, + {51.2, 91.2, 215}, + {49.4, 91.1, 215}, + {56.5, 91.1, 215}, + {53.6, 90.9, 215}, + {52.3, 90.8, 215}, + {53.9, 90.7, 215}, + {57.6, 90.7, 215}, + {41.6, 90.7, 215}, + {47.9, 90.6, 215}, + {43.8, 90.5, 215}, + {55.1, 90.5, 215}, + {50, 90.4, 215}, + {55.9, 90.4, 215}, + {46, 90.4, 215}, + {49.1, 90.1, 215}, + {43.6, 90, 215}, + {53.7, 89.9, 215}, + {43.4, 89.9, 215}, + {51.8, 89.9, 215}, + {51.9, 89.8, 215}, + {46, 89.8, 215}, + {47.3, 89.8, 215}, + {50.5, 89.8, 215}, + {48.5, 89.8, 215}, + {53.3, 89.8, 215}, + {45.7, 89.6, 215}, + {53.4, 89.3, 215}, + {54.8, 89.2, 215}, + {52.5, 89.2, 215}, + {53.9, 89.1, 215}, + {48.7, 89, 215}, + {51.1, 89, 215}, + {40.8, 88.9, 215}, + {42.1, 88.8, 215}, + {45.4, 88.8, 215}, + {41.6, 88.7, 215}, + {43.3, 88.5, 215}, + {44.8, 88.3, 215}, + {42, 87.9, 215}, + {49.8, 87.7, 215}, + {42.7, 87.7, 215}, + {41.6, 87.4, 215}, + {42.4, 85.9, 215}, + {42.1, 85, 215}, + {41.5, 84.2, 215}, + {42.8, 83.6, 215}, + {40.1, 83.3, 215}, + {40.8, 82.1, 215}, + }, + [2966] = { + {59.5, 90.5, 215}, + {59.6, 90.5, 215}, + {57.6, 90.1, 215}, + {56.9, 90.1, 215}, + {58.7, 90.1, 215}, + {56.9, 88.9, 215}, + {58.4, 88.9, 215}, + {60.2, 88.8, 215}, + {59.4, 88.6, 215}, + {58.1, 88, 215}, + {56.8, 88, 215}, + {55.9, 87.9, 215}, + {56.5, 87.5, 215}, + {55.1, 86.7, 215}, + {57.6, 86.7, 215}, + {55.7, 86.2, 215}, + {56.5, 85.9, 215}, + {55.1, 85.7, 215}, + {56.3, 85.2, 215}, + {57, 84.9, 215}, + {56.1, 84.2, 215}, + {56.4, 84.1, 215}, + {54.5, 84, 215}, + {55.5, 82.1, 215}, + {53.8, 82, 215}, + {56.5, 82, 215}, + {56.1, 81.9, 215}, + {55, 81.9, 215}, + {54.3, 81.8, 215}, + {55, 80.8, 215}, + {55.7, 80.2, 215}, + {52.6, 80.2, 215}, + {54.5, 80.1, 215}, + {55.1, 79.4, 215}, + {53.3, 79.3, 215}, + {52.6, 79.3, 215}, + {55.2, 78.1, 215}, + {53.1, 77.7, 215}, + {51.9, 77.2, 215}, + {55, 77.1, 215}, + {54, 76.9, 215}, + {54.4, 76.3, 215}, + {53.2, 76.2, 215}, + {55.8, 76.2, 215}, + {55.1, 75.2, 215}, + {52.4, 75, 215}, + {54.6, 84, 215}, + {54.6, 80.1, 215}, + }, + [2969] = { + {34.2, 78.3, 215}, + {44.7, 70.4, 215}, + {36.9, 70.4, 215}, + {55.2, 70.4, 215}, + {39.5, 70.4, 215}, + {49.9, 68.6, 215}, + {35.1, 66.5, 215}, + {37.9, 66.4, 215}, + {41, 66.3, 215}, + {40.7, 63.4, 215}, + {37.6, 62.6, 215}, + {37.1, 59.1, 215}, + {40.9, 58.7, 215}, + {53.9, 58.7, 215}, + {39, 57.8, 215}, + {40.6, 54.3, 215}, + {39, 53.7, 215}, + }, + [2970] = { + {57.7, 68.4, 215}, + {58.9, 64.5, 215}, + {59.8, 59.7, 215}, + {60.2, 54.9, 215}, + {58.3, 52, 215}, + {42.8, 51.9, 215}, + {46.1, 49.7, 215}, + {35.2, 49.5, 215}, + {43.3, 49.2, 215}, + {57.4, 48.6, 215}, + {41.5, 48.6, 215}, + {49, 48, 215}, + {56.7, 46.1, 215}, + {42.7, 45.2, 215}, + {53.8, 45, 215}, + {36.2, 44.9, 215}, + {49.8, 44.4, 215}, + {52.1, 44.1, 215}, + {48.6, 44.1, 215}, + {42.2, 43.5, 215}, + {34.8, 42.8, 215}, + {55.3, 42.3, 215}, + {37.6, 41.2, 215}, + {53.8, 40.7, 215}, + {49.6, 40.7, 215}, + {51.8, 40.3, 215}, + {42.8, 39.8, 215}, + {49.2, 38.4, 215}, + {54.8, 37.4, 215}, + {52.1, 37.2, 215}, + {47.4, 36.5, 215}, + {44.7, 34.9, 215}, + {46.8, 34.8, 215}, + {51.7, 34.8, 215}, + {88.4, 73, 405}, + }, + [2971] = { + {38.6, 58.1, 17}, + {39.5, 57.2, 17}, + {35.1, 42.5, 17}, + {35, 39.3, 17}, + {64.9, 59.6, 215}, + {66.8, 57.7, 215}, + {55.5, 32.4, 215}, + {53.8, 32, 215}, + {52.2, 30.3, 215}, + {50.8, 28.9, 215}, + {58, 28.8, 215}, + {53.9, 26.5, 215}, + {53.2, 23.7, 215}, + {57.8, 22.4, 215}, + {51.7, 20, 215}, + {53.3, 18.9, 215}, + {37.6, 17.3, 215}, + {51.8, 16.4, 215}, + {52.6, 13.8, 215}, + {46.6, 13.4, 215}, + {46.7, 10.1, 215}, + {42.2, 10, 215}, + {48.9, 9.2, 215}, + }, + [2997] = { + {38.2, 29.5, 215}, + {41.4, 62.4, 1638}, + }, + [3015] = { + {39.4, 26.1, 215}, + {47, 45.7, 1638}, + }, + [3018] = { + {41.1, 28.4, 215}, + {55.5, 57.1, 1638}, + }, + [3035] = { + {37.5, 54.1, 17}, + {57, 72.2, 215}, + {57.7, 71.9, 215}, + {59.7, 64.8, 215}, + {57.3, 63.8, 215}, + {57.1, 62.8, 215}, + {59.4, 58.4, 215}, + {59, 58.1, 215}, + {59.6, 54.6, 215}, + {59.4, 53.5, 215}, + {58.7, 52.7, 215}, + {43.9, 52.2, 215}, + {35, 51.9, 215}, + {62.7, 51.6, 215}, + {56.2, 51.4, 215}, + {42, 50, 215}, + {57.6, 49.4, 215}, + {57.3, 49.1, 215}, + {44.2, 48.8, 215}, + {36.5, 48.7, 215}, + {36.9, 48.6, 215}, + {56.5, 48.2, 215}, + {45.2, 47.4, 215}, + {54.4, 47, 215}, + {39, 46.9, 215}, + {39.6, 46, 215}, + {54.9, 45.9, 215}, + {38.4, 45.7, 215}, + {43.3, 45.4, 215}, + {49.3, 45.2, 215}, + {48.6, 44.5, 215}, + {36.9, 44, 215}, + {50.4, 43.3, 215}, + {42.8, 43.2, 215}, + {48, 43.1, 215}, + {55.5, 43.1, 215}, + {36.6, 43, 215}, + {34.4, 42.6, 215}, + {46.3, 42.3, 215}, + {35.7, 42.2, 215}, + {50.6, 42, 215}, + {56.1, 41.9, 215}, + {54.1, 41.9, 215}, + {51.7, 41.5, 215}, + {48.6, 41.4, 215}, + {39, 41.2, 215}, + {49.8, 41.1, 215}, + {53.4, 40.7, 215}, + {50.6, 40.6, 215}, + {54.6, 40.4, 215}, + {47.3, 40.2, 215}, + {53.1, 40.2, 215}, + {38.1, 39.9, 215}, + {55.9, 39.5, 215}, + {50.5, 39.3, 215}, + {37.4, 39.3, 215}, + {38.9, 39.1, 215}, + {55.1, 38.4, 215}, + {54, 38.2, 215}, + {40.8, 38.1, 215}, + {45.6, 38.1, 215}, + {56.2, 38.1, 215}, + {52.8, 38, 215}, + {49.4, 37.6, 215}, + {42.1, 37.3, 215}, + {52, 37.3, 215}, + {52.8, 37.3, 215}, + {55.8, 37.2, 215}, + {49.8, 36.8, 215}, + {47.3, 36.4, 215}, + {46.7, 36.4, 215}, + {45.4, 36.4, 215}, + {51.1, 36.4, 215}, + {56.8, 36.3, 215}, + {53.9, 35.9, 215}, + {55.7, 35.6, 215}, + {52.1, 35.5, 215}, + {54.5, 35.3, 215}, + {48.2, 35.2, 215}, + {47.3, 35, 215}, + {53.2, 34.9, 215}, + {50.4, 34.1, 215}, + {35, 33.5, 215}, + {52.1, 33.2, 215}, + {34.5, 32.7, 215}, + {48.1, 32.4, 215}, + {44.7, 31.6, 215}, + {34.8, 31.3, 215}, + {46, 30.6, 215}, + {34.1, 26.4, 215}, + {33.7, 25.6, 215}, + {88, 72.9, 405}, + {88.6, 62.4, 405}, + {88, 61.6, 405}, + {88.4, 60, 405}, + {87.6, 54.4, 405}, + {87.1, 53.4, 405}, + {38.8, 39.3, 215}, + }, + [3053] = { + {80.4, 77, 400}, + }, + [3068] = { + {50.4, 42.6, 215}, + }, + [3072] = { + {45.3, 76.5, 215}, + }, + [3076] = { + {45.9, 57.7, 215}, + }, + [3078] = { + {45.5, 58.5, 215}, + }, + [3088] = { + {23.8, 41.3, 44}, + }, + [3098] = { + {42, 72.6, 14}, + {42.6, 72.5, 14}, + {43.9, 72.5, 14}, + {43.3, 72.5, 14}, + {42, 71.8, 14}, + {41.4, 71.6, 14}, + {43.9, 71.6, 14}, + {42.7, 71.6, 14}, + {44.6, 71.6, 14}, + {44, 70.7, 14}, + {44.5, 70.6, 14}, + {45.2, 70.5, 14}, + {45.2, 69.7, 14}, + {45.8, 68.9, 14}, + {46.4, 68.8, 14}, + {47, 68.7, 14}, + {45.8, 67.1, 14}, + {45.2, 67, 14}, + {46.5, 66.9, 14}, + {44.5, 66, 14}, + {45.1, 65.9, 14}, + {43.9, 65.9, 14}, + {46.4, 65.9, 14}, + {45.7, 65.8, 14}, + {41.3, 65.1, 14}, + {47.1, 65.1, 14}, + {45.2, 65.1, 14}, + {44.5, 65, 14}, + {45.7, 65, 14}, + {43.3, 65, 14}, + {43.9, 65, 14}, + {42.6, 65, 14}, + {42, 64.9, 14}, + {42, 64.2, 14}, + {46.5, 64.1, 14}, + {42.7, 64.1, 14}, + {45.1, 64.1, 14}, + {44.5, 64.1, 14}, + {43.9, 64.1, 14}, + {43.2, 64, 14}, + {43.2, 63.2, 14}, + {43.9, 63.1, 14}, + {45.8, 63.1, 14}, + {42.1, 63.1, 14}, + {44.5, 63.1, 14}, + {45.1, 63.1, 14}, + {46.3, 63, 14}, + {43.9, 62.4, 14}, + {45.2, 62.2, 14}, + {45.8, 62.2, 14}, + {42, 62.1, 14}, + {43.4, 62.1, 14}, + {44.5, 62.1, 14}, + {44.4, 61.5, 14}, + {44, 61.2, 14}, + {45.8, 61.2, 14}, + {42, 61.2, 14}, + {42.6, 61.2, 14}, + {43.5, 61.1, 14}, + {45.2, 60.4, 14}, + {44.5, 60.4, 14}, + {43.9, 60.3, 14}, + {43.2, 60.3, 14}, + {42.7, 60.1, 14}, + {44.6, 59.3, 14}, + {67.2, 35, 17}, + {67.2, 34.5, 17}, + {66.8, 34.5, 17}, + {42.7, 72.7, 14}, + {43.9, 72.6, 14}, + {42, 72.5, 14}, + {43.4, 72.4, 14}, + {42.1, 71.9, 14}, + {44, 71.6, 14}, + {43.9, 70.6, 14}, + {45.2, 66.9, 14}, + {44, 66, 14}, + {45.2, 65.2, 14}, + {42.6, 65.1, 14}, + {44.5, 65.1, 14}, + {45.7, 65.1, 14}, + {43.8, 64.9, 14}, + {43.4, 64.8, 14}, + {43.8, 64.2, 14}, + {42.7, 64.2, 14}, + {43.1, 64, 14}, + {45.3, 64, 14}, + {44.6, 63.2, 14}, + {45, 63.1, 14}, + {43.8, 63.1, 14}, + {45.8, 63, 14}, + {45.6, 62.4, 14}, + {44.5, 61.9, 14}, + {45.8, 61.3, 14}, + {44, 61.1, 14}, + {43.1, 60.3, 14}, + {67.1, 34.9, 17}, + {67.2, 34.6, 17}, + }, + [3099] = { + {51.8, 78.1, 14}, + {51.6, 72.5, 14}, + {50.5, 71.8, 14}, + {54.1, 70.8, 14}, + {53.5, 68.1, 14}, + {54.6, 67.4, 14}, + {51.9, 66.9, 14}, + {51.7, 65.9, 14}, + {51.6, 65.4, 14}, + {52.7, 65, 14}, + {51.5, 63.7, 14}, + {52.2, 63, 14}, + {53.9, 62.6, 14}, + {51.5, 62.1, 14}, + {52, 61.7, 14}, + {52.2, 60.7, 14}, + {53.8, 59.1, 14}, + {51.7, 59, 14}, + {55.6, 58.7, 14}, + {53.2, 58.6, 14}, + {54.5, 57.4, 14}, + {52.6, 57.1, 14}, + {55.3, 57, 14}, + {51.2, 56.7, 14}, + {52.4, 56.6, 14}, + {55.7, 56.2, 14}, + {53.9, 56.2, 14}, + {50.7, 55.9, 14}, + {50.8, 53.9, 14}, + {51.7, 53.6, 14}, + {54.2, 53.1, 14}, + {51, 51.8, 14}, + {40.7, 50.8, 14}, + {41.7, 50.3, 14}, + {42.9, 50.1, 14}, + {52.1, 49.9, 14}, + {53.8, 49.8, 14}, + {51.2, 49.4, 14}, + {40.2, 49, 14}, + {53.3, 48.8, 14}, + {45.4, 48.6, 14}, + {46, 48.3, 14}, + {52.2, 47.9, 14}, + {40, 47.8, 14}, + {42.8, 47.7, 14}, + {40, 47.4, 14}, + {42, 47.1, 14}, + {38.9, 47.1, 14}, + {57.9, 46.8, 14}, + {43.7, 46.4, 14}, + {47.1, 46.2, 14}, + {53.4, 46.2, 14}, + {51.7, 46, 14}, + {42.7, 46, 14}, + {48.3, 45.9, 14}, + {38.6, 45, 14}, + {41.1, 44.9, 14}, + {40.8, 44.9, 14}, + {46.7, 44.7, 14}, + {45.7, 44.6, 14}, + {47.6, 44.5, 14}, + {46.2, 44.4, 14}, + {57.4, 44.4, 14}, + {42.1, 44.3, 14}, + {55.4, 44, 14}, + {43.1, 44, 14}, + {45, 43.5, 14}, + {47.7, 43.4, 14}, + {43.9, 43.3, 14}, + {42.9, 43.3, 14}, + {47, 42.8, 14}, + {58.4, 41.6, 14}, + {56.2, 41.3, 14}, + {45.2, 41.3, 14}, + {40.7, 40.7, 14}, + {55.2, 40.3, 14}, + {48.2, 40.2, 14}, + {56.2, 39.6, 14}, + {55.4, 39.6, 14}, + {46.2, 39.1, 14}, + {56, 39, 14}, + {54.5, 38.7, 14}, + {47.2, 38.3, 14}, + {45.7, 38.3, 14}, + {54.4, 38, 14}, + {39, 37.8, 14}, + {51.4, 37.6, 14}, + {44.6, 37.1, 14}, + {41.1, 36.8, 14}, + {53.2, 36.8, 14}, + {56.5, 36.7, 14}, + {48.3, 36.7, 14}, + {50.7, 36.7, 14}, + {39.4, 36.5, 14}, + {38.1, 35.9, 14}, + {54.4, 35.9, 14}, + {55.8, 35.4, 14}, + {53.5, 35, 14}, + {46, 34.9, 14}, + {49.5, 34.8, 14}, + {50.9, 34.7, 14}, + {54.6, 34.7, 14}, + {44.4, 34.6, 14}, + {51.5, 34.1, 14}, + {43.8, 34, 14}, + {51.1, 33.1, 14}, + {42.6, 32.9, 14}, + {53.4, 32.8, 14}, + {55.4, 32.8, 14}, + {40.4, 32.8, 14}, + {53.9, 31.6, 14}, + {42, 31.5, 14}, + {53.1, 31.3, 14}, + {54.2, 30.6, 14}, + {54.6, 56.3, 14}, + {52, 50, 14}, + {46.9, 46.3, 14}, + {47, 42.1, 14}, + {46.8, 41.7, 14}, + {47.1, 41.4, 14}, + {48, 41.3, 14}, + {48.8, 40.4, 14}, + {48.7, 39.6, 14}, + {49.1, 39.3, 14}, + {50.2, 39.3, 14}, + {48.8, 38.3, 14}, + {50.6, 38.2, 14}, + {46.8, 38, 14}, + {49.3, 37.7, 14}, + {46, 37.5, 14}, + {48, 37.4, 14}, + {48.8, 37.2, 14}, + {53.7, 36.9, 14}, + {49.3, 36.9, 14}, + {48.7, 36.6, 14}, + {47.9, 36.5, 14}, + {48.6, 36.2, 14}, + {53.3, 36, 14}, + {48.9, 35, 14}, + {49.8, 35, 14}, + }, + [3100] = { + {37.8, 55.8, 14}, + {35.4, 55.7, 14}, + {37.5, 55.6, 14}, + {36.4, 55.5, 14}, + {37.5, 54.6, 14}, + {37.1, 52.4, 14}, + {39.4, 51.7, 14}, + {39.6, 49.5, 14}, + {35.1, 49.3, 14}, + {36.3, 49.2, 14}, + {38.5, 48.9, 14}, + {38, 48.5, 14}, + {36.9, 48.2, 14}, + {35.7, 47, 14}, + {35, 46.5, 14}, + {36.9, 46.1, 14}, + {35.6, 45.6, 14}, + {37.6, 45.2, 14}, + {36.9, 44.9, 14}, + {38.6, 44.3, 14}, + {39.6, 44.3, 14}, + {35, 43.3, 14}, + {40.7, 42.5, 14}, + {38.3, 42.1, 14}, + {37.1, 41.3, 14}, + {35.6, 39.4, 14}, + {36.3, 38.9, 14}, + {36.5, 38.5, 14}, + {36.3, 35.7, 14}, + {35.9, 34.9, 14}, + {40.1, 33.8, 14}, + {38.6, 33.7, 14}, + {36.2, 31.9, 14}, + {37, 31.7, 14}, + {35.7, 31, 14}, + {37.7, 30, 14}, + {57, 29.8, 14}, + {38.3, 28.9, 14}, + {54.6, 28.8, 14}, + {56.9, 28.4, 14}, + {42.8, 28.3, 14}, + {55.2, 28.1, 14}, + {37, 28.1, 14}, + {43.1, 27.9, 14}, + {45.4, 27.3, 14}, + {58.4, 27.3, 14}, + {44.6, 27, 14}, + {37.2, 26.4, 14}, + {43.9, 26.3, 14}, + {36, 26, 14}, + {44.6, 25.2, 14}, + {45.4, 25.1, 14}, + {56.9, 24.6, 14}, + {57.8, 24.5, 14}, + {36.2, 24.3, 14}, + {59, 24.2, 14}, + {56.9, 23.5, 14}, + {44.5, 23.3, 14}, + {42.1, 22.8, 14}, + {57.7, 22.5, 14}, + {43.9, 22.4, 14}, + {42.6, 22.3, 14}, + {44.8, 22.3, 14}, + {46, 22.2, 14}, + {40.2, 21.8, 14}, + {56, 21.7, 14}, + {40.5, 21.6, 14}, + {43.9, 21.5, 14}, + {42.7, 21.5, 14}, + {43.2, 21.4, 14}, + {38.7, 21.2, 14}, + {44.6, 21.2, 14}, + {48.6, 20.7, 14}, + {48, 20.6, 14}, + {55.2, 20.5, 14}, + {57.7, 20.3, 14}, + {47.1, 20.2, 14}, + {39.5, 19.6, 14}, + {44.5, 19.5, 14}, + {46, 19.4, 14}, + {54.8, 19.4, 14}, + {57.1, 19.3, 14}, + {37.2, 19, 14}, + {54, 18.9, 14}, + {39.5, 18.7, 14}, + {45.1, 18.7, 14}, + {55.1, 18.6, 14}, + {57.5, 18.5, 14}, + {42.4, 18.5, 14}, + {47.8, 18.4, 14}, + {43.3, 18.1, 14}, + {46.3, 17.9, 14}, + {57, 17.8, 14}, + {44.7, 17.8, 14}, + {39.4, 17.7, 14}, + {44.9, 17.7, 14}, + {42.7, 17.6, 14}, + {52.9, 17.5, 14}, + {54.3, 17.4, 14}, + {44.2, 17.3, 14}, + {40.1, 17.1, 14}, + {56.1, 17, 14}, + {43.2, 16.9, 14}, + {53.6, 16.8, 14}, + {45.2, 16.8, 14}, + {41.9, 16.7, 14}, + {51.6, 16.7, 14}, + {45.5, 16.6, 14}, + {44.9, 16.2, 14}, + {55.4, 16.1, 14}, + {52.5, 15.9, 14}, + {45.8, 15.8, 14}, + {48.3, 15.8, 14}, + {50.8, 15.8, 14}, + {54.6, 15.8, 14}, + {43.8, 15.5, 14}, + {45.9, 15.5, 14}, + {44, 15.3, 14}, + {50.2, 14.9, 14}, + {52.2, 14.8, 14}, + {52.9, 14.2, 14}, + {55.2, 14, 14}, + {56.5, 13.2, 14}, + {51.6, 13.2, 14}, + {53.4, 13, 14}, + {50.2, 13, 14}, + {55.6, 12.9, 14}, + {47.7, 12.9, 14}, + {51.8, 11.8, 14}, + {54.5, 11, 14}, + {55.1, 11, 14}, + {55.8, 10, 14}, + {53.9, 9.3, 14}, + {65, 26.2, 17}, + {63.7, 26.1, 17}, + {64.8, 26.1, 17}, + {64.3, 26, 17}, + {64.8, 25.6, 17}, + {63.6, 22.8, 17}, + {64.2, 22.8, 17}, + {63.9, 21.6, 17}, + {63.5, 21.3, 17}, + {63.8, 20.9, 17}, + {63.5, 19.7, 17}, + {63.8, 17.6, 17}, + {64.2, 17.4, 17}, + {64.3, 17.2, 17}, + {64.2, 15.7, 17}, + {64, 15.3, 17}, + {64.1, 13.8, 17}, + {63.9, 13.3, 17}, + {64.9, 12.7, 17}, + {65.2, 12.2, 17}, + {64.6, 11.7, 17}, + {64.7, 10.9, 17}, + {64.1, 10.7, 17}, + {64.1, 9.8, 17}, + {64.7, 7, 17}, + {57.4, 98.5, 1637}, + {55.3, 20.4, 14}, + {39.3, 19.6, 14}, + {45.1, 18.6, 14}, + {39.4, 17.6, 14}, + {45.2, 16.7, 14}, + {45.7, 15.7, 14}, + {50.9, 15.6, 14}, + {50.2, 14.8, 14}, + {50.2, 12.9, 14}, + {47.7, 12.7, 14}, + {57.3, 97.6, 1637}, + }, + [3106] = { + {51.5, 84, 14}, + {52.1, 84, 14}, + {54.7, 84, 14}, + {52.2, 83.1, 14}, + {52.7, 83, 14}, + {53.9, 83, 14}, + {53.4, 83, 14}, + {54.6, 82.9, 14}, + {54, 82.2, 14}, + {54.6, 82.1, 14}, + {55.2, 81.7, 14}, + {55.8, 80.3, 14}, + {56.4, 80.1, 14}, + {55.4, 79.9, 14}, + {55.9, 79.3, 14}, + {57.2, 79.2, 14}, + {56.5, 79.2, 14}, + {58.2, 78.8, 14}, + {57.2, 78.1, 14}, + {59.7, 73.5, 14}, + {60.9, 69.9, 14}, + {62.7, 62.5, 14}, + {61.3, 61.8, 14}, + {63.4, 61, 14}, + {62.7, 58.2, 14}, + {63.9, 57.2, 14}, + {64.9, 55, 14}, + {62.9, 55, 14}, + {63.2, 54.7, 14}, + {63.8, 54.6, 14}, + {66, 53.5, 14}, + {61.7, 53.5, 14}, + {61, 52.3, 14}, + {62.8, 51.6, 14}, + {65.9, 50.9, 14}, + {61, 50.1, 14}, + {65.5, 49.3, 14}, + {60.3, 49, 14}, + {62.8, 48.9, 14}, + {64.1, 48.8, 14}, + {61.2, 48, 14}, + {63.1, 48, 14}, + {64.1, 47.2, 14}, + {60.3, 47, 14}, + {63.4, 45.3, 14}, + {60.3, 45.1, 14}, + {63.2, 44.2, 14}, + {62.5, 44, 14}, + {60.9, 43.8, 14}, + {60.3, 43.3, 14}, + {61.5, 39.5, 14}, + {72.1, 40.9, 17}, + {72.4, 40.9, 17}, + {73.8, 40.9, 17}, + {72.5, 40.5, 17}, + {54.5, 83, 14}, + {54.6, 82, 14}, + {56.5, 79.1, 14}, + {59.8, 73.6, 14}, + }, + [3107] = { + {64.6, 98.2, 14}, + {63.3, 98, 14}, + {64.6, 96.5, 14}, + {61.9, 94.9, 14}, + {64.6, 94.2, 14}, + {65.2, 93.3, 14}, + {63.2, 93.2, 14}, + {63.7, 93.2, 14}, + {63.4, 92.8, 14}, + {60.3, 92.3, 14}, + {65.9, 91.5, 14}, + {67, 90.8, 14}, + {62.1, 90.5, 14}, + {65.1, 89.9, 14}, + {63.9, 89, 14}, + {69.5, 87.5, 14}, + {61.5, 87.4, 14}, + {61, 86.7, 14}, + {58.9, 86.2, 14}, + {60.2, 85.8, 14}, + {58.3, 83.5, 14}, + {61.4, 82.1, 14}, + {62.8, 81.3, 14}, + {59.6, 81.3, 14}, + {62.1, 80.9, 14}, + {70.4, 80.1, 14}, + {66.1, 79.4, 14}, + {62.1, 79.3, 14}, + {64.1, 78.9, 14}, + {69.7, 78.8, 14}, + {68, 78.6, 14}, + {69.3, 78.3, 14}, + {63.6, 77, 14}, + {62.1, 76.6, 14}, + {67, 76.4, 14}, + {69, 76.3, 14}, + {62.7, 76.1, 14}, + {70.5, 75.9, 14}, + {65.9, 75.6, 14}, + {67.3, 75.5, 14}, + {65.5, 74.6, 14}, + {63.1, 74, 14}, + {71, 73.9, 14}, + {66.3, 72.9, 14}, + {65.1, 72.6, 14}, + {64.1, 72.2, 14}, + {71.1, 71.5, 14}, + {66.2, 70.8, 14}, + {66.1, 70, 14}, + {70.5, 69, 14}, + {67.9, 68.7, 14}, + {58.8, 28.3, 14}, + {59.9, 27.2, 14}, + {60.8, 25.4, 14}, + {60, 25.3, 14}, + {60.3, 24.7, 14}, + {59.8, 23.6, 14}, + {60.2, 22.8, 14}, + {60.9, 17.7, 14}, + {79, 48.4, 17}, + {78.3, 48.2, 17}, + {79, 47.4, 17}, + {77.5, 46.6, 17}, + {78.9, 46.2, 17}, + {79.3, 45.8, 17}, + {78.2, 45.7, 17}, + {78.5, 45.7, 17}, + {78.3, 45.5, 17}, + {76.7, 45.2, 17}, + {79.7, 44.8, 17}, + {80.2, 44.5, 17}, + {77.6, 44.3, 17}, + {79.2, 44, 17}, + {78.6, 43.6, 17}, + {81.5, 42.8, 17}, + {77.4, 42.7, 17}, + {77.1, 42.4, 17}, + {76, 42.1, 17}, + {76.7, 41.9, 17}, + {75.7, 40.7, 17}, + }, + [3108] = { + {59.1, 16.6, 14}, + {58.4, 15.7, 14}, + {57.9, 15.2, 14}, + {58.3, 13.8, 14}, + {57.7, 13.1, 14}, + {57.2, 12.3, 14}, + {58.1, 12, 14}, + {57.8, 11.4, 14}, + {56.3, 11.1, 14}, + {56.9, 10.2, 14}, + {56.5, 9.1, 14}, + {57.8, 96.6, 14}, + {57.6, 95.6, 14}, + {56.9, 95.2, 14}, + {55.5, 95.1, 14}, + {56.3, 94.8, 14}, + {55.6, 94.6, 14}, + {75, 50.8, 17}, + {75.5, 50.7, 17}, + {75.6, 50.2, 17}, + {73.5, 50, 17}, + {76, 49.6, 17}, + {75.4, 47.5, 17}, + {75.3, 47, 17}, + {74.9, 46.8, 17}, + {74.2, 46.7, 17}, + {74.6, 46.6, 17}, + {74.3, 46.5, 17}, + }, + [3110] = { + {38.5, 71.5, 14}, + {37.8, 69.8, 14}, + {37.6, 68.1, 14}, + {37.4, 66.1, 14}, + {36.9, 62.2, 14}, + {36.4, 61.2, 14}, + {36.5, 61.1, 14}, + {35.7, 59.1, 14}, + {35.7, 57.4, 14}, + {35.6, 57.3, 14}, + {35.3, 56.2, 14}, + {34.4, 55.5, 14}, + {34.8, 54.3, 14}, + {34.5, 53.8, 14}, + {33.8, 52.5, 14}, + {34.6, 52.1, 14}, + {33.7, 51.2, 14}, + {33.5, 50.9, 14}, + {34.1, 49.8, 14}, + {34.3, 48.1, 14}, + {33.9, 47.2, 14}, + {34.1, 46, 14}, + {33.8, 45.6, 14}, + {33.6, 45.2, 14}, + {34.5, 44.3, 14}, + {34.4, 44.2, 14}, + {35.1, 39.2, 14}, + {34.4, 37.1, 14}, + {34.2, 34.9, 14}, + {34.3, 34.7, 14}, + {34.5, 32.3, 14}, + {34.4, 32.3, 14}, + {35.2, 30.1, 14}, + {34.6, 29, 14}, + {35.3, 28.3, 14}, + {34.6, 27.2, 14}, + {34.7, 27.1, 14}, + {35.1, 26.3, 14}, + {34.4, 25.7, 14}, + {35.7, 25.3, 14}, + {35.2, 24.6, 14}, + {35.7, 23.4, 14}, + {36.5, 22.2, 14}, + {36.8, 22.1, 14}, + {35.5, 21.5, 14}, + {36.3, 20.9, 14}, + {36.8, 17.2, 14}, + {37.1, 15.8, 14}, + {65.3, 34.4, 17}, + {65, 33.5, 17}, + {64.9, 32.6, 17}, + {64.8, 31.6, 17}, + {64.5, 29.6, 17}, + {64.3, 29, 17}, + {63.9, 27.9, 17}, + {63.9, 27.1, 17}, + {63.8, 27, 17}, + {63.7, 26.4, 17}, + {63.2, 26, 17}, + {63.4, 25.4, 17}, + {63.3, 25.2, 17}, + {62.9, 24.5, 17}, + {63.3, 24.3, 17}, + {62.8, 23.8, 17}, + {62.7, 23.7, 17}, + {63, 23.1, 17}, + {63.2, 22.2, 17}, + {62.9, 21.7, 17}, + {63.1, 21.1, 17}, + {62.9, 20.9, 17}, + {62.8, 20.7, 17}, + {63.2, 20.2, 17}, + {63.2, 20.1, 17}, + {63.6, 17.6, 17}, + {63.2, 16.5, 17}, + {62.7, 15.5, 17}, + {63.1, 15.3, 17}, + {63.2, 15.2, 17}, + {63.3, 13.9, 17}, + {63.2, 13.9, 17}, + {63.6, 12.8, 17}, + {63.3, 12.2, 17}, + {63.7, 11.9, 17}, + {63.3, 11.3, 17}, + {63.4, 11.2, 17}, + {63.5, 10.8, 17}, + {63.2, 10.5, 17}, + {63.9, 10.3, 17}, + {63.6, 9.9, 17}, + {63.9, 9.3, 17}, + {64.3, 8.7, 17}, + {64.5, 8.6, 17}, + {63.8, 8.3, 17}, + {64.2, 8, 17}, + {64.4, 6.1, 17}, + {64.6, 5.4, 17}, + {35.8, 57.4, 14}, + {35, 26.1, 14}, + {63.9, 27, 17}, + {63.5, 10.7, 17}, + }, + [3121] = { + {64, 97.3, 14}, + {62.8, 97.1, 14}, + {63.3, 96.2, 14}, + {64.1, 95.4, 14}, + {62.4, 95, 14}, + {60.3, 91.5, 14}, + {61.5, 91.4, 14}, + {59, 90.5, 14}, + {60.9, 90.5, 14}, + {61.5, 89.6, 14}, + {60.9, 88.7, 14}, + {64.6, 88.5, 14}, + {65.3, 87.7, 14}, + {64.6, 85, 14}, + {69.1, 84.8, 14}, + {59.7, 84.8, 14}, + {59.9, 83.4, 14}, + {60.7, 82.3, 14}, + {65.9, 81.1, 14}, + {64.7, 81, 14}, + {65.3, 80.2, 14}, + {69.1, 80.2, 14}, + {60.2, 80, 14}, + {67.6, 73.9, 14}, + {69.2, 73.6, 14}, + {70.3, 73.4, 14}, + {69.8, 72.6, 14}, + {69, 72.3, 14}, + {67.2, 70.7, 14}, + {69.2, 69.7, 14}, + {78.6, 47.9, 17}, + {78, 47.8, 17}, + {78.3, 47.3, 17}, + {78.7, 46.9, 17}, + {77.8, 46.7, 17}, + {76.7, 44.8, 17}, + {77.3, 44.8, 17}, + {76, 44.3, 17}, + {77, 44.3, 17}, + {77.4, 43.9, 17}, + {77, 43.4, 17}, + {79, 43.3, 17}, + {79.3, 42.9, 17}, + {79, 41.4, 17}, + {81.3, 41.4, 17}, + {76.4, 41.3, 17}, + {76.5, 40.6, 17}, + {61.4, 91.3, 14}, + {61.7, 90.4, 14}, + {65.3, 87.8, 14}, + {60.8, 87.5, 14}, + {59.6, 84.7, 14}, + {59.8, 83.3, 14}, + {65.9, 81.3, 14}, + {69.1, 80.3, 14}, + {67.7, 73.8, 14}, + {69.2, 73.7, 14}, + {69.8, 72.8, 14}, + {69, 72.4, 14}, + {77.4, 44.2, 17}, + {77.3, 43.9, 17}, + {77, 42.8, 17}, + }, + [3122] = { + {62, 96.3, 14}, + {63.1, 94.4, 14}, + {59.9, 89.6, 14}, + {66.7, 89, 14}, + {68.2, 88.5, 14}, + {59.9, 88, 14}, + {68.5, 87.1, 14}, + {64, 86.8, 14}, + {59.4, 83.9, 14}, + {68.4, 83.9, 14}, + {63.9, 83.8, 14}, + {60.3, 82.6, 14}, + {64.9, 82.3, 14}, + {69.2, 82.1, 14}, + {68.1, 80.5, 14}, + {67.2, 80.5, 14}, + {60.9, 78.8, 14}, + {61.4, 78.1, 14}, + {69.8, 74.8, 14}, + {68.6, 74.7, 14}, + {63.5, 74.5, 14}, + {64.6, 73.3, 14}, + {68.3, 71.8, 14}, + {67.1, 71.3, 14}, + {68.9, 71.2, 14}, + {70.2, 70.9, 14}, + {69.8, 70.4, 14}, + {67.7, 69.8, 14}, + {39.5, 51.9, 14}, + {41.2, 50.3, 14}, + {42.3, 49.1, 14}, + {40.3, 47.7, 14}, + {39.2, 47, 14}, + {41.6, 47, 14}, + {40.5, 46.6, 14}, + {43.3, 46.5, 14}, + {44.2, 45.4, 14}, + {40.5, 38.2, 14}, + {46.3, 37.6, 14}, + {55.3, 37.3, 14}, + {49.8, 37, 14}, + {48.9, 36.9, 14}, + {47.6, 36.8, 14}, + {45.8, 36.6, 14}, + {50.4, 35.4, 14}, + {40.6, 34.6, 14}, + {54.9, 34.5, 14}, + {42.3, 34.4, 14}, + {44.5, 34, 14}, + {44.2, 33.6, 14}, + {54.4, 32.5, 14}, + {55.3, 30.5, 14}, + {56.1, 29.2, 14}, + {57.6, 28.5, 14}, + {56.9, 26.8, 14}, + {58.2, 26.3, 14}, + {57, 24.5, 14}, + {56.5, 24, 14}, + {58, 23.1, 14}, + {49.4, 19.2, 14}, + {77.6, 47.3, 17}, + {78.2, 46.4, 17}, + {76.5, 43.8, 17}, + {80.1, 43.5, 17}, + {80.8, 43.3, 17}, + {76.5, 43, 17}, + {81, 42.5, 17}, + {78.6, 42.4, 17}, + {76.2, 40.9, 17}, + {81, 40.9, 17}, + {78.6, 40.8, 17}, + {69.8, 74.7, 14}, + {67.2, 71.2, 14}, + {68.9, 71.1, 14}, + }, + [3123] = { + {37.2, 54.9, 14}, + {36.1, 53.2, 14}, + {35.9, 48.9, 14}, + {38.4, 48.9, 14}, + {37.1, 47.3, 14}, + {37, 46.4, 14}, + {37.9, 40, 14}, + {36.6, 39.1, 14}, + {38.5, 37.4, 14}, + {36, 37, 14}, + {37.5, 36.1, 14}, + {36.7, 34.3, 14}, + {37.5, 32.4, 14}, + {36, 31.8, 14}, + {36.9, 29.7, 14}, + {36.3, 27.7, 14}, + {36.8, 27.2, 14}, + {36.8, 25.4, 14}, + {42.1, 21.3, 14}, + {39.2, 19.5, 14}, + {54.9, 19.3, 14}, + {39.2, 19.1, 14}, + {42.9, 18.5, 14}, + {54.4, 18, 14}, + {39.2, 17.3, 14}, + {40.4, 16.1, 14}, + {53.2, 16, 14}, + {55.9, 15.9, 14}, + {56.9, 15, 14}, + {51.8, 14.5, 14}, + {53.9, 13.5, 14}, + {55.2, 13.1, 14}, + {53, 12.4, 14}, + {55.8, 12, 14}, + {54, 11.7, 14}, + {52.1, 10.6, 14}, + {64.7, 25.8, 17}, + {64.1, 24.9, 17}, + {64, 22.6, 17}, + {64.4, 17.5, 17}, + {64.1, 16.4, 17}, + {64.4, 15, 17}, + {64, 13.7, 17}, + {64.5, 12.6, 17}, + {64.2, 11.5, 17}, + {64.4, 11.3, 17}, + {64.4, 10.4, 17}, + {39.3, 17.1, 14}, + {56.8, 14.9, 14}, + }, + [3124] = { + {44, 73.7, 14}, + {43.2, 73.6, 14}, + {45.2, 72.8, 14}, + {45.1, 71.5, 14}, + {41.3, 70.6, 14}, + {40.7, 70.5, 14}, + {40.1, 68.6, 14}, + {40.7, 68.5, 14}, + {41.4, 67.9, 14}, + {40.1, 67.8, 14}, + {40.8, 67.7, 14}, + {40.1, 66.9, 14}, + {40.7, 66.9, 14}, + {41.3, 66.8, 14}, + {40, 66, 14}, + {40.7, 65.9, 14}, + {41.9, 65.9, 14}, + {41.5, 65.9, 14}, + {41.5, 64.3, 14}, + {40.8, 64.1, 14}, + {47.2, 64, 14}, + {47.6, 64, 14}, + {40.1, 64, 14}, + {40.1, 63.2, 14}, + {39.5, 63.2, 14}, + {47.1, 63.1, 14}, + {41.4, 63.1, 14}, + {40.8, 63.1, 14}, + {39.5, 62.3, 14}, + {41.4, 62.2, 14}, + {40.8, 62.1, 14}, + {40.1, 62.1, 14}, + {47, 62.1, 14}, + {46.5, 61.3, 14}, + {47.1, 61.3, 14}, + {40.1, 61.3, 14}, + {41.4, 61.3, 14}, + {40.7, 61.2, 14}, + {39.4, 61.1, 14}, + {41.4, 60.3, 14}, + {45.8, 60.1, 14}, + {46.4, 60.1, 14}, + {46.8, 60, 14}, + {43.2, 59.5, 14}, + {43.9, 59.4, 14}, + {45.8, 59.4, 14}, + {45.2, 59.3, 14}, + {46.5, 59.3, 14}, + {42.6, 59.2, 14}, + {45.6, 58.7, 14}, + {42.8, 58.5, 14}, + {42, 58.4, 14}, + {43.3, 58.4, 14}, + {41.8, 57.3, 14}, + {43.1, 56.4, 14}, + {66.8, 33.9, 17}, + {66.5, 33.9, 17}, + {66.2, 32.9, 17}, + {66.2, 32.5, 17}, + {66.2, 32, 17}, + {66.1, 31.5, 17}, + {45.1, 71.3, 14}, + {40.7, 67.7, 14}, + {40.7, 66.8, 14}, + {41.8, 65.8, 14}, + {40.8, 65.8, 14}, + {40.8, 64, 14}, + {40, 63.4, 14}, + {41.4, 63.4, 14}, + {39.6, 63.1, 14}, + {40.8, 62.9, 14}, + {40.2, 62.1, 14}, + {41.4, 62.1, 14}, + {40.9, 62, 14}, + {40, 61.4, 14}, + {41.4, 61.4, 14}, + {41.4, 60.4, 14}, + {44, 59.5, 14}, + {46.4, 59.3, 14}, + }, + [3125] = { + {54.8, 80.3, 14}, + {53.3, 76.4, 14}, + {50.2, 75.4, 14}, + {50.6, 74.9, 14}, + {49.5, 74.7, 14}, + {53.3, 74.4, 14}, + {52.2, 73.7, 14}, + {52.1, 73.6, 14}, + {50.1, 73.5, 14}, + {51.9, 73.5, 14}, + {49.6, 72.9, 14}, + {50.8, 72.5, 14}, + {52.6, 72.5, 14}, + {58.2, 72, 14}, + {50.1, 71.9, 14}, + {57.7, 71.3, 14}, + {56.6, 70.9, 14}, + {58.3, 70.8, 14}, + {57.2, 70.8, 14}, + {50.9, 70.6, 14}, + {56.1, 70.5, 14}, + {55.6, 70.1, 14}, + {54.8, 70, 14}, + {56.1, 69.6, 14}, + {54.1, 69, 14}, + {54.4, 69, 14}, + {53.8, 68.1, 14}, + {55.5, 68, 14}, + {53.2, 67, 14}, + {54.6, 67, 14}, + {54.1, 65.1, 14}, + {54.4, 64.9, 14}, + {54.1, 64.1, 14}, + {54.7, 63.6, 14}, + {52.1, 62.8, 14}, + {54.5, 61.1, 14}, + {52.9, 60.6, 14}, + {54.6, 59.3, 14}, + {54.5, 56.7, 14}, + {54, 55.2, 14}, + {51.9, 54.7, 14}, + {51, 54.2, 14}, + {53.4, 53.5, 14}, + {53.1, 51.8, 14}, + {53.9, 50.8, 14}, + {58.8, 50.5, 14}, + {54.7, 50.1, 14}, + {57.8, 49.5, 14}, + {56, 49.1, 14}, + {53.9, 49, 14}, + {57, 48.3, 14}, + {59.7, 48, 14}, + {55.9, 47.5, 14}, + {57.7, 46.8, 14}, + {57.8, 45.9, 14}, + {59.6, 45.9, 14}, + {56.9, 45.7, 14}, + {55.2, 45.4, 14}, + {56.9, 45.3, 14}, + {59.1, 44.7, 14}, + {58.5, 44.6, 14}, + {59.8, 44.4, 14}, + {57.1, 44.3, 14}, + {55.8, 44.2, 14}, + {56.5, 43.3, 14}, + {59, 43.2, 14}, + {57.8, 42.9, 14}, + {57.2, 42.7, 14}, + {58.4, 42.3, 14}, + {59.7, 42, 14}, + {57.8, 41.5, 14}, + {54.4, 67, 14}, + {54.8, 59.4, 14}, + {59.7, 48.1, 14}, + {57.1, 44.2, 14}, + {56.5, 43.2, 14}, + {58.3, 42.3, 14}, + }, + [3126] = { + {41.1, 51.7, 14}, + {41.4, 50.7, 14}, + {42.5, 48.6, 14}, + {41.1, 48.4, 14}, + {43.3, 48.1, 14}, + {39.6, 47.8, 14}, + {45.6, 46.7, 14}, + {41, 46.6, 14}, + {37.9, 46.5, 14}, + {42.6, 45.9, 14}, + {44.6, 45.7, 14}, + {43.7, 45.5, 14}, + {40.1, 45.3, 14}, + {44.6, 44.5, 14}, + {37.7, 42.5, 14}, + {39.2, 42.1, 14}, + {41.4, 41.4, 14}, + {36.3, 41.2, 14}, + {38.3, 40.8, 14}, + {40.2, 39.4, 14}, + {40.7, 38.8, 14}, + {48.3, 38.6, 14}, + {35.7, 37.9, 14}, + {51, 37.7, 14}, + {37, 37.2, 14}, + {35.7, 36.9, 14}, + {47.6, 36.9, 14}, + {36.2, 36.9, 14}, + {48.9, 36.8, 14}, + {49.3, 36.6, 14}, + {54.8, 36.6, 14}, + {39, 35.7, 14}, + {47.6, 35.6, 14}, + {46.8, 35.5, 14}, + {42.5, 34.4, 14}, + {37.1, 34.4, 14}, + {38.3, 33.9, 14}, + {37.3, 33.8, 14}, + {54.4, 33.8, 14}, + {44.4, 33.6, 14}, + {43.4, 32.4, 14}, + {55.7, 31.7, 14}, + {45.5, 31.4, 14}, + {57.6, 30.9, 14}, + {48.6, 30.6, 14}, + {57.4, 29.5, 14}, + {58.5, 29.2, 14}, + {56.5, 29, 14}, + {41.9, 28.8, 14}, + {45.2, 28.2, 14}, + {57.2, 27.4, 14}, + {45.2, 26.3, 14}, + {58.4, 25.2, 14}, + {57.2, 24.6, 14}, + {58.4, 23.3, 14}, + {54.9, 23.1, 14}, + {56.1, 22.8, 14}, + {55, 22.1, 14}, + {50.6, 21.6, 14}, + {56.5, 21.4, 14}, + {50.5, 19.5, 14}, + {52.8, 19.4, 14}, + {51.1, 17.8, 14}, + {64.2, 18.6, 17}, + {63.9, 16.9, 17}, + {63.9, 16.3, 17}, + {64.2, 16.3, 17}, + {45.3, 26.5, 14}, + }, + [3127] = { + {35.9, 56.6, 14}, + {36.8, 55.7, 14}, + {36.8, 53.8, 14}, + {35.3, 53.6, 14}, + {36.3, 52.6, 14}, + {35, 50.8, 14}, + {37.5, 50.6, 14}, + {36.7, 50.5, 14}, + {37.9, 49.9, 14}, + {35.8, 49.8, 14}, + {37, 48.9, 14}, + {36.3, 47.2, 14}, + {35.7, 47, 14}, + {35.5, 33.1, 14}, + {37.1, 31, 14}, + {36.5, 30.2, 14}, + {36.2, 28.4, 14}, + {36.8, 27, 14}, + {36.6, 25.1, 14}, + {38.8, 22.6, 14}, + {39.4, 22.1, 14}, + {42.6, 21.1, 14}, + {42, 20.6, 14}, + {44.9, 20.5, 14}, + {38.1, 20.2, 14}, + {41.4, 20.1, 14}, + {42.2, 18.9, 14}, + {43.9, 18.7, 14}, + {39.8, 18.4, 14}, + {43.9, 18.4, 14}, + {56, 18.3, 14}, + {43.6, 18.2, 14}, + {39, 17.7, 14}, + {38.8, 17.7, 14}, + {50.4, 17.6, 14}, + {49.7, 17.5, 14}, + {42, 17, 14}, + {43.9, 17, 14}, + {41, 16.9, 14}, + {44.1, 16.7, 14}, + {53.5, 15.7, 14}, + {55.9, 15.3, 14}, + {55.7, 14.8, 14}, + {56.9, 14.6, 14}, + {53.6, 12.7, 14}, + {55.8, 12.1, 14}, + {53.3, 11.9, 14}, + {54.4, 11.8, 14}, + {64, 26.6, 17}, + {64.4, 26.2, 17}, + {63.7, 25.1, 17}, + {64.2, 24.5, 17}, + {63.5, 23.6, 17}, + {64.4, 23.4, 17}, + {63.9, 23.1, 17}, + {64.2, 21.7, 17}, + {63.9, 21.6, 17}, + {63.8, 14.4, 17}, + {64.6, 13.3, 17}, + {64.3, 12.9, 17}, + {64.1, 11.9, 17}, + {64.5, 11.2, 17}, + {64.4, 10.2, 17}, + {65.2, 7.6, 17}, + {57, 14.3, 14}, + {55.9, 12.1, 14}, + }, + [3158] = { + {42.6, 67.3, 14}, + }, + [3164] = { + {54.4, 42.2, 14}, + }, + [3165] = { + {53, 41, 14}, + }, + [3186] = { + {56.3, 73.4, 14}, + }, + [3225] = { + {36.9, 40.1, 14}, + {43.7, 15.5, 14}, + }, + [3226] = { + {36.4, 48.4, 14}, + {36.6, 29.3, 14}, + {51.4, 14.1, 14}, + {64.2, 22.4, 17}, + {64.3, 12.4, 17}, + }, + [3227] = { + {37.6, 56.1, 14}, + {43.7, 31.7, 14}, + {39.8, 19.9, 14}, + {38.8, 18.5, 14}, + {44, 17.6, 14}, + {64.9, 26.4, 17}, + }, + [3228] = { + {60.4, 21.8, 14}, + {56.9, 10.7, 14}, + }, + [3231] = { + {34.3, 52.7, 14}, + {34.5, 25.9, 14}, + {63.1, 24.6, 17}, + {63.3, 10.6, 17}, + }, + [3241] = { + {61.6, 32.7, 17}, + {43.9, 16.7, 17}, + {45.5, 16.6, 17}, + {44.4, 15.8, 17}, + {43.1, 15, 17}, + {44.5, 14.1, 17}, + {47, 13.7, 17}, + }, + [3243] = { + {55, 30.5, 17}, + {55.3, 30.3, 17}, + {51.7, 26.2, 17}, + {50.7, 20.6, 17}, + {49.6, 16.1, 17}, + {50.1, 15.8, 17}, + {54.9, 30.5, 17}, + }, + [3244] = { + {52.9, 33.6, 17}, + {50.4, 33.4, 17}, + {50.8, 33.3, 17}, + {53, 33, 17}, + {51.2, 32.9, 17}, + {53.7, 32.9, 17}, + {53.4, 32.6, 17}, + {53.3, 32.4, 17}, + {53, 32.3, 17}, + {54.6, 32.1, 17}, + {51, 31.8, 17}, + {53, 31.5, 17}, + {50.3, 31, 17}, + {49.1, 30.6, 17}, + {50.3, 30.6, 17}, + {53.3, 30.6, 17}, + {53.7, 30.5, 17}, + {53.1, 30.4, 17}, + {49.6, 30.3, 17}, + {49.4, 29.8, 17}, + {53.6, 29.5, 17}, + {49.1, 29.5, 17}, + {48.6, 29.1, 17}, + {53.3, 29.1, 17}, + {53.9, 29, 17}, + {48.2, 28.6, 17}, + {53.9, 28.5, 17}, + {52.8, 28.2, 17}, + {51.4, 28.1, 17}, + {54, 28, 17}, + {50.7, 27.8, 17}, + {47.8, 27.6, 17}, + {51, 27.6, 17}, + {51.7, 27.6, 17}, + {48.1, 27.1, 17}, + {52, 27.1, 17}, + {52.7, 27.1, 17}, + {53, 26.6, 17}, + {48.6, 26.5, 17}, + {52.7, 26.1, 17}, + {52.7, 25.6, 17}, + {48.5, 25.6, 17}, + {47.8, 25.6, 17}, + {55.5, 25.1, 17}, + {48.7, 25, 17}, + {48.7, 24.7, 17}, + {55.4, 24.3, 17}, + {49.4, 24.3, 17}, + {49, 24, 17}, + {55.9, 24, 17}, + {53, 23.7, 17}, + {48.8, 23.6, 17}, + {54, 23.6, 17}, + {48.5, 23.5, 17}, + {54.7, 23.4, 17}, + {54, 23.1, 17}, + {51.7, 23.1, 17}, + {51.3, 23, 17}, + {48.7, 23, 17}, + {49.3, 22.8, 17}, + {49.1, 22.8, 17}, + {52.9, 22.7, 17}, + {51.9, 22.4, 17}, + {53.4, 22.1, 17}, + {51.4, 22.1, 17}, + {54.1, 22, 17}, + {52.4, 21.7, 17}, + {52.8, 21.7, 17}, + {51.6, 21.6, 17}, + {52.2, 21, 17}, + {52.1, 20.3, 17}, + {51.8, 19.7, 17}, + {50.9, 19.5, 17}, + {51.4, 19.4, 17}, + {51, 18.7, 17}, + {51.7, 18.6, 17}, + {51.3, 18.1, 17}, + {51.1, 17.7, 17}, + {51.2, 17.3, 17}, + {51.2, 16.9, 17}, + {51, 15.6, 17}, + {50.7, 15.3, 17}, + {48.1, 15.2, 17}, + {47.3, 14.9, 17}, + {47.8, 14.6, 17}, + {48, 14.3, 17}, + {49.3, 14.3, 17}, + {50.7, 14.2, 17}, + {47.3, 14.1, 17}, + {50.4, 13.9, 17}, + {49.8, 13.8, 17}, + {47.6, 13.8, 17}, + {48.6, 13.7, 17}, + {53.1, 31.5, 17}, + {53.6, 30.5, 17}, + {51.4, 28, 17}, + {52.6, 27, 17}, + {53.3, 22.2, 17}, + }, + [3245] = { + {59.6, 53.4, 17}, + {55, 52.6, 17}, + {54.2, 52.4, 17}, + {56, 52.3, 17}, + {59.5, 52.3, 17}, + {57.1, 52.2, 17}, + {58.3, 51.8, 17}, + {58, 51.8, 17}, + {53.7, 51.8, 17}, + {54.4, 51.4, 17}, + {55.5, 51.2, 17}, + {55.4, 50.8, 17}, + {54.3, 50.7, 17}, + {54.6, 50.4, 17}, + {59, 50.1, 17}, + {49.7, 50.1, 17}, + {48.8, 49.8, 17}, + {54.7, 49.7, 17}, + {50.2, 49.7, 17}, + {55.1, 49.4, 17}, + {56.2, 49.4, 17}, + {59.3, 49.3, 17}, + {62, 49.2, 17}, + {53.6, 48.9, 17}, + {61, 48.9, 17}, + {49.4, 48.8, 17}, + {53.4, 48.8, 17}, + {53.8, 48.4, 17}, + {49.7, 48.4, 17}, + {49, 48.4, 17}, + {56, 48.3, 17}, + {55.9, 48.3, 17}, + {54.3, 48, 17}, + {61.3, 47.8, 17}, + {53.5, 47.8, 17}, + {55.3, 47.8, 17}, + {52.8, 47.7, 17}, + {49.7, 47.4, 17}, + {49.1, 47.4, 17}, + {55.6, 47.3, 17}, + {51.7, 47.3, 17}, + {50.5, 47.2, 17}, + {52.6, 47.1, 17}, + {49.8, 46.3, 17}, + {47.1, 44.9, 17}, + {47.6, 44.1, 17}, + {45.8, 43.3, 17}, + {60.9, 34.5, 17}, + {60.5, 33.9, 17}, + {59.5, 33.5, 17}, + {59.9, 33.2, 17}, + {61.1, 33, 17}, + {59.3, 32.5, 17}, + {60.7, 32, 17}, + {59.6, 31.6, 17}, + {61.2, 30.7, 17}, + {37.3, 28.4, 17}, + {38, 27.9, 17}, + {38, 26.6, 17}, + {38.3, 26.1, 17}, + {38.9, 25.1, 17}, + {39.9, 24.7, 17}, + {44.1, 17.2, 17}, + {45.5, 17.1, 17}, + {42.1, 16, 17}, + {45.4, 15.8, 17}, + {44.5, 15.5, 17}, + {45.3, 15.4, 17}, + {42.1, 15.3, 17}, + {44.2, 15.3, 17}, + {44.6, 14.8, 17}, + {43.3, 14.8, 17}, + {45.7, 14.6, 17}, + {44.6, 13.7, 17}, + {45, 13.6, 17}, + {45.2, 13.4, 17}, + {48, 13.2, 17}, + {61.4, 13.2, 17}, + {46.8, 13.2, 17}, + {46.7, 12.3, 17}, + {60.9, 11, 17}, + {60.8, 9.8, 17}, + {60, 8.5, 17}, + {58.3, 7.9, 17}, + {59.6, 7.8, 17}, + {59.1, 6.7, 17}, + {57.3, 6.4, 17}, + {56, 6.2, 17}, + {56, 5.3, 17}, + {51.7, 47.4, 17}, + {37.2, 27.4, 17}, + {38.8, 25, 17}, + {86, 92.5, 406}, + {84.7, 91.8, 406}, + {86, 90.5, 406}, + {86.5, 89.5, 406}, + {87.4, 87.7, 406}, + {89.2, 87.2, 406}, + }, + [3246] = { + {35.8, 63.2, 14}, + {35.1, 60.3, 14}, + {49.2, 38.9, 17}, + {48.2, 38.5, 17}, + {54.7, 38.5, 17}, + {49.8, 38.1, 17}, + {47.7, 38, 17}, + {48.1, 37.9, 17}, + {47, 37.7, 17}, + {53.3, 37.4, 17}, + {48.3, 37.1, 17}, + {48.8, 37, 17}, + {52.4, 37, 17}, + {50, 36.6, 17}, + {54.4, 36.4, 17}, + {44.9, 35.9, 17}, + {49.4, 35.9, 17}, + {63.9, 35.9, 17}, + {50.1, 35.9, 17}, + {64, 35.6, 17}, + {51.1, 35.6, 17}, + {52, 35.5, 17}, + {54.3, 35.5, 17}, + {54.7, 35.5, 17}, + {53, 35.2, 17}, + {44.6, 35.1, 17}, + {54.4, 35, 17}, + {51.1, 35, 17}, + {54.2, 34.8, 17}, + {50.9, 34.7, 17}, + {47.6, 34.6, 17}, + {43.9, 34.6, 17}, + {56.8, 34.5, 17}, + {44.9, 34.5, 17}, + {53, 34.4, 17}, + {46.7, 34.2, 17}, + {47.8, 34.1, 17}, + {44.7, 34, 17}, + {54.3, 33.5, 17}, + {46.7, 33.4, 17}, + {56.1, 33, 17}, + {47.7, 33, 17}, + {43.2, 33, 17}, + {49.1, 32.8, 17}, + {45.8, 32.8, 17}, + {46.7, 32.6, 17}, + {47.6, 32.5, 17}, + {44.9, 32.5, 17}, + {63.9, 32.1, 17}, + {48.8, 32, 17}, + {56, 31.9, 17}, + {58.3, 31.9, 17}, + {63.8, 31.9, 17}, + {43.8, 31.9, 17}, + {44.9, 31.8, 17}, + {48.7, 31.7, 17}, + {47.5, 31.6, 17}, + {57, 31.5, 17}, + {43.1, 31.4, 17}, + {57.7, 31.3, 17}, + {48, 31.2, 17}, + {57.3, 31.1, 17}, + {63.5, 31.1, 17}, + {56.6, 31.1, 17}, + {58, 31, 17}, + {46, 30.9, 17}, + {46.3, 30.8, 17}, + {57.6, 30.5, 17}, + {56.9, 30.5, 17}, + {56.4, 30.2, 17}, + {46.2, 30.1, 17}, + {63.9, 30.1, 17}, + {42.6, 29.9, 17}, + {57.7, 29.9, 17}, + {57.3, 29.8, 17}, + {47.5, 29.8, 17}, + {55.7, 29.6, 17}, + {63.6, 29.6, 17}, + {56.8, 29.5, 17}, + {57.3, 29.1, 17}, + {56.7, 29.1, 17}, + {58, 28.8, 17}, + {46.4, 28.6, 17}, + {56.3, 28.6, 17}, + {57.6, 28.6, 17}, + {63.6, 28.6, 17}, + {57.1, 28.4, 17}, + {56.6, 28, 17}, + {57.6, 27.7, 17}, + {57, 27.6, 17}, + {46.2, 27.3, 17}, + {57.2, 27, 17}, + {45.1, 26.7, 17}, + {56.9, 26.7, 17}, + {47.1, 26.6, 17}, + {58, 26.1, 17}, + {46.8, 26.1, 17}, + {44.2, 25.5, 17}, + {42.6, 25.1, 17}, + {44.8, 25.1, 17}, + {43.6, 24.8, 17}, + {46.7, 24.4, 17}, + {57.6, 23.4, 17}, + {60.2, 23, 17}, + {56.8, 22.7, 17}, + {55.8, 22.7, 17}, + {61.5, 22.5, 17}, + {60.9, 22.4, 17}, + {50.1, 21.8, 17}, + {47, 21.7, 17}, + {47.9, 21.6, 17}, + {48.8, 21.5, 17}, + {47.6, 21.3, 17}, + {49.6, 21.1, 17}, + {56.6, 21.1, 17}, + {52.7, 21.1, 17}, + {57.4, 21.1, 17}, + {57.1, 20.8, 17}, + {46.7, 20.8, 17}, + {58.3, 20.8, 17}, + {49.4, 20.7, 17}, + {59.2, 20.7, 17}, + {48.7, 20.6, 17}, + {48.1, 20.6, 17}, + {49.8, 20.4, 17}, + {47.8, 20.2, 17}, + {49.1, 20.2, 17}, + {48.4, 20.1, 17}, + {48.8, 19.7, 17}, + {48.2, 19.6, 17}, + {46.2, 19.4, 17}, + {46.7, 19.3, 17}, + {49.5, 19.3, 17}, + {48.4, 19.2, 17}, + {53, 18.8, 17}, + {48.4, 18.2, 17}, + {49.5, 18.2, 17}, + {49.1, 17.6, 17}, + {49.9, 17.5, 17}, + {48.4, 17.3, 17}, + {52.6, 16.2, 17}, + {48.6, 15.8, 17}, + {52.3, 15.2, 17}, + {51.9, 14.8, 17}, + {51.2, 13.3, 17}, + {50.3, 13.2, 17}, + {50.7, 12.6, 17}, + {53, 34.3, 17}, + {95.6, 98.2, 718}, + {4.3, 85.1, 718}, + {84.1, 84.8, 718}, + {96.1, 84.3, 718}, + {52.2, 62.2, 718}, + {4.3, 59.5, 718}, + {36.8, 54.2, 718}, + {55, 54, 718}, + {0.7, 50.8, 718}, + {36.7, 40, 718}, + {54.8, 34.2, 718}, + {79.3, 30.9, 718}, + {20.6, 30.3, 718}, + {37, 27.4, 718}, + {52.6, 25.3, 718}, + {3.9, 24.8, 718}, + {73.8, 16.5, 718}, + {3.8, 13.2, 718}, + {72.6, 11.1, 718}, + {50.7, 8.9, 718}, + {59.3, 0.9, 718}, + }, + [3247] = { + {48.5, 56.7, 17}, + {49.4, 55, 17}, + {49.8, 54, 17}, + {50.8, 53.2, 17}, + {44.1, 53.2, 17}, + {45.5, 52.8, 17}, + {46.8, 50.9, 17}, + {47.6, 50.9, 17}, + {46.7, 49.1, 17}, + {45.8, 48.8, 17}, + {44.5, 47.2, 17}, + {45.8, 46.6, 17}, + {42.8, 46, 17}, + {44, 45.4, 17}, + }, + [3249] = { + {45.5, 81.2, 17}, + {46.7, 79.2, 17}, + {43.8, 78.8, 17}, + {43.8, 78.6, 17}, + {44.7, 78.2, 17}, + {43.8, 76.9, 17}, + {49.1, 76.3, 17}, + {48.6, 76.2, 17}, + {46.9, 76.2, 17}, + {44.5, 75.9, 17}, + {44.8, 75.4, 17}, + {47.5, 75.2, 17}, + {43.7, 75.1, 17}, + {47.1, 74.4, 17}, + {44.9, 73.9, 17}, + {47, 73.7, 17}, + }, + [3250] = { + {26.9, 29.7, 15}, + {43.8, 73, 17}, + {44.9, 72.6, 17}, + {42.5, 72.1, 17}, + {43.7, 72, 17}, + {45.2, 72, 17}, + {44.7, 71.6, 17}, + {42.9, 71.4, 17}, + {47.3, 71.3, 17}, + {43.7, 71.1, 17}, + {42.5, 71.1, 17}, + {47.3, 70.5, 17}, + {48.1, 70.4, 17}, + {48.7, 70.2, 17}, + {45.2, 70.2, 17}, + {43.9, 70, 17}, + {47.1, 70, 17}, + {42.5, 69.9, 17}, + {44.2, 69.5, 17}, + {48.1, 69.5, 17}, + {45.4, 69.5, 17}, + {45.4, 69.4, 17}, + {42.1, 69.4, 17}, + {49.4, 69.3, 17}, + {43.2, 69.2, 17}, + {49.1, 69.1, 17}, + {71.9, 81.8, 215}, + }, + [3252] = { + {26.5, 30.9, 15}, + {43.8, 72.7, 17}, + {45.6, 72.6, 17}, + {42.7, 72.6, 17}, + {45.4, 72.6, 17}, + {43.1, 71.9, 17}, + {44.6, 71.9, 17}, + {42.1, 71.7, 17}, + {48.2, 71.6, 17}, + {42.8, 71.1, 17}, + {43.1, 71, 17}, + {48.5, 70.9, 17}, + {47.6, 70.9, 17}, + {48.5, 70.8, 17}, + {42.8, 70.6, 17}, + {43.5, 70.5, 17}, + {42.2, 70.5, 17}, + {42.6, 70.1, 17}, + {47.7, 70.1, 17}, + {48.4, 70, 17}, + {49.1, 70, 17}, + {49.2, 70, 17}, + {44.9, 69.7, 17}, + {48.8, 69.5, 17}, + {43.5, 69.4, 17}, + {42.8, 69.4, 17}, + {44, 69.3, 17}, + {41.9, 69, 17}, + {45.1, 68.9, 17}, + {72, 84, 215}, + {71.4, 81.1, 215}, + {43.1, 70.9, 17}, + }, + [3254] = { + {52.9, 33, 17}, + {47.9, 26, 17}, + {48.4, 25.1, 17}, + {48.8, 23.8, 17}, + {54.6, 23.2, 17}, + {54.7, 23.1, 17}, + {51.3, 22.6, 17}, + {51.8, 18.7, 17}, + {47.7, 14.5, 17}, + }, + [3255] = { + {60.3, 47.1, 17}, + {61.7, 46.9, 17}, + {59.6, 46.8, 17}, + {53, 46.8, 17}, + {58.9, 46.8, 17}, + {60.9, 46.5, 17}, + {51.9, 46.5, 17}, + {59.3, 46.4, 17}, + {60, 46.3, 17}, + {58.7, 46.3, 17}, + {53.5, 46.2, 17}, + {54.7, 46.1, 17}, + {58.9, 45.9, 17}, + {58.2, 45.9, 17}, + {59.6, 45.8, 17}, + {58.6, 45.4, 17}, + {59.3, 45.4, 17}, + {59.9, 45.3, 17}, + {50.1, 45.3, 17}, + {59.6, 44.9, 17}, + {60.4, 44.8, 17}, + {60, 44.4, 17}, + {55.7, 44.4, 17}, + {53.6, 44.4, 17}, + {55, 44.4, 17}, + {59.3, 44.3, 17}, + {51.7, 44.2, 17}, + {58.3, 44.1, 17}, + {59.8, 43.9, 17}, + {52.1, 43.9, 17}, + {60.5, 43.7, 17}, + {58.8, 43.4, 17}, + {52.9, 43.4, 17}, + {51.1, 43.3, 17}, + {52.8, 43, 17}, + {60, 42.9, 17}, + {59, 42.7, 17}, + {57.6, 42.3, 17}, + {48.8, 41.7, 17}, + {44.2, 41.5, 17}, + {61.5, 41.3, 17}, + {44.4, 41.2, 17}, + {54.4, 41, 17}, + {43.8, 40.9, 17}, + {54, 40.9, 17}, + {54.2, 40.8, 17}, + {43.5, 40.7, 17}, + {44.5, 40.7, 17}, + {57.3, 40.5, 17}, + {55.9, 40.1, 17}, + {50.8, 40, 17}, + {58.8, 39.9, 17}, + {57.3, 39.9, 17}, + {54.8, 39.6, 17}, + {49.7, 39.4, 17}, + {56.2, 39.2, 17}, + {50, 39, 17}, + {56, 38.8, 17}, + {42.6, 38.5, 17}, + {57.6, 38.4, 17}, + {42.8, 38.1, 17}, + {57.9, 37.1, 17}, + {57.7, 36.9, 17}, + {57.1, 36.4, 17}, + {57.4, 36.2, 17}, + {53.7, 35.9, 17}, + {52.2, 35.9, 17}, + {54.6, 35.9, 17}, + {57.2, 35.7, 17}, + {60, 35.6, 17}, + {56.4, 35, 17}, + {44.2, 34.6, 17}, + {58.8, 34.5, 17}, + {53.3, 34.4, 17}, + {58.5, 34.4, 17}, + {41.9, 34.2, 17}, + {43.3, 34.1, 17}, + {42.2, 34, 17}, + {43.3, 33.5, 17}, + {58.6, 33.5, 17}, + {56, 33.5, 17}, + {44.7, 33.4, 17}, + {41.8, 32.9, 17}, + {39.8, 31.5, 17}, + {42.5, 30.5, 17}, + {45.6, 30.3, 17}, + {60.7, 29.6, 17}, + {60.1, 29.6, 17}, + {41.8, 29.6, 17}, + {40.5, 29, 17}, + {60.3, 28.5, 17}, + {44.3, 26.1, 17}, + {62.3, 25.9, 17}, + {42.9, 25.6, 17}, + {41.9, 25.6, 17}, + {41.4, 25.1, 17}, + {40.3, 25, 17}, + {40.2, 24.1, 17}, + {41.2, 23.6, 17}, + {60.8, 23.3, 17}, + {61.9, 23.1, 17}, + {41.1, 22.6, 17}, + {60.5, 22.1, 17}, + {55.4, 21.2, 17}, + {41.9, 20.6, 17}, + {58, 20.5, 17}, + {40.7, 20.2, 17}, + {41.5, 20.1, 17}, + {43.8, 19.8, 17}, + {44.1, 19.8, 17}, + {43.5, 19.7, 17}, + {53.1, 19.4, 17}, + {44.4, 18.8, 17}, + {43.8, 18.7, 17}, + {44.2, 18.3, 17}, + {60.2, 17.9, 17}, + {58.9, 17.7, 17}, + {59.5, 17.7, 17}, + {59.9, 17.4, 17}, + {57.6, 17.4, 17}, + {53.3, 17, 17}, + {56.3, 16.8, 17}, + {60.6, 16.7, 17}, + {54.3, 16.2, 17}, + {54.7, 16.2, 17}, + {59.2, 16, 17}, + {54.9, 15.5, 17}, + {56.6, 15.4, 17}, + {59.5, 15.1, 17}, + {60.4, 14.9, 17}, + {57.6, 14.8, 17}, + {53.3, 14.7, 17}, + {58.4, 14.3, 17}, + {58.9, 14.2, 17}, + {60.2, 14.1, 17}, + {59.7, 14.1, 17}, + {55.9, 14.1, 17}, + {55, 13.8, 17}, + {58, 13.8, 17}, + {58.6, 13.7, 17}, + {59.4, 13.7, 17}, + {58.2, 13.3, 17}, + {53, 13.3, 17}, + {52.6, 13.1, 17}, + {50.3, 13.1, 17}, + {59.2, 12.9, 17}, + {58, 12.8, 17}, + {54.4, 12.8, 17}, + {53, 12.8, 17}, + {57.3, 12.7, 17}, + {58.3, 12.3, 17}, + {57.7, 12.3, 17}, + {56.9, 11.9, 17}, + {53.3, 11.9, 17}, + {60.2, 11.9, 17}, + {58, 11.5, 17}, + {56.3, 11.3, 17}, + {54.7, 11.3, 17}, + {58.9, 10.7, 17}, + {59.5, 10.6, 17}, + {55, 10.3, 17}, + {57.2, 10.2, 17}, + {54.1, 10.1, 17}, + {56.4, 9.9, 17}, + {55.2, 9.4, 17}, + {54.7, 9.2, 17}, + {54.6, 8.4, 17}, + {55, 7.8, 17}, + {63.6, 6, 17}, + {56, 33.6, 17}, + {55, 13.9, 17}, + {1.9, 40.9, 718}, + }, + [3256] = { + {57, 54.5, 17}, + {57.1, 54.4, 17}, + {57.6, 54.1, 17}, + {57.7, 54.1, 17}, + {58, 54, 17}, + {57.9, 53.9, 17}, + {58, 53.9, 17}, + {57.4, 53.8, 17}, + {57.3, 53.7, 17}, + {57.1, 53.4, 17}, + {57, 53, 17}, + {57.1, 52.9, 17}, + {57.1, 52, 17}, + {55.8, 51.9, 17}, + {57.1, 51.9, 17}, + {50.7, 51.8, 17}, + {53, 51.2, 17}, + {49.4, 51.2, 17}, + {55.7, 50.8, 17}, + {51.5, 50.8, 17}, + {51.8, 50.7, 17}, + {60.3, 50.4, 17}, + {48.1, 50.3, 17}, + {58.3, 50, 17}, + {60.9, 50, 17}, + {60.9, 49.9, 17}, + {49.3, 49.6, 17}, + {54.3, 49.4, 17}, + {51.8, 49.4, 17}, + {56.7, 49, 17}, + {48.8, 48.7, 17}, + {53, 48.3, 17}, + {51.9, 48, 17}, + {56.7, 47.4, 17}, + {48.3, 47.4, 17}, + {48.2, 46.8, 17}, + {50.2, 46.7, 17}, + {52.4, 46.6, 17}, + {52.1, 46.6, 17}, + {52.6, 46.3, 17}, + {50.4, 46.1, 17}, + {52.7, 46.1, 17}, + {52.6, 46, 17}, + {47.7, 45.8, 17}, + {50.1, 45.7, 17}, + {45.1, 44.4, 17}, + {44.4, 44.3, 17}, + {46.3, 44.1, 17}, + {46, 43.8, 17}, + {44.4, 43.8, 17}, + {43.9, 43.4, 17}, + {47.1, 43.4, 17}, + {47.8, 43.2, 17}, + {44.5, 43, 17}, + {61.2, 34, 17}, + {60.8, 33.4, 17}, + {60.3, 32.2, 17}, + {61, 32, 17}, + {59.9, 32, 17}, + {61.2, 31.1, 17}, + {60.3, 30.5, 17}, + {36.2, 28.8, 17}, + {37.6, 27.5, 17}, + {37.3, 26.8, 17}, + {36.7, 26.4, 17}, + {39.3, 25.6, 17}, + {44.1, 16.6, 17}, + {43.8, 16.2, 17}, + {42.5, 15.6, 17}, + {46.1, 15.3, 17}, + {46.4, 15, 17}, + {43.5, 15, 17}, + {45.5, 14.7, 17}, + {44.1, 14.4, 17}, + {44.7, 14.3, 17}, + {47.7, 12.9, 17}, + {60.8, 12.7, 17}, + {48.2, 11.3, 17}, + {59.9, 8.8, 17}, + {60.4, 8.8, 17}, + {57.9, 8.2, 17}, + {59.2, 7.4, 17}, + {58.3, 7.4, 17}, + {59.9, 7.4, 17}, + {59, 6.3, 17}, + {59.3, 6.2, 17}, + {57.7, 6.2, 17}, + {55.7, 5.7, 17}, + {56.6, 4.3, 17}, + {87.9, 99.8, 406}, + {60, 32, 17}, + {61.3, 31, 17}, + {59.9, 7.5, 17}, + {83, 94.1, 406}, + {85.3, 91.9, 406}, + {84.8, 90.7, 406}, + {83.8, 90.1, 406}, + {88.2, 88.8, 406}, + }, + [3281] = { + {40.5, 66.8, 14}, + {66.4, 32, 17}, + }, + [3313] = { + {45.3, 8.2, 14}, + {48.1, 80.5, 1637}, + }, + [3322] = { + {52.1, 62.1, 1637}, + }, + [3350] = { + {46.1, 40.9, 1637}, + }, + [3409] = { + {81.2, 18.7, 1637}, + }, + [3410] = { + {78.1, 38.5, 1637}, + }, + [3415] = { + {50, 31.8, 17}, + {50.7, 31.5, 17}, + {54.3, 31.4, 17}, + {53.9, 31.1, 17}, + {54.9, 31.1, 17}, + {54.7, 30.8, 17}, + {55.2, 30.7, 17}, + {54.8, 30.5, 17}, + {55.6, 30.4, 17}, + {54.5, 30.4, 17}, + {55, 30.1, 17}, + {54.8, 30, 17}, + {54.3, 30, 17}, + {54.9, 29.6, 17}, + {54.3, 29.4, 17}, + {55, 29, 17}, + {49.1, 27.6, 17}, + {49.5, 27.6, 17}, + {53.6, 27.5, 17}, + {53.4, 27.2, 17}, + {51.3, 27.1, 17}, + {49.3, 26.9, 17}, + {51.4, 26.2, 17}, + {52, 26.1, 17}, + {51.8, 26, 17}, + {51.7, 25.7, 17}, + {51.2, 24, 17}, + {50, 23.5, 17}, + {50.3, 23.5, 17}, + {50.2, 23.4, 17}, + {50.8, 23.2, 17}, + {50.2, 23, 17}, + {51.1, 21.3, 17}, + {51.1, 21.1, 17}, + {51.2, 20.7, 17}, + {51.6, 20.7, 17}, + {51, 20.4, 17}, + {51.5, 20.2, 17}, + {55.9, 16.7, 17}, + {55.8, 16.6, 17}, + {50.2, 16.3, 17}, + {50.4, 15.6, 17}, + {49.6, 15.6, 17}, + {50, 15.4, 17}, + {49.8, 15.3, 17}, + {50.4, 15.2, 17}, + {49.7, 15.1, 17}, + {56, 16.7, 17}, + {95, 12.3, 718}, + }, + [3416] = { + {58.8, 35.7, 17}, + {58.9, 35.4, 17}, + {59.9, 34.8, 17}, + {62.3, 33.4, 17}, + {61.9, 33.4, 17}, + {61.7, 33.1, 17}, + {62.2, 33, 17}, + {62.1, 32.5, 17}, + {61.7, 32.4, 17}, + {61.9, 32.3, 17}, + }, + [3424] = { + {45.5, 67, 17}, + {44.8, 63.1, 17}, + {48.8, 62.3, 17}, + {46.7, 61.5, 17}, + {50, 60.5, 17}, + {48.1, 60, 17}, + }, + [3425] = { + {59.2, 41, 17}, + {59.6, 40.9, 17}, + {59.3, 40.8, 17}, + {58.6, 40.8, 17}, + {43.6, 39.4, 17}, + {43.5, 39.3, 17}, + {57.7, 39.3, 17}, + {43.6, 39.1, 17}, + {43.7, 37.9, 17}, + {43.8, 37.6, 17}, + {59, 36.5, 17}, + {43.6, 35.5, 17}, + {43.8, 35.4, 17}, + {43.4, 35.3, 17}, + {43.6, 35.2, 17}, + {42.5, 35.1, 17}, + {42.8, 35, 17}, + {42.7, 34.6, 17}, + {42.4, 34.6, 17}, + {59.7, 34.4, 17}, + {42.9, 33.8, 17}, + {43.2, 33.7, 17}, + {42.9, 33.4, 17}, + {42.8, 33.4, 17}, + {43.2, 33.3, 17}, + {42.7, 33.2, 17}, + {43, 33.1, 17}, + {42.6, 33, 17}, + {43.9, 32.9, 17}, + {44.2, 32.8, 17}, + {42.4, 32.8, 17}, + {42.7, 32.7, 17}, + {43.8, 32.6, 17}, + {42.2, 32.4, 17}, + {44.5, 31.9, 17}, + {44.2, 31.6, 17}, + {44.2, 31.2, 17}, + {63.2, 29.5, 17}, + {42.6, 28.9, 17}, + {62.8, 28.7, 17}, + {41.6, 28.7, 17}, + {41.4, 28.7, 17}, + {41.3, 28.4, 17}, + {63.2, 28.1, 17}, + {63.1, 27.5, 17}, + {62.6, 27.5, 17}, + {41, 27.2, 17}, + {41.2, 27, 17}, + {41, 26.8, 17}, + {41, 26.6, 17}, + {40.8, 26.3, 17}, + {41.3, 24.3, 17}, + {41.5, 24.2, 17}, + {42, 23.5, 17}, + {41.7, 23.3, 17}, + {40.8, 22.9, 17}, + {40.6, 22.5, 17}, + {40.9, 21.8, 17}, + {41, 21.5, 17}, + {41, 21.2, 17}, + {41, 21, 17}, + {40.7, 21, 17}, + {40.9, 20.8, 17}, + {41.1, 20.8, 17}, + {40.3, 20.7, 17}, + {40.3, 20.5, 17}, + {40.4, 20.2, 17}, + {55.6, 16.5, 17}, + {55.6, 16.3, 17}, + {55.8, 16.2, 17}, + {53.3, 16.1, 17}, + {55.5, 16, 17}, + {55.7, 15.9, 17}, + {53.5, 15.9, 17}, + {53.7, 15.7, 17}, + {54, 15.6, 17}, + {53.2, 15.5, 17}, + {53.5, 15.4, 17}, + {53.8, 15.3, 17}, + {54, 15.2, 17}, + {55, 15.2, 17}, + {53.3, 15.2, 17}, + {54.7, 15.1, 17}, + {54.2, 15.1, 17}, + {55.6, 15.1, 17}, + {53.8, 15, 17}, + {55, 14.9, 17}, + {55.5, 14.8, 17}, + {54.8, 14.8, 17}, + {55.8, 14.8, 17}, + {55.6, 14.6, 17}, + {54.6, 14.6, 17}, + {54.5, 14.5, 17}, + {53.9, 14.1, 17}, + {53.7, 13.9, 17}, + {53.7, 13.5, 17}, + {54, 13.5, 17}, + {60.6, 7.9, 17}, + {60.2, 7.9, 17}, + {63.3, 7.3, 17}, + {63.7, 6.3, 17}, + {63.5, 5.9, 17}, + {63.5, 5.3, 17}, + {43.6, 35.1, 17}, + {40.9, 26.6, 17}, + {55.8, 16.1, 17}, + {54, 15.5, 17}, + {54.7, 14.6, 17}, + }, + [3461] = { + {55.6, 43.7, 17}, + {55.9, 43.4, 17}, + {55.7, 43.3, 17}, + {55.1, 43, 17}, + {55.7, 42.9, 17}, + {55.3, 42.9, 17}, + {56, 42.8, 17}, + {56.1, 42.5, 17}, + {55.3, 42.4, 17}, + {55.6, 42.4, 17}, + {55, 42.3, 17}, + {55.9, 42, 17}, + {55.4, 41.9, 17}, + {55.6, 41.5, 17}, + {48.1, 40.5, 17}, + {46.5, 40.5, 17}, + {45.9, 40.4, 17}, + {46.9, 40.3, 17}, + {47.7, 40.2, 17}, + {48, 40, 17}, + {46.4, 40, 17}, + {47.4, 39.9, 17}, + {46.8, 39.9, 17}, + {48.1, 39.9, 17}, + {47.1, 39.9, 17}, + {46.2, 39.8, 17}, + {46.6, 39.8, 17}, + {47, 39.6, 17}, + {46.8, 39.5, 17}, + {47.6, 39.5, 17}, + {45.9, 39.4, 17}, + {46.4, 39.3, 17}, + {46.2, 39.3, 17}, + {45.8, 39.1, 17}, + {46.5, 38.9, 17}, + {46.1, 38.8, 17}, + {46.2, 38.3, 17}, + {55.6, 43.6, 17}, + {55.3, 42.8, 17}, + {56.1, 42.4, 17}, + {55, 42.2, 17}, + {56, 42, 17}, + {55.3, 41.9, 17}, + {48.1, 40.6, 17}, + {47.4, 40, 17}, + {45.7, 39, 17}, + }, + [3472] = { + {43.2, 80.8, 17}, + {47.4, 79.9, 17}, + {44.8, 78.9, 17}, + {44.8, 74.9, 17}, + {43.2, 80.9, 17}, + }, + [3481] = { + {51.7, 29.9, 17}, + }, + [3488] = { + {51.1, 29.1, 17}, + }, + [3498] = { + {61.9, 38.8, 17}, + }, + [3541] = { + {49.1, 55.1, 267}, + }, + [3566] = { + {37.5, 57.5, 17}, + {35.4, 43, 17}, + {34.7, 42.9, 17}, + {36, 42.4, 17}, + {36.9, 40.5, 17}, + {34.6, 40.4, 17}, + {37.1, 40.3, 17}, + {35.9, 39.9, 17}, + {37.3, 39.8, 17}, + {34.6, 37.6, 17}, + {35, 37.5, 17}, + {34.3, 37.4, 17}, + {33.7, 37.2, 17}, + {34.5, 37, 17}, + {33.3, 36.4, 17}, + {32.7, 36, 17}, + {32.8, 34.2, 17}, + {31.6, 34, 17}, + {32.3, 34, 17}, + {32, 34, 17}, + {32.9, 33.6, 17}, + {32.4, 33.5, 17}, + {32.8, 32.6, 17}, + {32.4, 32.3, 17}, + {62.8, 58.4, 215}, + {56.3, 34.4, 215}, + {55.3, 34.2, 215}, + {45, 33.4, 215}, + {55, 32.5, 215}, + {58.6, 29.8, 215}, + {57.2, 29.6, 215}, + {47.8, 28.8, 215}, + {59.9, 28.6, 215}, + {51.2, 28.4, 215}, + {46.3, 27.8, 215}, + {46.1, 27.5, 215}, + {56.4, 26.3, 215}, + {44.2, 26.1, 215}, + {52.5, 25.7, 215}, + {48.3, 25.7, 215}, + {53.3, 25.5, 215}, + {51.9, 25.2, 215}, + {47.3, 25.1, 215}, + {54.5, 24.8, 215}, + {61.6, 24.8, 215}, + {57, 24.7, 215}, + {55.7, 24.7, 215}, + {62.1, 24.4, 215}, + {42.8, 24.4, 215}, + {51, 24.3, 215}, + {52.8, 23.9, 215}, + {55.9, 23.8, 215}, + {59.7, 23.6, 215}, + {62.3, 23.5, 215}, + {55.3, 23.1, 215}, + {56.3, 23, 215}, + {50.7, 22.5, 215}, + {55, 22.4, 215}, + {52.5, 22.3, 215}, + {56.3, 21.9, 215}, + {53.4, 21.7, 215}, + {53.8, 21.7, 215}, + {55.8, 21.3, 215}, + {53.7, 20.6, 215}, + {51.6, 20.4, 215}, + {48.5, 20.3, 215}, + {46.7, 19.9, 215}, + {42.2, 19.8, 215}, + {54.3, 19.5, 215}, + {42.7, 19.2, 215}, + {57.1, 19.2, 215}, + {53.9, 19, 215}, + {32.3, 19, 215}, + {44.2, 19, 215}, + {57.8, 18.9, 215}, + {56.4, 18.7, 215}, + {43.3, 18.6, 215}, + {48.7, 18.6, 215}, + {55.3, 18.4, 215}, + {43.3, 18.4, 215}, + {49.8, 18.1, 215}, + {37.7, 18, 215}, + {56.9, 17.9, 215}, + {52, 17.6, 215}, + {46.3, 17.5, 215}, + {43.4, 17.4, 215}, + {42, 17.3, 215}, + {37.4, 16.9, 215}, + {48, 16.9, 215}, + {54.4, 16.9, 215}, + {45, 16.8, 215}, + {35.4, 16.2, 215}, + {53.4, 16, 215}, + {44.7, 15.8, 215}, + {50.6, 15.8, 215}, + {44.1, 15.7, 215}, + {38.1, 15.6, 215}, + {47.9, 15.3, 215}, + {44.6, 15.1, 215}, + {44.7, 13.9, 215}, + {48.5, 13.7, 215}, + {45.2, 13.7, 215}, + {47.6, 13, 215}, + {50.3, 13, 215}, + {48.9, 12.8, 215}, + {44.4, 12.4, 215}, + {53.4, 12.4, 215}, + {51.1, 12.1, 215}, + {45.4, 12, 215}, + {52.5, 12, 215}, + {51.9, 12, 215}, + {37.6, 12, 215}, + {46.5, 11.8, 215}, + {48.9, 11.5, 215}, + {43.8, 11.3, 215}, + {53.7, 11.3, 215}, + {44.3, 11.2, 215}, + {42.6, 11.1, 215}, + {39.8, 11.1, 215}, + {52.8, 11, 215}, + {44.2, 11, 215}, + {49.1, 10.1, 215}, + {41.6, 9.8, 215}, + {38.8, 9.2, 215}, + {53.4, 9.2, 215}, + {48.8, 8.9, 215}, + {52.8, 8.7, 215}, + {44.5, 8.7, 215}, + {47.4, 8.6, 215}, + {42.5, 7.3, 215}, + {85.5, 45.9, 405}, + {89, 42.7, 405}, + {64.2, 37, 1638}, + {53.7, 11.2, 215}, + {72.1, 99.9, 406}, + {67.9, 98.9, 406}, + }, + [3581] = { + {49.8, 69.8, 1519}, + {32.4, 62.5, 1519}, + {66.9, 57.3, 1519}, + {35.5, 45.4, 1519}, + {66.4, 31.8, 1519}, + {49.9, 22.2, 1519}, + {71.1, 68, 1519}, + }, + [3587] = { + {59.5, 40.9, 141}, + }, + [3589] = { + {59.3, 41.1, 141}, + }, + [3608] = { + {55.5, 57.1, 141}, + }, + [3610] = { + {55.9, 59.2, 141}, + }, + [3625] = { + {49.9, 82.1, 2597}, + }, + [3630] = { + {46.1, 35.3, 17}, + {46.1, 34.6, 17}, + {46.4, 34.2, 17}, + {45.9, 33.5, 17}, + {46.8, 34.5, 17}, + {45.7, 34, 17}, + {45.9, 33, 17}, + {26.5, 73.6, 718}, + {37.6, 60.4, 718}, + {19.1, 51.8, 718}, + {21.8, 34.3, 718}, + }, + [3631] = { + {48.1, 34.5, 17}, + {48.2, 34.4, 17}, + {49.1, 33.9, 17}, + {47.4, 33.6, 17}, + {49.4, 33.2, 17}, + {47.2, 32.8, 17}, + {48.2, 32.1, 17}, + {48.5, 34.5, 17}, + {48.5, 34, 17}, + {47.6, 32.6, 17}, + {47.4, 32.6, 17}, + {48.7, 32.1, 17}, + {68, 59.4, 718}, + {62.6, 57.9, 718}, + {68.1, 50.9, 718}, + {84.3, 36.6, 718}, + {52.2, 27.3, 718}, + {48.7, 26.2, 718}, + {71, 17.4, 718}, + }, + [3632] = { + {45.9, 35.6, 17}, + {46.2, 35.3, 17}, + {46.3, 35.1, 17}, + {46.2, 34.8, 17}, + {46.6, 34.8, 17}, + {46.2, 34.3, 17}, + {46.6, 34.2, 17}, + {45.8, 34.1, 17}, + {45.9, 33.5, 17}, + {45.9, 33, 17}, + {46.3, 32.5, 17}, + {46.1, 32.5, 17}, + {22.3, 79.7, 718}, + {27.2, 73.7, 718}, + {29.3, 70, 718}, + {27.5, 65.5, 718}, + {34.6, 65.1, 718}, + {27.4, 56, 718}, + {34.2, 55.3, 718}, + {20.2, 52.8, 718}, + {22.5, 43.3, 718}, + {21.9, 34.5, 718}, + {29.7, 25.5, 718}, + {25.5, 25.1, 718}, + }, + [3633] = { + {48.3, 34.3, 17}, + {47.5, 34.1, 17}, + {49, 34, 17}, + {47.4, 33.4, 17}, + {49.2, 33.3, 17}, + {47.6, 32.8, 17}, + {47.4, 32.8, 17}, + {49.2, 32.7, 17}, + {47.9, 32.6, 17}, + {48.8, 32.6, 17}, + {47.6, 32.4, 17}, + {48.4, 32.3, 17}, + {48, 32.1, 17}, + {48.8, 32, 17}, + {65.4, 55.8, 718}, + {51.1, 53.8, 718}, + {76.4, 51, 718}, + {49, 40.6, 718}, + {80.2, 39, 718}, + {52.7, 30.6, 718}, + {48.4, 29.8, 718}, + {80.4, 28.6, 718}, + {57.7, 26.9, 718}, + {73.3, 25.8, 718}, + {52.6, 22.6, 718}, + {67.3, 21, 718}, + {60.1, 18.4, 718}, + {74.2, 15.6, 718}, + }, + [3634] = { + {46.5, 35, 17}, + {48.1, 34, 17}, + {45.6, 33.8, 17}, + {47.9, 32.1, 17}, + {32.4, 69.1, 718}, + {60.8, 51.7, 718}, + {16.7, 47.4, 718}, + {57.1, 17.3, 718}, + }, + [3636] = { + {47, 41, 718}, + {48, 40.8, 718}, + {58.7, 37.4, 718}, + {50.6, 36.8, 718}, + {49.2, 36.6, 718}, + {58.4, 36.4, 718}, + {55.7, 35.1, 718}, + {57.3, 34.2, 718}, + {45.7, 33.5, 718}, + {49.6, 31.8, 718}, + {53.5, 31.6, 718}, + {60.3, 30.9, 718}, + {49.4, 25.8, 718}, + {52.8, 23.2, 718}, + {51.9, 22, 718}, + {50.1, 20.1, 718}, + }, + [3637] = { + {52, 40.2, 718}, + {56.4, 36.4, 718}, + {50.6, 31.9, 718}, + {52.7, 31.5, 718}, + {59.2, 30.6, 718}, + {41.2, 28.7, 718}, + {40.8, 27, 718}, + {42.7, 25.9, 718}, + {48.4, 25.9, 718}, + {53.8, 25.8, 718}, + {43.4, 24.3, 718}, + {44.5, 23.5, 718}, + {47, 22.1, 718}, + {47.9, 21.4, 718}, + }, + [3653] = { + {40.2, 35.6, 718}, + }, + [3809] = { + {66.8, 87.7, 331}, + {66.1, 86.5, 331}, + {67.9, 85.5, 331}, + {66.8, 83.9, 331}, + {66.5, 78.7, 331}, + {67.2, 78.2, 331}, + {63.7, 77.2, 331}, + {67, 76.8, 331}, + {37.4, 71.5, 331}, + {41.1, 71.1, 331}, + {39.3, 70.4, 331}, + {38.5, 70.3, 331}, + {42, 70, 331}, + {36.5, 70, 331}, + {41.4, 69.7, 331}, + {39.4, 69.3, 331}, + {36.7, 68.3, 331}, + {39.8, 67.2, 331}, + {41.4, 66.9, 331}, + {44.6, 66.7, 331}, + {40.6, 66.6, 331}, + {41, 66.6, 331}, + {40.6, 66.1, 331}, + {44.3, 65.9, 331}, + {39.6, 65.2, 331}, + {48.3, 65.2, 331}, + {45.8, 65, 331}, + {42.1, 64.5, 331}, + {38.8, 64.1, 331}, + {44.1, 63.8, 331}, + {48.7, 63.3, 331}, + {39.7, 63.3, 331}, + {45.8, 63.3, 331}, + {43.1, 62.9, 331}, + {38.3, 62.8, 331}, + {44.7, 62.5, 331}, + {39.5, 62.2, 331}, + {37.9, 62.2, 331}, + {41.3, 62, 331}, + {46.8, 61.9, 331}, + {41.9, 61.4, 331}, + {45.1, 61.3, 331}, + {42.5, 61.3, 331}, + {45.9, 60.8, 331}, + {45.9, 60.4, 331}, + {37.6, 60.2, 331}, + {42.9, 59.8, 331}, + {45.9, 59.6, 331}, + {43.2, 58.6, 331}, + {45, 58, 331}, + {51.3, 56.2, 331}, + {50.4, 56.1, 331}, + {57, 55.8, 331}, + {51.4, 55.3, 331}, + {55.2, 55.1, 331}, + {50.4, 55.1, 331}, + {45.5, 54.6, 331}, + {56.2, 54.6, 331}, + {52.3, 54.4, 331}, + {52.7, 53.9, 331}, + {55, 53.8, 331}, + {47.7, 53.6, 331}, + {48.4, 53.5, 331}, + {45, 53.4, 331}, + {50.8, 53.4, 331}, + {55.5, 53.3, 331}, + {47.3, 53.3, 331}, + {47.2, 52.8, 331}, + {50.1, 51.8, 331}, + {54.8, 51.3, 331}, + {44.4, 51.3, 331}, + {49.4, 51.1, 331}, + {51.5, 50.3, 331}, + {50.6, 47.5, 331}, + {43.5, 46, 331}, + {50.1, 45.1, 331}, + {50.4, 45, 331}, + {42.4, 44.3, 331}, + {42.9, 44.1, 331}, + {51.7, 44, 331}, + {41.3, 43.9, 331}, + {42, 43.1, 331}, + {45.9, 42.6, 331}, + {40.4, 42.6, 331}, + {32.5, 42.5, 331}, + {39.3, 40.8, 331}, + {31.5, 40.7, 331}, + {39.5, 40.5, 331}, + {37.8, 39.4, 331}, + {38.4, 37.5, 331}, + {33.7, 35.8, 331}, + {51.6, 44, 331}, + {73.2, 37.8, 406}, + {76.7, 37.5, 406}, + {75, 36.8, 406}, + {74.3, 36.7, 406}, + {77.7, 36.4, 406}, + {72.3, 36.4, 406}, + {77, 36.1, 406}, + {75.1, 35.7, 406}, + {72.5, 34.7, 406}, + }, + [3810] = { + {63.3, 68.3, 331}, + {62.2, 66.9, 331}, + {62.1, 64.5, 331}, + {62.6, 64.1, 331}, + {66, 63.5, 331}, + {61.1, 63.5, 331}, + {62.1, 63.5, 331}, + {65.6, 63.1, 331}, + {66.1, 62.7, 331}, + {67.4, 62.2, 331}, + {61.7, 62.1, 331}, + {62.7, 62, 331}, + {62.3, 61.7, 331}, + {68.5, 61.5, 331}, + {65.8, 60.6, 331}, + {65.1, 60.3, 331}, + {69, 59.5, 331}, + {64.7, 59.2, 331}, + {64.7, 58.7, 331}, + {64.2, 58.5, 331}, + {64.3, 56.8, 331}, + {69.6, 56.6, 331}, + {64.5, 55.5, 331}, + {63.8, 55.2, 331}, + {70.8, 54.6, 331}, + {63.9, 53.7, 331}, + {71.9, 52.1, 331}, + {72.5, 51.8, 331}, + {70, 51.1, 331}, + {73.2, 50.9, 331}, + {69.7, 50.9, 331}, + {68.8, 50.8, 331}, + {68.7, 50.2, 331}, + {67.4, 50.1, 331}, + {70.5, 50, 331}, + {71.2, 49.8, 331}, + {71, 49.7, 331}, + {71.7, 49.1, 331}, + {72.2, 48.2, 331}, + {73.6, 47.8, 331}, + {73, 47.4, 331}, + {73.2, 46.6, 331}, + {63.2, 68.3, 331}, + {90, 68.3, 331}, + {88.8, 67.9, 331}, + {88.5, 67.6, 331}, + {90.4, 67.5, 331}, + {90.6, 66.9, 331}, + {88.7, 66.7, 331}, + {90.7, 66.2, 331}, + {88.9, 65.5, 331}, + {90.7, 65.5, 331}, + {91.3, 65.4, 331}, + {89.4, 65.3, 331}, + {85.8, 65, 331}, + {90.7, 64.7, 331}, + {83.6, 63.2, 331}, + {82.9, 62.9, 331}, + {84.7, 62.8, 331}, + {82.7, 62.1, 331}, + {67.4, 62, 331}, + {82.6, 61, 331}, + }, + [3811] = { + {82.9, 48.4, 331}, + {82.6, 47.1, 331}, + {88.9, 46.6, 331}, + }, + [3814] = { + {32.1, 99.7, 148}, + {28.4, 98.9, 148}, + {25.4, 98.8, 148}, + {29.4, 98.8, 148}, + {28.8, 98.2, 148}, + {27.7, 98.2, 148}, + {29.4, 97.6, 148}, + {26.3, 97.6, 148}, + {27.3, 97.4, 148}, + {30.3, 97.2, 148}, + {24.1, 97.2, 148}, + {28.9, 96.6, 148}, + {24.4, 95.8, 148}, + {26.3, 95.5, 148}, + {26.8, 95.3, 148}, + {26.5, 94.5, 148}, + {27.3, 94.2, 148}, + {25.8, 93.5, 148}, + {9.5, 34.6, 331}, + {9, 33.5, 331}, + {9.8, 32.6, 331}, + {10.7, 32.5, 331}, + {10.1, 32, 331}, + {11.4, 31.7, 331}, + {9.7, 31, 331}, + {10.8, 30.7, 331}, + {13.5, 30.2, 331}, + {9, 28.6, 331}, + {9, 26.7, 331}, + {10.6, 25.9, 331}, + {11.3, 24.8, 331}, + {13.6, 23.1, 331}, + {14.2, 22.1, 331}, + {12.5, 21.4, 331}, + {13.6, 19.8, 331}, + {14.1, 18.9, 331}, + {15, 18, 331}, + {10.7, 17.1, 331}, + {7.3, 17, 331}, + {11.8, 17, 331}, + {11.1, 16.3, 331}, + {9.9, 16.3, 331}, + {11.8, 15.6, 331}, + {8.3, 15.6, 331}, + {9.5, 15.4, 331}, + {12.9, 15.2, 331}, + {5.9, 15.2, 331}, + {11.2, 14.6, 331}, + {6.2, 13.6, 331}, + {8.3, 13.3, 331}, + {8.9, 13, 331}, + {8.6, 12.1, 331}, + {9.4, 11.7, 331}, + {7.8, 11, 331}, + }, + [3819] = { + {64.2, 86.6, 331}, + {70.4, 86.1, 331}, + {70.5, 85.8, 331}, + {63.9, 85.6, 331}, + {65.6, 85.5, 331}, + {64.5, 85.5, 331}, + {64.3, 84.8, 331}, + {70.4, 84.7, 331}, + {65, 84.6, 331}, + {64.9, 83.7, 331}, + {73.5, 80.2, 331}, + {63.4, 79.5, 331}, + {61.5, 79.4, 331}, + {62.8, 78.9, 331}, + {62.2, 78.8, 331}, + {73.7, 78.7, 331}, + {63.5, 77.8, 331}, + {70.6, 76.3, 331}, + {77.1, 73.5, 331}, + {68.6, 72.8, 331}, + {73.7, 70.8, 331}, + {78.3, 66.3, 331}, + {35.7, 59.5, 331}, + {31, 59.2, 331}, + {19, 58.9, 331}, + {34.9, 58.8, 331}, + {20.6, 58.1, 331}, + {21.4, 57.9, 331}, + {22.3, 57.7, 331}, + {20.2, 57.7, 331}, + {34.8, 57.6, 331}, + {19.2, 57.6, 331}, + {23.1, 57.4, 331}, + {28.9, 57.1, 331}, + {21.2, 56.7, 331}, + {30.9, 56.5, 331}, + {18.9, 56.4, 331}, + {28.6, 56.1, 331}, + {35.7, 56.1, 331}, + {19.2, 55.6, 331}, + {22.7, 55.5, 331}, + {24.6, 55.3, 331}, + {35, 55.2, 331}, + {19, 54.8, 331}, + {29, 54.6, 331}, + {18.4, 54.1, 331}, + {31, 53.8, 331}, + {24.6, 53.6, 331}, + {33.3, 53.4, 331}, + {29.5, 52.9, 331}, + {27.1, 52.9, 331}, + {31.1, 52.7, 331}, + {19.1, 52.1, 331}, + {30.6, 52, 331}, + {18.3, 51.9, 331}, + {17.7, 51.6, 331}, + {26, 51.6, 331}, + {27, 51.5, 331}, + {26.9, 51.3, 331}, + {29.3, 50.6, 331}, + {44.2, 50.2, 331}, + {20.9, 50.1, 331}, + {19.4, 50, 331}, + {30.4, 49.8, 331}, + {19.1, 49.6, 331}, + {20.7, 49.5, 331}, + {26, 49.4, 331}, + {30.3, 49.3, 331}, + {21.6, 49.3, 331}, + {32, 49.2, 331}, + {22.8, 48.9, 331}, + {30.9, 48.9, 331}, + {22.4, 48.8, 331}, + {22.9, 48.5, 331}, + {20.9, 48.2, 331}, + {18.7, 48, 331}, + {43.5, 47.7, 331}, + {42.5, 47.5, 331}, + {17, 47.4, 331}, + {23.8, 47.1, 331}, + {23.3, 46.8, 331}, + {17.7, 46.7, 331}, + {22.9, 46.7, 331}, + {43, 46.5, 331}, + {15.4, 45.5, 331}, + {16.8, 45.3, 331}, + {35.5, 45.2, 331}, + {15.3, 44.9, 331}, + {41.8, 44.5, 331}, + {42, 43.9, 331}, + {40.7, 43.5, 331}, + {25.7, 43.3, 331}, + {33, 42, 331}, + {40.7, 41.5, 331}, + {39.3, 41.3, 331}, + {41, 40.8, 331}, + {35.5, 40.6, 331}, + {33.3, 40.3, 331}, + {38.3, 40.1, 331}, + {39.8, 39.8, 331}, + {41.6, 39.4, 331}, + {37.4, 39.2, 331}, + {38.2, 38.9, 331}, + {38.8, 38.7, 331}, + {32.7, 37.7, 331}, + {32.1, 37.1, 331}, + {33.3, 37, 331}, + {34.4, 33.6, 331}, + {37.7, 31.8, 331}, + {54.1, 15.5, 406}, + {73, 79.2, 331}, + {18.7, 58.7, 331}, + {34.9, 45.4, 331}, + {67, 26.1, 406}, + {55.3, 25.6, 406}, + {57.1, 25, 406}, + {57.9, 24.8, 406}, + {58.7, 24.6, 406}, + {56.7, 24.6, 406}, + {55.8, 24.4, 406}, + {59.6, 24.3, 406}, + {65.1, 24, 406}, + {57.7, 23.6, 406}, + {67, 23.4, 406}, + {55.4, 23.4, 406}, + {64.7, 23.1, 406}, + {55.7, 22.6, 406}, + {59.1, 22.5, 406}, + {61, 22.3, 406}, + {55.5, 21.8, 406}, + {65.1, 21.7, 406}, + {55, 21.1, 406}, + {61, 20.7, 406}, + {63.4, 20, 406}, + {55.6, 19.2, 406}, + {54.9, 19, 406}, + {54.3, 18.8, 406}, + {62.3, 18.7, 406}, + {63.2, 18.6, 406}, + {63.2, 18.5, 406}, + {57.4, 17.3, 406}, + {55.9, 17.2, 406}, + {55.6, 16.8, 406}, + {57.2, 16.8, 406}, + {58.1, 16.5, 406}, + {57.4, 15.5, 406}, + {53.7, 14.7, 406}, + {52.2, 12.9, 406}, + }, + [3820] = { + {57.2, 78.5, 331}, + {57, 77.8, 331}, + {55.4, 76.9, 331}, + {55.7, 75.9, 331}, + {55.8, 73.7, 331}, + {57.2, 73.5, 331}, + {57.6, 72.8, 331}, + {56.3, 71.6, 331}, + {54.1, 71.2, 331}, + {58.2, 70.6, 331}, + {57.6, 70.3, 331}, + {56.9, 69.5, 331}, + {57.6, 66.5, 331}, + {66.7, 63.1, 331}, + {65.8, 62.9, 331}, + {62.2, 62.2, 331}, + {64, 55.3, 331}, + {64.3, 54.6, 331}, + {70.9, 54.1, 331}, + {73.4, 50.7, 331}, + {67.4, 49.8, 331}, + {68.6, 49.1, 331}, + {72.5, 49, 331}, + {66.9, 48.9, 331}, + {66.7, 48.6, 331}, + {72.6, 48.1, 331}, + {74, 47.6, 331}, + {72.4, 46.9, 331}, + }, + [3821] = { + {88.7, 68.3, 331}, + {89.8, 68.3, 331}, + {90.1, 67.5, 331}, + {88.5, 66.3, 331}, + {90.7, 65.7, 331}, + {90.9, 63.8, 331}, + {82, 62.3, 331}, + {90.9, 61.3, 331}, + {92.9, 55.6, 331}, + {91, 55.3, 331}, + {82.9, 55, 331}, + {93.3, 53.5, 331}, + {92.1, 51.5, 331}, + {92.8, 51.3, 331}, + {91.2, 50.8, 331}, + {82.9, 49.1, 331}, + {83.8, 48.7, 331}, + {83.5, 47.8, 331}, + {90.7, 47.5, 331}, + {88.9, 47.2, 331}, + {81.1, 46.4, 331}, + {89.9, 46, 331}, + {81, 45.9, 331}, + {91.8, 45.9, 331}, + {89.3, 44.9, 331}, + {83.8, 44.6, 331}, + {87.8, 63.6, 331}, + {86.6, 62.9, 331}, + {85.2, 62.7, 331}, + {87.9, 62.5, 331}, + {85.7, 62.5, 331}, + {87.1, 62.2, 331}, + {84.9, 62.2, 331}, + {84.9, 61.3, 331}, + {84.9, 60.6, 331}, + {84.8, 60, 331}, + {85.2, 59.7, 331}, + {85.6, 59.5, 331}, + {86, 59.3, 331}, + {86.2, 58.6, 331}, + {86.5, 58.2, 331}, + {87, 57.4, 331}, + {86.4, 57.4, 331}, + {85.9, 57.1, 331}, + {86.6, 56.5, 331}, + {86, 56.1, 331}, + {86.4, 55.5, 331}, + {82.9, 49.2, 331}, + }, + [3823] = { + {41.7, 97.8, 148}, + {42.3, 96.8, 148}, + {45, 96, 148}, + {65.6, 87.2, 331}, + {66.1, 86, 331}, + {67.2, 85.9, 331}, + {63.6, 85.7, 331}, + {66.8, 85.6, 331}, + {63.8, 85.3, 331}, + {66.3, 84.7, 331}, + {72.2, 81.7, 331}, + {60.9, 80.4, 331}, + {73.3, 78.5, 331}, + {62.7, 77.6, 331}, + {76, 66.8, 331}, + {22.6, 62, 331}, + {33.6, 60.5, 331}, + {21.8, 60.4, 331}, + {33.2, 59.6, 331}, + {35.2, 58.3, 331}, + {19.3, 58.3, 331}, + {30.1, 56.7, 331}, + {19, 56.4, 331}, + {23.2, 56, 331}, + {28.3, 55.1, 331}, + {30.1, 55, 331}, + {27.5, 54.5, 331}, + {19.3, 54.1, 331}, + {24.7, 54, 331}, + {25.4, 53.8, 331}, + {27, 52.9, 331}, + {26.3, 52.6, 331}, + {25.5, 52.5, 331}, + {30.5, 51.9, 331}, + {30.7, 51.7, 331}, + {17.8, 51.3, 331}, + {31.2, 50.8, 331}, + {32.3, 50.5, 331}, + {17.8, 50.3, 331}, + {31.3, 49.8, 331}, + {24.5, 49.1, 331}, + {20.1, 49, 331}, + {30.1, 48, 331}, + {23, 47.6, 331}, + {22.9, 47.6, 331}, + {18.4, 47.4, 331}, + {21.9, 46.6, 331}, + {17.7, 46.3, 331}, + {15.7, 45.7, 331}, + {16.8, 45.4, 331}, + {16.5, 36.4, 331}, + {23.8, 36, 331}, + {17.9, 35.7, 331}, + {18.8, 33.6, 331}, + {22.9, 33, 331}, + {27.4, 31.4, 331}, + {21.9, 30.2, 331}, + {20, 29.8, 331}, + {23.2, 29.3, 331}, + {23.5, 28.5, 331}, + {28.1, 27.5, 331}, + {19.2, 27.4, 331}, + {27, 26.2, 331}, + {26, 22.1, 331}, + {16.5, 21.3, 331}, + {19.5, 21.1, 331}, + {25.7, 21.1, 331}, + {29.8, 19.7, 331}, + {27.1, 18.6, 331}, + {25.8, 15.9, 331}, + {26.5, 14.7, 331}, + {29.6, 13.8, 331}, + {58.3, 19.3, 406}, + {57.4, 17.3, 406}, + {17.7, 50.3, 331}, + {59, 28.7, 406}, + {58.3, 27.1, 406}, + {55.9, 25.1, 406}, + {66.2, 23.6, 406}, + {55.6, 23.4, 406}, + {59.6, 22.9, 406}, + {64.5, 22.1, 406}, + {66.2, 22, 406}, + {63.8, 21.5, 406}, + {55.9, 21.1, 406}, + {61, 21, 406}, + {61.7, 20.8, 406}, + {63.2, 20, 406}, + {62.6, 19.7, 406}, + {61.8, 19.6, 406}, + {54.4, 18.4, 406}, + {54.3, 17.5, 406}, + {56.6, 16.3, 406}, + {52.4, 13.1, 406}, + }, + [3824] = { + {57.5, 78.5, 331}, + {57.3, 77.8, 331}, + {57.4, 77.4, 331}, + {55.2, 76.9, 331}, + {56.6, 75.6, 331}, + {57.6, 75.2, 331}, + {55, 75.1, 331}, + {56.1, 74.4, 331}, + {56.7, 74.1, 331}, + {59.5, 73.7, 331}, + {57.7, 73.5, 331}, + {58, 72.1, 331}, + {54.5, 72, 331}, + {56.4, 71.8, 331}, + {60.4, 71.5, 331}, + {60.9, 71.3, 331}, + {59.9, 71.3, 331}, + {54.5, 71, 331}, + {56.8, 70.8, 331}, + {53.5, 70.7, 331}, + {53.6, 70.3, 331}, + {56.8, 70.1, 331}, + {62.2, 70.1, 331}, + {55.8, 70, 331}, + {54.8, 69.7, 331}, + {54.7, 69.5, 331}, + {59.1, 68.9, 331}, + {57.7, 68.9, 331}, + {57.7, 68.5, 331}, + {59, 68.2, 331}, + {58.7, 67.6, 331}, + {58.4, 67.6, 331}, + {58.8, 66.5, 331}, + {57.6, 65.4, 331}, + }, + [3825] = { + {66.6, 70.1, 331}, + {67.6, 69.6, 331}, + {65.3, 69.3, 331}, + {68.2, 69.2, 331}, + {65.7, 69.1, 331}, + {65.7, 68.3, 331}, + {69.2, 67.7, 331}, + {68.5, 65.3, 331}, + {70, 65.1, 331}, + {70.4, 64.3, 331}, + {68.6, 64, 331}, + {71.9, 58, 331}, + {75.9, 57.2, 331}, + {74.6, 55.9, 331}, + {72, 55.6, 331}, + {76.3, 55.5, 331}, + {77.8, 55.4, 331}, + {73.7, 55.4, 331}, + {73.4, 54.8, 331}, + {64.4, 49.4, 331}, + {60.5, 45.9, 331}, + {63.1, 45.3, 331}, + {61, 43.2, 331}, + {58.9, 41.6, 331}, + {57.3, 34.2, 331}, + {56.4, 98.4, 361}, + }, + [3861] = { + {82.6, 77.9, 209}, + {80.3, 76.5, 209}, + {83.3, 69.1, 209}, + {87.5, 59.8, 209}, + {77.9, 56.5, 209}, + {85.3, 53.7, 209}, + {81.4, 51.2, 209}, + {74.2, 26.2, 209}, + }, + [3862] = { + {70.9, 67.3, 209}, + {73.2, 66, 209}, + {76.1, 60.4, 209}, + {66.1, 56.3, 209}, + {74, 54.2, 209}, + {55.9, 51.7, 209}, + {48.8, 43.3, 209}, + {66.4, 30.9, 209}, + }, + [3866] = { + {81.1, 71.7, 209}, + }, + [3868] = { + {82.2, 73.9, 209}, + }, + [3951] = { + {50.3, 67.3, 331}, + }, + [3962] = { + {34.8, 50.9, 331}, + }, + [4005] = { + {64.3, 93.4, 406}, + {65.4, 92.3, 406}, + {68.8, 88.5, 406}, + {67.3, 87.7, 406}, + {64.8, 83.5, 406}, + {61.6, 81.8, 406}, + {61.4, 81.6, 406}, + {59.2, 78.1, 406}, + {60.5, 76.5, 406}, + {57.6, 76.3, 406}, + {56.3, 76.3, 406}, + {53.6, 76.2, 406}, + {55.9, 76.1, 406}, + {55, 75.9, 406}, + {53.8, 75.8, 406}, + {53.4, 75.8, 406}, + {58.9, 75.7, 406}, + {54.6, 75.2, 406}, + {60.1, 75.1, 406}, + {54.7, 74.7, 406}, + {59, 73.8, 406}, + {59.1, 72.7, 406}, + {60.8, 71.9, 406}, + {63.8, 89, 406}, + {64.7, 88, 406}, + {67.5, 85, 406}, + {66.3, 84.3, 406}, + {64.4, 80.8, 406}, + {61.7, 79.5, 406}, + {61.5, 79.3, 406}, + {59.7, 76.5, 406}, + {60.8, 75.2, 406}, + {57.3, 75.1, 406}, + {58.4, 75, 406}, + {57.1, 75, 406}, + {55.2, 74.9, 406}, + {56.3, 74.7, 406}, + {55.3, 74.7, 406}, + {55, 74.6, 406}, + {59.5, 74.5, 406}, + {56, 74.1, 406}, + {60.5, 74.1, 406}, + {56, 73.8, 406}, + {59.5, 73, 406}, + {59.6, 72.1, 406}, + {61, 71.5, 406}, + }, + [4006] = { + {54.5, 71.9, 406}, + {53.5, 71.8, 406}, + {52.9, 71.7, 406}, + {54.1, 71.4, 406}, + {53.5, 71.1, 406}, + {76.8, 54.1, 406}, + {77.5, 53.4, 406}, + {76.1, 53.1, 406}, + {78.6, 52.9, 406}, + {76.9, 52.5, 406}, + {75.7, 52.2, 406}, + {79.3, 51.5, 406}, + {74.8, 51.4, 406}, + {77.6, 50.9, 406}, + {76.4, 50.9, 406}, + {76.1, 50.6, 406}, + {77.3, 50.3, 406}, + {59.8, 50, 406}, + {59.4, 49.3, 406}, + {60.8, 48.7, 406}, + {61.7, 48.5, 406}, + {60.1, 48.5, 406}, + {75.7, 47.7, 406}, + {60.7, 47.1, 406}, + {74.9, 46.9, 406}, + {70.2, 46.1, 406}, + {76.5, 46, 406}, + {61.3, 46, 406}, + {74.6, 46, 406}, + {75.7, 46, 406}, + {74.3, 45.9, 406}, + {73, 45.3, 406}, + {68.1, 45.1, 406}, + {76.5, 45.1, 406}, + {69.6, 45, 406}, + {77.4, 45, 406}, + {74.1, 44.5, 406}, + {75.5, 44.5, 406}, + {77.1, 44.3, 406}, + {74.2, 43.7, 406}, + {75.1, 43.2, 406}, + {76.2, 43.1, 406}, + {74.1, 42.7, 406}, + {73.6, 42.7, 406}, + {69.2, 41, 406}, + {55.9, 71.5, 406}, + {55, 71.3, 406}, + {54.7, 71.3, 406}, + {55.6, 71, 406}, + {55.1, 70.9, 406}, + {74, 57, 406}, + {74.6, 56.4, 406}, + {73.4, 56.2, 406}, + {75.5, 56, 406}, + {74.1, 55.7, 406}, + {73.1, 55.4, 406}, + {76.1, 54.9, 406}, + {72.4, 54.8, 406}, + {74.7, 54.4, 406}, + {73.7, 54.4, 406}, + {73.5, 54.1, 406}, + {74.4, 53.9, 406}, + {60.2, 53.7, 406}, + {59.9, 53.1, 406}, + {61, 52.7, 406}, + {61.7, 52.5, 406}, + {60.5, 52.4, 406}, + {73.1, 51.8, 406}, + {61, 51.4, 406}, + {72.5, 51.1, 406}, + {68.6, 50.5, 406}, + {73.8, 50.5, 406}, + {61.4, 50.4, 406}, + {72.2, 50.4, 406}, + {73.1, 50.4, 406}, + {72, 50.4, 406}, + {70.9, 49.9, 406}, + {66.9, 49.7, 406}, + {73.7, 49.7, 406}, + {68.2, 49.7, 406}, + {74.5, 49.6, 406}, + {71.8, 49.2, 406}, + {73, 49.2, 406}, + {74.2, 49.1, 406}, + {71.9, 48.6, 406}, + {72.7, 48.2, 406}, + {73.6, 48.1, 406}, + {71.9, 47.7, 406}, + {71.4, 47.7, 406}, + {67.8, 46.4, 406}, + }, + [4007] = { + {52.8, 75.6, 406}, + {52.2, 75.5, 406}, + {53.4, 74.9, 406}, + {52.5, 74.8, 406}, + {53, 74.5, 406}, + {53.7, 74.2, 406}, + {52.5, 73.9, 406}, + {54, 73.9, 406}, + {54.8, 73.6, 406}, + {52.8, 73.5, 406}, + {53.2, 73.1, 406}, + {52.9, 72.8, 406}, + {54.7, 72.8, 406}, + {53.8, 72.7, 406}, + {52.9, 72.4, 406}, + {61, 66.4, 406}, + {61.6, 66.4, 406}, + {61.5, 64.9, 406}, + {62.1, 63.8, 406}, + {60.9, 63.8, 406}, + {61.6, 63.7, 406}, + {62.4, 63.4, 406}, + {60.5, 63, 406}, + {62.1, 62.8, 406}, + {52.7, 62.2, 406}, + {60, 61.6, 406}, + {54, 61.5, 406}, + {63.1, 61.4, 406}, + {61.8, 61, 406}, + {64.8, 60.9, 406}, + {52.4, 60.9, 406}, + {59, 60.7, 406}, + {61.1, 60.4, 406}, + {62.7, 60.3, 406}, + {60.6, 59.7, 406}, + {61.1, 59.4, 406}, + {62.2, 59.4, 406}, + {52.3, 58.4, 406}, + {61.7, 58.1, 406}, + {61.2, 57.3, 406}, + {51.8, 56.6, 406}, + {53.2, 55.4, 406}, + {52, 54.2, 406}, + {48.5, 48.6, 406}, + {47.2, 47.4, 406}, + {49.1, 47.3, 406}, + {51.8, 47.3, 406}, + {46.8, 46.2, 406}, + {48.8, 45.4, 406}, + {53.4, 44.9, 406}, + {48.4, 44.1, 406}, + {53.2, 43, 406}, + {51.2, 42.4, 406}, + {52.4, 42, 406}, + {50.9, 39.6, 406}, + {54.5, 74.4, 406}, + {54, 74.4, 406}, + {55, 73.9, 406}, + {54.3, 73.9, 406}, + {54.7, 73.5, 406}, + {55.3, 73.3, 406}, + {54.3, 73.1, 406}, + {55.5, 73.1, 406}, + {56.1, 72.9, 406}, + {54.5, 72.8, 406}, + {54.9, 72.4, 406}, + {54.6, 72.3, 406}, + {56.1, 72.2, 406}, + {55.3, 72.1, 406}, + {54.6, 71.8, 406}, + {61.2, 67, 406}, + {61.7, 67, 406}, + {61.6, 65.8, 406}, + {62.1, 64.9, 406}, + {61.1, 64.9, 406}, + {61.6, 64.8, 406}, + {62.3, 64.6, 406}, + {60.8, 64.2, 406}, + {62.1, 64.1, 406}, + {54.4, 63.6, 406}, + {60.4, 63.1, 406}, + {55.5, 63, 406}, + {62.9, 62.9, 406}, + {54.3, 62.7, 406}, + {61.8, 62.6, 406}, + {64.3, 62.6, 406}, + {59.5, 62.4, 406}, + {61.3, 62.2, 406}, + {62.6, 62.1, 406}, + {60.8, 61.6, 406}, + {61.3, 61.3, 406}, + {62.2, 61.3, 406}, + {54.1, 60.5, 406}, + {61.7, 60.3, 406}, + {61.3, 59.6, 406}, + {53.7, 59, 406}, + {54.9, 58.1, 406}, + {53.9, 57.1, 406}, + {51.1, 52.5, 406}, + {50, 51.5, 406}, + {51.5, 51.5, 406}, + {53.7, 51.5, 406}, + {49.7, 50.6, 406}, + {51.3, 49.9, 406}, + {55, 49.6, 406}, + {50.9, 48.9, 406}, + {54.8, 48, 406}, + {53.3, 47.5, 406}, + {54.2, 47, 406}, + {53, 45.2, 406}, + }, + [4040] = { + {46.7, 63.8, 11}, + {47.5, 63.3, 11}, + {49.8, 63.1, 11}, + {48.6, 62.8, 11}, + {50.8, 62.1, 11}, + {48.6, 62, 11}, + {45.7, 61.2, 11}, + {51.4, 61, 11}, + {48.4, 60.7, 11}, + {45.9, 60.6, 11}, + {49.3, 60.4, 11}, + {49.8, 60.3, 11}, + {47.5, 59.6, 11}, + {47, 59.4, 11}, + {46.9, 59.3, 11}, + {50.4, 59.2, 11}, + {49.5, 58.9, 11}, + {4.6, 28, 5602}, + {5.2, 27.6, 5602}, + {7, 27.4, 5602}, + {6.1, 27.2, 5602}, + {7.8, 26.6, 5602}, + {6.1, 26.6, 5602}, + {3.8, 26, 5602}, + {8.3, 25.8, 5602}, + {5.9, 25.6, 5602}, + {4, 25.5, 5602}, + {6.6, 25.3, 5602}, + {7, 25.3, 5602}, + {5.2, 24.7, 5602}, + {4.9, 24.6, 5602}, + {4.8, 24.5, 5602}, + {7.4, 24.5, 5602}, + {6.8, 24.2, 5602}, + }, + [4067] = { + {39.6, 17.7, 406}, + {31.6, 12.7, 406}, + {33.4, 12.3, 406}, + {39, 12.1, 406}, + {32.1, 11.5, 406}, + {31, 11.5, 406}, + {32.6, 11.2, 406}, + {32, 10.2, 406}, + {34.5, 10.1, 406}, + {33.3, 9.9, 406}, + {30.9, 9.6, 406}, + {34.5, 9.1, 406}, + {32.2, 8.9, 406}, + {33, 8, 406}, + {34.3, 8, 406}, + {43.8, 27.5, 406}, + {37.4, 23.4, 406}, + {38.7, 23, 406}, + {43.3, 22.9, 406}, + {37.7, 22.4, 406}, + {36.9, 22.4, 406}, + {38.2, 22.1, 406}, + {37.8, 21.5, 406}, + {39.7, 21.3, 406}, + {38.7, 21.1, 406}, + {36.8, 20.9, 406}, + {39.7, 20.5, 406}, + {37.8, 20.3, 406}, + {38.5, 19.6, 406}, + {39.5, 19.5, 406}, + }, + [4082] = { + {45.9, 58.7, 406}, + {48.9, 60.7, 406}, + }, + [4084] = { + {35.5, 6.2, 406}, + {36, 5.6, 406}, + {40.5, 18.1, 406}, + }, + [4117] = { + {45.4, 94.6, 17}, + {40.9, 58.3, 400}, + {37.4, 55.1, 400}, + {40.5, 50.2, 400}, + {37.1, 41.9, 400}, + {35.2, 27.6, 400}, + }, + [4119] = { + {55.5, 51.9, 400}, + {59.1, 51.6, 400}, + {56.1, 51.2, 400}, + {57.2, 49.9, 400}, + {56.3, 49.8, 400}, + {56.3, 49.7, 400}, + {55.4, 49.6, 400}, + {56, 49.3, 400}, + {56.2, 48.8, 400}, + {54.6, 48.7, 400}, + {54.2, 48.2, 400}, + {54.9, 47.6, 400}, + {56.2, 46.9, 400}, + {59.6, 46.4, 400}, + {55.6, 45.8, 400}, + {51.6, 43.9, 400}, + {19.4, 39.2, 400}, + {17.5, 36.7, 400}, + {18.7, 33, 400}, + {15, 31.7, 400}, + {54.2, 48.1, 400}, + {56.3, 46.9, 400}, + }, + [4124] = { + {41, 95.5, 17}, + {41.4, 94.9, 17}, + {37, 89.8, 17}, + {94, 46.9, 357}, + {91.6, 44.9, 357}, + {92.2, 43, 357}, + {91.4, 40.1, 357}, + {66.7, 62.3, 400}, + {64.9, 61.4, 400}, + {61.6, 60.1, 400}, + {67.6, 59.8, 400}, + {63.6, 59.8, 400}, + {59.6, 58.8, 400}, + {48.7, 55.8, 400}, + {64.8, 55.6, 400}, + {57.8, 55.4, 400}, + {34.1, 54.1, 400}, + {59, 51.9, 400}, + {58.8, 51, 400}, + {38.9, 50.5, 400}, + {64.1, 49.5, 400}, + {64, 48.5, 400}, + {59.7, 48.5, 400}, + {36.3, 47.8, 400}, + {31, 47.3, 400}, + {64, 47.3, 400}, + {54.1, 47, 400}, + {36.4, 46.9, 400}, + {61.6, 46.6, 400}, + {60.3, 46.3, 400}, + {51, 44.2, 400}, + {27.9, 42.8, 400}, + {24.8, 42.8, 400}, + {21.5, 41.9, 400}, + {20.1, 39.2, 400}, + {22.4, 38, 400}, + {21.9, 36.7, 400}, + {15.9, 35.1, 400}, + {15.1, 32.6, 400}, + {25, 29.8, 400}, + {25.9, 28.4, 400}, + {14.1, 26.7, 400}, + {14.8, 26.6, 400}, + {14.9, 19.5, 400}, + {15.8, 16.6, 400}, + {11.2, 16.4, 400}, + {12.2, 13.3, 400}, + {10.8, 8.7, 400}, + }, + [4126] = { + {44.5, 97.4, 17}, + {45.9, 96.5, 17}, + {44.5, 96.1, 17}, + {44.8, 95.7, 17}, + {45.2, 94.8, 17}, + {45.4, 94.1, 17}, + {44.7, 93.4, 17}, + {45.3, 60.1, 400}, + {46.5, 59.6, 400}, + {47.8, 59.2, 400}, + {44.2, 59.2, 400}, + {41.4, 58.7, 400}, + {45.7, 58.7, 400}, + {40.1, 56.2, 400}, + {41.7, 52.4, 400}, + {40.4, 51.7, 400}, + {51.4, 49.9, 400}, + {51.1, 48.5, 400}, + {50.3, 47.1, 400}, + {49.3, 46.8, 400}, + {36.8, 41, 400}, + {33, 34.1, 400}, + {36.2, 32, 400}, + {33, 31.2, 400}, + {33.8, 30.2, 400}, + {34.8, 28.1, 400}, + {35.1, 26.4, 400}, + {33.6, 24.9, 400}, + {44.5, 96.2, 17}, + {33.1, 31.4, 400}, + }, + [4127] = { + {36, 14.4, 14}, + {56, 46.9, 17}, + {54, 46, 17}, + {54.2, 45.4, 17}, + {53.9, 45.1, 17}, + {55.2, 44.9, 17}, + {54.2, 44.9, 17}, + {50, 44.9, 17}, + {53.9, 44.9, 17}, + {55.8, 44.7, 17}, + {51.1, 44.2, 17}, + {51.4, 44.2, 17}, + {53.3, 44, 17}, + {45.8, 42.8, 17}, + {51, 42.8, 17}, + {45.8, 42.7, 17}, + {45.1, 42.6, 17}, + {48.1, 42.4, 17}, + {48.2, 42.3, 17}, + {44.5, 42.2, 17}, + {46, 42, 17}, + {46.6, 42, 17}, + {57.2, 42, 17}, + {61.1, 41.9, 17}, + {46.7, 41.9, 17}, + {49.5, 41.8, 17}, + {59.5, 41.8, 17}, + {60.3, 41.8, 17}, + {43.4, 41.7, 17}, + {61.9, 41.7, 17}, + {49, 41.6, 17}, + {56.3, 41.5, 17}, + {57.6, 41.5, 17}, + {47.1, 41.4, 17}, + {44.5, 41, 17}, + {57.6, 40.9, 17}, + {55.9, 40.6, 17}, + {49, 40.5, 17}, + {43.3, 40.5, 17}, + {54.2, 40.2, 17}, + {56.5, 39.9, 17}, + {44.8, 39.9, 17}, + {49.3, 39.8, 17}, + {54.1, 39.8, 17}, + {55.5, 39.7, 17}, + {43.8, 39.7, 17}, + {55.9, 39.2, 17}, + {55.2, 38.7, 17}, + {42.2, 37.8, 17}, + {59.3, 37.8, 17}, + {58.3, 37.5, 17}, + {43, 37, 17}, + {43.9, 36.5, 17}, + {43, 36.4, 17}, + {44.1, 36.2, 17}, + {43.1, 35.7, 17}, + {42.4, 35.6, 17}, + {60.4, 35.5, 17}, + {57.9, 34.7, 17}, + {41.1, 32.4, 17}, + {40.9, 32, 17}, + {42.2, 31, 17}, + {59.5, 30.5, 17}, + {60.8, 30.1, 17}, + {40.6, 29.2, 17}, + {59.2, 29.1, 17}, + {62.5, 28.9, 17}, + {40.5, 28.6, 17}, + {42, 28.3, 17}, + {62.7, 28.1, 17}, + {60.1, 27.2, 17}, + {62.8, 27.1, 17}, + {41.5, 26.4, 17}, + {41.2, 25.2, 17}, + {41.2, 24.7, 17}, + {40.5, 22.6, 17}, + {41.5, 22.3, 17}, + {42.2, 21.5, 17}, + {42.1, 21.2, 17}, + {41.5, 21.1, 17}, + {43.9, 20.7, 17}, + {42.5, 20.7, 17}, + {42.5, 19.7, 17}, + {44.9, 18.7, 17}, + {57.3, 18.3, 17}, + {58.3, 18.3, 17}, + {44.6, 18.3, 17}, + {59.2, 18.1, 17}, + {56.2, 17.5, 17}, + {56.6, 17.4, 17}, + {57.5, 17.2, 17}, + {57.3, 16.2, 17}, + {56.3, 15.5, 17}, + {52, 14.2, 17}, + {52.4, 13.9, 17}, + {54.7, 13.3, 17}, + {52, 13.2, 17}, + {54.7, 12.7, 17}, + {53.7, 12.3, 17}, + {54.7, 12.2, 17}, + {55.1, 11.9, 17}, + {54.4, 11.8, 17}, + {54, 11.1, 17}, + {54.7, 10.2, 17}, + {55.7, 9.8, 17}, + {62.3, 8.7, 17}, + {61.6, 8.4, 17}, + {63.5, 7.6, 17}, + {64, 4.6, 17}, + {44.8, 40, 17}, + {52.1, 13.1, 17}, + }, + [4128] = { + {28.2, 48.7, 15}, + {28, 48.4, 15}, + {28.3, 44.9, 15}, + {27.5, 44.4, 15}, + {28.7, 42.5, 15}, + {46.4, 81.8, 17}, + {47.4, 81.5, 17}, + {46.8, 81.3, 17}, + {46.4, 81, 17}, + {48.3, 80.9, 17}, + {47.2, 80.9, 17}, + {46.7, 79.9, 17}, + {48.4, 79.6, 17}, + {44.1, 79.4, 17}, + {48.9, 79.3, 17}, + {50.1, 79.2, 17}, + {49.2, 79, 17}, + {50, 79, 17}, + {45.3, 79, 17}, + {45.1, 77.4, 17}, + {47.1, 77.4, 17}, + {45.4, 77.2, 17}, + {50.2, 77.2, 17}, + {49.7, 77, 17}, + {44.6, 76.7, 17}, + {45.5, 76.7, 17}, + {43.9, 76.5, 17}, + {45.4, 76, 17}, + {50.4, 76, 17}, + {49.6, 75.9, 17}, + {49.5, 75.6, 17}, + {48.1, 75.5, 17}, + {44.6, 75, 17}, + {44.9, 74.9, 17}, + {44, 74.9, 17}, + {47.8, 74.7, 17}, + {45.5, 74.7, 17}, + {48.3, 74.5, 17}, + {44.3, 74.5, 17}, + {47.1, 73.9, 17}, + {47.8, 73.7, 17}, + {47.5, 73.5, 17}, + {47.5, 73, 17}, + {45.1, 77.9, 17}, + {45.1, 78, 17}, + }, + [4129] = { + {50.4, 59.1, 17}, + {42.9, 57.6, 17}, + {43.2, 56.8, 17}, + {50.3, 56.2, 17}, + {46.9, 55.6, 17}, + {46.5, 55.2, 17}, + {49.8, 55.2, 17}, + {49.4, 54.7, 17}, + {43.5, 53.5, 17}, + {44.4, 53.1, 17}, + {44.7, 52.8, 17}, + {45.5, 52.5, 17}, + {46.3, 49.3, 17}, + {45.5, 49.3, 17}, + {45.9, 48.5, 17}, + {47.4, 48.3, 17}, + {47.5, 47.8, 17}, + {46.6, 47.2, 17}, + {43.4, 46.8, 17}, + {46.8, 46.7, 17}, + {44.7, 46.2, 17}, + {44.9, 46.2, 17}, + {43.9, 45.9, 17}, + {43.5, 45.8, 17}, + {47.4, 45.8, 17}, + {44.8, 45.4, 17}, + {43.2, 45.1, 17}, + {45.4, 44.7, 17}, + }, + [4139] = { + {73.5, 89.8, 400}, + {82.5, 87.8, 400}, + {81.4, 86.8, 400}, + {73.1, 81.1, 400}, + {85.2, 80.2, 400}, + {82.8, 79.7, 400}, + {70, 79.1, 400}, + {70.8, 76.8, 400}, + {71.6, 74.5, 400}, + {70.9, 73.3, 400}, + {83.1, 73.3, 400}, + {71.8, 72.9, 400}, + {83.8, 72.2, 400}, + {87.1, 69.9, 400}, + {88.2, 69, 400}, + {86.6, 68.6, 400}, + {71.5, 74.7, 400}, + }, + [4140] = { + {71.8, 73.7, 400}, + {72.1, 73.2, 400}, + {71.3, 72.9, 400}, + {73.7, 70.1, 400}, + {82.7, 69.1, 400}, + {70.4, 68.9, 400}, + {79.9, 68.7, 400}, + {78.3, 68, 400}, + {75.5, 67.8, 400}, + {77, 67.7, 400}, + {78.4, 66.9, 400}, + {70.8, 66.5, 400}, + {82.9, 66.4, 400}, + {69.3, 65.3, 400}, + {70.1, 63.1, 400}, + {83.7, 63, 400}, + {84.5, 61.4, 400}, + {86.4, 61.3, 400}, + {73.9, 61, 400}, + {72.4, 59.7, 400}, + {75.2, 59.5, 400}, + {82.9, 59.3, 400}, + {75.4, 58.4, 400}, + {86.6, 57.7, 400}, + {72.4, 57.5, 400}, + {71.3, 57.2, 400}, + {78, 56.7, 400}, + {71.6, 55.4, 400}, + {78.4, 55.2, 400}, + {73.9, 55.1, 400}, + {76.1, 55.1, 400}, + {78.3, 52.8, 400}, + {77.6, 51.8, 400}, + {76.1, 51.7, 400}, + {71.6, 74.5, 400}, + {71.3, 72.8, 400}, + {72.3, 59.7, 400}, + {73.9, 55.2, 400}, + {78.4, 55.1, 400}, + }, + [4142] = { + {72.7, 68.6, 400}, + {69.3, 67.1, 400}, + {69.2, 65.6, 400}, + {70.9, 64.1, 400}, + {75.4, 63.1, 400}, + {74.5, 63.1, 400}, + {74.7, 60.9, 400}, + {76.2, 58.6, 400}, + {72.7, 58.4, 400}, + {74.7, 56.4, 400}, + {83.6, 56.2, 400}, + {76.1, 56.2, 400}, + {73, 55.8, 400}, + {82.3, 55.2, 400}, + {70.8, 55.1, 400}, + {81.4, 55.1, 400}, + {82.7, 55.1, 400}, + {83.1, 54.8, 400}, + {82.7, 54.4, 400}, + {76.7, 54.3, 400}, + {82.3, 54.1, 400}, + {83.7, 54, 400}, + {77.6, 54, 400}, + {75.1, 53.1, 400}, + {81.3, 52.7, 400}, + {70.8, 66.5, 400}, + {72.4, 59.7, 400}, + {73.9, 55.1, 400}, + {81.5, 54.9, 400}, + {77.6, 53.9, 400}, + }, + [4143] = { + {88.3, 79.1, 400}, + {72.1, 78.8, 400}, + {70.2, 78, 400}, + {70.5, 76.8, 400}, + {83.7, 74.6, 400}, + {87.3, 74.4, 400}, + {87.5, 73.3, 400}, + {78.2, 72.1, 400}, + {87.5, 71, 400}, + {77.2, 70.9, 400}, + {82.9, 70, 400}, + {76.9, 68.4, 400}, + {83.9, 67.4, 400}, + {84.4, 66.6, 400}, + {79.9, 66.3, 400}, + {84.8, 64.4, 400}, + {87.3, 61.1, 400}, + {86, 61, 400}, + {83.8, 60.7, 400}, + {83.7, 58.6, 400}, + {86, 58.2, 400}, + {81.4, 56.2, 400}, + {82.1, 54.8, 400}, + {82.9, 54, 400}, + {78.9, 53.8, 400}, + {81.6, 53.5, 400}, + {79, 51.6, 400}, + {83.6, 56.2, 400}, + {82.7, 55.1, 400}, + {82.7, 54.4, 400}, + {82.3, 54.1, 400}, + {83.7, 54, 400}, + {81.4, 56.5, 400}, + {82.3, 54.2, 400}, + }, + [4144] = { + {81.7, 89.2, 400}, + {72.4, 88.8, 400}, + {79.9, 85.7, 400}, + {82.2, 85.1, 400}, + {84.2, 84.5, 400}, + {84.8, 81.8, 400}, + }, + [4154] = { + {70.3, 70.9, 400}, + {88.2, 66, 400}, + {87.1, 65.7, 400}, + {87.9, 65.3, 400}, + {87.4, 65, 400}, + {85, 56.4, 400}, + {70.3, 55, 400}, + {71.2, 90, 400}, + {75.7, 88.4, 400}, + {78.1, 86.1, 400}, + {78.3, 86, 400}, + {83.9, 82.8, 400}, + {84.1, 81.9, 400}, + {84.5, 81.7, 400}, + {82.9, 80, 400}, + {69.1, 77, 400}, + {71.6, 73.3, 400}, + {71.9, 73.2, 400}, + {70.4, 69, 400}, + {88.2, 66.1, 400}, + {87.8, 65.8, 400}, + {87.4, 65.6, 400}, + {87.4, 64.9, 400}, + {80.7, 59.5, 400}, + {73.7, 58.9, 400}, + }, + [4158] = { + {77.9, 87.8, 400}, + {80.1, 85.8, 400}, + {73, 85.7, 400}, + {84.2, 77.4, 400}, + {73, 76.2, 400}, + {71, 75.3, 400}, + {83.2, 73.3, 400}, + {86, 73.2, 400}, + {84.2, 70.7, 400}, + {78, 89.8, 400}, + }, + [4170] = { + {30.7, 58, 141}, + {64.7, 53, 1657}, + }, + [4173] = { + {30.4, 60.7, 141}, + {63.3, 66.3, 1657}, + }, + [4182] = { + {37.4, 40.5, 148}, + }, + [4203] = { + {29.4, 56.2, 141}, + {58.8, 44.5, 1657}, + }, + [4241] = { + {31.9, 56.4, 141}, + {70.7, 45.4, 1657}, + }, + [4248] = { + {46.8, 97.2, 17}, + {42.4, 95.7, 17}, + {46, 95.6, 17}, + {45.3, 95.4, 17}, + {46, 95.2, 17}, + {41.2, 95.2, 17}, + {42.2, 95, 17}, + {41.7, 94.6, 17}, + {40.7, 94.4, 17}, + {42.1, 94.4, 17}, + {41.6, 94.3, 17}, + {40.6, 93.9, 17}, + {41.1, 57.7, 400}, + {49.2, 57.6, 400}, + {40, 55.8, 400}, + {35.8, 54.9, 400}, + {47.5, 54.6, 400}, + {48.7, 54, 400}, + {33.8, 52.8, 400}, + {38, 52.7, 400}, + {31.8, 52.2, 400}, + {34.9, 51.7, 400}, + {32.9, 51.6, 400}, + {29.8, 51.4, 400}, + {37.2, 51, 400}, + {32.2, 50.7, 400}, + {35.5, 50.7, 400}, + {37.2, 50.1, 400}, + {36.1, 49.5, 400}, + {31.9, 49.4, 400}, + {31.6, 47.1, 400}, + {37, 46.9, 400}, + {25.2, 46.9, 400}, + {35.3, 46.2, 400}, + {24.5, 44.9, 400}, + {27.8, 44.7, 400}, + {24.4, 43.7, 400}, + {22.8, 42.5, 400}, + {28.2, 41.8, 400}, + {21.4, 40.6, 400}, + {28.7, 40, 400}, + {27.2, 40, 400}, + {22.2, 38.2, 400}, + {22.9, 37.8, 400}, + {25.2, 37.4, 400}, + {38.3, 33.7, 400}, + {25.3, 32.5, 400}, + {28.1, 30.3, 400}, + {36.4, 30, 400}, + {35, 29.6, 400}, + {36.4, 29.1, 400}, + {25.3, 29, 400}, + {27.8, 28.6, 400}, + {26.6, 27.7, 400}, + {24.3, 27.3, 400}, + {27.6, 27.3, 400}, + {26.4, 27, 400}, + {24.1, 26.1, 400}, + }, + [4249] = { + {37.9, 91.3, 17}, + {37.8, 91.2, 17}, + {35.8, 87.4, 17}, + {92, 48.1, 357}, + {92.2, 44.7, 357}, + {90.9, 44.5, 357}, + {92.3, 44.2, 357}, + {93.4, 43.7, 357}, + {93, 42.9, 357}, + {92.7, 41.5, 357}, + {90.6, 40.4, 357}, + {66.3, 62.1, 400}, + {68.3, 61.9, 400}, + {64.2, 60.6, 400}, + {62.2, 60.3, 400}, + {58.8, 57.8, 400}, + {67.9, 57.5, 400}, + {60.2, 57.4, 400}, + {63.8, 56.5, 400}, + {60.4, 55.5, 400}, + {53.9, 54.7, 400}, + {59.6, 53.7, 400}, + {64.8, 52.8, 400}, + {65.3, 51.3, 400}, + {67.5, 51.1, 400}, + {66.6, 50.8, 400}, + {64, 50.5, 400}, + {65.8, 49.9, 400}, + {58.7, 49.2, 400}, + {64.9, 48.7, 400}, + {53.2, 46.7, 400}, + {63, 45.9, 400}, + {21.7, 34.2, 400}, + {18.8, 33.9, 400}, + {20.1, 33.3, 400}, + {15.7, 30.8, 400}, + {12.2, 25.8, 400}, + {11.8, 21.5, 400}, + {17.9, 20, 400}, + {17.7, 19.9, 400}, + {12.2, 16, 400}, + {10.1, 15.7, 400}, + {12.2, 15.3, 400}, + {14, 14.4, 400}, + {13.3, 13.1, 400}, + {12.9, 11, 400}, + {9.6, 9.3, 400}, + {53.9, 54.6, 400}, + }, + [4304] = { + {29.8, 87.3, 5135}, + {30, 86.3, 5135}, + {31, 86, 5135}, + {33.3, 65.2, 5135}, + {35.4, 58.1, 5135}, + {29.9, 56.4, 5135}, + {21.3, 49.8, 5135}, + }, + [4316] = { + {55.3, 46.5, 17}, + {55.6, 45.8, 17}, + {52.4, 44.9, 17}, + {52.7, 44.8, 17}, + {57, 42.9, 17}, + {54.1, 42.8, 17}, + {53.6, 42, 17}, + {48.7, 41.6, 17}, + {48.7, 41.5, 17}, + {53.3, 40.9, 17}, + {56.8, 40.6, 17}, + {52.8, 38.9, 17}, + {47.7, 37.3, 17}, + }, + [4341] = { + {59.5, 35.1, 15}, + {60, 34.9, 15}, + {59.4, 34.6, 15}, + {59.1, 34.4, 15}, + {53.7, 33.5, 15}, + {54.8, 32.3, 15}, + {58.4, 30.4, 15}, + {52.2, 29.9, 15}, + {53.7, 29.7, 15}, + {38, 29.5, 15}, + {51.6, 29.2, 15}, + {50.3, 29, 15}, + {51.2, 28.7, 15}, + {38.1, 28.5, 15}, + {56.9, 28.5, 15}, + {37, 28.2, 15}, + {50.7, 27.5, 15}, + {36.4, 27.2, 15}, + {40, 27.2, 15}, + {41.2, 26.9, 15}, + {37, 26.3, 15}, + {40.6, 26.3, 15}, + {57.3, 26.2, 15}, + {51.1, 26, 15}, + {35.9, 25.9, 15}, + {54.6, 25.5, 15}, + {40.2, 25.3, 15}, + {37.4, 25.2, 15}, + {58.1, 24.9, 15}, + {37.2, 24.7, 15}, + {53.4, 24.5, 15}, + {57.3, 24.3, 15}, + {41.7, 24.3, 15}, + {53.8, 23.8, 15}, + {51.8, 23.7, 15}, + {38.9, 23.4, 15}, + {41.4, 23.4, 15}, + {53.4, 22.6, 15}, + {37.3, 22.5, 15}, + {38.2, 22.5, 15}, + {49.9, 22.1, 15}, + {49.4, 22, 15}, + {46.2, 21.7, 15}, + {41.4, 21.4, 15}, + {46.4, 21.2, 15}, + {42.4, 21.1, 15}, + {40.2, 21, 15}, + {53.7, 20.7, 15}, + {45.9, 20.5, 15}, + {44.9, 20.5, 15}, + {50.4, 20.4, 15}, + {52.1, 20.3, 15}, + {51.7, 19.9, 15}, + {40.4, 19.8, 15}, + {42.8, 19.4, 15}, + {45, 19.1, 15}, + {37.3, 19, 15}, + {44.3, 18.4, 15}, + {43, 18.1, 15}, + {36.8, 18, 15}, + {38.7, 17.7, 15}, + {36.9, 16.8, 15}, + {43.3, 16.8, 15}, + {51.4, 16.3, 15}, + {42.7, 15.9, 15}, + {41.8, 14.2, 15}, + {37.6, 13.4, 15}, + {55.2, 69.2, 17}, + {55.3, 68.7, 17}, + {54.7, 68.6, 17}, + {54.4, 68.1, 17}, + {54.7, 67.6, 17}, + {54.1, 67.4, 17}, + {54.9, 67, 17}, + {54.8, 66.8, 17}, + {54.8, 65.6, 17}, + {55.3, 65.6, 17}, + {54.8, 63.8, 17}, + {54.6, 63.3, 17}, + {54.6, 62.7, 17}, + {55, 60.9, 17}, + {46, 20.6, 15}, + }, + [4342] = { + {41.9, 45.5, 15}, + {44.5, 45.2, 15}, + {43.3, 45.1, 15}, + {38, 45.1, 15}, + {34.8, 45, 15}, + {39.3, 44.9, 15}, + {41.1, 44.8, 15}, + {39.9, 44.6, 15}, + {36.1, 44.2, 15}, + {44.1, 43.9, 15}, + {44.5, 43.5, 15}, + {35.8, 43.4, 15}, + {39.3, 43.1, 15}, + {37, 42.8, 15}, + {33.4, 42.6, 15}, + {34.1, 42.3, 15}, + {44.8, 42, 15}, + {38, 42, 15}, + {40.4, 42, 15}, + {41, 41.9, 15}, + {41.8, 41.7, 15}, + {37.1, 41.6, 15}, + {39.2, 41.4, 15}, + {34.4, 41.3, 15}, + {34.1, 40.7, 15}, + {43.3, 39.5, 15}, + {44.7, 39.3, 15}, + {42.4, 39.2, 15}, + {41.4, 38.7, 15}, + {40.1, 38.3, 15}, + {40.8, 37.6, 15}, + {42, 37.6, 15}, + {43, 37.2, 15}, + {35.7, 35.9, 15}, + {56.2, 35.6, 15}, + {37.5, 35, 15}, + {36, 34.8, 15}, + {40.2, 34.6, 15}, + {38.8, 34.4, 15}, + {41, 33.8, 15}, + {34.5, 33.8, 15}, + {40.1, 33.1, 15}, + {41.1, 33, 15}, + {40.7, 32, 15}, + {41.1, 30.9, 15}, + {54.3, 30.7, 15}, + {41.3, 29, 15}, + {41.9, 28.4, 15}, + {42.3, 28.2, 15}, + {56.1, 28, 15}, + {58.4, 27.3, 15}, + {40.6, 26.5, 15}, + {37.1, 23.4, 15}, + {52, 22.7, 15}, + {48.7, 21.8, 15}, + {37.5, 21.8, 15}, + {52.6, 21.1, 15}, + {45.1, 19.8, 15}, + {51.3, 18, 15}, + {43.6, 17.2, 15}, + {38.2, 15.3, 15}, + {42.2, 14.9, 15}, + {40.4, 14.1, 15}, + {39.1, 13.7, 15}, + {55.2, 77.3, 17}, + {53.5, 77.3, 17}, + {54.2, 76.9, 17}, + {54, 76.5, 17}, + {54.7, 76.1, 17}, + {52.8, 76.1, 17}, + {53.2, 75.9, 17}, + {55.2, 75.7, 17}, + {54.7, 75.5, 17}, + {53.3, 75.4, 17}, + {53.2, 75.1, 17}, + {54, 72.5, 17}, + {55, 72.1, 17}, + {54.1, 72, 17}, + {53.4, 71.5, 17}, + {54.7, 66.1, 17}, + {54.9, 65.2, 17}, + {55.3, 61.9, 17}, + {55.8, 61.1, 17}, + {60, 34.9, 15}, + {59.1, 34.4, 15}, + {53.7, 33.5, 15}, + {54.8, 32.3, 15}, + {58.4, 30.4, 15}, + {52.2, 29.9, 15}, + {53.7, 29.7, 15}, + {38, 29.5, 15}, + {51.2, 28.7, 15}, + {56.9, 28.5, 15}, + {36.4, 27.2, 15}, + {40, 27.2, 15}, + {41.2, 26.9, 15}, + {37, 26.3, 15}, + {57.3, 26.2, 15}, + {51.1, 26, 15}, + {40.2, 25.3, 15}, + {58.1, 24.9, 15}, + {57.3, 24.3, 15}, + {41.7, 24.3, 15}, + {51.8, 23.7, 15}, + {38.9, 23.4, 15}, + {38.2, 22.5, 15}, + {49.9, 22.1, 15}, + {41.4, 21.4, 15}, + {46.4, 21.2, 15}, + {40.2, 21, 15}, + {45.9, 20.5, 15}, + {44.9, 20.5, 15}, + {50.4, 20.4, 15}, + {52.1, 20.3, 15}, + {51.7, 19.9, 15}, + {40.4, 19.8, 15}, + {42.8, 19.4, 15}, + {55.2, 69.2, 17}, + {54.4, 68.1, 17}, + {54.7, 67.6, 17}, + {55.3, 65.6, 17}, + {41.6, 38.8, 15}, + {40.9, 37.9, 15}, + {42.1, 37.6, 15}, + {35.6, 35.8, 15}, + {53.9, 72.5, 17}, + {46, 20.6, 15}, + }, + [4343] = { + {48.9, 65.6, 15}, + {48.7, 64.9, 15}, + {41.8, 64.7, 15}, + {49.5, 64.4, 15}, + {47.2, 64.3, 15}, + {43.3, 63.8, 15}, + {46.7, 63.7, 15}, + {41.5, 63.3, 15}, + {50.3, 63.3, 15}, + {45.7, 62.7, 15}, + {44.7, 62.1, 15}, + {51, 62, 15}, + {45.5, 61.8, 15}, + {41.7, 61.7, 15}, + {43.7, 61.6, 15}, + {46.3, 61.4, 15}, + {42.5, 61.1, 15}, + {46.2, 60.9, 15}, + {43, 60.9, 15}, + {36.8, 60.8, 15}, + {35.9, 60.7, 15}, + {34.8, 60.6, 15}, + {40.9, 60.6, 15}, + {50.9, 60.4, 15}, + {42.5, 60, 15}, + {50.1, 59.9, 15}, + {37.9, 59.6, 15}, + {46.5, 59.6, 15}, + {36.2, 59.5, 15}, + {38.9, 59.4, 15}, + {36.4, 58.8, 15}, + {39.6, 58.7, 15}, + {43.2, 58.5, 15}, + {40.5, 58.5, 15}, + {34.8, 58.4, 15}, + {49.8, 58.2, 15}, + {36, 58.1, 15}, + {49.1, 58, 15}, + {41.4, 57.8, 15}, + {35.1, 57.4, 15}, + {44.1, 57.1, 15}, + {48.5, 56.7, 15}, + {43.4, 56.6, 15}, + {49.4, 56.3, 15}, + {41.7, 56, 15}, + {50.3, 56, 15}, + {42.7, 55.7, 15}, + {35.1, 55.6, 15}, + {48, 55.4, 15}, + {40.8, 54.8, 15}, + {47.1, 54.7, 15}, + {41.9, 54.7, 15}, + {50.7, 54.6, 15}, + {38.6, 54.3, 15}, + {46.2, 54.1, 15}, + {37.9, 54, 15}, + {42.5, 53.9, 15}, + {50.3, 53.7, 15}, + {47.8, 53.5, 15}, + {39, 53.5, 15}, + {49.9, 53.2, 15}, + {39.6, 53.1, 15}, + {50.7, 53.1, 15}, + {46, 52.9, 15}, + {47, 52, 15}, + {45.1, 51.7, 15}, + {39.9, 51.5, 15}, + {44.6, 51.2, 15}, + {36.9, 51.2, 15}, + {43.3, 50.8, 15}, + {39.1, 50.5, 15}, + {48, 50.2, 15}, + {40.2, 50.1, 15}, + {37.6, 50.1, 15}, + {35, 49.8, 15}, + {42.4, 49.8, 15}, + {43.7, 49.6, 15}, + {44.6, 49.5, 15}, + {48.1, 49.5, 15}, + {34.4, 49.5, 15}, + {45.8, 49.4, 15}, + {38.2, 49.1, 15}, + {42.3, 49, 15}, + {35.7, 49, 15}, + {39.5, 48.8, 15}, + {36.4, 48.2, 15}, + {42.6, 48, 15}, + {38.8, 47.9, 15}, + {45.2, 47.9, 15}, + {43.4, 47.5, 15}, + {35.5, 47.4, 15}, + {38, 47.2, 15}, + {44.5, 46.9, 15}, + {34.3, 46.9, 15}, + {36.8, 46.9, 15}, + {37.9, 46.8, 15}, + {35.2, 44.3, 15}, + {35.1, 34.4, 15}, + {54.6, 85.4, 17}, + {54.1, 85.4, 17}, + {53.5, 85.3, 17}, + {55.2, 84.8, 17}, + {54.3, 84.8, 17}, + {54.4, 84.4, 17}, + {53.5, 84.2, 17}, + {54.1, 84, 17}, + {53.7, 83.7, 17}, + {53.7, 82.8, 17}, + {55.2, 81.9, 17}, + {54.6, 80.5, 17}, + {55, 79.9, 17}, + {53.6, 79.8, 17}, + {53.3, 79.6, 17}, + {55.3, 79.4, 17}, + {54, 79.3, 17}, + {54.4, 78.9, 17}, + {53.9, 78.5, 17}, + {55.2, 78.4, 17}, + {53.3, 78.3, 17}, + {54.6, 78.2, 17}, + {55.1, 78.2, 17}, + {53.7, 76.9, 17}, + {53.7, 71.8, 17}, + {41.9, 45.5, 15}, + {44.5, 45.2, 15}, + {43.3, 45.1, 15}, + {38, 45.1, 15}, + {34.8, 45, 15}, + {39.3, 44.9, 15}, + {41.1, 44.8, 15}, + {39.9, 44.6, 15}, + {36.1, 44.2, 15}, + {44.1, 43.9, 15}, + {44.5, 43.5, 15}, + {35.8, 43.4, 15}, + {39.3, 43.1, 15}, + {37, 42.8, 15}, + {33.4, 42.6, 15}, + {34.1, 42.3, 15}, + {44.8, 42, 15}, + {38, 42, 15}, + {40.4, 42, 15}, + {41, 41.9, 15}, + {41.8, 41.7, 15}, + {37.1, 41.6, 15}, + {39.2, 41.4, 15}, + {34.4, 41.3, 15}, + {34.1, 40.7, 15}, + {43.3, 39.5, 15}, + {44.7, 39.3, 15}, + {42.4, 39.2, 15}, + {41.4, 38.7, 15}, + {40.1, 38.3, 15}, + {40.8, 37.6, 15}, + {42, 37.6, 15}, + {43, 37.2, 15}, + {35.7, 35.9, 15}, + {37.5, 35, 15}, + {36, 34.8, 15}, + {40.2, 34.6, 15}, + {38.8, 34.4, 15}, + {41, 33.8, 15}, + {34.5, 33.8, 15}, + {40.1, 33.1, 15}, + {41.1, 33, 15}, + {40.7, 32, 15}, + {41.1, 30.9, 15}, + {41.3, 29, 15}, + {42.3, 28.2, 15}, + {55.2, 77.3, 17}, + {53.5, 77.3, 17}, + {54.2, 76.9, 17}, + {54, 76.5, 17}, + {54.7, 76.1, 17}, + {52.8, 76.1, 17}, + {53.2, 75.9, 17}, + {55.2, 75.7, 17}, + {54.7, 75.5, 17}, + {53.3, 75.4, 17}, + {53.2, 75.1, 17}, + {54, 72.5, 17}, + {55, 72.1, 17}, + {54.1, 72, 17}, + {53.4, 71.5, 17}, + {40.2, 50.3, 15}, + {41.6, 38.8, 15}, + {40.9, 37.9, 15}, + {42.1, 37.6, 15}, + {35.6, 35.8, 15}, + {53.9, 72.5, 17}, + }, + [4344] = { + {41.1, 60.6, 15}, + {37.9, 57.7, 15}, + {38.6, 57.5, 15}, + {40.4, 51.8, 15}, + {55.1, 83.9, 17}, + {48.9, 65.6, 15}, + {48.7, 64.9, 15}, + {41.8, 64.7, 15}, + {49.5, 64.4, 15}, + {47.2, 64.3, 15}, + {43.3, 63.8, 15}, + {46.7, 63.7, 15}, + {41.5, 63.3, 15}, + {50.3, 63.3, 15}, + {45.7, 62.7, 15}, + {44.7, 62.1, 15}, + {51, 62, 15}, + {45.5, 61.8, 15}, + {41.7, 61.7, 15}, + {43.7, 61.6, 15}, + {46.3, 61.4, 15}, + {42.5, 61.1, 15}, + {46.2, 60.9, 15}, + {43, 60.9, 15}, + {36.8, 60.8, 15}, + {35.9, 60.7, 15}, + {34.8, 60.6, 15}, + {40.9, 60.6, 15}, + {50.9, 60.4, 15}, + {42.5, 60, 15}, + {50.1, 59.9, 15}, + {37.9, 59.6, 15}, + {46.5, 59.6, 15}, + {36.2, 59.5, 15}, + {38.9, 59.4, 15}, + {36.4, 58.8, 15}, + {39.6, 58.7, 15}, + {43.2, 58.5, 15}, + {40.5, 58.5, 15}, + {34.8, 58.4, 15}, + {49.8, 58.2, 15}, + {36, 58.1, 15}, + {49.1, 58, 15}, + {41.4, 57.8, 15}, + {35.1, 57.4, 15}, + {44.1, 57.1, 15}, + {48.5, 56.7, 15}, + {43.4, 56.6, 15}, + {49.4, 56.3, 15}, + {41.7, 56, 15}, + {50.3, 56, 15}, + {42.7, 55.7, 15}, + {48, 55.4, 15}, + {40.8, 54.8, 15}, + {47.1, 54.7, 15}, + {41.9, 54.7, 15}, + {50.7, 54.6, 15}, + {38.6, 54.3, 15}, + {46.2, 54.1, 15}, + {37.9, 54, 15}, + {42.5, 53.9, 15}, + {50.3, 53.7, 15}, + {47.8, 53.5, 15}, + {39, 53.5, 15}, + {49.9, 53.2, 15}, + {39.6, 53.1, 15}, + {50.7, 53.1, 15}, + {46, 52.9, 15}, + {47, 52, 15}, + {45.1, 51.7, 15}, + {44.6, 51.2, 15}, + {36.9, 51.2, 15}, + {43.3, 50.8, 15}, + {48, 50.2, 15}, + {40.2, 50.1, 15}, + {37.6, 50.1, 15}, + {35, 49.8, 15}, + {42.4, 49.8, 15}, + {43.7, 49.6, 15}, + {44.6, 49.5, 15}, + {48.1, 49.5, 15}, + {34.4, 49.5, 15}, + {45.8, 49.4, 15}, + {38.2, 49.1, 15}, + {42.3, 49, 15}, + {35.7, 49, 15}, + {39.5, 48.8, 15}, + {36.4, 48.2, 15}, + {42.6, 48, 15}, + {38.8, 47.9, 15}, + {45.2, 47.9, 15}, + {43.4, 47.5, 15}, + {35.5, 47.4, 15}, + {38, 47.2, 15}, + {44.5, 46.9, 15}, + {36.8, 46.9, 15}, + {37.9, 46.8, 15}, + {54.6, 85.4, 17}, + {54.1, 85.4, 17}, + {53.5, 85.3, 17}, + {55.2, 84.8, 17}, + {54.3, 84.8, 17}, + {54.4, 84.4, 17}, + {53.5, 84.2, 17}, + {54.1, 84, 17}, + {53.7, 83.7, 17}, + {55.2, 81.9, 17}, + {54.6, 80.5, 17}, + {55, 79.9, 17}, + {53.6, 79.8, 17}, + {53.3, 79.6, 17}, + {55.3, 79.4, 17}, + {54, 79.3, 17}, + {54.4, 78.9, 17}, + {53.9, 78.5, 17}, + {55.2, 78.4, 17}, + {54.6, 78.2, 17}, + {55.1, 78.2, 17}, + {40.2, 50.3, 15}, + }, + [4345] = { + {50.5, 80.9, 15}, + {49.9, 79.9, 15}, + {47.8, 79.8, 15}, + {45.3, 79.8, 15}, + {44.2, 79.5, 15}, + {49.2, 79.5, 15}, + {46.7, 79.1, 15}, + {47.3, 78.8, 15}, + {43.2, 78.3, 15}, + {48.8, 78.3, 15}, + {45.4, 77.6, 15}, + {47.7, 77.5, 15}, + {43.6, 76.9, 15}, + {46.1, 76.7, 15}, + {50.8, 76.5, 15}, + {46.5, 76.1, 15}, + {44, 76.1, 15}, + {47.7, 75.8, 15}, + {46, 75, 15}, + {47.3, 74.9, 15}, + {49.6, 74.9, 15}, + {51, 74.8, 15}, + {43.2, 74.8, 15}, + {55.4, 73.9, 15}, + {49.3, 73.9, 15}, + {46.6, 73.8, 15}, + {50.1, 73.5, 15}, + {44.5, 73.1, 15}, + {55.9, 73.1, 15}, + {43.5, 73, 15}, + {52.1, 72.9, 15}, + {47.2, 72.8, 15}, + {51.2, 72.7, 15}, + {38.6, 72.7, 15}, + {55.6, 72.2, 15}, + {49, 72.2, 15}, + {54.2, 72.1, 15}, + {47.8, 71.9, 15}, + {37.6, 71.9, 15}, + {56.4, 71.9, 15}, + {42.9, 71.7, 15}, + {53, 71.6, 15}, + {41.6, 71.6, 15}, + {39.8, 71.5, 15}, + {55, 71.3, 15}, + {38.4, 71.2, 15}, + {49.8, 71, 15}, + {43.3, 70.9, 15}, + {47.1, 70.8, 15}, + {44.5, 70.8, 15}, + {48.3, 70.7, 15}, + {41, 70.7, 15}, + {53.6, 70.7, 15}, + {52.5, 70.6, 15}, + {51, 70.6, 15}, + {42.6, 70.3, 15}, + {50.3, 70, 15}, + {44.9, 69.8, 15}, + {55.2, 69.8, 15}, + {39, 69.7, 15}, + {46.3, 69.7, 15}, + {56.7, 69.6, 15}, + {52.3, 69.2, 15}, + {54.2, 69.2, 15}, + {56, 69.2, 15}, + {42.1, 69.2, 15}, + {50.9, 69.1, 15}, + {41.4, 69.1, 15}, + {47, 68.9, 15}, + {48.4, 68.7, 15}, + {44, 68.2, 15}, + {50.5, 68.2, 15}, + {41.2, 66.5, 15}, + {55, 91.2, 17}, + {55.4, 90.9, 17}, + {42.2, 68.6, 15}, + }, + [4351] = { + {63.6, 40.2, 15}, + {57.9, 38.6, 15}, + {56.3, 38.4, 15}, + {62.1, 37.9, 15}, + {58.5, 37.9, 15}, + {61.5, 36.8, 15}, + {54.8, 35.7, 15}, + {60.8, 35.1, 15}, + {52.3, 34.9, 15}, + {50.9, 33.8, 15}, + {50.8, 31.9, 15}, + {57.3, 30, 15}, + {60.1, 29.4, 15}, + {46.3, 28.2, 15}, + {52.9, 28, 15}, + {48.4, 27.8, 15}, + {37.7, 27.7, 15}, + {34, 27.7, 15}, + {60.2, 27.3, 15}, + {52.2, 27.2, 15}, + {49.6, 26.7, 15}, + {38.8, 26, 15}, + {37.7, 25.8, 15}, + {40.9, 25.3, 15}, + {56.5, 25, 15}, + {38.5, 24.9, 15}, + {60, 24.6, 15}, + {38.1, 24.1, 15}, + {56.3, 23.8, 15}, + {44, 22.4, 15}, + {39.4, 22.4, 15}, + {50.8, 21.5, 15}, + {52, 21.4, 15}, + {44.1, 21.2, 15}, + {55.5, 21.2, 15}, + {45.1, 20.9, 15}, + {51.6, 20.6, 15}, + {58.8, 20.6, 15}, + {43.7, 20.2, 15}, + {38.9, 19.6, 15}, + {50.2, 19.5, 15}, + {41.5, 19.3, 15}, + {40.8, 18.8, 15}, + {48.3, 18.8, 15}, + {42.1, 18.6, 15}, + {48.2, 18, 15}, + {41.4, 17.7, 15}, + {47.5, 17.6, 15}, + {40.2, 17.6, 15}, + {52.3, 17, 15}, + {45.8, 16.6, 15}, + {50.9, 16, 15}, + {49.1, 15.8, 15}, + {46.7, 15.5, 15}, + {52.7, 15.4, 15}, + {35.7, 14.8, 15}, + {37, 14.8, 15}, + {39.6, 14.3, 15}, + {44, 13.4, 15}, + {51.3, 12.9, 15}, + {43.7, 12.5, 15}, + {51.6, 12.2, 15}, + {37.1, 12.2, 15}, + {41.8, 12, 15}, + {41.8, 11.6, 15}, + {40.5, 11.5, 15}, + {40.7, 11.3, 15}, + {41, 9.8, 15}, + {42.6, 9.7, 15}, + {55.1, 68.3, 17}, + {53.1, 68.3, 17}, + {55, 67.3, 17}, + {55.2, 66.4, 17}, + {54, 61.6, 17}, + {54.7, 61.6, 17}, + {56, 61.3, 17}, + {62.1, 60.7, 17}, + {58.2, 60.4, 17}, + {62.2, 60.3, 17}, + {54.7, 60.3, 17}, + {57.2, 60.2, 17}, + {57.1, 60, 17}, + {56.5, 59.9, 17}, + {56.6, 59.8, 17}, + {56.8, 59.1, 17}, + {57.6, 59, 17}, + {56.4, 25.2, 15}, + {56.3, 23.5, 15}, + }, + [4352] = { + {51.6, 22, 15}, + {49.1, 20.5, 15}, + {47.5, 20.2, 15}, + {49.7, 19.8, 15}, + {48.4, 19.6, 15}, + {45.9, 19.5, 15}, + {48.7, 19.5, 15}, + {46.8, 19.3, 15}, + {52.4, 19.3, 15}, + {47.4, 19, 15}, + {46.3, 18.9, 15}, + {49, 18.7, 15}, + {45.9, 17.6, 15}, + {49.7, 17.5, 15}, + {46.5, 16.6, 15}, + {47.3, 16.5, 15}, + {48.2, 16.5, 15}, + {48.6, 15.8, 15}, + {47.8, 15.8, 15}, + {47.3, 15.7, 15}, + {50.5, 15.7, 15}, + {52.2, 15.2, 15}, + {47.2, 15.1, 15}, + {48, 14.9, 15}, + {50.2, 19.5, 15}, + {48.2, 18, 15}, + {47.5, 17.6, 15}, + {50.9, 16, 15}, + {49.1, 15.8, 15}, + {49.2, 20.6, 15}, + {46, 19.5, 15}, + {48, 18.3, 15}, + {46.6, 16.6, 15}, + }, + [4355] = { + {49.5, 63.7, 15}, + {48.3, 63.3, 15}, + {42.2, 63.1, 15}, + {48.1, 62.4, 15}, + {48.9, 62.1, 15}, + {46.6, 61.9, 15}, + {49.7, 61.8, 15}, + {43.3, 61.7, 15}, + {48.5, 61.6, 15}, + {43.8, 60.7, 15}, + {49, 60.6, 15}, + {41.5, 60.4, 15}, + {40.2, 60.1, 15}, + {45.6, 60, 15}, + {41.9, 59.8, 15}, + {49.6, 59.5, 15}, + {44.7, 59.5, 15}, + {35.4, 59.4, 15}, + {48.2, 59.2, 15}, + {41.7, 59.2, 15}, + {48.8, 59, 15}, + {47.9, 58.5, 15}, + {33.2, 57.8, 15}, + {39.8, 57.2, 15}, + {36.4, 56.7, 15}, + {40.4, 56.3, 15}, + {45.7, 56.1, 15}, + {36.9, 55.8, 15}, + {38.2, 55.7, 15}, + {39.6, 55.6, 15}, + {35.8, 55.5, 15}, + {47, 55.5, 15}, + {44.6, 55.2, 15}, + {45.3, 54.9, 15}, + {33.9, 54.8, 15}, + {40.1, 54.7, 15}, + {36.9, 54.1, 15}, + {43.2, 53.9, 15}, + {43.9, 53, 15}, + {41.5, 52.8, 15}, + {40.2, 52.4, 15}, + {38.5, 52.4, 15}, + {40.9, 51.6, 15}, + {35.8, 51.4, 15}, + {38.9, 51, 15}, + {36.3, 51, 15}, + {41.5, 50.4, 15}, + {41.9, 49.9, 15}, + {41.3, 49, 15}, + {44.1, 49, 15}, + {44.8, 48.7, 15}, + {37.3, 48.5, 15}, + {43.4, 48.2, 15}, + {40.8, 48.2, 15}, + {41.7, 47.6, 15}, + {40.2, 47.4, 15}, + {42, 46.9, 15}, + {43.9, 46.8, 15}, + {39.1, 46.4, 15}, + {46.7, 45.8, 15}, + {46.9, 44.9, 15}, + {48.5, 44.8, 15}, + {42.7, 44.3, 15}, + {37.5, 44.2, 15}, + {41.3, 43.7, 15}, + {41, 43.7, 15}, + {43.3, 43.3, 15}, + {45.9, 43.3, 15}, + {38.2, 43.2, 15}, + {42.1, 43.1, 15}, + {41.5, 43.1, 15}, + {42.6, 42.6, 15}, + {45.5, 42.1, 15}, + {43, 41.8, 15}, + {46, 41.2, 15}, + {35.4, 40.9, 15}, + {40.1, 40.4, 15}, + {41.2, 40.2, 15}, + {46, 39.8, 15}, + {45.6, 38.3, 15}, + {37.4, 38.2, 15}, + {45, 36.6, 15}, + {43.5, 35.2, 15}, + {43.5, 35.1, 15}, + {41.6, 34.8, 15}, + {44, 32.6, 15}, + {44.1, 30.7, 15}, + {44.3, 30.3, 15}, + {45, 28.6, 15}, + {45, 27.3, 15}, + {53.9, 84.7, 17}, + {52.7, 83.9, 17}, + {54.4, 83.3, 17}, + {54.6, 82.8, 17}, + {55.3, 82.8, 17}, + {54, 82.7, 17}, + {53.1, 82.3, 17}, + {54.6, 82, 17}, + {54, 80.6, 17}, + {54.3, 80.4, 17}, + {54.8, 79.1, 17}, + {55, 76.9, 17}, + {55.3, 76.3, 17}, + {53.9, 75.2, 17}, + {54.9, 73.8, 17}, + {44.9, 60.4, 15}, + {47.9, 59.3, 15}, + }, + [4356] = { + {34.2, 73.2, 15}, + {35, 72.1, 15}, + {33.8, 72, 15}, + {34.5, 71, 15}, + {38, 70.4, 15}, + {36.1, 69.9, 15}, + {33.7, 69.7, 15}, + {36.9, 68.8, 15}, + {38.1, 68.8, 15}, + {34.2, 68.6, 15}, + {37.5, 68, 15}, + {38.2, 67.2, 15}, + {32, 66.8, 15}, + {37.8, 66.6, 15}, + {35.1, 66.6, 15}, + {32.7, 66.2, 15}, + {33.1, 65.7, 15}, + {32.6, 65.4, 15}, + {34, 65.1, 15}, + {33.1, 65.1, 15}, + {32.6, 64.9, 15}, + {32.1, 64.4, 15}, + {33.1, 64.3, 15}, + {31.5, 64.1, 15}, + {32.2, 64.1, 15}, + {53.2, 91.9, 17}, + {53.7, 91.3, 17}, + {53, 91.3, 17}, + {53.4, 90.8, 17}, + {55.2, 90.4, 17}, + {54.2, 90.2, 17}, + {52.9, 90.1, 17}, + {54.6, 89.6, 17}, + {55.2, 89.6, 17}, + {53.2, 89.5, 17}, + {54.9, 89.2, 17}, + {55.3, 88.8, 17}, + {52.1, 88.6, 17}, + {55.1, 88.5, 17}, + {53.7, 88.4, 17}, + {52.5, 88.3, 17}, + {52.7, 88, 17}, + {52.4, 87.8, 17}, + {53.1, 87.7, 17}, + {52.6, 87.7, 17}, + {52.4, 87.6, 17}, + {52.1, 87.3, 17}, + {52.6, 87.3, 17}, + {51.9, 87.2, 17}, + {52.2, 87.2, 17}, + }, + [4357] = { + {36.4, 66.1, 15}, + {31.3, 66, 15}, + {31.4, 65.8, 15}, + {32.1, 65.8, 15}, + {31.8, 65.7, 15}, + {33.1, 65, 15}, + {33.7, 64.1, 15}, + {54.4, 88.2, 17}, + {51.7, 88.2, 17}, + {51.8, 88, 17}, + {52.1, 88, 17}, + {52, 88, 17}, + {52.7, 87.6, 17}, + {52.9, 87.2, 17}, + {34.2, 73.2, 15}, + {35, 72.1, 15}, + {33.8, 72, 15}, + {34.5, 71, 15}, + {38, 70.4, 15}, + {36.1, 69.9, 15}, + {33.7, 69.7, 15}, + {36.9, 68.8, 15}, + {38.1, 68.8, 15}, + {34.2, 68.6, 15}, + {37.5, 68, 15}, + {38.2, 67.2, 15}, + {32, 66.8, 15}, + {37.8, 66.6, 15}, + {35.1, 66.6, 15}, + {32.7, 66.2, 15}, + {33.1, 65.7, 15}, + {32.6, 65.4, 15}, + {34, 65.1, 15}, + {33.1, 65.1, 15}, + {32.6, 64.9, 15}, + {32.1, 64.4, 15}, + {33.1, 64.3, 15}, + {31.5, 64.1, 15}, + {32.2, 64.1, 15}, + {53.2, 91.9, 17}, + {53.7, 91.3, 17}, + {53, 91.3, 17}, + {53.4, 90.8, 17}, + {55.2, 90.4, 17}, + {54.2, 90.2, 17}, + {52.9, 90.1, 17}, + {54.6, 89.6, 17}, + {55.2, 89.6, 17}, + {53.2, 89.5, 17}, + {54.9, 89.2, 17}, + {55.3, 88.8, 17}, + {52.1, 88.6, 17}, + {55.1, 88.5, 17}, + {53.7, 88.4, 17}, + {52.5, 88.3, 17}, + {52.7, 88, 17}, + {52.4, 87.8, 17}, + {53.1, 87.7, 17}, + {52.6, 87.7, 17}, + {52.4, 87.6, 17}, + {52.1, 87.3, 17}, + {52.6, 87.3, 17}, + {51.9, 87.2, 17}, + {52.2, 87.2, 17}, + }, + [4376] = { + {34.5, 25.3, 15}, + {34.8, 24.3, 15}, + {35.1, 24.2, 15}, + {34.3, 24.1, 15}, + {32.5, 23.3, 15}, + {34.3, 23.2, 15}, + {33.8, 22.4, 15}, + {35.1, 22.4, 15}, + {32.3, 22.3, 15}, + {31.5, 22, 15}, + {34.1, 21.9, 15}, + {31.3, 21.9, 15}, + {30.3, 21.8, 15}, + {35.1, 21.4, 15}, + {34.4, 21.4, 15}, + {33.8, 21.4, 15}, + {30.2, 21.3, 15}, + {34.4, 20.9, 15}, + {35, 20.4, 15}, + {30.8, 20.3, 15}, + {53.4, 67.1, 17}, + {53.5, 66.5, 17}, + {53.7, 66.5, 17}, + {53.3, 66.5, 17}, + {52.3, 66, 17}, + {53.3, 66, 17}, + {53, 65.6, 17}, + {53.7, 65.6, 17}, + {52.3, 65.5, 17}, + {51.8, 65.3, 17}, + {53.2, 65.3, 17}, + {51.7, 65.3, 17}, + {51.2, 65.2, 17}, + {53.7, 65.1, 17}, + {53.3, 65.1, 17}, + {53, 65.1, 17}, + {51.2, 65, 17}, + {53.3, 64.8, 17}, + {53.7, 64.5, 17}, + {51.5, 64.5, 17}, + {34.6, 25.3, 15}, + {35.1, 24.3, 15}, + {34.3, 23.1, 15}, + {35.1, 22.5, 15}, + {33.9, 22.4, 15}, + {35, 21.5, 15}, + {34.5, 21.4, 15}, + {35, 20.3, 15}, + {53.7, 66.6, 17}, + {53.3, 65.9, 17}, + {53.4, 65.1, 17}, + {53.6, 64.5, 17}, + }, + [4377] = { + {33, 22.8, 15}, + {32.6, 22.6, 15}, + {31.5, 22.5, 15}, + {32.5, 22.5, 15}, + {32, 22.5, 15}, + {31.9, 22.2, 15}, + {52.6, 65.8, 17}, + {52.4, 65.7, 17}, + {51.8, 65.6, 17}, + {52.4, 65.6, 17}, + {52.1, 65.6, 17}, + {52.1, 65.5, 17}, + {32.5, 23.3, 15}, + {52.3, 66, 17}, + {31.9, 22.3, 15}, + }, + [4378] = { + {34.9, 23.1, 15}, + {34.1, 22.9, 15}, + {30.7, 22.3, 15}, + {31.3, 22, 15}, + {32.8, 21.3, 15}, + {33.6, 20.6, 15}, + {30.6, 20.3, 15}, + {53.6, 65.9, 17}, + {53.2, 65.8, 17}, + {51.4, 65.5, 17}, + {51.7, 65.4, 17}, + {52.5, 65, 17}, + {52.9, 64.6, 17}, + {51.4, 64.5, 17}, + {34.5, 25.3, 15}, + {35.1, 24.2, 15}, + {34.3, 24.1, 15}, + {32.6, 22.6, 15}, + {33.8, 22.4, 15}, + {35.1, 22.4, 15}, + {34.1, 21.9, 15}, + {35.1, 21.4, 15}, + {34.4, 21.4, 15}, + {33.8, 21.4, 15}, + {34.4, 20.9, 15}, + {35, 20.4, 15}, + {53.4, 67.1, 17}, + {53.7, 66.5, 17}, + {53.3, 66.5, 17}, + {52.4, 65.7, 17}, + {53, 65.6, 17}, + {53.7, 65.6, 17}, + {53.2, 65.3, 17}, + {53.7, 65.1, 17}, + {53.3, 65.1, 17}, + {53, 65.1, 17}, + {53.3, 64.8, 17}, + {53.7, 64.5, 17}, + {33.8, 21.3, 15}, + {34.5, 20.9, 15}, + {53, 65, 17}, + {53.4, 64.8, 17}, + {35.1, 22.5, 15}, + {35, 21.5, 15}, + {35, 20.3, 15}, + {53.6, 64.5, 17}, + }, + [4379] = { + {34.8, 22.8, 15}, + {31.1, 22.5, 15}, + {30.8, 22.4, 15}, + {34.4, 22.3, 15}, + {31.8, 21.5, 15}, + {31.2, 21.5, 15}, + {35.7, 21.1, 15}, + {30.6, 20.8, 15}, + {53.5, 65.8, 17}, + {51.6, 65.6, 17}, + {51.5, 65.6, 17}, + {53.3, 65.5, 17}, + {52, 65.1, 17}, + {51.7, 65.1, 17}, + {54, 64.9, 17}, + {51.4, 64.8, 17}, + {34.8, 24.3, 15}, + {34.3, 23.2, 15}, + {34.9, 23.1, 15}, + {33, 22.8, 15}, + {31.5, 22.5, 15}, + {32, 22.5, 15}, + {30.7, 22.3, 15}, + {32.3, 22.3, 15}, + {31.9, 22.2, 15}, + {31.5, 22, 15}, + {31.3, 21.9, 15}, + {32.8, 21.3, 15}, + {33.6, 20.6, 15}, + {30.8, 20.3, 15}, + {53.5, 66.5, 17}, + {53.3, 66, 17}, + {53.6, 65.9, 17}, + {52.6, 65.8, 17}, + {51.8, 65.6, 17}, + {52.1, 65.6, 17}, + {51.4, 65.5, 17}, + {52.3, 65.5, 17}, + {52.1, 65.5, 17}, + {51.8, 65.3, 17}, + {51.7, 65.3, 17}, + {52.5, 65, 17}, + {52.9, 64.6, 17}, + {51.5, 64.5, 17}, + {33.8, 21.4, 15}, + {34.4, 20.9, 15}, + {53, 65.1, 17}, + {53.3, 64.8, 17}, + {31.5, 22.6, 15}, + {51.8, 65.7, 17}, + {34.3, 23.1, 15}, + {31.9, 22.3, 15}, + {33.8, 21.3, 15}, + {34.5, 20.9, 15}, + {53.3, 65.9, 17}, + {53, 65, 17}, + {53.4, 64.8, 17}, + }, + [4380] = { + {31.2, 20.4, 15}, + {51.6, 64.5, 17}, + }, + [4396] = { + {64.8, 40.8, 15}, + {64.7, 39.7, 15}, + {63.5, 38.2, 15}, + {63.3, 36.4, 15}, + {63, 33.9, 15}, + {62.2, 33.5, 15}, + {61.7, 30.8, 15}, + {64.2, 30.6, 15}, + {65.3, 30.1, 15}, + {63.4, 29.3, 15}, + {62.2, 28.8, 15}, + {65.6, 28.1, 15}, + {61.4, 27.3, 15}, + {62, 26.6, 15}, + {63.8, 25.4, 15}, + {62.4, 25.3, 15}, + {65, 24.8, 15}, + {61.9, 22.7, 15}, + {61.4, 21, 15}, + {64.1, 20.4, 15}, + {63.3, 20.1, 15}, + {60.4, 16.5, 15}, + {60, 15.4, 15}, + {54.5, 13.5, 15}, + {54.3, 12.9, 15}, + {54.7, 12.5, 15}, + {54.3, 11.4, 15}, + {60.3, 10.9, 15}, + {63.6, 59.9, 17}, + {66.8, 59.6, 17}, + {62.8, 33.8, 15}, + {62.4, 25.1, 15}, + }, + [4397] = { + {65, 42.2, 15}, + {63.2, 37.5, 15}, + {63.1, 35.7, 15}, + {62.1, 31.1, 15}, + {65.9, 30.6, 15}, + {64.3, 30.1, 15}, + {63, 30.1, 15}, + {62.4, 28.6, 15}, + {61.7, 28.1, 15}, + {66.1, 28.1, 15}, + {63.1, 25.9, 15}, + {62.2, 20.8, 15}, + {63.6, 20.2, 15}, + {60.3, 19.3, 15}, + {64.4, 18.7, 15}, + {60.2, 18.6, 15}, + {63.3, 17.1, 15}, + {55.8, 15.9, 15}, + {62.4, 15.7, 15}, + {59.6, 14.5, 15}, + {55.1, 14.3, 15}, + {55, 13.2, 15}, + {61.1, 11.1, 15}, + {62.2, 10.1, 15}, + {56.7, 9.2, 15}, + {62.1, 9.1, 15}, + {56.1, 9.1, 15}, + {64.3, 9, 15}, + {60.2, 8.7, 15}, + {57.6, 8.7, 15}, + {57.5, 7.8, 15}, + {60, 7.7, 15}, + {61.9, 7.7, 15}, + {61.1, 7.6, 15}, + {60.9, 7.6, 15}, + {64.6, 7.2, 15}, + {59.1, 6.8, 15}, + {57.9, 6.4, 15}, + {62.5, 5.6, 15}, + {63, 5.3, 15}, + {67.2, 59.7, 17}, + {67.7, 59.2, 17}, + {64.9, 58.7, 17}, + {67.7, 58.7, 17}, + {64.6, 58.7, 17}, + {68.8, 58.6, 17}, + {66.7, 58.5, 17}, + {65.3, 58.4, 17}, + {65.3, 58, 17}, + {66.6, 57.9, 17}, + {67.6, 57.9, 17}, + {67.2, 57.9, 17}, + {67.1, 57.9, 17}, + {69, 57.7, 17}, + {66.1, 57.5, 17}, + {65.5, 57.3, 17}, + {67.9, 56.9, 17}, + {68.1, 56.7, 17}, + {64.8, 40.8, 15}, + {64.7, 39.7, 15}, + {63.5, 38.2, 15}, + {63.3, 36.4, 15}, + {63, 33.9, 15}, + {61.7, 30.8, 15}, + {64.2, 30.6, 15}, + {65.3, 30.1, 15}, + {63.4, 29.3, 15}, + {62, 26.6, 15}, + {63.8, 25.4, 15}, + {62.4, 25.3, 15}, + {61.9, 22.7, 15}, + {61.4, 21, 15}, + {64.1, 20.4, 15}, + {63.3, 20.1, 15}, + {60.4, 16.5, 15}, + {54.3, 12.9, 15}, + {54.7, 12.5, 15}, + {54.3, 11.4, 15}, + {63.6, 59.9, 17}, + {64.2, 30.2, 15}, + {63.1, 30, 15}, + {61.8, 28.2, 15}, + {62.8, 33.8, 15}, + {62.4, 25.1, 15}, + }, + [4398] = { + {58.9, 51.5, 15}, + {55.7, 48.7, 15}, + {56.4, 48.5, 15}, + {57.8, 48.4, 15}, + {60.7, 47.8, 15}, + {54.4, 47.7, 15}, + {56.4, 45.8, 15}, + {57.3, 45.4, 15}, + {52, 43.5, 15}, + {56.8, 42.7, 15}, + {55.4, 42.5, 15}, + {51.3, 42, 15}, + {57.1, 40.9, 15}, + {49.3, 40.7, 15}, + {50, 39.7, 15}, + {48.3, 39.1, 15}, + {47.1, 34, 15}, + {48.8, 36.2, 15}, + {45.6, 34.1, 15}, + {46, 31.3, 15}, + }, + [4399] = { + {60.9, 65.6, 15}, + {59.2, 65.3, 15}, + {61.6, 65.1, 15}, + {58.3, 64, 15}, + {60.1, 63.9, 15}, + {59.6, 63.4, 15}, + {62.2, 62.7, 15}, + {58.7, 62, 15}, + {60.4, 61.8, 15}, + {61.5, 61, 15}, + {60, 52.8, 15}, + }, + [4400] = { + {57, 62, 15}, + {58.7, 60.3, 15}, + {61.7, 60, 15}, + {60.8, 59.9, 15}, + {59.9, 58.4, 15}, + {60.4, 57.5, 15}, + {59.5, 56.4, 15}, + {61.7, 55.5, 15}, + {59.8, 54.7, 15}, + {61.3, 54.5, 15}, + {60.7, 54.2, 15}, + {60.8, 51.9, 15}, + }, + [4411] = { + {34, 46, 15}, + {36.8, 45.2, 15}, + {35.4, 45.2, 15}, + {34.7, 41.8, 15}, + {38.3, 41, 15}, + {39.2, 39.5, 15}, + {62.9, 39.3, 15}, + {44.2, 37.6, 15}, + {57.4, 34.6, 15}, + {55.3, 34.3, 15}, + {56.2, 33.3, 15}, + {53.3, 32.1, 15}, + {52.3, 31.9, 15}, + {52.8, 30.8, 15}, + {47.9, 25.9, 15}, + {58.8, 25.5, 15}, + {52.8, 23.8, 15}, + {49.6, 23.4, 15}, + {53.2, 22.9, 15}, + {42.5, 22.6, 15}, + {38.9, 21.3, 15}, + {43.3, 21, 15}, + {40.5, 20.5, 15}, + {39.4, 14.8, 15}, + {52.6, 12.8, 15}, + {42.6, 11.8, 15}, + {53.1, 77.8, 17}, + {54.6, 77.4, 17}, + {53.9, 77.4, 17}, + {53.5, 75.6, 17}, + {55.3, 75.2, 17}, + {55.9, 61.6, 17}, + {62.8, 60.6, 17}, + {57.6, 60.1, 17}, + {46.9, 44.9, 15}, + {42.7, 44.3, 15}, + {37.5, 44.2, 15}, + {41.3, 43.7, 15}, + {41, 43.7, 15}, + {43.3, 43.3, 15}, + {45.9, 43.3, 15}, + {38.2, 43.2, 15}, + {42.1, 43.1, 15}, + {41.5, 43.1, 15}, + {42.6, 42.6, 15}, + {45.5, 42.1, 15}, + {46, 41.2, 15}, + {40.1, 40.4, 15}, + {63.6, 40.2, 15}, + {41.2, 40.2, 15}, + {46, 39.8, 15}, + {61.5, 36.8, 15}, + {45, 36.6, 15}, + {44.1, 30.7, 15}, + {45, 27.3, 15}, + {49.6, 26.7, 15}, + {60, 24.6, 15}, + {56.3, 23.8, 15}, + {44.1, 21.2, 15}, + {38.9, 19.6, 15}, + {41.5, 19.3, 15}, + {40.8, 18.8, 15}, + {40.2, 17.6, 15}, + {51.3, 12.9, 15}, + {41.8, 12, 15}, + {40.5, 11.5, 15}, + {55, 76.9, 17}, + {55.3, 76.3, 17}, + {62.1, 60.7, 17}, + {57.2, 60.2, 17}, + {56.5, 59.9, 17}, + {38.3, 40.7, 15}, + {55.3, 75.1, 17}, + {56.3, 23.5, 15}, + }, + [4412] = { + {44.4, 53.5, 15}, + {38.2, 36.4, 15}, + {55.3, 72.8, 17}, + {49, 60.6, 15}, + {44.7, 59.5, 15}, + {41.7, 59.2, 15}, + {37.1, 57.6, 15}, + {40.4, 56.3, 15}, + {36.9, 55.8, 15}, + {38.2, 55.7, 15}, + {44.6, 55.2, 15}, + {43.8, 55.1, 15}, + {40.5, 53.4, 15}, + {41.5, 52.8, 15}, + {40.2, 52.4, 15}, + {36.8, 52.2, 15}, + {38.5, 51.2, 15}, + {38.9, 51, 15}, + {41.5, 50.4, 15}, + {41.9, 49.9, 15}, + {36.6, 49.1, 15}, + {35.4, 45.2, 15}, + {54.7, 83.8, 17}, + {54.6, 82.8, 17}, + {55.3, 82.8, 17}, + {54.6, 81, 17}, + {54.5, 79.4, 17}, + {53.9, 77.4, 17}, + {49.5, 63.7, 15}, + {48.3, 63.3, 15}, + {42.2, 63.1, 15}, + {48.9, 62.1, 15}, + {46.6, 61.9, 15}, + {43.3, 61.7, 15}, + {41.5, 60.4, 15}, + {45.6, 60, 15}, + {35.4, 59.4, 15}, + {48.2, 59.2, 15}, + {48.8, 59, 15}, + {36.4, 56.7, 15}, + {45.7, 56.1, 15}, + {39.6, 55.6, 15}, + {35.8, 55.5, 15}, + {40.1, 54.7, 15}, + {43.2, 53.9, 15}, + {41.3, 49, 15}, + {44.1, 49, 15}, + {44.8, 48.7, 15}, + {40.8, 48.2, 15}, + {41.7, 47.6, 15}, + {40.2, 47.4, 15}, + {42, 46.9, 15}, + {53.9, 84.7, 17}, + {54.4, 83.3, 17}, + {54, 82.7, 17}, + {44.9, 60.4, 15}, + }, + [4413] = { + {57.3, 37.7, 15}, + {54.2, 34.9, 15}, + {52.1, 32.8, 15}, + {50.3, 30.9, 15}, + {48.6, 29, 15}, + {55.2, 23.7, 15}, + {39.9, 18, 15}, + {52.8, 17.1, 15}, + {44.4, 15.1, 15}, + {43.5, 15, 15}, + {52.6, 14, 15}, + {62.8, 61.2, 17}, + {62.9, 39.3, 15}, + {57.9, 38.6, 15}, + {62.1, 37.9, 15}, + {58.5, 37.9, 15}, + {60.8, 35.1, 15}, + {52.3, 34.9, 15}, + {55.3, 34.3, 15}, + {56.2, 33.3, 15}, + {53.3, 32.1, 15}, + {52.3, 31.9, 15}, + {52.8, 30.8, 15}, + {46.3, 28.2, 15}, + {52.9, 28, 15}, + {48.4, 27.8, 15}, + {37.7, 27.7, 15}, + {60.2, 27.3, 15}, + {52.2, 27.2, 15}, + {47.9, 25.9, 15}, + {58.8, 25.5, 15}, + {52.8, 23.8, 15}, + {49.6, 23.4, 15}, + {53.2, 22.9, 15}, + {38.9, 21.3, 15}, + {43.7, 20.2, 15}, + {42.1, 18.6, 15}, + {52.6, 12.8, 15}, + {55.1, 68.3, 17}, + {62.8, 60.6, 17}, + }, + [4414] = { + {46.7, 42.6, 15}, + {56.8, 39.4, 15}, + {50.9, 33.8, 15}, + {50.1, 30.6, 15}, + {41.3, 26.1, 15}, + {60, 23, 15}, + {58.6, 21, 15}, + {56.4, 19.4, 15}, + {52.7, 15.4, 15}, + {51, 15.3, 15}, + {49.7, 61.8, 15}, + {47.9, 60.3, 15}, + {42.1, 57.7, 15}, + {47.9, 57.2, 15}, + {37.7, 55.1, 15}, + {44.4, 53.5, 15}, + {34.1, 52.8, 15}, + {40.9, 51.6, 15}, + {42.1, 51.5, 15}, + {36.3, 51, 15}, + {35.2, 48.7, 15}, + {37.3, 48.5, 15}, + {34, 46, 15}, + {46.7, 45.8, 15}, + {34.7, 41.8, 15}, + {43, 41.8, 15}, + {38.3, 41, 15}, + {35.4, 40.9, 15}, + {39.2, 39.5, 15}, + {45.6, 38.3, 15}, + {37.4, 38.2, 15}, + {44.2, 37.6, 15}, + {43.5, 35.2, 15}, + {43.5, 35.1, 15}, + {41.6, 34.8, 15}, + {44, 32.6, 15}, + {44.3, 30.3, 15}, + {45, 28.6, 15}, + {35.7, 14.8, 15}, + {55, 82.5, 17}, + {53.2, 81.3, 17}, + {54.3, 80.4, 17}, + {53.7, 79.2, 17}, + {54.8, 79.1, 17}, + {53.1, 77.8, 17}, + {53.5, 75.6, 17}, + {55.3, 75.2, 17}, + {53.9, 75.2, 17}, + {54.9, 73.8, 17}, + {54, 61.6, 17}, + {48.1, 62.4, 15}, + {48.5, 61.6, 15}, + {43.8, 60.7, 15}, + {49, 60.6, 15}, + {41.9, 59.8, 15}, + {49.6, 59.5, 15}, + {37.1, 57.6, 15}, + {39.8, 57.2, 15}, + {40.4, 56.3, 15}, + {36.9, 55.8, 15}, + {38.2, 55.7, 15}, + {47, 55.5, 15}, + {44.6, 55.2, 15}, + {45.3, 54.9, 15}, + {33.9, 54.8, 15}, + {36.9, 54.1, 15}, + {43.9, 53, 15}, + {41.5, 52.8, 15}, + {36.8, 52.2, 15}, + {35.8, 51.4, 15}, + {38.9, 51, 15}, + {41.5, 50.4, 15}, + {43.4, 48.2, 15}, + {39.1, 46.4, 15}, + {37.5, 44.2, 15}, + {41.3, 43.7, 15}, + {41, 43.7, 15}, + {45.9, 43.3, 15}, + {42.1, 43.1, 15}, + {42.6, 42.6, 15}, + {46, 41.2, 15}, + {40.1, 40.4, 15}, + {41.2, 40.2, 15}, + {45, 36.6, 15}, + {54.7, 83.8, 17}, + {54.6, 82.8, 17}, + {55.3, 82.8, 17}, + {53.1, 82.3, 17}, + {54.6, 82, 17}, + {54.6, 81, 17}, + {54, 80.6, 17}, + {55, 76.9, 17}, + {49.5, 63.7, 15}, + {48.3, 63.3, 15}, + {46.6, 61.9, 15}, + {45.7, 56.1, 15}, + {39.6, 55.6, 15}, + {44.8, 48.7, 15}, + {40.8, 48.2, 15}, + {41.7, 47.6, 15}, + {40.2, 47.4, 15}, + {38.3, 40.7, 15}, + {55.3, 75.1, 17}, + }, + [4415] = { + {52.6, 67.2, 15}, + {54.7, 67.1, 15}, + {53.7, 66.9, 15}, + {56.2, 66.8, 15}, + {53.4, 66.6, 15}, + {51.6, 64.6, 15}, + {51.4, 63.6, 15}, + {52.6, 62, 15}, + {52.7, 61.6, 15}, + {52.6, 60.9, 15}, + {53.6, 60.4, 15}, + {54.2, 60.2, 15}, + {55.1, 59.8, 15}, + {56.4, 57.8, 15}, + {56.8, 56, 15}, + {56.1, 56, 15}, + {56, 54, 15}, + {55.7, 52.5, 15}, + {53.1, 50.7, 15}, + {54.5, 50.5, 15}, + {52.1, 49.9, 15}, + {52.3, 48.4, 15}, + {49.9, 48.3, 15}, + {50.9, 48.1, 15}, + {50.7, 47.6, 15}, + {49.6, 46.4, 15}, + {48.8, 46.4, 15}, + {49.2, 45.2, 15}, + }, + [4538] = { + {18.8, 59.7, 491}, + {16.3, 58.5, 491}, + {13.1, 55.3, 491}, + {16.2, 54.5, 491}, + {12.5, 52.5, 491}, + {10.2, 47.8, 491}, + {7.9, 45.1, 491}, + {10.9, 43.6, 491}, + {12.8, 40.4, 491}, + {9.6, 40.3, 491}, + {13.3, 38.2, 491}, + {14.2, 37.8, 491}, + {11.4, 32.7, 491}, + }, + [4539] = { + {10.5, 55.5, 491}, + {15.4, 54.7, 491}, + {10.2, 50, 491}, + {7.1, 44.5, 491}, + {6.3, 41.9, 491}, + {8.8, 38.4, 491}, + {11, 30.3, 491}, + }, + [4548] = { + {37.9, 91.2, 17}, + {17.8, 19.8, 400}, + }, + [4555] = { + {69.2, 48.9, 1497}, + }, + [4602] = { + {58.8, 32.8, 1497}, + }, + [4603] = { + {62.7, 26.8, 1497}, + }, + [4604] = { + {54.7, 38.8, 1497}, + }, + [4662] = { + {65.4, 80.7, 405}, + {65.5, 80.6, 405}, + {73.8, 77.7, 405}, + {73.8, 77.6, 405}, + {72.2, 74.5, 405}, + {72.3, 74.5, 405}, + }, + [4688] = { + {37, 72.7, 405}, + {55.9, 41, 405}, + {45.4, 30.3, 405}, + }, + [4689] = { + {71.2, 34.8, 405}, + {62, 32.3, 405}, + {61.2, 22.5, 405}, + }, + [4690] = { + {43.1, 50.3, 405}, + {56.5, 47.7, 405}, + }, + [4692] = { + {38.5, 75.7, 405}, + {35.7, 74.5, 405}, + {36.7, 73.5, 405}, + {44.1, 73.4, 405}, + {67.9, 71.6, 405}, + {43, 71.1, 405}, + {43.4, 69.8, 405}, + {68.8, 68.2, 405}, + {54.5, 66.8, 405}, + {48.3, 66.4, 405}, + {69.4, 66.2, 405}, + {64.8, 65.5, 405}, + {41.9, 65.4, 405}, + {71.5, 65.4, 405}, + {56, 64.8, 405}, + {74, 64.7, 405}, + {71, 64.1, 405}, + {70.4, 63.4, 405}, + {67.1, 62.8, 405}, + {69.1, 62.2, 405}, + {71.2, 62.2, 405}, + {72.8, 62.1, 405}, + {68.1, 60.9, 405}, + {68.5, 60.4, 405}, + {73.9, 60.2, 405}, + {66.7, 60.2, 405}, + {68.1, 59.5, 405}, + {71.7, 59.4, 405}, + {65.7, 58.5, 405}, + {71.8, 58.2, 405}, + {69.1, 58.1, 405}, + {74.5, 58, 405}, + {74.6, 56.8, 405}, + {71.8, 56.8, 405}, + {70.1, 56.4, 405}, + {74, 55.6, 405}, + {72.2, 55.1, 405}, + {69.4, 54.5, 405}, + {65.5, 54.4, 405}, + {69.2, 54.1, 405}, + {67.1, 53.7, 405}, + {67.1, 53.3, 405}, + {70.2, 41.2, 405}, + {56.5, 41.1, 405}, + {47.8, 41, 405}, + {48.8, 39.9, 405}, + {45.6, 38.9, 405}, + {47, 38.8, 405}, + {50, 37.8, 405}, + {44.3, 37.8, 405}, + {73.3, 35.9, 405}, + {70.3, 35.8, 405}, + {80.6, 35.7, 405}, + {51.4, 35.4, 405}, + {57.4, 34.6, 405}, + {61.3, 34.6, 405}, + {75, 34.4, 405}, + {45.9, 34.3, 405}, + {49.6, 34.1, 405}, + {38.2, 33.5, 405}, + {76, 33.5, 405}, + {47, 33, 405}, + {47.2, 32.3, 405}, + {50.1, 32.2, 405}, + {44.9, 31.3, 405}, + {48.2, 31.3, 405}, + {45.5, 31.2, 405}, + {47, 30.7, 405}, + {49, 30, 405}, + {43.4, 29.6, 405}, + {47.6, 29.2, 405}, + {58.9, 29, 405}, + {71.5, 27.9, 405}, + {48.7, 27.9, 405}, + {48.4, 27.8, 405}, + {46.6, 27.8, 405}, + {65.4, 27.7, 405}, + {50.3, 26.4, 405}, + {44.6, 25, 405}, + {52.1, 24.4, 405}, + {59.8, 24.3, 405}, + {53.7, 23.4, 405}, + {51.3, 23.3, 405}, + {44.2, 22.9, 405}, + {52.8, 22, 405}, + {69.7, 20.5, 405}, + {69.5, 20.2, 405}, + {65.6, 20.2, 405}, + {56.7, 18.8, 405}, + {44.3, 18.6, 405}, + {61.1, 18.5, 405}, + {55.7, 18.5, 405}, + {44.9, 16.9, 405}, + {62, 16.8, 405}, + {46.6, 14.7, 405}, + {59.4, 13.3, 405}, + {57.4, 12.4, 405}, + {50, 7.8, 405}, + {36.8, 91.1, 406}, + {34.5, 87.9, 406}, + {32.6, 87.1, 406}, + {25.8, 82.8, 406}, + {67.1, 63.6, 405}, + {66.3, 59.5, 405}, + {60.1, 35.9, 405}, + {76.2, 33.4, 405}, + {69.5, 20.4, 405}, + {59.7, 13.3, 405}, + {37.7, 88.6, 406}, + {40.9, 88.4, 406}, + {36.9, 88.3, 406}, + {41.6, 87.1, 406}, + {39.9, 84.5, 406}, + {38.1, 83.8, 406}, + {32.6, 80.3, 406}, + }, + [4694] = { + {55.2, 85.7, 405}, + {57.4, 85.7, 405}, + {29.8, 81.2, 405}, + {62.3, 79.8, 405}, + {59.6, 79.1, 405}, + {61.2, 77.9, 405}, + {62.6, 75.7, 405}, + {59.5, 75.6, 405}, + {61.2, 74.5, 405}, + {62.7, 71, 405}, + {62.5, 75.9, 405}, + }, + [4695] = { + {53.6, 63.6, 405}, + {51.7, 60.5, 405}, + {53, 60.5, 405}, + {53.4, 60.3, 405}, + {50.8, 60.2, 405}, + {49.2, 60, 405}, + {49, 59.4, 405}, + {51.5, 59.3, 405}, + {50, 59, 405}, + {53.3, 58.8, 405}, + {50.7, 58.4, 405}, + {48, 57.7, 405}, + {51.5, 57.7, 405}, + {49.3, 57.4, 405}, + {50.2, 56.6, 405}, + {50, 58.9, 405}, + }, + [4696] = { + {53.1, 68.9, 405}, + {56, 64.6, 405}, + {59.5, 64.2, 405}, + {64.1, 63.8, 405}, + {63.6, 37.6, 405}, + {80.4, 36.7, 405}, + {71.1, 36.6, 405}, + {79.8, 36.1, 405}, + {76.8, 35.8, 405}, + {65.5, 35.7, 405}, + {78.2, 35.6, 405}, + {63.5, 35, 405}, + {68.6, 34.8, 405}, + {76, 34.7, 405}, + {71.1, 34.4, 405}, + {68.6, 33.5, 405}, + {59.9, 33.5, 405}, + {75.2, 33.4, 405}, + {71.1, 33.2, 405}, + {73.5, 33.2, 405}, + {71.5, 33.2, 405}, + {63.3, 33.2, 405}, + {78.3, 32.9, 405}, + {73, 32.4, 405}, + {68.8, 31.8, 405}, + {73.9, 31.3, 405}, + {63, 31.2, 405}, + {74.4, 30.2, 405}, + {72.7, 30, 405}, + {71.9, 29.9, 405}, + {61, 29.7, 405}, + {65.5, 29.2, 405}, + {70.8, 28.9, 405}, + {71.8, 28.1, 405}, + {65.5, 27.9, 405}, + {58.9, 26.9, 405}, + {65.7, 26.8, 405}, + {61.2, 26.8, 405}, + {70.5, 26.7, 405}, + {72.4, 26.6, 405}, + {66.9, 25.8, 405}, + {69.9, 25.7, 405}, + {66.3, 25.7, 405}, + {67.8, 25.6, 405}, + {64.1, 24.9, 405}, + {70.8, 23.9, 405}, + {64.4, 22.4, 405}, + {66.3, 22.3, 405}, + {68.7, 22.2, 405}, + {54.3, 22, 405}, + {70.4, 21.3, 405}, + {64.1, 20.1, 405}, + {57.7, 19.4, 405}, + {67.1, 19.1, 405}, + {54.2, 18.8, 405}, + {69.3, 18, 405}, + {63.5, 17.9, 405}, + {62.6, 16.9, 405}, + {55.8, 14.9, 405}, + {62.5, 14.7, 405}, + {54.4, 13.9, 405}, + {58.4, 11.7, 405}, + {57.3, 11.6, 405}, + {52.2, 11.3, 405}, + {58.4, 8.9, 405}, + {52.3, 8.1, 405}, + {50.8, 7.8, 405}, + {54.6, 6.9, 405}, + {50.3, 6.1, 405}, + {37.4, 91.2, 406}, + {37.3, 89.2, 406}, + {33.6, 86.4, 406}, + {32.6, 86.4, 406}, + {33.5, 83.9, 406}, + {27.9, 83.2, 406}, + {26.6, 82.8, 406}, + {30.1, 82, 406}, + {26, 81.3, 406}, + {75.3, 33.5, 405}, + {72.8, 32.5, 405}, + {70.8, 29.2, 405}, + {67.2, 19.1, 405}, + {69.4, 17.9, 405}, + {35.9, 91, 406}, + {38.3, 89, 406}, + {45.4, 88.8, 406}, + {35.8, 88.6, 406}, + {47.1, 88, 406}, + {42.7, 87.9, 406}, + {42.1, 87.2, 406}, + {36.9, 85.6, 406}, + {42, 85.5, 406}, + {35.9, 84.9, 406}, + {38.9, 83.3, 406}, + {38.1, 83.2, 406}, + {34.3, 83, 406}, + {38.9, 81.2, 406}, + {34.4, 80.6, 406}, + {33.2, 80.3, 406}, + {36.1, 79.7, 406}, + {32.8, 79.1, 406}, + }, + [4697] = { + {38, 74.5, 405}, + {45.5, 73.6, 405}, + {36.9, 73.1, 405}, + {42.9, 72.3, 405}, + {67.8, 68, 405}, + {71.6, 64.6, 405}, + {70.3, 61.9, 405}, + {71.4, 59.3, 405}, + {68.8, 59.1, 405}, + {73.3, 58.8, 405}, + {70.5, 58.2, 405}, + {64.1, 57.7, 405}, + {41.8, 57.3, 405}, + {65.9, 56.6, 405}, + {71.6, 55.9, 405}, + {58.9, 54.7, 405}, + {60.8, 54.5, 405}, + {58.5, 54.4, 405}, + {42.7, 50.1, 405}, + {57.1, 48.5, 405}, + {62.7, 48.3, 405}, + {45.2, 47.9, 405}, + {55.5, 45.7, 405}, + {44.1, 45.5, 405}, + {77.6, 44, 405}, + {40.4, 40.8, 405}, + }, + [4699] = { + {50.7, 92.4, 405}, + {48.5, 91.3, 405}, + {45.7, 86.9, 405}, + {47.4, 86.8, 405}, + {57, 86.7, 405}, + {55.3, 86.3, 405}, + {55.3, 83.3, 405}, + {29.3, 82.7, 405}, + {28.6, 81.9, 405}, + {61.1, 79.3, 405}, + {62.1, 75.6, 405}, + {61.1, 73.3, 405}, + {58.9, 72.1, 405}, + {63.9, 71.5, 405}, + {62.7, 69.8, 405}, + }, + [4821] = { + {35.6, 32.9, 719}, + {38.9, 29.1, 719}, + {33.3, 28.1, 719}, + {31.3, 26.5, 719}, + {29.6, 26.1, 719}, + {33.9, 24.1, 719}, + {32.6, 22.6, 719}, + {39, 21.8, 719}, + {38.4, 21.5, 719}, + {33.8, 19.8, 719}, + {36.8, 15.4, 719}, + }, + [4822] = { + {20.2, 57.1, 719}, + {21.6, 57, 719}, + {21.5, 54.7, 719}, + {24.2, 50.5, 719}, + {19.9, 50.1, 719}, + {18.7, 48.9, 719}, + {29.9, 47.2, 719}, + {17.3, 46.2, 719}, + {30.9, 44.6, 719}, + {16.8, 44, 719}, + {29, 41.6, 719}, + {31, 40.5, 719}, + {24.9, 37.3, 719}, + {17.3, 36.5, 719}, + {25.5, 34.6, 719}, + {19.1, 33.4, 719}, + {23.4, 31.4, 719}, + {16.5, 31, 719}, + }, + [4823] = { + {45.5, 91.3, 719}, + {49.2, 86.8, 719}, + {42.6, 81, 719}, + }, + [4841] = { + {48.8, 56.7, 15}, + }, + [4861] = { + {40.3, 65.5, 1337}, + {45.1, 55, 1337}, + {32.7, 53.1, 1337}, + {30.4, 47.3, 1337}, + {32.1, 46, 1337}, + {30.6, 43.7, 1337}, + {30.8, 42.5, 1337}, + {32.2, 41.7, 1337}, + }, + [4876] = { + {45.9, 51.5, 400}, + }, + [4887] = { + {22.4, 41.5, 719}, + }, + [4889] = { + {67.5, 48, 15}, + }, + [4892] = { + {67.9, 49.9, 15}, + }, + [4896] = { + {67.5, 51.7, 15}, + }, + [4950] = { + {68, 46.8, 15}, + }, + [5048] = { + {32.3, 43.1, 718}, + {27.2, 37.5, 718}, + {26.6, 30.5, 718}, + {27.3, 28.4, 718}, + {22.9, 25.5, 718}, + {33.5, 24.4, 718}, + {22.7, 24.2, 718}, + {28.7, 23.7, 718}, + {30.2, 19.1, 718}, + }, + [5053] = { + {85.8, 83, 718}, + {84.8, 80.6, 718}, + {68.2, 62.3, 718}, + {69, 59.2, 718}, + {68, 53.3, 718}, + {68, 50.7, 718}, + {63.1, 48, 718}, + {65.5, 46.9, 718}, + {61.6, 45.7, 718}, + {61.7, 41, 718}, + {37.8, 36.5, 718}, + {41.1, 34, 718}, + {60.5, 33.8, 718}, + {57.8, 32.4, 718}, + {54, 28.9, 718}, + {49.2, 28.2, 718}, + }, + [5056] = { + {85.5, 81, 718}, + {76.2, 75, 718}, + {73.7, 69.9, 718}, + {80.1, 66.6, 718}, + {76.4, 66.2, 718}, + {86.3, 63.1, 718}, + {82.9, 62, 718}, + {88.8, 55.5, 718}, + {97.7, 50.3, 718}, + {84.1, 46.8, 718}, + {67.3, 46, 718}, + {94.1, 43.5, 718}, + }, + [5101] = { + {39.2, 74.5, 1537}, + }, + [5122] = { + {71.8, 66.7, 1537}, + }, + [5123] = { + {72.2, 65.2, 1537}, + }, + [5134] = { + {43.1, 17.5, 2597}, + }, + [5225] = { + {76.7, 46.2, 8}, + {79.4, 40.4, 8}, + {77.8, 39.6, 8}, + {77.1, 38.7, 8}, + {77.4, 38.6, 8}, + {77.3, 38.2, 8}, + }, + [5262] = { + {48.8, 35.4, 357}, + {48.8, 34.8, 357}, + {49.8, 32.7, 357}, + {50.7, 32.7, 357}, + {50.2, 32.1, 357}, + {50.2, 29.9, 357}, + {46.5, 29.8, 357}, + {46.3, 29.4, 357}, + {50.3, 28.7, 357}, + {50.6, 28.2, 357}, + {43.6, 25.7, 357}, + {42.8, 25, 357}, + {46.7, 25, 357}, + {46.4, 24.2, 357}, + {41.1, 24.1, 357}, + {43.9, 23.8, 357}, + {37.8, 23.4, 357}, + {41.8, 23.3, 357}, + {43.2, 23.1, 357}, + {43.7, 22.2, 357}, + {45.7, 22.1, 357}, + {41.8, 21.6, 357}, + {40.6, 20.5, 357}, + {44.1, 20.3, 357}, + {45.1, 19.8, 357}, + {39.9, 18.6, 357}, + {41.1, 17.1, 357}, + {41.6, 16.9, 357}, + {41.3, 15.9, 357}, + {44.8, 12.2, 357}, + {42.6, 11.8, 357}, + {43, 11.5, 357}, + {42.2, 9, 357}, + {43.7, 8.3, 357}, + {50.2, 31.9, 357}, + {37.7, 23.4, 357}, + {2.7, 55.6, 2557}, + {7.3, 55.4, 2557}, + {4.9, 51.1, 2557}, + {4.7, 40.4, 2557}, + {4.9, 34.2, 2557}, + {6.7, 31.5, 2557}, + }, + [5268] = { + {61.3, 61.6, 357}, + {58.3, 60.4, 357}, + {57.1, 60.1, 357}, + {60.9, 60, 357}, + {59, 59.5, 357}, + {57.9, 58.3, 357}, + {58, 55.8, 357}, + {57.8, 55.3, 357}, + {58.7, 54.4, 357}, + {55.7, 53.8, 357}, + {54.7, 52.8, 357}, + {80.1, 46.6, 357}, + {70.9, 46.5, 357}, + {78.9, 46.2, 357}, + {81.2, 46.1, 357}, + {83.4, 46.1, 357}, + {72.2, 45.9, 357}, + {81.6, 45.9, 357}, + {84.4, 45.9, 357}, + {79.5, 45.8, 357}, + {85.2, 45.7, 357}, + {86.1, 45.6, 357}, + {78, 45.4, 357}, + {70.5, 44.9, 357}, + {80.4, 44.9, 357}, + {68.7, 44.9, 357}, + {68.1, 44.8, 357}, + {86.1, 44.3, 357}, + {82.9, 44.2, 357}, + {69.7, 44.2, 357}, + {86.8, 44.1, 357}, + {68.6, 44, 357}, + {71.6, 43.8, 357}, + {79.9, 43.6, 357}, + {72.6, 43.4, 357}, + {85.8, 42.8, 357}, + {71.2, 42.8, 357}, + {70.4, 42.7, 357}, + {83.3, 42.5, 357}, + {69.4, 42.4, 357}, + {70.4, 42.1, 357}, + {73.1, 41.9, 357}, + {70.5, 41.5, 357}, + {79.2, 41.2, 357}, + {76.9, 40.8, 357}, + {71, 40.8, 357}, + {83.4, 40.6, 357}, + {82.8, 40.5, 357}, + {76.1, 39.9, 357}, + {84.3, 39.6, 357}, + {89.6, 39.3, 357}, + {87.7, 39.3, 357}, + {79.3, 39, 357}, + {81.7, 39, 357}, + {85, 38.9, 357}, + {82.9, 38.6, 357}, + {86.6, 38.6, 357}, + {77.6, 38.5, 357}, + {85.1, 38.5, 357}, + {87.1, 38.3, 357}, + {80.4, 38.3, 357}, + {87.9, 38.1, 357}, + {74.8, 38, 357}, + {85.7, 37.1, 357}, + {78.9, 37, 357}, + {75.9, 36.5, 357}, + {70.3, 36.3, 357}, + {71.6, 35.3, 357}, + {8, 7.6, 400}, + {85.2, 45.8, 357}, + {77.6, 38.4, 357}, + }, + [5272] = { + {70.7, 63.8, 357}, + {69.9, 63.6, 357}, + {70.1, 62.9, 357}, + {71.2, 62.7, 357}, + {70, 61.6, 357}, + {71, 61.3, 357}, + {69.8, 61, 357}, + {69.5, 60.8, 357}, + {67.9, 60.6, 357}, + {71.8, 60.1, 357}, + {68.6, 59.4, 357}, + {68.2, 59.1, 357}, + {69.5, 58.5, 357}, + {69.9, 58.4, 357}, + {73.4, 57.9, 357}, + {59.8, 51.2, 357}, + {60.4, 51.1, 357}, + {61.4, 50.9, 357}, + {58.2, 50.7, 357}, + {57.2, 50.1, 357}, + {55, 49.8, 357}, + {56.2, 49.5, 357}, + {53.3, 48.6, 357}, + {57.5, 48.4, 357}, + {51, 48.4, 357}, + {53.7, 48, 357}, + {52.2, 47.9, 357}, + {57, 47.8, 357}, + {50, 47.5, 357}, + {53.2, 47.3, 357}, + {51.5, 46.6, 357}, + {52.6, 46.5, 357}, + }, + [5274] = { + {48.9, 37.5, 357}, + {49.8, 33.9, 357}, + {49.6, 33.5, 357}, + {49.5, 31.3, 357}, + {50.6, 31.3, 357}, + {49.8, 29.6, 357}, + {49.4, 28.3, 357}, + {49.9, 28.2, 357}, + {50.1, 27.9, 357}, + {46.5, 26, 357}, + {46.7, 25.4, 357}, + {46.1, 25.1, 357}, + {39.6, 25, 357}, + {45.9, 24.2, 357}, + {42.1, 24.1, 357}, + {47.3, 24.1, 357}, + {43.1, 24.1, 357}, + {38.5, 23.1, 357}, + {44.6, 22, 357}, + {39.9, 21.8, 357}, + {44.1, 21.6, 357}, + {42.9, 20.3, 357}, + {40.9, 19.1, 357}, + {41.3, 17.8, 357}, + {41.1, 17.5, 357}, + {44.5, 11.7, 357}, + {43.7, 11.7, 357}, + {43, 9.7, 357}, + {43.1, 23.9, 357}, + {2.4, 62, 2557}, + {1.5, 59.6, 2557}, + {0.9, 48, 2557}, + {6.6, 47.8, 2557}, + {2.6, 38.9, 2557}, + {0.4, 32, 2557}, + {3.2, 31.4, 2557}, + {4, 29.8, 2557}, + }, + [5286] = { + {30.7, 52.6, 357}, + {30.9, 51.8, 357}, + {31, 51.5, 357}, + {31, 50.8, 357}, + {31.9, 50.5, 357}, + {30.6, 50.1, 357}, + {30, 49.9, 357}, + {28.7, 49.6, 357}, + {29.2, 49.5, 357}, + {29.8, 49.5, 357}, + {30.8, 49.4, 357}, + {31.5, 49.3, 357}, + {68.7, 48.8, 357}, + {28.2, 48.6, 357}, + {29.7, 48.6, 357}, + {64.5, 48.6, 357}, + {31, 48.5, 357}, + {29.7, 48.4, 357}, + {27.6, 48.3, 357}, + {26.7, 47.4, 357}, + {27.6, 47.3, 357}, + {29.1, 47.2, 357}, + {25.8, 47.2, 357}, + {28.3, 47.1, 357}, + {27.3, 46.7, 357}, + {79.7, 46.6, 357}, + {28.8, 46.6, 357}, + {80.3, 46.6, 357}, + {26.8, 46.5, 357}, + {70.7, 46.4, 357}, + {71.2, 46.3, 357}, + {27.4, 46.2, 357}, + {26.5, 46.1, 357}, + {85.7, 46, 357}, + {83.5, 45.9, 357}, + {80.6, 45.8, 357}, + {84.7, 45.7, 357}, + {28.3, 45.6, 357}, + {72, 45.5, 357}, + {70.7, 45.3, 357}, + {80.1, 45.3, 357}, + {27.1, 45.2, 357}, + {27.4, 45.2, 357}, + {82.1, 45.2, 357}, + {86.6, 45.1, 357}, + {28.7, 45, 357}, + {82.8, 44.8, 357}, + {85.8, 44.7, 357}, + {78.4, 44.7, 357}, + {82.8, 44.3, 357}, + {26.9, 44.1, 357}, + {69.4, 44, 357}, + {70.9, 44, 357}, + {87.7, 43.7, 357}, + {27.3, 43.6, 357}, + {72.4, 43.5, 357}, + {71.1, 43.4, 357}, + {69, 42.9, 357}, + {71.4, 42.8, 357}, + {70, 42.7, 357}, + {87.7, 42.7, 357}, + {68.7, 42.3, 357}, + {71.1, 42.2, 357}, + {86.5, 42, 357}, + {71.7, 41.4, 357}, + {77.8, 41.1, 357}, + {86.7, 40.6, 357}, + {85, 40.4, 357}, + {70.8, 40.3, 357}, + {89.2, 40.2, 357}, + {79.6, 40.2, 357}, + {74.4, 40.1, 357}, + {76.7, 40, 357}, + {77.4, 39.8, 357}, + {86.9, 39.7, 357}, + {88.9, 39.2, 357}, + {80.6, 39.2, 357}, + {74.2, 39.1, 357}, + {81.2, 39, 357}, + {76.9, 38.9, 357}, + {76.2, 38.7, 357}, + {82.8, 38.7, 357}, + {75.5, 38.6, 357}, + {84.7, 38.5, 357}, + {88.2, 38.4, 357}, + {86.2, 38.4, 357}, + {79.9, 38.2, 357}, + {84.8, 38.1, 357}, + {78.3, 37.9, 357}, + {86.8, 37.8, 357}, + {78.7, 37.8, 357}, + {79.3, 37.7, 357}, + {85.9, 37.6, 357}, + {88.5, 37.5, 357}, + {75.7, 37.1, 357}, + {74.9, 36.3, 357}, + {70.8, 36.2, 357}, + {5.1, 14.5, 400}, + {5, 12.9, 400}, + {7.4, 8.9, 400}, + {7, 7.4, 400}, + {70.4, 42.1, 357}, + {77.6, 38.5, 357}, + {30.1, 50, 357}, + {31.5, 49.5, 357}, + {28.7, 44.9, 357}, + {70.3, 42.1, 357}, + {76.8, 41.4, 357}, + }, + [5287] = { + {69.6, 63.5, 357}, + {71.5, 62.1, 357}, + {69.7, 61.8, 357}, + {69.4, 61.6, 357}, + {71.8, 61.1, 357}, + {68.8, 61.1, 357}, + {70.9, 60.7, 357}, + {68.9, 60.3, 357}, + {71.3, 60.1, 357}, + {70.2, 59.7, 357}, + {72.3, 59.4, 357}, + {70, 59.2, 357}, + {71.7, 58.8, 357}, + {74.5, 58.8, 357}, + {70.6, 58.7, 357}, + {68, 58.5, 357}, + {73.9, 54.3, 357}, + {73.9, 52.8, 357}, + {59, 51.4, 357}, + {61.9, 51.1, 357}, + {60.9, 51.1, 357}, + {68.5, 50.6, 357}, + {57.4, 50.5, 357}, + {60.8, 50, 357}, + {52.8, 49.5, 357}, + {54.4, 49.1, 357}, + {55.4, 49.1, 357}, + {57.3, 48.9, 357}, + {54.2, 48.8, 357}, + {53.5, 48.7, 357}, + {50.8, 48, 357}, + {52.2, 47.4, 357}, + {53.9, 47.3, 357}, + {51.5, 47.3, 357}, + {56.9, 47, 357}, + {51.6, 46.9, 357}, + {56.1, 46.6, 357}, + {52.3, 45.9, 357}, + }, + [5288] = { + {48.8, 37.1, 357}, + {50.6, 33.5, 357}, + {49.1, 32.2, 357}, + {49.5, 30.6, 357}, + {46.6, 29.5, 357}, + {49, 26.6, 357}, + {42.7, 26, 357}, + {45, 24, 357}, + {42.6, 23.4, 357}, + {39.5, 23.2, 357}, + {43.7, 22.9, 357}, + {39.8, 22.8, 357}, + {45.8, 21.9, 357}, + {38.3, 21.8, 357}, + {43.1, 21.1, 357}, + {41.7, 20.4, 357}, + {43.6, 20.2, 357}, + {42, 19.9, 357}, + {39.6, 19.6, 357}, + {40.4, 18.7, 357}, + {40.4, 18, 357}, + {41.5, 15.3, 357}, + {44.5, 13.3, 357}, + {44.1, 12.9, 357}, + {43.3, 12.6, 357}, + {44.4, 11, 357}, + {44.5, 9.1, 357}, + {6.8, 59.7, 2557}, + {1.1, 44.2, 2557}, + }, + [5291] = { + {24.8, 64.8, 1477}, + {34.5, 60.5, 1477}, + {24.4, 50.4, 1477}, + {21, 46.6, 1477}, + {33.6, 31.1, 1477}, + {24.8, 27.1, 1477}, + {27.6, 26.7, 1477}, + }, + [5307] = { + {59.8, 61.8, 357}, + {57.6, 59.4, 357}, + {58.2, 57.8, 357}, + {57.1, 54.8, 357}, + {60, 61.3, 357}, + }, + [5308] = { + {47.9, 59.2, 357}, + {47.9, 55.7, 357}, + {59, 51.1, 357}, + {46.1, 50, 357}, + {49.4, 49.9, 357}, + {60.8, 49.8, 357}, + {45.9, 49.2, 357}, + {52.3, 49.2, 357}, + {46.8, 48.5, 357}, + {56.9, 48.1, 357}, + {46.6, 47.6, 357}, + {55.7, 47, 357}, + {53.2, 46.8, 357}, + {46.5, 40.7, 357}, + {46.4, 39.3, 357}, + {46, 37.7, 357}, + {43.1, 36.9, 357}, + {44, 36.4, 357}, + {42, 36.3, 357}, + {44.8, 36.1, 357}, + {46.5, 39.4, 357}, + }, + [5349] = { + {44.8, 25, 357}, + }, + [5352] = { + {59.5, 59.4, 357}, + }, + [5356] = { + {84.4, 38.3, 357}, + {80.2, 39.6, 357}, + }, + [5422] = { + {78.3, 99.7, 400}, + {55.1, 36.8, 440}, + {54.6, 35.8, 440}, + {53.9, 35.7, 440}, + {52, 35.6, 440}, + {53.1, 35.6, 440}, + {56.6, 35.6, 440}, + {55.9, 35, 440}, + {51.2, 34.5, 440}, + {50.4, 33, 440}, + {57.9, 33, 440}, + {54.6, 32.8, 440}, + {55.8, 32.7, 440}, + {52, 32.7, 440}, + {54, 32.3, 440}, + {54.2, 31.5, 440}, + {58.4, 31.4, 440}, + {49.2, 31.1, 440}, + {57, 31.1, 440}, + {52.9, 30.9, 440}, + {48.2, 30.2, 440}, + {48.3, 29.9, 440}, + {54.6, 29.9, 440}, + {49.2, 29.5, 440}, + {66.5, 29, 440}, + {65.8, 28.9, 440}, + {48.3, 28.8, 440}, + {61.9, 28.6, 440}, + {49.1, 28.4, 440}, + {58.5, 27.3, 440}, + {63.5, 27.1, 440}, + {47.3, 27.1, 440}, + {55.8, 27, 440}, + {66.1, 27, 440}, + {47.7, 27, 440}, + {62.8, 24.2, 440}, + {55.2, 24.2, 440}, + {48.3, 24.2, 440}, + {54.1, 23.4, 440}, + {57.2, 22.9, 440}, + {62.5, 22.7, 440}, + {63.3, 22.7, 440}, + {53.1, 22.1, 440}, + {63.5, 21, 440}, + {65.7, 19.9, 440}, + {61.6, 30.4, 440}, + {66.3, 29.7, 440}, + {64.6, 29.2, 440}, + {63.1, 28.1, 440}, + {59.5, 25.5, 440}, + {65.8, 25.2, 440}, + {62, 25.1, 440}, + {58.5, 24.5, 440}, + {61.6, 23.8, 440}, + {62.2, 23.7, 440}, + {65.3, 22.5, 440}, + {64.3, 21.9, 440}, + {63, 21.6, 440}, + }, + [5423] = { + {54.1, 54.3, 440}, + {53.3, 53.3, 440}, + {52, 53.3, 440}, + {56, 53.2, 440}, + {58.2, 53.1, 440}, + {59.9, 51.7, 440}, + {53.2, 51.2, 440}, + {55.1, 50.2, 440}, + {55.9, 50.1, 440}, + {53.2, 49.8, 440}, + {57.6, 48.7, 440}, + {59.9, 48.7, 440}, + {55.3, 48.4, 440}, + {52.2, 47.7, 440}, + {58, 47.3, 440}, + {60.9, 47.2, 440}, + {61.8, 45.8, 440}, + {55, 45.7, 440}, + {59.1, 44.8, 440}, + {60, 44.6, 440}, + {63.8, 44.4, 440}, + {55, 44.4, 440}, + {61.8, 44.3, 440}, + {57.9, 44.1, 440}, + {52, 43, 440}, + {64.2, 43, 440}, + {40.4, 43, 440}, + {58, 43, 440}, + {47.3, 42.9, 440}, + {61.8, 42.9, 440}, + {43.5, 42.9, 440}, + {55.1, 42.9, 440}, + {54.4, 42.8, 440}, + {57.2, 42.8, 440}, + {51.3, 42.7, 440}, + {44.4, 42.4, 440}, + {50.2, 42.2, 440}, + {46.3, 41.7, 440}, + {47.2, 41.6, 440}, + {51, 41.6, 440}, + {61.8, 41.5, 440}, + {57, 41.5, 440}, + {48.3, 41.5, 440}, + {54.1, 41.5, 440}, + {56, 41.3, 440}, + {62.8, 41.1, 440}, + {53.2, 40.8, 440}, + {56.3, 40.4, 440}, + {42.5, 40.4, 440}, + {46.3, 40.1, 440}, + {53.2, 40.1, 440}, + {47.3, 40, 440}, + {48.1, 40, 440}, + {50.2, 39.4, 440}, + {46.5, 38.7, 440}, + {40.6, 38.5, 440}, + {51.2, 37.6, 440}, + {44.4, 37.6, 440}, + {48.3, 37.4, 440}, + {52.1, 37.2, 440}, + {49.9, 37, 440}, + {50.2, 36.4, 440}, + {45.4, 36.1, 440}, + {43.4, 35.4, 440}, + {50.2, 34.2, 440}, + {47.3, 33.5, 440}, + {39.6, 33.4, 440}, + {37.7, 33, 440}, + {48.2, 32.9, 440}, + {42.9, 32.9, 440}, + {46.4, 32.9, 440}, + {36.6, 31.3, 440}, + {44.5, 30.8, 440}, + {45.7, 29.8, 440}, + {36.9, 28.5, 440}, + {34.8, 27.2, 440}, + {46.5, 25.6, 440}, + {47.4, 25.2, 440}, + {44.5, 24, 440}, + {39.7, 24, 440}, + {34.8, 23.9, 440}, + {46.3, 23, 440}, + {36.1, 21.9, 440}, + {34.7, 21.2, 440}, + {85.2, 47.1, 490}, + {85, 35.8, 490}, + {52.2, 48.7, 440}, + {51.2, 38.6, 440}, + {43.4, 43.1, 440}, + {47.3, 40.2, 440}, + {51.3, 38.4, 440}, + {50.3, 34.1, 440}, + {19.2, 39.3, 1941}, + {15, 34.3, 1941}, + {8.5, 34, 1941}, + {28.8, 33.4, 1941}, + {40, 33.2, 1941}, + {48.4, 25.8, 1941}, + {14.2, 23.3, 1941}, + {24.2, 17.9, 1941}, + {28.1, 17.5, 1941}, + {14.3, 16.2, 1941}, + {36.9, 10.5, 1941}, + {48.5, 10.1, 1941}, + {25.3, 9, 1941}, + {9.4, 5, 1941}, + {38.8, 3.3, 1941}, + {53.6, 2.6, 1941}, + }, + [5424] = { + {49.3, 76.3, 440}, + {29, 75.9, 440}, + {37.6, 74.9, 440}, + {44.6, 74.8, 440}, + {33.7, 74.8, 440}, + {31.9, 73.4, 440}, + {47.3, 72, 440}, + {34.6, 70.5, 440}, + {32.9, 69.1, 440}, + {40.6, 67.7, 440}, + {34.6, 67.6, 440}, + {39.7, 67.6, 440}, + {33.8, 67.5, 440}, + {44.1, 67.4, 440}, + {37.8, 66.1, 440}, + {38.6, 66.1, 440}, + {41.6, 66, 440}, + {42.3, 64.9, 440}, + {35.7, 64.6, 440}, + {34.7, 63.2, 440}, + {33, 63.2, 440}, + {50.2, 63.2, 440}, + {35.6, 63.1, 440}, + {52.2, 61.9, 440}, + {37.6, 61.9, 440}, + {51.3, 61.9, 440}, + {42.5, 61.7, 440}, + {49.3, 61.7, 440}, + {38.7, 61.6, 440}, + {34.9, 61.6, 440}, + {33.9, 61.3, 440}, + {56, 61, 440}, + {36.7, 60.9, 440}, + {40.9, 60.4, 440}, + {34.9, 60.4, 440}, + {28.1, 60.2, 440}, + {52, 60.1, 440}, + {30.7, 60, 440}, + {41.3, 59.2, 440}, + {45.4, 59, 440}, + {54.1, 59, 440}, + {55.5, 58.9, 440}, + {44.5, 58.9, 440}, + {46.7, 58.9, 440}, + {59.7, 58.8, 440}, + {43.5, 58.7, 440}, + {34.7, 58, 440}, + {43.7, 57.6, 440}, + {31.9, 57.3, 440}, + {51.1, 56, 440}, + {48.3, 55.9, 440}, + {33, 55.9, 440}, + {31.9, 55.6, 440}, + {45.2, 54.9, 440}, + {58.9, 54.9, 440}, + {33.1, 54.6, 440}, + {35.8, 54.3, 440}, + {31.8, 54.2, 440}, + {33.9, 54.1, 440}, + {32, 53.3, 440}, + {31.2, 53.2, 440}, + {59.9, 53.1, 440}, + {58.9, 53, 440}, + {29.4, 53, 440}, + {47.3, 52.7, 440}, + {29.9, 51.7, 440}, + {49.2, 51, 440}, + {29.6, 50.4, 440}, + {45.1, 50.2, 440}, + {47.1, 50.1, 440}, + {36.6, 49.8, 440}, + {50.2, 48.9, 440}, + {49.2, 48.4, 440}, + {51.1, 48, 440}, + {38.7, 47.6, 440}, + {44.1, 47.4, 440}, + {40.5, 47.3, 440}, + {42.4, 47.3, 440}, + {41.4, 47.3, 440}, + {37.4, 47.2, 440}, + {42.6, 46, 440}, + {45.3, 45.9, 440}, + {44.5, 45.8, 440}, + {46.3, 45.8, 440}, + {51.2, 45.7, 440}, + {51.1, 44.9, 440}, + {41.6, 44.7, 440}, + {45.3, 44.6, 440}, + {46.1, 44.5, 440}, + {37.8, 44.3, 440}, + {38.6, 43.7, 440}, + {38.3, 43, 440}, + {40, 42.9, 440}, + {39.6, 41.5, 440}, + {38.6, 37.2, 440}, + {38, 35.8, 440}, + {36.7, 35.5, 440}, + {39.5, 34.6, 440}, + {75.6, 90.3, 490}, + {34.7, 64.6, 440}, + {29.9, 58.9, 440}, + {53.1, 56, 440}, + {31.9, 73.5, 440}, + {38.6, 66.2, 440}, + {34.6, 64.6, 440}, + {34.9, 63.2, 440}, + {30, 58.7, 440}, + {51.2, 56, 440}, + {9.2, 78.8, 1941}, + {4.5, 78.6, 1941}, + {28.8, 74.3, 1941}, + {8, 69.5, 1941}, + {19.1, 63.7, 1941}, + {26.2, 63.4, 1941}, + {47.8, 62.7, 1941}, + {4.1, 48, 1941}, + {43.3, 42.2, 1941}, + {48.5, 33, 1941}, + {43.4, 32.9, 1941}, + {3.7, 6.6, 1941}, + }, + [5425] = { + {54.5, 38.7, 440}, + {56, 37.5, 440}, + {54.2, 37.3, 440}, + {49.4, 36.6, 440}, + {52.1, 35.8, 440}, + {49.3, 35.6, 440}, + {52, 34.5, 440}, + {54.1, 34, 440}, + {40.5, 33.3, 440}, + {57, 32.8, 440}, + {51.5, 32.8, 440}, + {51.1, 32.7, 440}, + {58.7, 32.7, 440}, + {52.9, 32.4, 440}, + {47.3, 32.1, 440}, + {52.1, 32, 440}, + {48.3, 32, 440}, + {55.2, 31.7, 440}, + {57.9, 31.1, 440}, + {34.9, 30.9, 440}, + {55.9, 30.7, 440}, + {35.6, 30.3, 440}, + {60.2, 30, 440}, + {37.6, 29.9, 440}, + {43.4, 29.9, 440}, + {65.5, 29.6, 440}, + {46.4, 29.4, 440}, + {35.6, 28.7, 440}, + {34.7, 28.5, 440}, + {43.7, 28.5, 440}, + {44.4, 28.4, 440}, + {61.1, 28.3, 440}, + {45.6, 28.3, 440}, + {49.2, 28.3, 440}, + {47.1, 28.2, 440}, + {42.5, 27.8, 440}, + {38.5, 27.4, 440}, + {49.2, 27.2, 440}, + {41.9, 27.1, 440}, + {43.4, 26.7, 440}, + {45, 26.7, 440}, + {48.3, 26.3, 440}, + {55.1, 26, 440}, + {58.3, 25.6, 440}, + {40.1, 25.6, 440}, + {57.2, 25.6, 440}, + {47.3, 25.5, 440}, + {43.7, 25.5, 440}, + {53.5, 25.4, 440}, + {55.9, 25.3, 440}, + {44.2, 25.2, 440}, + {56, 24.4, 440}, + {56.8, 24, 440}, + {46, 24, 440}, + {53.3, 23.9, 440}, + {39.6, 22.6, 440}, + {44.1, 22.5, 440}, + {58.2, 22.4, 440}, + {62.8, 21.4, 440}, + {61.9, 20.5, 440}, + {85.4, 53.9, 490}, + {85.1, 49.4, 490}, + {48.3, 29.9, 440}, + {48.3, 29.7, 440}, + {47.2, 25.7, 440}, + }, + [5426] = { + {51.2, 63.4, 440}, + {52.1, 63.2, 440}, + {45.2, 61.8, 440}, + {61.7, 60.6, 440}, + {44.4, 60.4, 440}, + {60.7, 60.3, 440}, + {59.9, 60.3, 440}, + {57.9, 60.3, 440}, + {51.2, 60.3, 440}, + {58.8, 60, 440}, + {58.7, 59, 440}, + {48.3, 58.9, 440}, + {57.9, 58.9, 440}, + {53.2, 58.6, 440}, + {56.1, 58.4, 440}, + {56.2, 57.8, 440}, + {47.3, 57.6, 440}, + {60.8, 57.6, 440}, + {58, 57.5, 440}, + {50.3, 57.5, 440}, + {44.4, 57.4, 440}, + {49.2, 57.4, 440}, + {53.2, 57.4, 440}, + {51.9, 57.3, 440}, + {61.8, 57.3, 440}, + {63.7, 57.3, 440}, + {57, 57.1, 440}, + {60.8, 56.5, 440}, + {63.8, 56.4, 440}, + {56, 56.1, 440}, + {58, 56.1, 440}, + {50.2, 55.9, 440}, + {58.9, 55.4, 440}, + {53, 55.2, 440}, + {57, 54.9, 440}, + {51.3, 54.7, 440}, + {52, 54.5, 440}, + {60.9, 54.5, 440}, + {50.3, 54, 440}, + {49.2, 53.7, 440}, + {48.5, 53.4, 440}, + {55.1, 53.1, 440}, + {60.9, 53.1, 440}, + {49.7, 53.1, 440}, + {45.6, 53, 440}, + {49.3, 52.8, 440}, + {46.4, 52.4, 440}, + {50.6, 52.1, 440}, + {50, 51.8, 440}, + {47.1, 51.7, 440}, + {57.9, 51.7, 440}, + {57, 51.6, 440}, + {45.1, 51.5, 440}, + {52.2, 51.4, 440}, + {48.3, 51, 440}, + {55.1, 50.9, 440}, + {37.7, 50.5, 440}, + {58.5, 50.1, 440}, + {58, 49.4, 440}, + {56.9, 49.3, 440}, + {53.3, 49, 440}, + {54.1, 48.9, 440}, + {37.6, 48.8, 440}, + {38.6, 48.8, 440}, + {56.2, 48.6, 440}, + {39.6, 48, 440}, + {39.7, 47, 440}, + {48.2, 46.6, 440}, + {47, 46.5, 440}, + {47.7, 46.1, 440}, + {56, 46, 440}, + {54.3, 45.9, 440}, + {48.7, 45.8, 440}, + {62.7, 45.8, 440}, + {60.8, 45.4, 440}, + {48.8, 45, 440}, + {48.3, 45, 440}, + {39.5, 44.3, 440}, + {56, 44.3, 440}, + {53.2, 43.4, 440}, + {46.2, 43.1, 440}, + {58.9, 43, 440}, + {60.9, 43, 440}, + {59.8, 42.9, 440}, + {62.8, 42.8, 440}, + {45.3, 42.5, 440}, + {49.6, 42.3, 440}, + {43, 42, 440}, + {65.6, 42, 440}, + {52.3, 41.8, 440}, + {55, 41.6, 440}, + {49.3, 41.6, 440}, + {45.7, 41.5, 440}, + {59.8, 41.5, 440}, + {42.5, 41.4, 440}, + {44.4, 41.4, 440}, + {44.8, 40.1, 440}, + {55.5, 40.1, 440}, + {57, 39.9, 440}, + {44.7, 39.6, 440}, + {45.5, 39.3, 440}, + {45, 39.3, 440}, + {44.4, 39.2, 440}, + {41.4, 38.8, 440}, + {45.8, 38.6, 440}, + {37.6, 38.2, 440}, + {45.8, 38, 440}, + {37.8, 37.2, 440}, + {41.4, 37.2, 440}, + {36.7, 37.1, 440}, + {42.7, 37, 440}, + {39.6, 36.8, 440}, + {51.2, 36, 440}, + {42.5, 35.8, 440}, + {47.5, 35.7, 440}, + {38.9, 35.6, 440}, + {40.4, 35.6, 440}, + {35.7, 35.5, 440}, + {45.5, 34.5, 440}, + {35.4, 34.4, 440}, + {40.9, 34.4, 440}, + {41.4, 34.3, 440}, + {37.7, 33.8, 440}, + {45.9, 31.3, 440}, + {45.4, 30.7, 440}, + {57, 53.1, 440}, + {52.2, 50.2, 440}, + {53.1, 56, 440}, + {52.2, 48.7, 440}, + {52.1, 50.3, 440}, + {52.3, 48.7, 440}, + {4.4, 86.3, 1941}, + {8.9, 85.4, 1941}, + {58, 72, 1941}, + {53, 70.7, 1941}, + {48.7, 70.6, 1941}, + {38.6, 70.6, 1941}, + {4, 70.4, 1941}, + {43.1, 68.9, 1941}, + {42.5, 63.5, 1941}, + {38.4, 63.1, 1941}, + {14.5, 61.8, 1941}, + {29.1, 60.8, 1941}, + {29.9, 57.4, 1941}, + {53.5, 56.4, 1941}, + {38.8, 55.9, 1941}, + {14.5, 55.2, 1941}, + {7.6, 55, 1941}, + {58.5, 55, 1941}, + {68, 54.7, 1941}, + {33.7, 54.1, 1941}, + {53, 50.9, 1941}, + {68.5, 50.1, 1941}, + {28.9, 48.6, 1941}, + {38.8, 48.5, 1941}, + {43.6, 45.1, 1941}, + {13.5, 43.9, 1941}, + {34, 42.7, 1941}, + {4.9, 41.4, 1941}, + {8.3, 40.3, 1941}, + {53.6, 40.2, 1941}, + {33.9, 33.4, 1941}, + {24, 33, 1941}, + {53.8, 33, 1941}, + {1.2, 27.8, 1941}, + {38.5, 25.9, 1941}, + {33.6, 25.5, 1941}, + {9.1, 24.4, 1941}, + {24, 21.9, 1941}, + {8.5, 18.5, 1941}, + {41.4, 17.8, 1941}, + {39.1, 14.2, 1941}, + {33.5, 13.7, 1941}, + {14.7, 11.8, 1941}, + {18.9, 11.3, 1941}, + {9.7, 10.5, 1941}, + {29.6, 10, 1941}, + }, + [5427] = { + {32.9, 79.1, 440}, + {40.6, 77.8, 440}, + {32.8, 77.8, 440}, + {34.7, 77.7, 440}, + {29.9, 77.7, 440}, + {33.7, 77.7, 440}, + {30.9, 77.7, 440}, + {48.3, 77.6, 440}, + {43.4, 77.5, 440}, + {47.2, 76.5, 440}, + {43.5, 76.4, 440}, + {44.5, 76.3, 440}, + {46.3, 76.3, 440}, + {32.9, 76.3, 440}, + {48.3, 76.3, 440}, + {45.4, 76.3, 440}, + {38.8, 76.1, 440}, + {34.7, 74.9, 440}, + {46.4, 74.9, 440}, + {31.9, 74.9, 440}, + {45.4, 74.8, 440}, + {30.2, 73.5, 440}, + {35.7, 73.4, 440}, + {37.7, 72.2, 440}, + {35.7, 71.9, 440}, + {29, 71.9, 440}, + {46, 70.7, 440}, + {47.1, 70.6, 440}, + {37.8, 70.5, 440}, + {30.9, 70.2, 440}, + {49.3, 69.3, 440}, + {30.1, 69.2, 440}, + {46.5, 69.2, 440}, + {35.7, 68.9, 440}, + {31.9, 68.8, 440}, + {37.1, 68.4, 440}, + {37.6, 68.4, 440}, + {36.7, 68.4, 440}, + {38.2, 67.7, 440}, + {37.3, 67.5, 440}, + {31.9, 67.5, 440}, + {32.9, 67.5, 440}, + {37.6, 67.4, 440}, + {33.8, 66.2, 440}, + {34.3, 66.1, 440}, + {33.7, 64.6, 440}, + {40.5, 64.6, 440}, + {41.9, 63.9, 440}, + {40.2, 63.9, 440}, + {32.1, 63.7, 440}, + {41.6, 63.4, 440}, + {34.1, 63.3, 440}, + {42.5, 63.3, 440}, + {40.6, 63.3, 440}, + {42, 62.6, 440}, + {41.4, 62.4, 440}, + {31.4, 61.8, 440}, + {31.4, 61.1, 440}, + {32.7, 61, 440}, + {30.3, 60.9, 440}, + {35.8, 60.5, 440}, + {38.4, 60.5, 440}, + {32.8, 60.2, 440}, + {33.7, 59.7, 440}, + {29, 59.3, 440}, + {32.3, 58.9, 440}, + {34, 58.9, 440}, + {31.1, 58.9, 440}, + {28.6, 57.4, 440}, + {33.3, 57.4, 440}, + {30.9, 57.1, 440}, + {25.7, 56.4, 440}, + {27.7, 56.1, 440}, + {34, 56, 440}, + {31.3, 56, 440}, + {30.4, 53.2, 440}, + {43.4, 52.4, 440}, + {43.4, 51.7, 440}, + {42.9, 51.4, 440}, + {43.4, 51, 440}, + {43.9, 51, 440}, + {42.9, 50.7, 440}, + {43.5, 50.3, 440}, + {43.9, 50.2, 440}, + {44.4, 50.1, 440}, + {44.4, 49.6, 440}, + {48.2, 49.5, 440}, + {44.9, 49.5, 440}, + {51.1, 49.4, 440}, + {40.6, 49.2, 440}, + {29.5, 48.9, 440}, + {43.4, 48.7, 440}, + {42.5, 48.6, 440}, + {45.4, 47.4, 440}, + {43.1, 47.3, 440}, + {50.4, 46, 440}, + {40.5, 45, 440}, + {44.3, 44.4, 440}, + {42.4, 44.3, 440}, + {75.3, 87.4, 490}, + {34.7, 64.6, 440}, + {29.9, 58.9, 440}, + {31.5, 61.9, 440}, + {3.6, 13.7, 1941}, + }, + [5428] = { + {49.2, 37.1, 440}, + {48.9, 36.4, 440}, + {49.3, 36.2, 440}, + {49.8, 35.9, 440}, + {49.7, 35.8, 440}, + {49.3, 35.5, 440}, + {49.9, 35.3, 440}, + {49.8, 35.3, 440}, + {40.7, 33.1, 440}, + {57.4, 31.9, 440}, + {49.9, 31.2, 440}, + {59.2, 31, 440}, + {45.9, 29, 440}, + {35.3, 29, 440}, + {48.7, 28.5, 440}, + {49.4, 28.2, 440}, + {56.1, 25.4, 440}, + {40.9, 24.9, 440}, + }, + [5429] = { + {49.8, 53.7, 440}, + {50.7, 53.4, 440}, + {52.3, 53.1, 440}, + {50.3, 53, 440}, + {48.8, 52.9, 440}, + {55.1, 51.1, 440}, + {47.1, 48.2, 440}, + {46.9, 47.4, 440}, + {47.7, 47.3, 440}, + {55.2, 47.1, 440}, + {47.5, 46.7, 440}, + {58.6, 46, 440}, + {47.3, 45.7, 440}, + {48, 45.6, 440}, + {47.7, 45, 440}, + {48.3, 44.2, 440}, + {48.8, 43.7, 440}, + {37.7, 43.2, 440}, + {49.3, 43.1, 440}, + {48.9, 42.4, 440}, + {48.2, 41.3, 440}, + {43.3, 41.3, 440}, + {44, 41, 440}, + {42.8, 40.9, 440}, + {44.5, 40.7, 440}, + {43.5, 40.6, 440}, + {44.4, 40.1, 440}, + {43.4, 39.9, 440}, + {45.3, 39.9, 440}, + {43.8, 39.9, 440}, + {44.1, 39.5, 440}, + {48.6, 39.2, 440}, + {41.5, 38.8, 440}, + {44.9, 38.6, 440}, + {45.4, 38.5, 440}, + {45.4, 37.9, 440}, + {44.9, 37.8, 440}, + {45.8, 37.2, 440}, + {47.5, 36, 440}, + {40.4, 35.6, 440}, + {36.7, 34.8, 440}, + {1.7, 34.6, 1941}, + {9.7, 33.1, 1941}, + {24.3, 22.9, 1941}, + {24.4, 1.9, 1941}, + }, + [5430] = { + {32, 74.9, 440}, + {36.6, 70.4, 440}, + {36.7, 69.7, 440}, + {37.3, 69.1, 440}, + {36.7, 69.1, 440}, + {40, 64.6, 440}, + {41.1, 64.6, 440}, + {39.6, 64.6, 440}, + {41.6, 64.1, 440}, + {41.1, 63.9, 440}, + {39.6, 63.9, 440}, + {42.1, 63.4, 440}, + {40.9, 63.2, 440}, + {31.9, 61.1, 440}, + {33.3, 60.3, 440}, + {31.9, 60.3, 440}, + {32.8, 59.6, 440}, + {29.2, 55.4, 440}, + }, + [5431] = { + {60.9, 82, 440}, + {60.6, 81.7, 440}, + {61.3, 81.4, 440}, + {64.7, 65.6, 440}, + {64.5, 64.7, 440}, + {65.1, 64.7, 440}, + {64.5, 64, 440}, + {64.4, 63.2, 440}, + {66.3, 63.1, 440}, + {65.6, 63.1, 440}, + {65.7, 62.7, 440}, + {67.1, 62, 440}, + {65.1, 62, 440}, + {66, 61.9, 440}, + {66.6, 61.8, 440}, + {64.3, 61.7, 440}, + {64.8, 60.9, 440}, + {67.1, 60.4, 440}, + {65.9, 60.3, 440}, + {65.3, 60.3, 440}, + {67.5, 59.6, 440}, + {65.6, 59.3, 440}, + {68.2, 59.1, 440}, + {68.9, 59, 440}, + {66.3, 58.9, 440}, + {67.3, 58.9, 440}, + {68.7, 58.1, 440}, + {70, 57.9, 440}, + {67, 57.5, 440}, + {68.2, 57.2, 440}, + {71.2, 57.2, 440}, + {71.4, 57.2, 440}, + {69.2, 57.1, 440}, + {69.8, 56.7, 440}, + {71.9, 56.3, 440}, + {69.9, 55.7, 440}, + {70.9, 55.7, 440}, + {72.5, 55.5, 440}, + {70.7, 55.1, 440}, + {72, 54.7, 440}, + {71.3, 54.7, 440}, + {71.4, 53.6, 440}, + {53.1, 97.9, 440}, + {54.2, 97.7, 440}, + {54.6, 97.7, 440}, + {52.7, 97.3, 440}, + {53.6, 97.3, 440}, + {55.1, 96.7, 440}, + {54.3, 96.7, 440}, + {52.3, 96.6, 440}, + {52.9, 96.6, 440}, + {52.2, 96, 440}, + {55.7, 95.9, 440}, + {53.5, 95.9, 440}, + {54.6, 95.7, 440}, + {52.1, 95.5, 440}, + {55, 95.5, 440}, + {54.2, 95.4, 440}, + {52.9, 95.4, 440}, + {51.2, 94.7, 440}, + {55.4, 94.6, 440}, + {53.4, 94.5, 440}, + {51.7, 94.5, 440}, + {52.8, 94.3, 440}, + {54.7, 94.3, 440}, + {56.4, 94.2, 440}, + {53.1, 93.7, 440}, + {56, 93.7, 440}, + {54.2, 93.7, 440}, + {55.6, 93.7, 440}, + {50.1, 93.5, 440}, + {53.5, 93.4, 440}, + {56.7, 93.4, 440}, + {52.3, 93.3, 440}, + {58, 93.1, 440}, + {57.7, 93.1, 440}, + {51.2, 93, 440}, + {51.4, 93, 440}, + {58.5, 92.9, 440}, + {48.8, 92.9, 440}, + {51.1, 92.9, 440}, + {52.6, 92.9, 440}, + {49.9, 92.9, 440}, + {56.8, 92.7, 440}, + {58, 92.5, 440}, + {57, 92.3, 440}, + {51.5, 92.2, 440}, + {49.2, 92.2, 440}, + {50, 92.2, 440}, + {48.7, 91.8, 440}, + {53, 91.7, 440}, + {50.7, 91.7, 440}, + {48.8, 91.7, 440}, + {58.5, 91.6, 440}, + {49.8, 91.5, 440}, + {58, 90.9, 440}, + {59, 90.8, 440}, + {48.2, 90.7, 440}, + {50.2, 90.5, 440}, + {49.2, 90.5, 440}, + {57.2, 90.3, 440}, + {50.6, 90.2, 440}, + {59.4, 90.2, 440}, + {58.3, 90.1, 440}, + {47.8, 90.1, 440}, + {48.8, 90.1, 440}, + {59.2, 89.5, 440}, + {60, 89.4, 440}, + {48.3, 89.3, 440}, + {60.2, 88.8, 440}, + {48.7, 88.6, 440}, + {59.4, 88.6, 440}, + {47.8, 88.4, 440}, + {49.2, 88, 440}, + {48.2, 87.8, 440}, + {59.9, 87.8, 440}, + {47.9, 87.4, 440}, + {48.8, 87.3, 440}, + {59.4, 87.2, 440}, + {60.6, 86.7, 440}, + {60.9, 86.5, 440}, + {48.3, 86.4, 440}, + {49.3, 86.4, 440}, + {60.3, 86.1, 440}, + {60.4, 85.2, 440}, + {61.5, 85.2, 440}, + {60.2, 85, 440}, + {61.1, 84.7, 440}, + {59.9, 84.6, 440}, + {27.1, 84.4, 440}, + {61.4, 84, 440}, + {24.7, 83.6, 440}, + {25.4, 83.6, 440}, + {26.6, 83.6, 440}, + {60.9, 83.5, 440}, + {61.3, 82.8, 440}, + {25.3, 82.8, 440}, + {24.3, 82.7, 440}, + {60.2, 82.5, 440}, + {26.1, 82, 440}, + {24.9, 81.8, 440}, + {73.5, 97.8, 1941}, + {72.1, 93.3, 1941}, + {75, 93.1, 1941}, + {72.3, 89.7, 1941}, + {71.8, 85.4, 1941}, + {81.3, 84.8, 1941}, + {77.9, 84.7, 1941}, + {78.2, 83.1, 1941}, + {85.7, 79.2, 1941}, + {75.5, 79.2, 1941}, + {79.8, 78.6, 1941}, + {83, 78, 1941}, + {71, 78, 1941}, + {73.5, 73.5, 1941}, + {85.3, 70.8, 1941}, + {79.4, 70.3, 1941}, + {76.5, 70.2, 1941}, + {87.4, 66.7, 1941}, + {77.7, 65, 1941}, + {90.9, 64.3, 1941}, + {94.9, 63.8, 1941}, + {81.2, 63.3, 1941}, + {86.7, 63, 1941}, + {93.5, 59, 1941}, + {84.9, 55.9, 1941}, + {91.1, 54.6, 1941}, + {96.1, 54.1, 1941}, + {99.3, 51.8, 1941}, + {99.6, 46.8, 1941}, + {0.1, 58.6, 5121}, + {1.2, 56.8, 5121}, + {0.3, 55.1, 5121}, + }, + [5510] = { + {54.6, 15.7, 1519}, + {61.9, 36.6, 1519}, + {49.7, 97, 5581}, + }, + [5708] = { + {60.3, 59.5, 1477}, + {51.4, 24.7, 1477}, + }, + [5755] = { + {28.8, 40.3, 718}, + {27.5, 38.5, 718}, + {35.3, 33.6, 718}, + {26.5, 32.9, 718}, + {32.4, 31.7, 718}, + {25.3, 29.8, 718}, + {28.6, 26.5, 718}, + {30, 25.2, 718}, + }, + [5756] = { + {86.9, 81.3, 718}, + {79.3, 73.2, 718}, + {79, 68.5, 718}, + {86.2, 63.3, 718}, + {81.1, 58.4, 718}, + {62.5, 55.4, 718}, + {63.8, 52.1, 718}, + {87.8, 52, 718}, + {97.5, 51.1, 718}, + {88.6, 48.6, 718}, + {84.2, 45.8, 718}, + {88.6, 43.1, 718}, + {68.9, 41.5, 718}, + {92.9, 41.1, 718}, + }, + [5766] = { + {58.9, 35.7, 17}, + {59, 35.4, 17}, + {62.3, 33.4, 17}, + {61.9, 33.4, 17}, + {61.7, 33.1, 17}, + {62.1, 33, 17}, + {61.7, 32.5, 17}, + {62, 32.4, 17}, + {61.9, 32.3, 17}, + }, + [5807] = { + {53.4, 17, 215}, + }, + [5823] = { + {36.5, 49.7, 14}, + {64.3, 23, 17}, + {38.2, 51.1, 14}, + }, + [5828] = { + {62.1, 33.3, 17}, + }, + [5829] = { + {41.2, 22.2, 17}, + }, + [5834] = { + {44.8, 64.1, 17}, + {45, 63.6, 17}, + }, + [5856] = { + {64.6, 75.3, 51}, + {62.4, 70.4, 51}, + {60, 69.9, 51}, + {60.6, 68.8, 51}, + {67.9, 68.6, 51}, + {64, 68.3, 51}, + {62.8, 54.2, 51}, + {66.6, 53, 51}, + {58.9, 52.6, 51}, + {61.7, 51.1, 51}, + {63.5, 50.8, 51}, + {69.4, 50.7, 51}, + {65.6, 48.2, 51}, + {68.3, 45.6, 51}, + {67.9, 38.6, 51}, + {65, 38.3, 51}, + {66, 34.5, 51}, + {71.1, 18.4, 51}, + {73.6, 16.6, 51}, + {71.9, 16.2, 51}, + {62.4, 71, 51}, + {64, 68, 51}, + {65.5, 48.9, 51}, + }, + [5857] = { + {57.6, 83.9, 51}, + {56.8, 82.9, 51}, + {59.8, 81.9, 51}, + {48.8, 73.5, 51}, + {50, 73.5, 51}, + {52.2, 72.9, 51}, + {56.4, 72.8, 51}, + {57.5, 72.1, 51}, + {48.1, 71.8, 51}, + {53.4, 70.4, 51}, + {51.3, 69.7, 51}, + {56.8, 69.4, 51}, + {55.9, 69.3, 51}, + {49.2, 65.6, 51}, + {56.2, 65.6, 51}, + {47.4, 62.5, 51}, + {51.1, 61.6, 51}, + {53.4, 38.6, 51}, + {55.3, 38.1, 51}, + {56.1, 35.4, 51}, + {50.5, 34.7, 51}, + {55.9, 33.4, 51}, + {58, 27.7, 51}, + {56.7, 26.2, 51}, + {59.8, 25.8, 51}, + {57.6, 22.7, 51}, + {53.8, 70.1, 51}, + {50.6, 34.2, 51}, + }, + [5858] = { + {28.9, 77.6, 51}, + {22.2, 77.3, 51}, + {24.9, 76.7, 51}, + {29.1, 76.2, 51}, + {26.4, 75.4, 51}, + {22.8, 74.9, 51}, + {30.4, 70.9, 51}, + {26, 69.2, 51}, + {40.4, 69, 51}, + {31.1, 68.4, 51}, + {43.8, 68.2, 51}, + {27.6, 67.2, 51}, + {29.3, 62.4, 51}, + {31.1, 61.4, 51}, + {29.7, 57.1, 51}, + {28, 55.2, 51}, + {26.3, 51, 51}, + {28.2, 50.2, 51}, + {26.9, 48.3, 51}, + {28.1, 44.8, 51}, + {29.6, 43.7, 51}, + {27.7, 43.5, 51}, + {31.1, 41.4, 51}, + {32.6, 39.4, 51}, + {27.1, 37.3, 51}, + {37.8, 33.4, 51}, + {12.7, 17.9, 46}, + {14.7, 17.7, 46}, + {14, 16.1, 46}, + {15.6, 15.4, 46}, + {11.7, 14.6, 46}, + {13, 14.4, 46}, + {17.1, 13.9, 46}, + {14.9, 13.7, 46}, + {16, 13.2, 46}, + {14.4, 13.1, 46}, + {13.8, 11.4, 46}, + {14.2, 86, 51}, + {16.8, 85.9, 51}, + {15.8, 83.8, 51}, + {17.9, 82.8, 51}, + {12.9, 81.7, 51}, + {14.6, 81.5, 51}, + {20, 80.8, 51}, + {17, 80.6, 51}, + {18.5, 79.9, 51}, + {16.4, 79.8, 51}, + {11.6, 79.3, 51}, + {15.6, 77.5, 51}, + {22, 77.3, 51}, + {13.2, 76.8, 51}, + {15.1, 75.3, 51}, + {31.4, 68, 51}, + {27.9, 66.9, 51}, + {29.6, 56.5, 51}, + {28.3, 55.2, 51}, + {28, 50.4, 51}, + {27.2, 48.6, 51}, + {29.5, 43.8, 51}, + {31, 40.8, 51}, + {32.3, 39.8, 51}, + {38, 33.3, 51}, + {89.4, 48.8, 5581}, + {91.2, 48.7, 5581}, + {90.5, 47.3, 5581}, + {92, 46.6, 5581}, + {88.5, 45.8, 5581}, + {89.7, 45.7, 5581}, + {93.4, 45.2, 5581}, + {91.4, 45, 5581}, + {92.3, 44.6, 5581}, + {90.9, 44.5, 5581}, + {87.6, 44.2, 5581}, + {99.6, 43, 5581}, + {90.3, 42.9, 5581}, + {94.8, 42.8, 5581}, + {88.7, 42.4, 5581}, + {96.8, 42.4, 5581}, + {99.7, 42, 5581}, + {97.8, 41.5, 5581}, + {90.1, 41.4, 5581}, + {95.4, 41.1, 5581}, + {97.6, 37.2, 5581}, + {98.9, 35.6, 5581}, + {99.8, 32.5, 5581}, + {100, 28.5, 5581}, + {99.1, 27.5, 5581}, + {97.7, 24.7, 5581}, + {98.9, 24.2, 5581}, + {98.3, 23, 5581}, + {99, 20.3, 5581}, + {99.9, 19.7, 5581}, + {98.7, 19.4, 5581}, + {98.3, 15.2, 5581}, + }, + [5865] = { + {50.9, 18.2, 17}, + }, + [5937] = { + {71.5, 63.6, 400}, + }, + [5982] = { + {54.9, 44.6, 4}, + {58.2, 44.3, 4}, + {54.7, 44.3, 4}, + {53.6, 39.1, 4}, + {56.7, 37.4, 4}, + {56, 36.4, 4}, + {54.5, 36.3, 4}, + {45.4, 35.6, 4}, + {58.6, 28.3, 4}, + {61, 26.8, 4}, + {62.7, 26.6, 4}, + {62.5, 26.1, 4}, + {47.8, 23.5, 4}, + {49.1, 19.4, 4}, + {46.1, 18.7, 4}, + {50.2, 17.4, 4}, + {49.9, 17, 4}, + {56.7, 37.5, 4}, + {56, 36.5, 4}, + {62.7, 26.5, 4}, + {46, 18.9, 4}, + {50, 17, 4}, + }, + [5985] = { + {52.4, 57.2, 4}, + {60.9, 45.5, 4}, + {58.9, 45.4, 4}, + {60.9, 44.3, 4}, + {59, 44.1, 4}, + {55, 42.6, 4}, + {54, 41, 4}, + {51.2, 40, 4}, + {51.6, 38.8, 4}, + {60.2, 38.3, 4}, + {53.2, 38.1, 4}, + {59, 36.5, 4}, + {49, 36.5, 4}, + {59.3, 36, 4}, + {46.1, 35.1, 4}, + {44.9, 33.8, 4}, + {61, 45.6, 4}, + {58.9, 45.3, 4}, + {51, 39.4, 4}, + {51.9, 38.2, 4}, + {51, 36.5, 4}, + {44.9, 33.7, 4}, + }, + [5988] = { + {61, 36.6, 4}, + {52.4, 57.2, 4}, + {60.9, 45.5, 4}, + {58.9, 45.4, 4}, + {60.9, 44.3, 4}, + {59, 44.1, 4}, + {55, 42.6, 4}, + {54, 41, 4}, + {58.6, 41, 4}, + {51.2, 40, 4}, + {51.6, 38.8, 4}, + {60.2, 38.3, 4}, + {53.2, 38.1, 4}, + {59, 36.5, 4}, + {49, 36.5, 4}, + {59.3, 36, 4}, + {46.1, 35.1, 4}, + {44.9, 33.8, 4}, + {52.5, 30.5, 4}, + {51, 30.3, 4}, + {44, 29.2, 4}, + {50.8, 28.1, 4}, + {45, 27.6, 4}, + {44, 26.1, 4}, + {43.9, 25.5, 4}, + {55.7, 25.2, 4}, + {45.5, 24.7, 4}, + {45.1, 24.6, 4}, + {49.1, 24.6, 4}, + {48.6, 24.5, 4}, + {56.7, 23.4, 4}, + {48, 23.2, 4}, + {46, 23.1, 4}, + {44.1, 23.1, 4}, + {49.8, 22, 4}, + {56.6, 21.9, 4}, + {49, 21.7, 4}, + {45.1, 21.6, 4}, + {47, 21.5, 4}, + {46.7, 20.9, 4}, + {48.1, 20.1, 4}, + {46, 20.1, 4}, + {44.2, 20.1, 4}, + {44.7, 20, 4}, + {47.8, 19.7, 4}, + {57.1, 18.8, 4}, + {48, 18.8, 4}, + {45.1, 18.8, 4}, + {49, 18.7, 4}, + {47.1, 18.6, 4}, + {48.3, 17.8, 4}, + {56, 17.6, 4}, + {48, 17.2, 4}, + {46, 17.2, 4}, + {49.3, 16.8, 4}, + {49.1, 15.7, 4}, + {46.7, 14.4, 4}, + {50.8, 30, 4}, + {44, 29.1, 4}, + {44.9, 27.7, 4}, + {44, 23.1, 4}, + {48.8, 19.4, 4}, + {47.7, 18.8, 4}, + {45.8, 17.5, 4}, + }, + [5992] = { + {58.2, 30.4, 4}, + {58.3, 27.3, 4}, + {57.9, 35, 4}, + {56.9, 33.5, 4}, + {55.9, 32.1, 4}, + {55, 30.7, 4}, + {56.9, 30.6, 4}, + {61.1, 30.5, 4}, + {57.9, 29.1, 4}, + {56, 29.1, 4}, + {55, 29, 4}, + {61.8, 29, 4}, + {60.9, 27.8, 4}, + {59, 27.7, 4}, + {62.9, 27.6, 4}, + {61.9, 26.1, 4}, + {64.3, 54.7, 4}, + {50.1, 52, 4}, + {45.2, 49.2, 4}, + {45.2, 44.8, 4}, + {59.2, 43.2, 4}, + {57.7, 42.8, 4}, + {55.4, 41.6, 4}, + {60.9, 40.5, 4}, + {52.6, 40.3, 4}, + {45.8, 39.6, 4}, + {45.9, 38.3, 4}, + {51.3, 37.6, 4}, + {62.5, 36.9, 4}, + {57.9, 36.2, 4}, + {48.1, 35.4, 4}, + {44.1, 32.8, 4}, + {62.8, 30.9, 4}, + {44.8, 29.9, 4}, + {60.7, 29.4, 4}, + {51.9, 29.2, 4}, + {57.1, 28.4, 4}, + {50.7, 26.9, 4}, + {49.9, 24, 4}, + {55.9, 23.6, 4}, + {44.6, 23.2, 4}, + {46.7, 23, 4}, + {49, 20.8, 4}, + {56, 19.7, 4}, + {47.1, 18.1, 4}, + {55.4, 15.5, 4}, + }, + [6028] = { + {73.5, 60.3, 331}, + }, + [6206] = { + {52.4, 91.8, 721}, + {49.2, 89.7, 721}, + {47.6, 88, 721}, + {56.3, 85.5, 721}, + {60.7, 81.8, 721}, + {51.4, 80.5, 721}, + {51.2, 77.8, 721}, + {53.9, 76.6, 721}, + {49, 76.1, 721}, + {53.5, 76, 721}, + {53.7, 71.4, 721}, + {43, 68.4, 721}, + {37.2, 67.7, 721}, + {58.1, 66.5, 721}, + {57.1, 65.2, 721}, + {50.2, 63.7, 721}, + {58.3, 62.1, 721}, + {40.7, 53.6, 721}, + {58.7, 52.7, 721}, + {49.5, 47.6, 721}, + {52.9, 36.5, 721}, + {50.7, 36.3, 721}, + }, + [6207] = { + {47.7, 82.9, 721}, + {35.8, 71.2, 721}, + {50.9, 64, 721}, + {59.2, 63.9, 721}, + {53.6, 36.4, 721}, + }, + [6210] = { + {18.8, 39.2, 1}, + {20.4, 36.9, 1}, + {21.8, 33.7, 1}, + {20.6, 37, 1}, + {26.2, 94, 721}, + {42.1, 75, 721}, + {52.3, 46.6, 721}, + }, + [6211] = { + {56, 89.6, 721}, + {52.3, 88.7, 721}, + {50.7, 88.2, 721}, + {45.3, 84.2, 721}, + {37.7, 81.4, 721}, + {51.6, 80.5, 721}, + {60.7, 80, 721}, + {59.2, 76.3, 721}, + {33.9, 75.8, 721}, + {48.4, 75.7, 721}, + {61, 73.9, 721}, + {61.5, 72, 721}, + {54.5, 71.1, 721}, + {58.1, 70.4, 721}, + {58.6, 69.3, 721}, + {48.1, 68.9, 721}, + }, + [6218] = { + {46.9, 61.7, 721}, + {49.8, 57.5, 721}, + {51.9, 53.8, 721}, + {46, 48.5, 721}, + {49.9, 44.5, 721}, + {43.2, 44.2, 721}, + {57.2, 43, 721}, + {46.8, 37.5, 721}, + }, + [6219] = { + {53.2, 60.5, 721}, + {57.5, 56.9, 721}, + {51.7, 48.6, 721}, + {54.3, 44.5, 721}, + {55.3, 41.3, 721}, + }, + [6220] = { + {42.4, 57.8, 721}, + {55.1, 56.8, 721}, + {48.5, 53, 721}, + {41.3, 51.5, 721}, + {46.1, 42.3, 721}, + {51.2, 38.9, 721}, + }, + [6221] = { + {23.1, 39.5, 1}, + {22.4, 39.3, 1}, + {22.8, 39.2, 1}, + {21.8, 39.2, 1}, + {22.6, 39.2, 1}, + {22.5, 39.1, 1}, + {22.2, 39.1, 1}, + {22.8, 39.1, 1}, + {22.8, 39, 1}, + {23.5, 38.9, 1}, + {22.5, 38.8, 1}, + {21.9, 38.7, 1}, + {21.9, 38.6, 1}, + {64, 97.1, 721}, + {57.2, 95, 721}, + {61.1, 94.5, 721}, + {52, 94.4, 721}, + {59.7, 94.1, 721}, + {58.6, 93.7, 721}, + {58.6, 93.4, 721}, + {61.2, 93.3, 721}, + {55.8, 93.1, 721}, + {60.9, 92.7, 721}, + {67.5, 91.8, 721}, + {58.1, 91.3, 721}, + {53.1, 89.9, 721}, + {53.5, 89.2, 721}, + }, + [6223] = { + {15.7, 85.1, 721}, + {24.2, 74.2, 721}, + {15.9, 73, 721}, + {27.7, 71.8, 721}, + {29.7, 64.4, 721}, + {28.5, 60.3, 721}, + {29.8, 56, 721}, + {29.1, 50.5, 721}, + {32.9, 48, 721}, + }, + [6233] = { + {9.2, 92.1, 721}, + {11.7, 89.4, 721}, + {17.9, 89.4, 721}, + {23.4, 74.1, 721}, + {28.2, 71, 721}, + {25, 69.8, 721}, + {15.7, 69.7, 721}, + {29.8, 65.6, 721}, + {27.4, 65.4, 721}, + {27.3, 57.3, 721}, + {29.7, 57.1, 721}, + {6.8, 56.9, 721}, + {2.2, 55.8, 721}, + {30.4, 53.4, 721}, + {30.8, 52.8, 721}, + {34.5, 51.8, 721}, + {33.6, 51.7, 721}, + {33.3, 51.6, 721}, + {32.9, 49, 721}, + {34.2, 48.1, 721}, + }, + [6329] = { + {53.9, 90, 721}, + {50.7, 89.8, 721}, + {46.4, 88.8, 721}, + {54.4, 88.4, 721}, + {49.1, 88.2, 721}, + {46.7, 87.6, 721}, + {38.8, 83, 721}, + {38.2, 82.1, 721}, + {61.5, 79.3, 721}, + {46.1, 78.2, 721}, + {45.7, 77.3, 721}, + {59.2, 77.2, 721}, + {57.8, 76.9, 721}, + {33.6, 75, 721}, + {41.5, 73.6, 721}, + {51.3, 73.3, 721}, + {41, 73.1, 721}, + {61.3, 73, 721}, + {50.6, 72.9, 721}, + {41.9, 72.8, 721}, + {46, 72, 721}, + {46, 71.2, 721}, + {59.5, 70.1, 721}, + {42.5, 68.4, 721}, + {37.7, 68.2, 721}, + {61.6, 66.9, 721}, + {41.1, 64.6, 721}, + {49.5, 63.7, 721}, + {53.7, 63.6, 721}, + {48.4, 63.5, 721}, + {40.2, 63.5, 721}, + {45, 62.3, 721}, + {55.1, 61.6, 721}, + {43.3, 59.6, 721}, + {58.2, 56.8, 721}, + {40.9, 56, 721}, + {40.6, 55.4, 721}, + {49.5, 55, 721}, + {59, 53.4, 721}, + {48, 49.8, 721}, + {52.9, 49.5, 721}, + {40.5, 48.4, 721}, + {40.7, 47.6, 721}, + {58.8, 44.7, 721}, + {57.4, 42.2, 721}, + {47.5, 36.3, 721}, + }, + [6352] = { + {65.8, 97, 16}, + {53.3, 96, 16}, + {60.6, 95.5, 16}, + {63.1, 95.3, 16}, + {59.1, 95.3, 16}, + {54, 95.2, 16}, + {47.3, 93.3, 16}, + {45.3, 92.3, 16}, + {77.8, 87.7, 16}, + {79.1, 86.3, 16}, + {80.3, 85.3, 16}, + {63.8, 85, 16}, + {63, 83.5, 16}, + {79, 82.6, 16}, + {78.9, 77.7, 16}, + {77.9, 77.6, 16}, + {68.4, 75.7, 16}, + {71.9, 74.9, 16}, + {75.2, 74.5, 16}, + {72.3, 73.5, 16}, + {56.6, 73.5, 16}, + {61.5, 72.7, 16}, + {65.4, 72.5, 16}, + {56.2, 72.5, 16}, + {73.2, 71.8, 16}, + {54.7, 69.3, 16}, + {56.2, 69.1, 16}, + {55.3, 68.6, 16}, + {52.6, 44.4, 16}, + {52.2, 42.7, 16}, + {69.8, 36.5, 16}, + {71.2, 36.3, 16}, + {92, 34.4, 16}, + {86.9, 34.3, 16}, + {68.4, 34.2, 16}, + {88.8, 31.6, 16}, + {86.9, 30.5, 16}, + {85.7, 30.4, 16}, + {84.4, 30.3, 16}, + {86.2, 29.4, 16}, + {86.3, 27.7, 16}, + {89, 27.5, 16}, + {85.7, 26.3, 16}, + {88.2, 26.1, 16}, + {88.1, 22.6, 16}, + {55, 96.9, 16}, + {53.6, 96.8, 16}, + {50, 96.7, 16}, + }, + [6369] = { + {87.5, 16.5, 16}, + {87.6, 14.7, 16}, + {85.4, 13.5, 16}, + {84.8, 12.7, 16}, + {84.9, 10.8, 16}, + {83.7, 10.5, 16}, + {82.3, 8.8, 16}, + {68.5, 7.9, 16}, + }, + [6391] = { + {71, 38, 1}, + {41.8, 71.9, 721}, + {42.2, 70.7, 721}, + {36.9, 68.4, 721}, + {37.7, 67.4, 721}, + {40.9, 64.8, 721}, + {35.1, 62.9, 721}, + {34.9, 62.2, 721}, + }, + [6392] = { + {40.3, 75.1, 721}, + {40, 74.6, 721}, + {40.7, 65.5, 721}, + {35, 62.6, 721}, + }, + [6407] = { + {39.9, 74.9, 721}, + {38.5, 60.7, 721}, + {38.2, 60.5, 721}, + }, + [6505] = { + {61.9, 83.2, 490}, + {61.4, 81.9, 490}, + {59.8, 81.6, 490}, + {61, 81.6, 490}, + {59.6, 81.1, 490}, + {62.5, 80.4, 490}, + {63.6, 80.2, 490}, + {58.3, 80, 490}, + {59.6, 79.2, 490}, + {64.8, 79.1, 490}, + {61.3, 78.8, 490}, + {67.9, 78.7, 490}, + {58, 78.5, 490}, + {60.6, 78.1, 490}, + {63.4, 78.1, 490}, + {66.3, 77.8, 490}, + {64, 77.5, 490}, + {57.4, 77.1, 490}, + {62.4, 76.8, 490}, + {62.7, 76.3, 490}, + {58.3, 76.1, 490}, + {59.8, 75.9, 490}, + {61.4, 75.8, 490}, + {60.7, 75.3, 490}, + {64.6, 75.1, 490}, + {68, 75, 490}, + {59.2, 74.7, 490}, + {64.2, 74.2, 490}, + {60, 73.7, 490}, + {67.5, 73.6, 490}, + {60.9, 73.4, 490}, + {67.6, 72.9, 490}, + {61.5, 72.7, 490}, + {62.6, 72.3, 490}, + {67.6, 72.1, 490}, + {64.1, 72.1, 490}, + {66.8, 71.1, 490}, + {65.1, 71.1, 490}, + {59.7, 71, 490}, + {61.6, 70.9, 490}, + {60.7, 69.7, 490}, + {66.3, 68, 490}, + {66.5, 68, 490}, + {68.1, 67.4, 490}, + {60.7, 67, 490}, + {62.9, 66.7, 490}, + {61.7, 66.3, 490}, + {63.1, 66.3, 490}, + {67.2, 65.7, 490}, + {65.2, 65.4, 490}, + {64.5, 64, 490}, + {67, 62.9, 490}, + {63.6, 62.7, 490}, + {64.5, 61.9, 490}, + {66.4, 61.8, 490}, + {68.1, 61.5, 490}, + {63.9, 61.1, 490}, + {65.2, 60.6, 490}, + {67.5, 60.2, 490}, + {59.9, 75.9, 490}, + {68.1, 75, 490}, + {62.6, 72, 490}, + {68.2, 61.2, 490}, + }, + [6506] = { + {61.9, 83.2, 490}, + {61.4, 81.9, 490}, + {59.8, 81.6, 490}, + {61, 81.6, 490}, + {59.6, 81.1, 490}, + {62.5, 80.4, 490}, + {63.6, 80.2, 490}, + {58.3, 80, 490}, + {59.6, 79.2, 490}, + {64.8, 79.1, 490}, + {61.3, 78.8, 490}, + {67.9, 78.7, 490}, + {58, 78.5, 490}, + {60.6, 78.1, 490}, + {63.4, 78.1, 490}, + {66.3, 77.8, 490}, + {64, 77.5, 490}, + {57.4, 77.1, 490}, + {62.4, 76.8, 490}, + {62.7, 76.3, 490}, + {58.3, 76.1, 490}, + {59.8, 75.9, 490}, + {61.4, 75.8, 490}, + {60.7, 75.3, 490}, + {64.6, 75.1, 490}, + {68, 75, 490}, + {59.2, 74.7, 490}, + {64.2, 74.2, 490}, + {60, 73.7, 490}, + {67.5, 73.6, 490}, + {60.9, 73.4, 490}, + {67.6, 72.9, 490}, + {61.5, 72.7, 490}, + {62.6, 72.3, 490}, + {67.6, 72.1, 490}, + {64.1, 72.1, 490}, + {66.8, 71.1, 490}, + {65.1, 71.1, 490}, + {59.7, 71, 490}, + {61.6, 70.9, 490}, + {60.7, 69.7, 490}, + {66.3, 68, 490}, + {66.5, 68, 490}, + {68.1, 67.4, 490}, + {60.7, 67, 490}, + {62.9, 66.7, 490}, + {61.7, 66.3, 490}, + {63.1, 66.3, 490}, + {67.2, 65.7, 490}, + {65.2, 65.4, 490}, + {64.5, 64, 490}, + {67, 62.9, 490}, + {63.6, 62.7, 490}, + {64.5, 61.9, 490}, + {66.4, 61.8, 490}, + {68.1, 61.5, 490}, + {63.9, 61.1, 490}, + {65.2, 60.6, 490}, + {67.5, 60.2, 490}, + {59.9, 75.9, 490}, + {68.1, 75, 490}, + {62.6, 72, 490}, + {68.2, 61.2, 490}, + }, + [6507] = { + {71.6, 61.5, 490}, + {70.7, 60.1, 490}, + {62.6, 59, 490}, + {70.5, 58.8, 490}, + {64.3, 58.7, 490}, + {69, 57.4, 490}, + {67.1, 57.3, 490}, + {69.6, 56.1, 490}, + {68.3, 56, 490}, + {63.6, 54.9, 490}, + {62.7, 54.8, 490}, + {68.9, 54.7, 490}, + {68, 53.5, 490}, + {70.9, 53.5, 490}, + {71.6, 53.4, 490}, + {69.7, 53.3, 490}, + {62.7, 53.3, 490}, + {70.3, 52.9, 490}, + {67.5, 52.8, 490}, + {70.8, 52.1, 490}, + {63.5, 52, 490}, + {66.4, 51.8, 490}, + {64.5, 50.7, 490}, + {72.6, 50.6, 490}, + {70.3, 50.6, 490}, + {67.5, 50.5, 490}, + {62.4, 50.5, 490}, + {71.8, 50.3, 490}, + {72.6, 50.2, 490}, + {63.8, 50.1, 490}, + {69.6, 49.7, 490}, + {65.7, 49.5, 490}, + {67.8, 49.4, 490}, + {65.8, 49.1, 490}, + {64.5, 49.1, 490}, + {66.9, 49.1, 490}, + {66.6, 48.1, 490}, + {64.1, 47.8, 490}, + {65.5, 46.8, 490}, + {70.6, 45.4, 490}, + {68, 45.2, 490}, + {70.6, 60.1, 490}, + {69.2, 57.5, 490}, + {68.7, 54.9, 490}, + {68, 53.9, 490}, + {64.4, 50.5, 490}, + }, + [6508] = { + {71.6, 61.5, 490}, + {70.7, 60.1, 490}, + {62.6, 59, 490}, + {70.5, 58.8, 490}, + {64.3, 58.7, 490}, + {69, 57.4, 490}, + {67.1, 57.3, 490}, + {69.6, 56.1, 490}, + {68.3, 56, 490}, + {63.6, 54.9, 490}, + {62.7, 54.8, 490}, + {68.9, 54.7, 490}, + {68, 53.5, 490}, + {70.9, 53.5, 490}, + {71.6, 53.4, 490}, + {69.7, 53.3, 490}, + {62.7, 53.3, 490}, + {70.3, 52.9, 490}, + {67.5, 52.8, 490}, + {70.8, 52.1, 490}, + {63.5, 52, 490}, + {66.4, 51.8, 490}, + {64.5, 50.7, 490}, + {72.6, 50.6, 490}, + {70.3, 50.6, 490}, + {67.5, 50.5, 490}, + {62.4, 50.5, 490}, + {71.8, 50.3, 490}, + {72.6, 50.2, 490}, + {63.8, 50.1, 490}, + {69.6, 49.7, 490}, + {65.7, 49.5, 490}, + {67.8, 49.4, 490}, + {65.8, 49.1, 490}, + {64.5, 49.1, 490}, + {66.9, 49.1, 490}, + {66.6, 48.1, 490}, + {64.1, 47.8, 490}, + {65.5, 46.8, 490}, + {70.6, 45.4, 490}, + {68, 45.2, 490}, + {70.6, 60.1, 490}, + {69.2, 57.5, 490}, + {68.7, 54.9, 490}, + {68, 53.9, 490}, + {64.4, 50.5, 490}, + }, + [6581] = { + {62.3, 65.9, 490}, + }, + [6788] = { + {51.5, 38.3, 148}, + {36, 12.3, 361}, + }, + [6789] = { + {51.4, 38.3, 148}, + {51.6, 38.3, 148}, + {51.5, 38.1, 148}, + {51.6, 37.8, 148}, + {36, 12.4, 361}, + {36.1, 12.3, 361}, + {36.1, 12.1, 361}, + {36.1, 11.8, 361}, + {51.5, 38.2, 148}, + {36, 12.2, 361}, + }, + [6906] = { + {59.1, 93.5, 1337}, + }, + [7022] = { + {36.5, 60.5, 1337}, + {41.4, 60, 1337}, + {46.5, 56.7, 1337}, + {45.4, 55, 1337}, + }, + [7055] = { + {49.2, 58.5, 46}, + {44.6, 58, 46}, + {48.7, 57.6, 46}, + {38.7, 54.5, 46}, + {55.7, 54.1, 46}, + {51, 53.4, 46}, + }, + [7078] = { + {39.4, 63.6, 1337}, + {39.5, 63.3, 1337}, + {39.9, 63.3, 1337}, + {39.1, 63.1, 1337}, + {40.3, 63, 1337}, + {39.5, 62.9, 1337}, + {39.4, 62.7, 1337}, + {40.4, 62.4, 1337}, + {39.8, 62.2, 1337}, + {48.8, 62, 1337}, + {40.1, 62, 1337}, + {48.2, 61.7, 1337}, + {48.9, 61.4, 1337}, + {49.7, 61.3, 1337}, + {47.9, 61.3, 1337}, + {49.4, 61.2, 1337}, + {48.5, 61, 1337}, + {49.6, 60.7, 1337}, + {48.9, 60.5, 1337}, + {49.2, 60.3, 1337}, + {42.4, 58.6, 1337}, + {41.9, 58.1, 1337}, + {41.7, 57.9, 1337}, + {41.2, 57.9, 1337}, + {43.1, 57.8, 1337}, + {42.8, 57.5, 1337}, + {43.3, 57.3, 1337}, + {42.2, 57.2, 1337}, + {43, 57.1, 1337}, + {43.2, 56.6, 1337}, + }, + [7097] = { + {54.8, 88.1, 148}, + {57.5, 26.6, 331}, + {56.7, 90.9, 361}, + {46.5, 87.7, 361}, + {51.5, 85.6, 361}, + {53.8, 84.3, 361}, + {56.8, 83.6, 361}, + {44.6, 82.8, 361}, + {50.7, 82.1, 361}, + {49, 81.5, 361}, + {44.5, 81.5, 361}, + {42.2, 81.5, 361}, + {47.8, 77.2, 361}, + {46.9, 73.8, 361}, + {37.6, 72.8, 361}, + {39.9, 69, 361}, + {6, 99.9, 616}, + {2.9, 98.9, 616}, + {0.8, 91.1, 616}, + }, + [7098] = { + {53.7, 27, 361}, + {56.7, 26.4, 361}, + {55.6, 23.3, 361}, + {63.7, 21.9, 361}, + {56.4, 21.4, 361}, + {62.5, 19.4, 361}, + {64, 18.3, 361}, + {53.8, 16.2, 361}, + {51.2, 16.2, 361}, + {62.5, 16, 361}, + {57.5, 15.9, 361}, + {53.2, 11.6, 361}, + {55.7, 10.8, 361}, + {11.5, 1.3, 616}, + {16.9, 0.1, 616}, + }, + [7099] = { + {44.9, 69.9, 361}, + {45, 66.9, 361}, + {42.6, 64.6, 361}, + {43.2, 63.4, 361}, + {41.3, 57, 361}, + {39.5, 56.8, 361}, + {38.5, 55.1, 361}, + {38.1, 53.7, 361}, + {41.5, 53.7, 361}, + {41.8, 50.1, 361}, + {38.6, 49.4, 361}, + {38.2, 42, 361}, + {39.2, 37, 361}, + {40.6, 34.3, 361}, + {49.5, 33.6, 361}, + {51.7, 29.5, 361}, + {45.2, 17.6, 361}, + {50.2, 16, 361}, + {47.9, 15.4, 361}, + {3.9, 13, 616}, + {7.8, 5.8, 616}, + }, + [7268] = { + {22.1, 47.3, 1176}, + {16.7, 45.7, 1176}, + {15.7, 45.2, 1176}, + {16.5, 44.3, 1176}, + {13.8, 36.5, 1176}, + {14.1, 36.4, 1176}, + {41.1, 36.1, 1176}, + {40.9, 35.4, 1176}, + {44.2, 31.3, 1176}, + {43.4, 30.8, 1176}, + }, + [7319] = { + {47.5, 26, 141}, + {48.1, 25.1, 141}, + {41.5, 25.1, 141}, + }, + [7405] = { + {34.8, 38.4, 1337}, + {34.7, 38.2, 1337}, + {34.4, 38.2, 1337}, + {34.9, 38.2, 1337}, + {35, 37.9, 1337}, + {35.4, 37.7, 1337}, + {35.3, 37.6, 1337}, + {35.1, 37.5, 1337}, + {35.1, 37.4, 1337}, + {35.5, 37.3, 1337}, + }, + [7430] = { + {48.9, 19, 618}, + {48.9, 18.8, 618}, + {50.2, 17.1, 618}, + {50.3, 17, 618}, + {52.2, 14.8, 618}, + {52.1, 14.7, 618}, + {55.7, 14.6, 618}, + {55.7, 14.5, 618}, + {48.9, 11.5, 618}, + {48.9, 11.4, 618}, + {51.7, 11.3, 618}, + {54.7, 10.8, 618}, + {54.6, 10.7, 618}, + {51.9, 9.3, 618}, + {49.6, 8.9, 618}, + {49.6, 8.8, 618}, + {48.5, 7.9, 618}, + {48.5, 7.8, 618}, + {49.6, 7, 618}, + {49.7, 7, 618}, + }, + [7431] = { + {51.1, 22.3, 618}, + {50.7, 21.4, 618}, + {50.5, 20.1, 618}, + {50.7, 18.7, 618}, + {49.8, 18.7, 618}, + {53.7, 18.1, 618}, + {54.5, 17.2, 618}, + {55.5, 17.1, 618}, + {53.1, 16.8, 618}, + {54.4, 15.8, 618}, + {56.8, 15.3, 618}, + {53.1, 15.2, 618}, + {54, 15.2, 618}, + {54.9, 15.1, 618}, + {56.9, 14, 618}, + {54, 13.7, 618}, + {54.9, 13.7, 618}, + {54.5, 13, 618}, + {56.3, 13, 618}, + {55.5, 12.8, 618}, + {56.8, 12.5, 618}, + {54.8, 12.2, 618}, + {56.4, 11.7, 618}, + {49.7, 18.7, 618}, + {54.5, 15.7, 618}, + {54.9, 15.2, 618}, + {54.1, 15, 618}, + {54.9, 13.5, 618}, + {54.6, 13.2, 618}, + }, + [7432] = { + {51.5, 21.5, 618}, + {50.1, 20.8, 618}, + {49.8, 20, 618}, + {51.2, 19.4, 618}, + {50.2, 19.4, 618}, + {50.2, 18, 618}, + {48.4, 17.7, 618}, + {49.8, 17.3, 618}, + {50.7, 17.3, 618}, + {56.6, 17.2, 618}, + {48.9, 17, 618}, + {56.7, 16.7, 618}, + {55.9, 16.6, 618}, + {54, 16.5, 618}, + {55, 16.5, 618}, + {49.4, 16.3, 618}, + {48.9, 16, 618}, + {56.3, 15.9, 618}, + {53.6, 15.8, 618}, + {52.7, 15.8, 618}, + {49.9, 15.6, 618}, + {55.7, 15.2, 618}, + {51.1, 15.2, 618}, + {48.3, 15.2, 618}, + {52.1, 15.2, 618}, + {50.1, 15.2, 618}, + {48.8, 14.6, 618}, + {52.5, 14.6, 618}, + {56.4, 14.4, 618}, + {49.7, 14.4, 618}, + {55.5, 14.3, 618}, + {51.6, 14.2, 618}, + {48.3, 14, 618}, + {50.3, 13.7, 618}, + {50.9, 13.2, 618}, + {53.5, 13, 618}, + {54, 12.2, 618}, + {53.1, 12.2, 618}, + {53.6, 11.8, 618}, + {53.9, 10.8, 618}, + {50.7, 18.7, 618}, + {49.8, 18.7, 618}, + {54, 15.2, 618}, + {54.9, 15.1, 618}, + {54, 13.7, 618}, + {54.9, 13.7, 618}, + {54.5, 13, 618}, + {51.3, 19.3, 618}, + {50.7, 18.6, 618}, + {50.1, 18, 618}, + {50.7, 17.4, 618}, + {50.3, 13.8, 618}, + {54, 13.6, 618}, + }, + [7433] = { + {50.4, 16.6, 618}, + {50.8, 16, 618}, + {51.6, 15.8, 618}, + {49.3, 15.1, 618}, + {50.8, 14.4, 618}, + {52.6, 14.4, 618}, + {53, 14.2, 618}, + {51.2, 13.9, 618}, + {49.2, 13.7, 618}, + {48.6, 13, 618}, + {49.3, 12.3, 618}, + {52, 12.1, 618}, + {48.6, 12.1, 618}, + {52.6, 11.7, 618}, + {54.9, 11.1, 618}, + {53.1, 11, 618}, + {49.4, 10.9, 618}, + {52.2, 10.8, 618}, + {51.7, 10.2, 618}, + {52.4, 9.6, 618}, + {49.3, 8.1, 618}, + {50.2, 8.1, 618}, + {51.3, 8, 618}, + {49.8, 7.5, 618}, + {48.7, 7.4, 618}, + {50.4, 6.7, 618}, + {50.2, 18, 618}, + {49.8, 17.3, 618}, + {50.7, 17.3, 618}, + }, + [7434] = { + {48.3, 9.5, 618}, + {51.7, 8.9, 618}, + {50.7, 8.8, 618}, + {50.7, 7.4, 618}, + {49.3, 12.3, 618}, + }, + [7443] = { + {66.7, 54.4, 618}, + {68.9, 53.8, 618}, + {67.2, 52.9, 618}, + {66.1, 51.4, 618}, + {68.1, 50.2, 618}, + {54.3, 49, 618}, + {66.4, 48.9, 618}, + {54.9, 48.3, 618}, + {69, 48.3, 618}, + {66.3, 48.3, 618}, + {62.4, 48.2, 618}, + {61.8, 48.1, 618}, + {62.9, 47.8, 618}, + {62.3, 46.4, 618}, + {57.6, 46.1, 618}, + {56.4, 46, 618}, + {58.2, 46, 618}, + {58.7, 45.9, 618}, + {54.6, 45.5, 618}, + {60.3, 45.4, 618}, + {62.5, 44.9, 618}, + {59.2, 44.8, 618}, + {62.4, 44.8, 618}, + {60.2, 44.7, 618}, + {61.6, 43.9, 618}, + {58.2, 43.8, 618}, + {59.8, 42.6, 618}, + {60.5, 42.1, 618}, + {59.6, 41.8, 618}, + {58.3, 41.3, 618}, + {59.7, 41.3, 618}, + {57.8, 40.5, 618}, + {57.7, 40.3, 618}, + {57.1, 39, 618}, + {56.6, 38.7, 618}, + {56.5, 38.4, 618}, + {55.9, 38.3, 618}, + {59.6, 34.4, 618}, + {62.4, 33.4, 618}, + {68.6, 32.9, 618}, + {67.9, 32.7, 618}, + {51.9, 32.6, 618}, + {68.3, 32.5, 618}, + {64.4, 32, 618}, + {63, 31.7, 618}, + {61.7, 31.7, 618}, + {67.2, 31.4, 618}, + {65.8, 30.8, 618}, + {56.6, 30.8, 618}, + {64, 28.7, 618}, + {68, 28.6, 618}, + {57.3, 28.6, 618}, + {64.9, 27.7, 618}, + {64.1, 26.5, 618}, + {66, 26, 618}, + {62.5, 48.1, 618}, + {57.4, 28.5, 618}, + {49.7, 90.2, 5179}, + }, + [7444] = { + {30.2, 47.2, 618}, + {48.2, 46.7, 618}, + {46.3, 46.7, 618}, + {48.5, 46.4, 618}, + {45.4, 45.8, 618}, + {48.1, 45.7, 618}, + {47.8, 44.3, 618}, + {45.3, 44, 618}, + {34.4, 43.9, 618}, + {44.2, 43, 618}, + {46, 41.9, 618}, + {50.7, 39.6, 618}, + {30.4, 39.4, 618}, + {35.8, 39.3, 618}, + {31.6, 39.3, 618}, + {50.7, 39.2, 618}, + {38.9, 39.2, 618}, + {33.8, 39.1, 618}, + {39.4, 39, 618}, + {30.5, 39, 618}, + {52, 38.7, 618}, + {39.4, 38, 618}, + {44.6, 37.9, 618}, + {54.9, 37.7, 618}, + {40.4, 37.2, 618}, + {47.2, 37, 618}, + {53.4, 37, 618}, + {42.2, 37, 618}, + {41.8, 36.3, 618}, + {47.6, 35.9, 618}, + {45.9, 34.9, 618}, + {48.1, 34.8, 618}, + {48.6, 34.3, 618}, + {42.7, 34.1, 618}, + {33.7, 39, 618}, + {39.5, 38.9, 618}, + }, + [7445] = { + {60.8, 64, 618}, + {58.3, 60.5, 618}, + {61.1, 58.7, 618}, + {62.7, 55.1, 618}, + {63.5, 55, 618}, + {62.3, 29.5, 618}, + {61.5, 27.3, 618}, + {61.9, 26.9, 618}, + {56.6, 26.4, 618}, + {62.7, 26.2, 618}, + {59.3, 25.9, 618}, + {62.6, 24.9, 618}, + {56.9, 24.9, 618}, + {60.4, 24.6, 618}, + {59.9, 24.2, 618}, + {61.1, 24.2, 618}, + {59.3, 23.7, 618}, + {55.8, 23.6, 618}, + {55.8, 22.2, 618}, + {61.5, 21.6, 618}, + {59, 21.3, 618}, + {60.7, 21.1, 618}, + {58.4, 20.8, 618}, + {59.8, 20.3, 618}, + {54.7, 20.2, 618}, + {57.6, 19.5, 618}, + {56.1, 19.4, 618}, + {60.4, 18.1, 618}, + {59.8, 17.9, 618}, + {58.1, 16.7, 618}, + {59.3, 16.6, 618}, + {59.2, 16.5, 618}, + {58.6, 15.8, 618}, + {57.9, 15.3, 618}, + {58.7, 15.2, 618}, + {60.6, 15.2, 618}, + {59.5, 15.1, 618}, + {60.5, 14.4, 618}, + {58.2, 14, 618}, + {58.7, 13.8, 618}, + {60.2, 13.8, 618}, + {59.6, 13.7, 618}, + {60.3, 12.7, 618}, + {57.8, 12.6, 618}, + {59.1, 12.1, 618}, + {61.6, 21.5, 618}, + {49.8, 90.9, 5179}, + }, + [7446] = { + {63.4, 74.4, 618}, + {58.7, 74, 618}, + {61.1, 72.9, 618}, + {58.4, 72.3, 618}, + {58, 72.2, 618}, + {59.7, 72.2, 618}, + {58.4, 71.8, 618}, + {60.1, 71.5, 618}, + {59.2, 71.5, 618}, + {57.1, 71.4, 618}, + {56.4, 71.4, 618}, + {56.8, 70.9, 618}, + {57.6, 70.8, 618}, + {66.7, 70.6, 618}, + {58.6, 70.6, 618}, + {57.2, 69.9, 618}, + {67.1, 69.8, 618}, + {56.7, 69.3, 618}, + {66.8, 69.3, 618}, + {67.1, 69, 618}, + {57.3, 68.7, 618}, + {50.1, 90.6, 5179}, + }, + [7456] = { + {60.3, 64.5, 618}, + {63, 63.6, 618}, + {61.5, 62.3, 618}, + {60, 61.6, 618}, + {60.2, 61.5, 618}, + {60.4, 61.2, 618}, + {61.1, 60.6, 618}, + {60.6, 60.3, 618}, + {60.1, 59.9, 618}, + {60.8, 59.8, 618}, + {57.8, 59.7, 618}, + {60.3, 58.8, 618}, + {60.3, 58.2, 618}, + {60.4, 58, 618}, + {60.6, 55.8, 618}, + {62.5, 55.7, 618}, + {60.1, 55.3, 618}, + {62.1, 55.2, 618}, + {62.1, 53.9, 618}, + {62.4, 53, 618}, + {62.5, 27.9, 618}, + {57.8, 27, 618}, + {58.4, 26.5, 618}, + {57.8, 25.2, 618}, + {61.4, 24.1, 618}, + {54.9, 23.3, 618}, + {58.6, 21.5, 618}, + {61.2, 21, 618}, + {57.8, 21, 618}, + {54.6, 20.6, 618}, + {59.3, 19.4, 618}, + {59.2, 19, 618}, + {58.4, 17.7, 618}, + {60.1, 15.9, 618}, + {60.1, 14.4, 618}, + {57.1, 13.2, 618}, + {59.1, 11.8, 618}, + }, + [7942] = { + {30.6, 43.4, 357}, + }, + [7976] = { + {61.6, 89.4, 1537}, + }, + [8131] = { + {50.7, 27.5, 440}, + }, + [8139] = { + {67, 22, 440}, + }, + [8207] = { + {54.2, 39.9, 440}, + }, + [8208] = { + {47.4, 25, 440}, + }, + [8211] = { + {11.9, 53.7, 47}, + {96.9, 14.6, 267}, + }, + [8213] = { + {81.5, 49.2, 47}, + }, + [8299] = { + {61.1, 35.4, 4}, + }, + [8300] = { + {61.1, 36.6, 4}, + }, + [8301] = { + {48.3, 38.7, 4}, + }, + [8303] = { + {56.1, 31.1, 4}, + }, + [8336] = { + {24.8, 64.8, 1477}, + {34.5, 60.5, 1477}, + {24.4, 50.4, 1477}, + {21, 46.6, 1477}, + {33.6, 31.1, 1477}, + {24.8, 27.1, 1477}, + {27.6, 26.7, 1477}, + }, + [8362] = { + {37.7, 30, 215}, + {38.9, 64.7, 1638}, + }, + [8600] = { + {84.1, 69, 28}, + {82.4, 68.5, 28}, + {83.1, 64.7, 28}, + {82.8, 64.1, 28}, + {82.8, 61.6, 28}, + {77.4, 58.6, 28}, + {76, 57.4, 28}, + {78.6, 57.2, 28}, + {78.6, 57.1, 28}, + {74, 56.9, 28}, + {72, 56.5, 28}, + {75.3, 56.4, 28}, + {77.5, 56.1, 28}, + {75.6, 56.1, 28}, + {74.4, 55.8, 28}, + {75.4, 55.8, 28}, + {75.7, 55.6, 28}, + {78, 55.3, 28}, + {73, 54.8, 28}, + {76.9, 54.8, 28}, + {74, 52.8, 28}, + {73.4, 52.6, 28}, + {75.9, 52.4, 28}, + {76.9, 51.6, 28}, + {77.4, 48.4, 28}, + {76.2, 47.9, 28}, + {75.2, 44.8, 28}, + {74.4, 43.6, 28}, + {74.1, 43.4, 28}, + {74.8, 42.2, 28}, + {75.8, 40.9, 28}, + {70.2, 37.5, 28}, + {68, 34.2, 28}, + {67.5, 33.4, 28}, + {68.5, 30.6, 28}, + {66.1, 23.8, 28}, + {26.2, 93.5, 139}, + {24.4, 92.9, 139}, + {26.7, 89.1, 139}, + {44.6, 88.9, 139}, + {25.1, 88.7, 139}, + {24.8, 88, 139}, + {61.2, 87.3, 139}, + {62, 86.7, 139}, + {45.1, 86.6, 139}, + {60.6, 85.9, 139}, + {42.9, 85.8, 139}, + {47.2, 85.6, 139}, + {32.7, 85.3, 139}, + {24.7, 85.2, 139}, + {64.5, 84.9, 139}, + {35.2, 84.5, 139}, + {42, 84.5, 139}, + {33.5, 84.5, 139}, + {32, 84.4, 139}, + {32.5, 84.3, 139}, + {37.5, 84.2, 139}, + {74, 84, 139}, + {57.7, 83.8, 139}, + {72, 83.7, 139}, + {58.2, 83.2, 139}, + {42.1, 82.6, 139}, + {44.4, 82.5, 139}, + {36.1, 82.4, 139}, + {32, 82.4, 139}, + {30.8, 82.3, 139}, + {33.7, 82.3, 139}, + {42, 82.2, 139}, + {18.8, 81.8, 139}, + {71.5, 81.7, 139}, + {69.7, 81.6, 139}, + {54.7, 80.9, 139}, + {55.8, 80.8, 139}, + {68.8, 80.6, 139}, + {50.6, 80.6, 139}, + {50.2, 80.6, 139}, + {17.2, 80.5, 139}, + {57.5, 80.4, 139}, + {20.1, 80.3, 139}, + {44.3, 80.2, 139}, + {20.1, 80.2, 139}, + {15, 80, 139}, + {23.4, 79.9, 139}, + {68.8, 79.9, 139}, + {12.8, 79.6, 139}, + {16.4, 79.4, 139}, + {18.9, 79.1, 139}, + {20.5, 79.1, 139}, + {16.8, 79.1, 139}, + {15.4, 78.8, 139}, + {16.5, 78.7, 139}, + {16.9, 78.6, 139}, + {52.8, 78.3, 139}, + {19.5, 78.2, 139}, + {56.6, 78.1, 139}, + {52.9, 77.8, 139}, + {13.8, 77.7, 139}, + {18.2, 77.7, 139}, + {52.1, 77.6, 139}, + {56.1, 77.3, 139}, + {44.3, 77.2, 139}, + {65.5, 77, 139}, + {54.1, 76.8, 139}, + {50, 76.6, 139}, + {56.6, 76.4, 139}, + {33.2, 76.2, 139}, + {19.3, 76, 139}, + {53.4, 75.9, 139}, + {55, 75.6, 139}, + {35.2, 75.5, 139}, + {15, 75.5, 139}, + {14.4, 75.2, 139}, + {17.1, 75, 139}, + {42.1, 74.9, 139}, + {45.3, 74.8, 139}, + {44.6, 74.8, 139}, + {18.2, 74.1, 139}, + {63.2, 74, 139}, + {33.9, 73, 139}, + {36.8, 72.5, 139}, + {24.2, 72.4, 139}, + {19.5, 72.3, 139}, + {41.9, 71.7, 139}, + {28.4, 71.5, 139}, + {35.6, 71.5, 139}, + {24.9, 71.4, 139}, + {45.8, 71.2, 139}, + {48.2, 71.1, 139}, + {28.6, 70.7, 139}, + {36.1, 70.6, 139}, + {18.8, 70.5, 139}, + {27.8, 70.5, 139}, + {42.9, 70.3, 139}, + {46.3, 70.1, 139}, + {44.1, 70, 139}, + {17.4, 70, 139}, + {29.1, 69.7, 139}, + {33.4, 69.1, 139}, + {42, 69, 139}, + {31, 68.6, 139}, + {21.2, 68.4, 139}, + {21.1, 68.2, 139}, + {40, 68.1, 139}, + {19.2, 68, 139}, + {36.2, 68, 139}, + {29.1, 67.9, 139}, + {27.6, 67.7, 139}, + {25.7, 67, 139}, + {21.2, 66.8, 139}, + {40.8, 66.7, 139}, + {24.2, 66.6, 139}, + {16.3, 66.5, 139}, + {21.6, 66.4, 139}, + {18.2, 66.2, 139}, + {20.2, 66, 139}, + {42.6, 65.5, 139}, + {15.4, 65.2, 139}, + {15.1, 65, 139}, + {21.5, 64.7, 139}, + {31.2, 64.2, 139}, + {23, 64.1, 139}, + {17.7, 64.1, 139}, + {27.1, 64, 139}, + {26.9, 63.8, 139}, + {15.9, 63.7, 139}, + {17, 62.2, 139}, + {21.8, 62.1, 139}, + {31.5, 61.4, 139}, + {10.8, 58.4, 139}, + {8.3, 54.8, 139}, + {7.7, 53.9, 139}, + {8.9, 50.7, 139}, + {6.2, 43.2, 139}, + {75.2, 44.5, 28}, + {33.6, 84.5, 139}, + {35.3, 84.3, 139}, + {55, 75.7, 139}, + {28.3, 71.4, 139}, + {33.3, 69.1, 139}, + {29.3, 68, 139}, + {27.6, 67.5, 139}, + {16.3, 66.2, 139}, + {16, 73.1, 4012}, + {16.9, 72.3, 4012}, + {15.2, 71.4, 4012}, + {20, 70.2, 4012}, + {31.7, 69, 4012}, + {29.1, 68.6, 4012}, + {8, 65.3, 4012}, + {9.3, 65.1, 4012}, + {3, 64.9, 4012}, + {2.5, 64.8, 4012}, + {25.3, 64.1, 4012}, + {10.3, 61.8, 4012}, + {5.7, 61.4, 4012}, + {4.8, 61.2, 4012}, + {9.7, 60.9, 4012}, + {21.2, 60.4, 4012}, + {7.3, 60.2, 4012}, + {10.3, 59.7, 4012}, + {8.3, 58.8, 4012}, + {0, 53.3, 4012}, + }, + [8601] = { + {72.2, 72.8, 139}, + {78.2, 72.2, 139}, + {68.6, 71.5, 139}, + {80.8, 70.6, 139}, + {65.1, 69.1, 139}, + {44.8, 67.6, 139}, + {49.1, 67.6, 139}, + {68, 66.3, 139}, + {46.2, 65.8, 139}, + {67.4, 65.8, 139}, + {49.6, 65.5, 139}, + {64.8, 64.9, 139}, + {45.4, 64.9, 139}, + {64.7, 64.5, 139}, + {48.5, 64.1, 139}, + {45.6, 64, 139}, + {47.3, 63.8, 139}, + {69.8, 63.7, 139}, + {66.5, 63.6, 139}, + {50.7, 63.4, 139}, + {52.8, 63.3, 139}, + {62.5, 63, 139}, + {70, 62.6, 139}, + {72.5, 62.3, 139}, + {67.3, 62.3, 139}, + {48.8, 62.3, 139}, + {56.3, 62.1, 139}, + {69.2, 62.1, 139}, + {50.1, 61.6, 139}, + {73.6, 61.4, 139}, + {60.9, 61, 139}, + {49.8, 60.8, 139}, + {52.7, 60.3, 139}, + {51.2, 60.2, 139}, + {76.5, 60.2, 139}, + {48.8, 60.2, 139}, + {61.9, 60, 139}, + {66.6, 59.9, 139}, + {74, 59.7, 139}, + {60.1, 59.5, 139}, + {57.7, 58.9, 139}, + {48.8, 58.6, 139}, + {58.4, 57.6, 139}, + {61, 57.1, 139}, + {69.5, 57.1, 139}, + {68.6, 56.7, 139}, + {69.8, 56.4, 139}, + {52.1, 56.3, 139}, + {73.2, 55.4, 139}, + {56.4, 55.1, 139}, + {70.6, 55, 139}, + {60.1, 54.9, 139}, + {50.1, 54.2, 139}, + {57.7, 53.2, 139}, + {55.3, 53.1, 139}, + {56.9, 52.4, 139}, + {53.5, 51.8, 139}, + {53.5, 51.4, 139}, + {55.5, 51, 139}, + {53.3, 50.3, 139}, + {45.7, 64, 139}, + {58.4, 57.5, 139}, + {70.7, 54.8, 139}, + {29.5, 55.3, 4012}, + {36.8, 54.6, 4012}, + {25, 53.8, 4012}, + {40, 52.6, 4012}, + {20.7, 50.8, 4012}, + {1.1, 49, 4012}, + {24.3, 47.4, 4012}, + {23.6, 46.7, 4012}, + {1.7, 46.4, 4012}, + {20.3, 45.7, 4012}, + {20.2, 45.2, 4012}, + {0.5, 44.7, 4012}, + {26.5, 44.2, 4012}, + {22.5, 44, 4012}, + {3.1, 43.8, 4012}, + {5.7, 43.7, 4012}, + {17.5, 43.3, 4012}, + {26.8, 42.9, 4012}, + {29.9, 42.5, 4012}, + {23.4, 42.5, 4012}, + {0.8, 42.4, 4012}, + {9.9, 42.3, 4012}, + {25.8, 42.2, 4012}, + {2.4, 41.6, 4012}, + {31.1, 41.3, 4012}, + {15.6, 40.9, 4012}, + {2, 40.6, 4012}, + {5.6, 40, 4012}, + {3.7, 39.9, 4012}, + {34.7, 39.9, 4012}, + {0.8, 39.9, 4012}, + {16.8, 39.6, 4012}, + {22.6, 39.6, 4012}, + {31.7, 39.3, 4012}, + {14.7, 39.1, 4012}, + {11.7, 38.3, 4012}, + {0.8, 38, 4012}, + {12.5, 36.6, 4012}, + {15.7, 36.1, 4012}, + {26.1, 36, 4012}, + {25, 35.6, 4012}, + {26.5, 35.3, 4012}, + {4.9, 35.1, 4012}, + {30.7, 34, 4012}, + {10.1, 33.6, 4012}, + {14.6, 33.4, 4012}, + {27.6, 33.3, 4012}, + {2.4, 32.6, 4012}, + {11.6, 31.3, 4012}, + {8.7, 31.3, 4012}, + {10.7, 30.3, 4012}, + {6.5, 29.6, 4012}, + {6.6, 29.1, 4012}, + {8.9, 28.6, 4012}, + {6.3, 27.8, 4012}, + }, + [8602] = { + {67.7, 50.9, 139}, + {62.1, 48.8, 139}, + {60.1, 47.4, 139}, + {51.6, 44.6, 139}, + {65.1, 44, 139}, + {31.9, 43.4, 139}, + {71.6, 43.3, 139}, + {32.7, 43.1, 139}, + {43.5, 43, 139}, + {55.8, 42.8, 139}, + {51.5, 42, 139}, + {29.6, 41.8, 139}, + {51.7, 41.8, 139}, + {53, 41.7, 139}, + {33.3, 41.6, 139}, + {45.8, 39.9, 139}, + {33.5, 39.4, 139}, + {30.9, 39.4, 139}, + {69.9, 39.1, 139}, + {51.6, 38.7, 139}, + {62.2, 38.2, 139}, + {20.6, 38, 139}, + {31.3, 37.6, 139}, + {21.7, 37.2, 139}, + {22.3, 37.1, 139}, + {46.2, 36.5, 139}, + {50.6, 36.5, 139}, + {19.2, 36.4, 139}, + {19.9, 35.8, 139}, + {75, 35.8, 139}, + {48.3, 35.5, 139}, + {63.5, 34.3, 139}, + {60.6, 34.2, 139}, + {75.8, 33.8, 139}, + {67.8, 33.6, 139}, + {55.6, 32.9, 139}, + {75.2, 32, 139}, + {60.1, 31.4, 139}, + {60.2, 29.8, 139}, + {66.7, 29.6, 139}, + {62.3, 29.5, 139}, + {64.4, 28.2, 139}, + {47.5, 28.1, 139}, + {19.5, 28, 139}, + {51.1, 27.7, 139}, + {49.7, 27.2, 139}, + {63.3, 26.1, 139}, + {18.8, 25.1, 139}, + {49.5, 25, 139}, + {51.5, 24.9, 139}, + {52.8, 24.2, 139}, + {53.1, 23.3, 139}, + {19.1, 23.1, 139}, + {20.2, 23, 139}, + {57.7, 20.9, 139}, + {55.3, 20.7, 139}, + {20.8, 20.1, 139}, + {51.7, 44.4, 139}, + {23.9, 28.4, 4012}, + {17.1, 25.9, 4012}, + {14.6, 24.3, 4012}, + {4.4, 20.5, 4012}, + {20.8, 20.1, 4012}, + {28.7, 19.2, 4012}, + {9.4, 18.5, 4012}, + {4.1, 17.6, 4012}, + {4.3, 17.4, 4012}, + {5.9, 17.2, 4012}, + {26.7, 14, 4012}, + {4.2, 13.5, 4012}, + {17.2, 12.9, 4012}, + {3, 10.9, 4012}, + {32.9, 10, 4012}, + {0.2, 9.6, 4012}, + {18.8, 8.1, 4012}, + {15.3, 8, 4012}, + {33.9, 7.5, 4012}, + {24, 7.3, 4012}, + {9.1, 6.4, 4012}, + {33.1, 5.3, 4012}, + {14.6, 4.6, 4012}, + {14.7, 2.7, 4012}, + {22.8, 2.4, 4012}, + {17.3, 2.2, 4012}, + {19.9, 0.7, 4012}, + {3.6, 0, 4012}, + {97.9, 91.1, 5225}, + {62.8, 91, 5225}, + {61.9, 87.3, 5225}, + {62.3, 84.8, 5225}, + {63.7, 84.6, 5225}, + {64.4, 81, 5225}, + }, + [8762] = { + {24.4, 49.8, 16}, + {31.7, 49.8, 16}, + {30.7, 49.2, 16}, + {24.9, 49.1, 16}, + {30.7, 49, 16}, + {32, 48.8, 16}, + {32.3, 48.6, 16}, + {30.4, 48.3, 16}, + {25, 48.2, 16}, + {25.7, 48, 16}, + {29.7, 45.8, 16}, + {41.2, 42.8, 16}, + {37, 42, 16}, + {38.5, 41.1, 16}, + {37, 40.8, 16}, + {37.6, 39.5, 16}, + {40.3, 39.3, 16}, + {38.7, 37.3, 16}, + {45.1, 35.9, 16}, + {40.8, 35.7, 16}, + {40.4, 34, 16}, + {47, 33.5, 16}, + {46.1, 31.2, 16}, + {41.3, 28.9, 16}, + }, + [8956] = { + {57.4, 25.5, 331}, + {56.5, 89.8, 361}, + {57.8, 87.5, 361}, + {45.7, 87.4, 361}, + {48, 87.2, 361}, + {58.3, 87.2, 361}, + {58.2, 86.8, 361}, + {58, 86.7, 361}, + {46.2, 86.5, 361}, + {48.5, 86.4, 361}, + {49.3, 85.5, 361}, + {45.8, 85.4, 361}, + {56.8, 84.9, 361}, + {45.1, 84.9, 361}, + {49.6, 84.7, 361}, + {44.5, 83.9, 361}, + {54.9, 83.8, 361}, + {45.7, 83.8, 361}, + {56.2, 83.7, 361}, + {55.5, 83.2, 361}, + {43.8, 82.8, 361}, + {44.5, 82.1, 361}, + {45.6, 82, 361}, + {49.1, 79.7, 361}, + {48.4, 78.8, 361}, + {41.5, 78.7, 361}, + {41.2, 78.5, 361}, + {40.4, 78, 361}, + {40.8, 77.2, 361}, + {46.4, 77.2, 361}, + {48.5, 76.8, 361}, + {48, 76, 361}, + {46.3, 75.5, 361}, + {41.5, 75.3, 361}, + {47.9, 74.6, 361}, + {45.8, 74.2, 361}, + {46.8, 74.2, 361}, + {45.2, 73.6, 361}, + {45.7, 73.5, 361}, + {47.1, 73.1, 361}, + {45.6, 72.8, 361}, + {45.1, 72.7, 361}, + {46.6, 72.5, 361}, + {37.4, 71.9, 361}, + {39.7, 70.1, 361}, + {41.6, 70.1, 361}, + {45.6, 87.6, 361}, + {46.3, 86.5, 361}, + {45.7, 85.3, 361}, + {45.1, 73.5, 361}, + {3.1, 95.5, 616}, + {2, 93.9, 616}, + {2.2, 90.4, 616}, + {1.3, 88.9, 616}, + {1.1, 86.4, 616}, + }, + [8958] = { + {43.8, 67.6, 361}, + {45, 67.4, 361}, + {43.7, 65.7, 361}, + {43.3, 65.6, 361}, + {44.7, 65.5, 361}, + {44.2, 65.3, 361}, + {43.7, 65.2, 361}, + {44.4, 64.9, 361}, + {44, 64.8, 361}, + {43, 63.7, 361}, + {44.1, 63.4, 361}, + {42.8, 63.3, 361}, + {44.7, 63.3, 361}, + {43.6, 62.9, 361}, + {42.9, 62.9, 361}, + {42.3, 62.5, 361}, + {42, 58.6, 361}, + {38.2, 56, 361}, + {39.6, 51.6, 361}, + {38.8, 45.3, 361}, + {39.3, 45.1, 361}, + {38.1, 45.1, 361}, + {37.7, 45, 361}, + {38.3, 44.4, 361}, + {38.6, 44.3, 361}, + {38.8, 44.3, 361}, + {37.9, 43.9, 361}, + {37.9, 43.2, 361}, + {37.6, 42.9, 361}, + {38.2, 41.9, 361}, + {48, 38.5, 361}, + {49.1, 35.9, 361}, + {40, 32.9, 361}, + {39.3, 31, 361}, + {39.8, 30.7, 361}, + {41.7, 24.5, 361}, + {41.2, 23.6, 361}, + {46.8, 19.6, 361}, + {44.5, 18, 361}, + {49.7, 16.3, 361}, + {46.2, 15.6, 361}, + {44.9, 67.3, 361}, + {43.7, 65.8, 361}, + {43.3, 65.5, 361}, + {38.1, 56, 361}, + {3.1, 17.3, 616}, + }, + [8959] = { + {57.2, 27.5, 331}, + {55.1, 27.3, 331}, + {57.5, 26.7, 331}, + {54.6, 26.6, 331}, + {56, 25, 331}, + {56.3, 91.8, 361}, + {54.2, 91.6, 361}, + {56.7, 90.9, 361}, + {53.7, 90.8, 361}, + {55.1, 89.2, 361}, + {52.5, 89, 361}, + {50.4, 87.6, 361}, + {52.6, 87.3, 361}, + {52, 86.3, 361}, + {51.2, 86.2, 361}, + {55, 85.8, 361}, + {51.5, 85.6, 361}, + {54.1, 85.4, 361}, + {53.4, 84.8, 361}, + {54.3, 84.8, 361}, + {53.8, 84.1, 361}, + {53.2, 83.4, 361}, + {53.6, 83.3, 361}, + {49.1, 83.2, 361}, + {50.3, 83, 361}, + {52.1, 82.9, 361}, + {53.6, 82.8, 361}, + {52.7, 82.3, 361}, + {52.4, 82.2, 361}, + {49.7, 82.1, 361}, + {48.7, 82.1, 361}, + {42.2, 82.1, 361}, + {42, 81.8, 361}, + {41.5, 81.7, 361}, + {47.9, 81.4, 361}, + {49.2, 81.4, 361}, + {42.7, 81.2, 361}, + {45, 81.2, 361}, + {41.7, 81, 361}, + {51.4, 80.6, 361}, + {42.2, 80.6, 361}, + {44.6, 80.5, 361}, + {47.4, 80.5, 361}, + {41.1, 80.5, 361}, + {43.3, 80.4, 361}, + {49.7, 80.4, 361}, + {50.3, 80.1, 361}, + {48, 79.6, 361}, + {46.4, 78.6, 361}, + {47.4, 77, 361}, + {42, 76.2, 361}, + {37.7, 74.6, 361}, + {37.3, 73.6, 361}, + {52.4, 82.1, 361}, + {49.6, 81.9, 361}, + {47.9, 81.6, 361}, + {49.1, 81.5, 361}, + {49.7, 80.2, 361}, + {9, 99.9, 616}, + {2.4, 99.9, 616}, + {4.1, 99.6, 616}, + {1, 98.9, 616}, + {3.1, 98.8, 616}, + {7.3, 97.2, 616}, + {0.2, 96.9, 616}, + {4.3, 96.5, 616}, + {5.3, 96.3, 616}, + {1.3, 95.3, 616}, + {0, 90.8, 616}, + }, + [8960] = { + {52.3, 62, 148}, + {45, 70.9, 361}, + {45.6, 70.6, 361}, + {45.2, 70.4, 361}, + {44.4, 70.2, 361}, + {45.7, 70, 361}, + {45.2, 69.7, 361}, + {44.4, 69.4, 361}, + {44.9, 69.4, 361}, + {44.5, 68.3, 361}, + {45.5, 68.2, 361}, + {42.4, 57.7, 361}, + {38, 54.4, 361}, + {38, 52.8, 361}, + {38.2, 50.8, 361}, + {39.3, 50.6, 361}, + {39.2, 47.3, 361}, + {38.2, 47.2, 361}, + {38.8, 46, 361}, + {38.3, 39.7, 361}, + {37, 39.3, 361}, + {37.6, 38.9, 361}, + {39.3, 38.8, 361}, + {49.4, 35.5, 361}, + {41.4, 34.3, 361}, + {40.4, 34.3, 361}, + {49.6, 33.6, 361}, + {41.2, 33.5, 361}, + {41.3, 33.5, 361}, + {40.7, 33.3, 361}, + {40.7, 32.9, 361}, + {50.3, 32.6, 361}, + {49.3, 32.6, 361}, + {50.8, 31.9, 361}, + {51.5, 31.9, 361}, + {51.9, 31.8, 361}, + {51.3, 31.6, 361}, + {52.2, 30.9, 361}, + {52.3, 30.6, 361}, + {51.6, 30, 361}, + {52, 29.8, 361}, + {40.4, 25.3, 361}, + {42.9, 24.1, 361}, + {45, 24, 361}, + {44.3, 22.5, 361}, + {43.3, 22.2, 361}, + {45.5, 18.5, 361}, + {47.6, 16, 361}, + {50.3, 15.4, 361}, + {44.5, 68.4, 361}, + {39.2, 50.8, 361}, + {38.3, 47.1, 361}, + {40.3, 34.3, 361}, + {41.5, 34.3, 361}, + {41.3, 33.4, 361}, + {40.6, 32.9, 361}, + {3.8, 16.4, 616}, + {4.1, 13.1, 616}, + {5.3, 11.3, 616}, + {3.5, 11.2, 616}, + {6.2, 10, 616}, + {7.5, 9.9, 616}, + {8.2, 9.9, 616}, + {7.1, 9.4, 616}, + {8.8, 8.3, 616}, + {8.9, 7.7, 616}, + {7.6, 6.7, 616}, + {8.3, 6.3, 616}, + }, + [8961] = { + {53.8, 27.7, 361}, + {52.7, 27.5, 361}, + {55.5, 26.9, 361}, + {54.5, 26.6, 361}, + {56.5, 21.2, 361}, + {64.8, 20.4, 361}, + {62.1, 20.3, 361}, + {56.6, 19.4, 361}, + {62, 19, 361}, + {57.3, 18.6, 361}, + {58.3, 16.9, 361}, + {53.8, 13.5, 361}, + {56.7, 12.9, 361}, + {56.1, 8.2, 361}, + {24.9, 46.1, 618}, + {54.5, 25, 361}, + {11.6, 2.6, 616}, + {9.7, 2.1, 616}, + {14.7, 1.1, 616}, + {12.9, 0.4, 616}, + }, + [9025] = { + {57.6, 70.8, 1584}, + }, + [9236] = { + {55.6, 72, 1583}, + }, + [9416] = { + {62.7, 62.1, 1583}, + {63.3, 61.5, 1583}, + {63.9, 59, 1583}, + {37.2, 48.9, 1583}, + {37.3, 47.9, 1583}, + {57, 44.4, 1583}, + {46.6, 43.8, 1583}, + {57.4, 43.5, 1583}, + {46.6, 42.7, 1583}, + {54.5, 41, 1583}, + {54.1, 41, 1583}, + }, + [9548] = { + {74.9, 45.7, 357}, + }, + [9549] = { + {45.4, 59.1, 406}, + {48.5, 61.1, 406}, + }, + [9551] = { + {44.9, 50.7, 400}, + }, + [9552] = { + {35.5, 30.1, 15}, + {53.9, 69.6, 17}, + }, + [9553] = { + {45, 39.3, 130}, + }, + [9555] = { + {72.5, 33.4, 45}, + }, + [9676] = { + {38.5, 73.8, 721}, + }, + [9690] = { + {84.1, 60.6, 46}, + {91.9, 59.3, 46}, + {86.7, 55.5, 46}, + {89.9, 51.7, 46}, + {89.3, 50.6, 46}, + {90.6, 40, 46}, + {79.3, 30.6, 46}, + }, + [9691] = { + {86.8, 59.7, 46}, + {86.5, 57.6, 46}, + {89.7, 55.5, 46}, + {88.9, 45.4, 46}, + {91.9, 44.3, 46}, + {92.5, 37, 46}, + {90.9, 32.9, 46}, + {89.1, 31.3, 46}, + {86.2, 30, 46}, + {90, 29.7, 46}, + {84.7, 22.7, 46}, + }, + [9694] = { + {58.4, 63.9, 46}, + {68.2, 61, 46}, + {61.5, 59.9, 46}, + {63.1, 54.3, 46}, + {72.4, 46.9, 46}, + }, + [9695] = { + {54.2, 67, 46}, + {55.2, 65.8, 46}, + {73.3, 64.8, 46}, + {65, 60.9, 46}, + {62.2, 57.7, 46}, + {62.5, 55.6, 46}, + {71, 45.2, 46}, + {48.1, 29.2, 46}, + {49.8, 28.2, 46}, + {70.5, 28, 46}, + {76, 27, 46}, + {57.2, 26.6, 46}, + }, + [9696] = { + {53.3, 79.7, 1583}, + {52.9, 79.1, 1583}, + {40.2, 76.2, 1583}, + {39.7, 76.2, 1583}, + {37.5, 75.1, 1583}, + {36.8, 75.1, 1583}, + {42.5, 73, 1583}, + {43, 73, 1583}, + {40.9, 72.1, 1583}, + {40.6, 71.5, 1583}, + {33.7, 67.2, 1583}, + {34.2, 66.2, 1583}, + {55.7, 60.3, 1583}, + {55.3, 59.5, 1583}, + {59.7, 57.9, 1583}, + {60.2, 57.6, 1583}, + }, + [9697] = { + {26.5, 62.8, 46}, + {23.6, 59.5, 46}, + {26.5, 57.7, 46}, + {24.4, 69.4, 46}, + {22.4, 69.1, 46}, + {24.5, 69.2, 46}, + {99.3, 86.5, 5581}, + }, + [9698] = { + {25.6, 60.9, 46}, + {26.7, 60.4, 46}, + {27.8, 57.8, 46}, + {29.3, 55.6, 46}, + {32.4, 55.5, 46}, + {27.3, 55.3, 46}, + {32.7, 55.3, 46}, + {24.4, 69.4, 46}, + {22.4, 69.1, 46}, + }, + [10077] = { + {86.6, 49.6, 46}, + {75.3, 32.7, 46}, + }, + [10221] = { + {37.4, 84.6, 1583}, + {37.9, 84.5, 1583}, + {37, 84.2, 1583}, + {38.7, 84.2, 1583}, + {37.6, 83.9, 1583}, + {38.1, 83.8, 1583}, + {36.6, 83.6, 1583}, + {39.4, 83.5, 1583}, + {37.2, 83.4, 1583}, + {36.9, 82.9, 1583}, + {34.2, 67.3, 1583}, + {34.4, 66.6, 1583}, + }, + [10357] = { + {42.9, 67.6, 85}, + }, + [10369] = { + {56.5, 73.1, 14}, + }, + [10375] = { + {62.6, 76.4, 1583}, + {56.1, 75.9, 1583}, + {54.6, 75.6, 1583}, + {61.8, 75.4, 1583}, + {61.2, 75, 1583}, + {55.7, 74.8, 1583}, + {54.7, 74.6, 1583}, + {61.9, 74.4, 1583}, + {63.6, 71.2, 1583}, + {63.4, 70.1, 1583}, + {63, 69.2, 1583}, + {63.7, 69, 1583}, + {54.1, 74.9, 1583}, + }, + [10644] = { + {20.9, 36.9, 331}, + }, + [10737] = { + {49.5, 9.7, 618}, + }, + [10806] = { + {64, 24.1, 618}, + }, + [10997] = { + {25.1, 74.7, 2017}, + }, + [11038] = { + {79.5, 63.9, 139}, + {38.4, 44.4, 4012}, + }, + [11184] = { + {61.7, 38, 618}, + }, + [11357] = { + {45.5, 45.3, 1977}, + {45.5, 44.9, 1977}, + {52.1, 44.7, 1977}, + {52.4, 44.7, 1977}, + {46.3, 43.7, 1977}, + {51.6, 43.6, 1977}, + {51.6, 43.4, 1977}, + {47.9, 40.8, 1977}, + {47.7, 40.4, 1977}, + {49.9, 40.3, 1977}, + {49.9, 39.1, 1977}, + {47.8, 39.1, 1977}, + {47.6, 38.5, 1977}, + {46.1, 36.3, 1977}, + {45.9, 36.2, 1977}, + {45.9, 36.1, 1977}, + {45.5, 35, 1977}, + {51.9, 34.8, 1977}, + {52.1, 34.8, 1977}, + {45.5, 34.7, 1977}, + }, + [11360] = { + {59.7, 41.8, 1977}, + {60.2, 41.5, 1977}, + {60.7, 41.5, 1977}, + {59.7, 41.1, 1977}, + {59.9, 40.9, 1977}, + {60.3, 40.6, 1977}, + {60.9, 36.8, 1977}, + {61.4, 36.2, 1977}, + {61.9, 34.8, 1977}, + {62.3, 34.7, 1977}, + {61.8, 32.9, 1977}, + {61.6, 32.5, 1977}, + }, + [11361] = { + {54.3, 50.6, 1977}, + {53.7, 50.5, 1977}, + {56.1, 42.5, 1977}, + {59.3, 42.5, 1977}, + {59, 42.4, 1977}, + {58.7, 42.4, 1977}, + {60.4, 41.9, 1977}, + {56.2, 41.6, 1977}, + {56.5, 41.3, 1977}, + {60.6, 41.2, 1977}, + {60.5, 40, 1977}, + {61.5, 37.1, 1977}, + {61.8, 36.8, 1977}, + {62.6, 35.9, 1977}, + {62.1, 35.9, 1977}, + {62.5, 35.3, 1977}, + {65.5, 34, 1977}, + {57.7, 33.8, 1977}, + {57.4, 33.8, 1977}, + {65.5, 33.2, 1977}, + {57.7, 33.1, 1977}, + {62.4, 32.8, 1977}, + {62.1, 32.2, 1977}, + {62.5, 31.9, 1977}, + {61.8, 31.8, 1977}, + {62.1, 31.4, 1977}, + {60.1, 30.8, 1977}, + {60.7, 30.3, 1977}, + {61.2, 30.3, 1977}, + {60.5, 30.1, 1977}, + {60.3, 29.5, 1977}, + {53.1, 26.8, 1977}, + {52.7, 26.6, 1977}, + {54.3, 26.4, 1977}, + {53.9, 25.9, 1977}, + }, + [11365] = { + {50.3, 28.9, 1977}, + {50.5, 28.8, 1977}, + {50.3, 28.7, 1977}, + {45, 27.6, 1977}, + {44.9, 27.4, 1977}, + {45.1, 27.4, 1977}, + {45.6, 25.6, 1977}, + {45.5, 25.6, 1977}, + {45.5, 25.4, 1977}, + {45.5, 25.3, 1977}, + {49.7, 24.9, 1977}, + {49.8, 24.6, 1977}, + {49.7, 24.6, 1977}, + {44.6, 24.3, 1977}, + {44.9, 23.7, 1977}, + {44.9, 23.6, 1977}, + {44.4, 22.9, 1977}, + {44.2, 22.8, 1977}, + {44.4, 22.8, 1977}, + {44.3, 22.7, 1977}, + {46.9, 22.1, 1977}, + {47.1, 22, 1977}, + {47.2, 21.9, 1977}, + {47.1, 21.9, 1977}, + {50.5, 21.3, 1977}, + {45.7, 21.2, 1977}, + {50.6, 21.2, 1977}, + {50.5, 21.2, 1977}, + {49.9, 21.2, 1977}, + {45.3, 21.1, 1977}, + {50.6, 21.1, 1977}, + {48.3, 20.9, 1977}, + {49.6, 20.8, 1977}, + {48.3, 20.7, 1977}, + {48.4, 20.7, 1977}, + {48.4, 20.5, 1977}, + {45.3, 20.3, 1977}, + {45.6, 20.2, 1977}, + {49.9, 20.2, 1977}, + {49.5, 20.2, 1977}, + {47.5, 19.2, 1977}, + {47.6, 19.1, 1977}, + {47.4, 19.1, 1977}, + {47.5, 19.1, 1977}, + {46.9, 18.9, 1977}, + {47.1, 18.9, 1977}, + {47, 18.9, 1977}, + {47.1, 18.8, 1977}, + {46.9, 16.3, 1977}, + {47.2, 16.2, 1977}, + {47, 16.2, 1977}, + {47.1, 16.2, 1977}, + }, + [11368] = { + {34.9, 77.4, 1977}, + {34.9, 77.2, 1977}, + {34.7, 77.1, 1977}, + {34.8, 77, 1977}, + {34.7, 76.7, 1977}, + {39.8, 74.9, 1977}, + {36.7, 74.8, 1977}, + {39.5, 74.7, 1977}, + {39.6, 74.7, 1977}, + {39.3, 74.5, 1977}, + {37.5, 74.4, 1977}, + {39.5, 74.4, 1977}, + {39.6, 74.3, 1977}, + {40.1, 73.6, 1977}, + {39.6, 73.4, 1977}, + {38.3, 72.1, 1977}, + {38.3, 72, 1977}, + {38.1, 71.8, 1977}, + {38.7, 71.3, 1977}, + {41.2, 71.3, 1977}, + {37.5, 71.1, 1977}, + {38.5, 70.8, 1977}, + {41.2, 70.5, 1977}, + {40.9, 70.2, 1977}, + {37.8, 70, 1977}, + {40.8, 69.6, 1977}, + {40.5, 68.5, 1977}, + {40.1, 66.9, 1977}, + {40.1, 66.6, 1977}, + {41.2, 65.8, 1977}, + {41.3, 65.7, 1977}, + {41, 65.7, 1977}, + {40.4, 65.4, 1977}, + {38.9, 58.9, 1977}, + {39, 58.6, 1977}, + {39.2, 58.6, 1977}, + {38.4, 58.4, 1977}, + {40.3, 54, 1977}, + {40.7, 53.8, 1977}, + {40.2, 53.2, 1977}, + {53, 24, 1977}, + {53.2, 23.3, 1977}, + }, + [11370] = { + {47.2, 80.5, 1977}, + {50.2, 80.2, 1977}, + {47.1, 80.2, 1977}, + {50.2, 79.8, 1977}, + {50.7, 76.9, 1977}, + {48.7, 75, 1977}, + {49, 74.8, 1977}, + }, + [11371] = { + {51, 60.7, 1977}, + {51.4, 59.6, 1977}, + {50.9, 59.6, 1977}, + {48.2, 56.9, 1977}, + {54.4, 56.8, 1977}, + {54.1, 56.4, 1977}, + {53.7, 56.3, 1977}, + {50.3, 55, 1977}, + {57.8, 54.3, 1977}, + {50.2, 54.2, 1977}, + {56.7, 54.1, 1977}, + {57.1, 54, 1977}, + {57.4, 54, 1977}, + {51.6, 53.2, 1977}, + {50.6, 53.2, 1977}, + {56.2, 52.2, 1977}, + {56.2, 52.1, 1977}, + {44.2, 51.9, 1977}, + {36.5, 49.6, 1977}, + {35.5, 47.6, 1977}, + {35.6, 46.8, 1977}, + {35.7, 46.4, 1977}, + {33, 45.3, 1977}, + {34.1, 36.2, 1977}, + {44.9, 30, 1977}, + {41.7, 29.9, 1977}, + {56.2, 25.3, 1977}, + {55.7, 25.3, 1977}, + {41.4, 24, 1977}, + {56, 23.6, 1977}, + {55.8, 23.4, 1977}, + {55.4, 22.6, 1977}, + {42.4, 22, 1977}, + }, + [11372] = { + {50.8, 60.3, 1977}, + {48.2, 56.7, 1977}, + {53.6, 55.4, 1977}, + {50, 55.1, 1977}, + {50.1, 54.6, 1977}, + {51.6, 54.5, 1977}, + {52, 54.3, 1977}, + {57.2, 54.1, 1977}, + {57.7, 54, 1977}, + {52, 53.7, 1977}, + {50.7, 53.3, 1977}, + {44.3, 52.4, 1977}, + {33.8, 50, 1977}, + {36.9, 50, 1977}, + {34.1, 49.5, 1977}, + {36.1, 46.6, 1977}, + {33.2, 45, 1977}, + {33.5, 36.5, 1977}, + {33.8, 36.2, 1977}, + {34.2, 35.6, 1977}, + {45.3, 29.5, 1977}, + {41.3, 29.1, 1977}, + {58.2, 27.6, 1977}, + {58.4, 27.6, 1977}, + {56.6, 25.8, 1977}, + {56.8, 25.4, 1977}, + {41.3, 23.4, 1977}, + {55.5, 23.3, 1977}, + {42.4, 21.8, 1977}, + }, + [11555] = { + {65.2, 2.7, 361}, + }, + [11735] = { + {10.4, 42.7, 490}, + {9.3, 42.6, 490}, + {9.5, 41.8, 490}, + {11.5, 41.1, 490}, + {7.5, 40.2, 490}, + {10.3, 39.9, 490}, + {7.6, 39.2, 490}, + {8.7, 37.4, 490}, + {13.9, 37.3, 490}, + {11, 37.1, 490}, + {11.3, 37.1, 490}, + {11.7, 36.8, 490}, + {9.7, 36.3, 490}, + {15.5, 35.5, 490}, + {14.2, 34.3, 490}, + {13, 33.1, 490}, + {12.9, 33.1, 490}, + {12, 32.9, 490}, + {15.8, 31.5, 490}, + {17.1, 30.9, 490}, + {10.8, 30.7, 490}, + {11.1, 30.2, 490}, + {17.4, 29, 490}, + {18.2, 28.8, 490}, + {18.3, 27.5, 490}, + {17.7, 27.3, 490}, + {68.6, 45.7, 1377}, + {67.4, 45.5, 1377}, + {64.6, 45.3, 1377}, + {62.1, 44.9, 1377}, + {67.6, 44.8, 1377}, + {69.8, 44, 1377}, + {62.1, 43.9, 1377}, + {62.9, 43.6, 1377}, + {65.5, 43, 1377}, + {60.9, 43, 1377}, + {68.5, 42.7, 1377}, + {44.6, 42.6, 1377}, + {65.6, 42, 1377}, + {43, 41.1, 1377}, + {61.7, 41, 1377}, + {64, 40.8, 1377}, + {44.8, 40.7, 1377}, + {62.9, 40.5, 1377}, + {61.6, 40.1, 1377}, + {66.8, 40.1, 1377}, + {72.3, 40, 1377}, + {69.2, 39.8, 1377}, + {69.6, 39.8, 1377}, + {70, 39.4, 1377}, + {67.8, 38.9, 1377}, + {60.4, 38.6, 1377}, + {40.7, 38.4, 1377}, + {42.7, 38.4, 1377}, + {74, 38, 1377}, + {65.7, 37.6, 1377}, + {39.4, 37.1, 1377}, + {44.4, 37.1, 1377}, + {72.6, 36.8, 1377}, + {40.5, 36.7, 1377}, + {71.4, 35.5, 1377}, + {71.2, 35.5, 1377}, + {38, 35.3, 1377}, + {70.3, 35.3, 1377}, + {42.3, 35.1, 1377}, + {36.3, 34, 1377}, + {57.1, 33.8, 1377}, + {74.3, 33.8, 1377}, + {38.9, 33.2, 1377}, + {75.7, 33.2, 1377}, + {69, 32.9, 1377}, + {39.8, 32.8, 1377}, + {66.4, 32.7, 1377}, + {56.5, 32.6, 1377}, + {69.4, 32.5, 1377}, + {63, 32.2, 1377}, + {63.9, 32.2, 1377}, + {68.3, 32, 1377}, + {60.9, 31.9, 1377}, + {58, 31.8, 1377}, + {36.9, 31.6, 1377}, + {35.6, 31.5, 1377}, + {33.9, 31.5, 1377}, + {61.1, 31.4, 1377}, + {64.7, 31.3, 1377}, + {40, 31.3, 1377}, + {62.8, 31.2, 1377}, + {76, 31.2, 1377}, + {76.8, 30.9, 1377}, + {53.7, 30.7, 1377}, + {54.1, 30.3, 1377}, + {51, 30.3, 1377}, + {56.1, 30.2, 1377}, + {36.7, 29.9, 1377}, + {65, 29.7, 1377}, + {66.9, 29.6, 1377}, + {52.5, 29.6, 1377}, + {76.9, 29.5, 1377}, + {76.3, 29.3, 1377}, + {33.1, 29.3, 1377}, + {66.5, 29, 1377}, + {63.9, 28.9, 1377}, + {34.4, 28.7, 1377}, + {53.4, 28.4, 1377}, + {31.8, 28.3, 1377}, + {59.9, 28.1, 1377}, + {36, 28.1, 1377}, + {64.6, 28, 1377}, + {59.8, 27.9, 1377}, + {58.5, 27.7, 1377}, + {56.7, 27.6, 1377}, + {36.8, 27.3, 1377}, + {65.5, 27.2, 1377}, + {53, 27.1, 1377}, + {64.3, 27, 1377}, + {56, 27, 1377}, + {58.7, 26.9, 1377}, + {34.5, 26.7, 1377}, + {61.8, 26.6, 1377}, + {31.4, 26.2, 1377}, + {60.5, 25.7, 1377}, + {53.5, 25.6, 1377}, + {30.5, 25.6, 1377}, + {62.7, 25.5, 1377}, + {58.9, 25.4, 1377}, + {34.1, 25.4, 1377}, + {52.2, 24.7, 1377}, + {54.5, 24.5, 1377}, + {62.9, 24.1, 1377}, + {62.8, 24.1, 1377}, + {58, 24, 1377}, + {36.6, 23.6, 1377}, + {66.1, 23.4, 1377}, + {64.4, 23.4, 1377}, + {58.3, 22.8, 1377}, + {55.6, 22.8, 1377}, + {53.2, 22.7, 1377}, + {36, 22.5, 1377}, + {56.1, 22.4, 1377}, + {61, 22.3, 1377}, + {34, 21.8, 1377}, + {34.6, 21.4, 1377}, + {33.1, 21.1, 1377}, + {37.7, 20.9, 1377}, + {57.8, 20.6, 1377}, + {59.9, 20.5, 1377}, + {58.8, 20.5, 1377}, + {34.4, 20.2, 1377}, + {37.3, 19.9, 1377}, + {62.6, 19.7, 1377}, + {35.9, 19.6, 1377}, + {51.3, 19.6, 1377}, + {57.2, 19.6, 1377}, + {53.5, 19.6, 1377}, + {60.2, 19.5, 1377}, + {38.7, 18.7, 1377}, + {60.1, 18.4, 1377}, + {63, 18.3, 1377}, + {61.8, 18.1, 1377}, + {35.7, 18.1, 1377}, + {58.1, 18, 1377}, + {61.1, 17.2, 1377}, + {63.7, 17, 1377}, + {55.2, 16.8, 1377}, + {50.5, 16.8, 1377}, + {56.6, 16.8, 1377}, + {36.9, 16.7, 1377}, + {63.7, 16, 1377}, + {50.2, 15.3, 1377}, + {61.5, 15.2, 1377}, + {38.7, 15, 1377}, + {54.4, 14.5, 1377}, + {61.4, 14.5, 1377}, + {64.1, 14.2, 1377}, + {55.1, 13.9, 1377}, + {51.6, 13.9, 1377}, + {48.9, 13.4, 1377}, + {59, 13.3, 1377}, + {57.9, 12.5, 1377}, + {53.7, 12.2, 1377}, + {56.8, 11.3, 1377}, + {56.9, 9.5, 1377}, + {42.9, 38.2, 1377}, + {75.8, 33.2, 1377}, + {58.9, 25.6, 1377}, + {35.9, 19.4, 1377}, + {55, 13.6, 1377}, + }, + [11736] = { + {7.5, 57.7, 490}, + {34.1, 66.8, 1377}, + {40.3, 66.7, 1377}, + {62.6, 66, 1377}, + {58.4, 65.8, 1377}, + {45.7, 65.8, 1377}, + {49.6, 65.7, 1377}, + {38, 65.6, 1377}, + {57.7, 64.7, 1377}, + {42.3, 64.6, 1377}, + {50.7, 64.4, 1377}, + {55.1, 64.2, 1377}, + {37.4, 64.2, 1377}, + {60.4, 64.1, 1377}, + {48.6, 64, 1377}, + {62.4, 64, 1377}, + {38.4, 63.9, 1377}, + {35.2, 63.4, 1377}, + {57.2, 63.4, 1377}, + {45.8, 63.3, 1377}, + {58.8, 63.2, 1377}, + {52.2, 62.9, 1377}, + {49.5, 62.8, 1377}, + {61, 62.8, 1377}, + {41, 62.8, 1377}, + {39, 62.7, 1377}, + {55.3, 62.3, 1377}, + {51.4, 62.3, 1377}, + {33.7, 62.1, 1377}, + {65.2, 62.1, 1377}, + {46.1, 61.7, 1377}, + {61.4, 61.6, 1377}, + {65.5, 61.6, 1377}, + {42.8, 61.5, 1377}, + {41, 61.4, 1377}, + {36, 61.3, 1377}, + {37, 61.3, 1377}, + {56, 61.2, 1377}, + {39.4, 60.8, 1377}, + {48.3, 60.4, 1377}, + {63.7, 60.4, 1377}, + {47.1, 60.3, 1377}, + {59.8, 60.1, 1377}, + {35.1, 59.9, 1377}, + {49.3, 59.9, 1377}, + {36.5, 59.3, 1377}, + {43.1, 59.1, 1377}, + {56.1, 58.9, 1377}, + {35, 58.6, 1377}, + {51.4, 58.6, 1377}, + {46.5, 58.4, 1377}, + {51.8, 58.4, 1377}, + {37, 58.3, 1377}, + {35.9, 57.9, 1377}, + {53, 57.2, 1377}, + {54.2, 57, 1377}, + {57.1, 56.9, 1377}, + {42.2, 56.9, 1377}, + {44.9, 56.3, 1377}, + {49, 56, 1377}, + {42, 55.7, 1377}, + {38.4, 55.6, 1377}, + {50.4, 55.6, 1377}, + {55.9, 55.2, 1377}, + {35.3, 55.2, 1377}, + {33.9, 55.2, 1377}, + {41.9, 54.7, 1377}, + {34.9, 54.1, 1377}, + {40.4, 54, 1377}, + {39.5, 53, 1377}, + {46.9, 52.8, 1377}, + {37.9, 52.3, 1377}, + {43.3, 52.2, 1377}, + {42.1, 52, 1377}, + {36.7, 51.1, 1377}, + {43.6, 49.9, 1377}, + {32.2, 48.8, 1377}, + {34.2, 47.3, 1377}, + {33.9, 46.9, 1377}, + {34.4, 45.4, 1377}, + {43.4, 45, 1377}, + {35, 44.9, 1377}, + {25.5, 44.3, 1377}, + {22.7, 44.2, 1377}, + {28.5, 43.2, 1377}, + {21.7, 43.2, 1377}, + {26.7, 43, 1377}, + {21.5, 42.6, 1377}, + {32.5, 42.5, 1377}, + {24.4, 42.5, 1377}, + {32.9, 41.4, 1377}, + {23.6, 41, 1377}, + {36.4, 40.7, 1377}, + {22.9, 40.1, 1377}, + {21.5, 39.9, 1377}, + {33.3, 39.8, 1377}, + {33.2, 39.2, 1377}, + {34.7, 38.8, 1377}, + {28.9, 38.7, 1377}, + {31.3, 38.4, 1377}, + {31, 36.6, 1377}, + {21.5, 36.5, 1377}, + {32.3, 36.1, 1377}, + {34.6, 35.8, 1377}, + {32.9, 35.3, 1377}, + {31.4, 34.4, 1377}, + {33.9, 34.3, 1377}, + {19.8, 34.1, 1377}, + {32.4, 33.2, 1377}, + {30.5, 28.9, 1377}, + {31.1, 28.7, 1377}, + {28.9, 27, 1377}, + }, + [11737] = { + {9.7, 73.4, 490}, + {44.9, 92.3, 1377}, + {44.9, 92.2, 1377}, + {35.3, 91.1, 1377}, + {24.3, 89.9, 1377}, + {37.1, 89.6, 1377}, + {36.8, 88.8, 1377}, + {34.9, 88.7, 1377}, + {23.1, 88.6, 1377}, + {38, 88.3, 1377}, + {44.4, 88.1, 1377}, + {44.5, 87.5, 1377}, + {45.4, 87.2, 1377}, + {46.6, 87.2, 1377}, + {42.2, 87.2, 1377}, + {38.9, 87.1, 1377}, + {23.6, 86.8, 1377}, + {37.2, 86.7, 1377}, + {34.8, 86.5, 1377}, + {64.9, 86.3, 1377}, + {39.8, 86.3, 1377}, + {46.7, 86.1, 1377}, + {36, 86, 1377}, + {23.5, 85.8, 1377}, + {41.8, 85.8, 1377}, + {38, 85.4, 1377}, + {44.8, 84.9, 1377}, + {43.7, 84.8, 1377}, + {42.4, 84.7, 1377}, + {46.2, 84.5, 1377}, + {37.9, 84.5, 1377}, + {27.9, 84.4, 1377}, + {37, 84.3, 1377}, + {23.4, 84.1, 1377}, + {45.6, 84, 1377}, + {40.6, 83.9, 1377}, + {40.2, 83.8, 1377}, + {41.4, 83.4, 1377}, + {30.3, 83.4, 1377}, + {36.4, 83, 1377}, + {28.6, 82.9, 1377}, + {31.8, 82.8, 1377}, + {43.7, 82.8, 1377}, + {27.4, 82.7, 1377}, + {44.7, 82.6, 1377}, + {24.5, 82.5, 1377}, + {31.1, 82.1, 1377}, + {37.9, 81.9, 1377}, + {24.7, 81.7, 1377}, + {39.9, 81.7, 1377}, + {26.4, 81.5, 1377}, + {37, 81.5, 1377}, + {29.5, 81.4, 1377}, + {42.6, 81.2, 1377}, + {38, 81, 1377}, + {25.1, 81, 1377}, + {42.5, 80.6, 1377}, + {41, 80.5, 1377}, + {67, 80.2, 1377}, + {24.4, 80.1, 1377}, + {40.8, 80.1, 1377}, + {45.6, 80, 1377}, + {32.6, 80, 1377}, + {40.7, 80, 1377}, + {27.4, 80, 1377}, + {30.2, 79.9, 1377}, + {42.2, 79.8, 1377}, + {34.2, 79.7, 1377}, + {28.1, 79.3, 1377}, + {23, 79.3, 1377}, + {33.5, 79.2, 1377}, + {43, 79.1, 1377}, + {29.4, 78.6, 1377}, + {22.6, 78.6, 1377}, + {22.2, 78.6, 1377}, + {35, 78.5, 1377}, + {44.9, 78.5, 1377}, + {25.9, 78.4, 1377}, + {31.7, 78.4, 1377}, + {40.7, 78.3, 1377}, + {67.8, 78.3, 1377}, + {36, 78.1, 1377}, + {26.7, 77.9, 1377}, + {34.3, 77.5, 1377}, + {19.6, 77.4, 1377}, + {27.4, 77.2, 1377}, + {32.1, 77.1, 1377}, + {23.2, 77.1, 1377}, + {66.6, 77.1, 1377}, + {29.3, 77, 1377}, + {22.6, 77, 1377}, + {25.5, 76.9, 1377}, + {28, 76.9, 1377}, + {20.2, 76.9, 1377}, + {65.3, 76.8, 1377}, + {63.5, 76.8, 1377}, + {42.1, 76.8, 1377}, + {25, 76.6, 1377}, + {43.3, 76.6, 1377}, + {67.6, 76.1, 1377}, + {33.7, 75.9, 1377}, + {38.5, 75.8, 1377}, + {45.3, 75.7, 1377}, + {36, 75.7, 1377}, + {65.6, 75.6, 1377}, + {31.2, 75.5, 1377}, + {42.5, 75.5, 1377}, + {33.2, 75.3, 1377}, + {29.7, 75.3, 1377}, + {27.2, 75.3, 1377}, + {45.6, 74.6, 1377}, + {26.1, 74.6, 1377}, + {32.3, 74.5, 1377}, + {30.9, 74.5, 1377}, + {64.7, 74.3, 1377}, + {36, 74.3, 1377}, + {66.5, 74.3, 1377}, + {39.2, 74.3, 1377}, + {28.4, 74.1, 1377}, + {43.1, 73.9, 1377}, + {33.6, 73.9, 1377}, + {62.7, 73.8, 1377}, + {36.7, 73.7, 1377}, + {34.9, 73.5, 1377}, + {40.9, 73.3, 1377}, + {33.1, 72.9, 1377}, + {45.2, 72.9, 1377}, + {65.4, 72.7, 1377}, + {45, 72.6, 1377}, + {31.6, 72.5, 1377}, + {34.1, 72.5, 1377}, + {63.4, 72.5, 1377}, + {45.7, 71.5, 1377}, + {33.1, 71.4, 1377}, + {39.7, 71.3, 1377}, + {33.4, 71.2, 1377}, + {48.9, 70.9, 1377}, + {35.4, 70.9, 1377}, + {63.8, 70.4, 1377}, + {64.7, 70.4, 1377}, + {44.9, 70.2, 1377}, + {38.7, 70, 1377}, + {36.9, 70, 1377}, + {41.4, 69.9, 1377}, + {47.5, 69.9, 1377}, + {35.1, 69.8, 1377}, + {47.7, 69.8, 1377}, + {44.6, 69.8, 1377}, + {41.2, 69.7, 1377}, + {48.3, 69.6, 1377}, + {62.3, 69.3, 1377}, + {36, 68.7, 1377}, + {41.9, 68.7, 1377}, + {39.5, 68.6, 1377}, + {33, 67.8, 1377}, + {31.8, 67.8, 1377}, + {41.8, 67.5, 1377}, + {39.3, 67.2, 1377}, + {48.6, 67.2, 1377}, + {63.8, 67.2, 1377}, + {34, 67.1, 1377}, + {61.5, 67.1, 1377}, + {40.4, 66.7, 1377}, + {64.2, 66.6, 1377}, + {49.5, 66.6, 1377}, + {37.1, 66.1, 1377}, + {44.8, 90.9, 1377}, + {44.6, 90.7, 1377}, + {38.9, 87, 1377}, + {41.9, 85.6, 1377}, + {32.2, 77.4, 1377}, + {71.5, 3.3, 3478}, + {71.5, 3.2, 3478}, + {71.4, 2.2, 3478}, + {71.3, 2, 3478}, + {54.1, 1.4, 3478}, + {64.7, 0.5, 3478}, + {65.7, 0.2, 3478}, + }, + [11738] = { + {10.4, 42.7, 490}, + {9.3, 42.6, 490}, + {9.5, 41.8, 490}, + {11.5, 41.1, 490}, + {7.5, 40.2, 490}, + {10.3, 39.9, 490}, + {7.6, 39.2, 490}, + {8.7, 37.4, 490}, + {13.9, 37.3, 490}, + {11, 37.1, 490}, + {11.3, 37.1, 490}, + {11.7, 36.8, 490}, + {9.7, 36.3, 490}, + {15.5, 35.5, 490}, + {14.2, 34.3, 490}, + {13, 33.1, 490}, + {12.9, 33.1, 490}, + {12, 32.9, 490}, + {15.8, 31.5, 490}, + {17.1, 30.9, 490}, + {10.8, 30.7, 490}, + {11.1, 30.2, 490}, + {17.4, 29, 490}, + {18.2, 28.8, 490}, + {18.3, 27.5, 490}, + {17.7, 27.3, 490}, + {68.6, 45.7, 1377}, + {67.4, 45.5, 1377}, + {64.6, 45.3, 1377}, + {62.1, 44.9, 1377}, + {67.6, 44.8, 1377}, + {69.8, 44, 1377}, + {62.1, 43.9, 1377}, + {62.9, 43.6, 1377}, + {65.5, 43, 1377}, + {60.9, 43, 1377}, + {68.5, 42.7, 1377}, + {44.6, 42.6, 1377}, + {65.6, 42, 1377}, + {43, 41.1, 1377}, + {61.7, 41, 1377}, + {64, 40.8, 1377}, + {44.8, 40.7, 1377}, + {62.9, 40.5, 1377}, + {61.6, 40.1, 1377}, + {66.8, 40.1, 1377}, + {72.3, 40, 1377}, + {69.2, 39.8, 1377}, + {69.6, 39.8, 1377}, + {70, 39.4, 1377}, + {67.8, 38.9, 1377}, + {60.4, 38.6, 1377}, + {40.7, 38.4, 1377}, + {42.7, 38.4, 1377}, + {74, 38, 1377}, + {65.7, 37.6, 1377}, + {39.4, 37.1, 1377}, + {44.4, 37.1, 1377}, + {72.6, 36.8, 1377}, + {40.5, 36.7, 1377}, + {71.4, 35.5, 1377}, + {71.2, 35.5, 1377}, + {38, 35.3, 1377}, + {70.3, 35.3, 1377}, + {42.3, 35.1, 1377}, + {36.3, 34, 1377}, + {57.1, 33.8, 1377}, + {74.3, 33.8, 1377}, + {38.9, 33.2, 1377}, + {75.7, 33.2, 1377}, + {69, 32.9, 1377}, + {39.8, 32.8, 1377}, + {66.4, 32.7, 1377}, + {56.5, 32.6, 1377}, + {69.4, 32.5, 1377}, + {63, 32.2, 1377}, + {63.9, 32.2, 1377}, + {68.3, 32, 1377}, + {60.9, 31.9, 1377}, + {58, 31.8, 1377}, + {36.9, 31.6, 1377}, + {35.6, 31.5, 1377}, + {33.9, 31.5, 1377}, + {61.1, 31.4, 1377}, + {64.7, 31.3, 1377}, + {40, 31.3, 1377}, + {62.8, 31.2, 1377}, + {76, 31.2, 1377}, + {76.8, 30.9, 1377}, + {53.7, 30.7, 1377}, + {54.1, 30.3, 1377}, + {51, 30.3, 1377}, + {56.1, 30.2, 1377}, + {36.7, 29.9, 1377}, + {65, 29.7, 1377}, + {66.9, 29.6, 1377}, + {52.5, 29.6, 1377}, + {76.9, 29.5, 1377}, + {76.3, 29.3, 1377}, + {33.1, 29.3, 1377}, + {66.5, 29, 1377}, + {63.9, 28.9, 1377}, + {34.4, 28.7, 1377}, + {53.4, 28.4, 1377}, + {31.8, 28.3, 1377}, + {59.9, 28.1, 1377}, + {36, 28.1, 1377}, + {64.6, 28, 1377}, + {59.8, 27.9, 1377}, + {58.5, 27.7, 1377}, + {56.7, 27.6, 1377}, + {36.8, 27.3, 1377}, + {65.5, 27.2, 1377}, + {53, 27.1, 1377}, + {64.3, 27, 1377}, + {56, 27, 1377}, + {58.7, 26.9, 1377}, + {34.5, 26.7, 1377}, + {61.8, 26.6, 1377}, + {31.4, 26.2, 1377}, + {60.5, 25.7, 1377}, + {53.5, 25.6, 1377}, + {30.5, 25.6, 1377}, + {62.7, 25.5, 1377}, + {58.9, 25.4, 1377}, + {34.1, 25.4, 1377}, + {52.2, 24.7, 1377}, + {54.5, 24.5, 1377}, + {62.9, 24.1, 1377}, + {62.8, 24.1, 1377}, + {58, 24, 1377}, + {36.6, 23.6, 1377}, + {66.1, 23.4, 1377}, + {64.4, 23.4, 1377}, + {58.3, 22.8, 1377}, + {55.6, 22.8, 1377}, + {53.2, 22.7, 1377}, + {36, 22.5, 1377}, + {56.1, 22.4, 1377}, + {61, 22.3, 1377}, + {34, 21.8, 1377}, + {34.6, 21.4, 1377}, + {33.1, 21.1, 1377}, + {37.7, 20.9, 1377}, + {57.8, 20.6, 1377}, + {59.9, 20.5, 1377}, + {58.8, 20.5, 1377}, + {34.4, 20.2, 1377}, + {37.3, 19.9, 1377}, + {62.6, 19.7, 1377}, + {35.9, 19.6, 1377}, + {51.3, 19.6, 1377}, + {57.2, 19.6, 1377}, + {53.5, 19.6, 1377}, + {60.2, 19.5, 1377}, + {38.7, 18.7, 1377}, + {60.1, 18.4, 1377}, + {63, 18.3, 1377}, + {61.8, 18.1, 1377}, + {35.7, 18.1, 1377}, + {58.1, 18, 1377}, + {61.1, 17.2, 1377}, + {63.7, 17, 1377}, + {55.2, 16.8, 1377}, + {50.5, 16.8, 1377}, + {56.6, 16.8, 1377}, + {36.9, 16.7, 1377}, + {63.7, 16, 1377}, + {50.2, 15.3, 1377}, + {61.5, 15.2, 1377}, + {38.7, 15, 1377}, + {54.4, 14.5, 1377}, + {61.4, 14.5, 1377}, + {64.1, 14.2, 1377}, + {55.1, 13.9, 1377}, + {51.6, 13.9, 1377}, + {48.9, 13.4, 1377}, + {59, 13.3, 1377}, + {57.9, 12.5, 1377}, + {53.7, 12.2, 1377}, + {56.8, 11.3, 1377}, + {56.9, 9.5, 1377}, + {44.9, 41, 1377}, + {77, 29.6, 1377}, + {53.1, 22.9, 1377}, + {36.2, 22.6, 1377}, + {55, 16.9, 1377}, + }, + [11739] = { + {9.7, 73.4, 490}, + {44.9, 92.3, 1377}, + {44.9, 92.2, 1377}, + {35.3, 91.1, 1377}, + {24.3, 89.9, 1377}, + {37.1, 89.6, 1377}, + {36.8, 88.8, 1377}, + {34.9, 88.7, 1377}, + {23.1, 88.6, 1377}, + {38, 88.3, 1377}, + {44.4, 88.1, 1377}, + {44.5, 87.5, 1377}, + {45.4, 87.2, 1377}, + {46.6, 87.2, 1377}, + {42.2, 87.2, 1377}, + {38.9, 87.1, 1377}, + {23.6, 86.8, 1377}, + {37.2, 86.7, 1377}, + {34.8, 86.5, 1377}, + {64.9, 86.3, 1377}, + {39.8, 86.3, 1377}, + {46.7, 86.1, 1377}, + {36, 86, 1377}, + {23.5, 85.8, 1377}, + {41.8, 85.8, 1377}, + {38, 85.4, 1377}, + {44.8, 84.9, 1377}, + {43.7, 84.8, 1377}, + {42.4, 84.7, 1377}, + {46.2, 84.5, 1377}, + {37.9, 84.5, 1377}, + {27.9, 84.4, 1377}, + {37, 84.3, 1377}, + {23.4, 84.1, 1377}, + {45.6, 84, 1377}, + {40.6, 83.9, 1377}, + {40.2, 83.8, 1377}, + {41.4, 83.4, 1377}, + {30.3, 83.4, 1377}, + {36.4, 83, 1377}, + {28.6, 82.9, 1377}, + {31.8, 82.8, 1377}, + {43.7, 82.8, 1377}, + {27.4, 82.7, 1377}, + {44.7, 82.6, 1377}, + {24.5, 82.5, 1377}, + {31.1, 82.1, 1377}, + {37.9, 81.9, 1377}, + {24.7, 81.7, 1377}, + {39.9, 81.7, 1377}, + {26.4, 81.5, 1377}, + {37, 81.5, 1377}, + {29.5, 81.4, 1377}, + {42.6, 81.2, 1377}, + {38, 81, 1377}, + {25.1, 81, 1377}, + {42.5, 80.6, 1377}, + {41, 80.5, 1377}, + {67, 80.2, 1377}, + {24.4, 80.1, 1377}, + {40.8, 80.1, 1377}, + {45.6, 80, 1377}, + {32.6, 80, 1377}, + {40.7, 80, 1377}, + {27.4, 80, 1377}, + {30.2, 79.9, 1377}, + {42.2, 79.8, 1377}, + {34.2, 79.7, 1377}, + {28.1, 79.3, 1377}, + {23, 79.3, 1377}, + {33.5, 79.2, 1377}, + {43, 79.1, 1377}, + {29.4, 78.6, 1377}, + {22.6, 78.6, 1377}, + {22.2, 78.6, 1377}, + {35, 78.5, 1377}, + {44.9, 78.5, 1377}, + {25.9, 78.4, 1377}, + {31.7, 78.4, 1377}, + {40.7, 78.3, 1377}, + {67.8, 78.3, 1377}, + {36, 78.1, 1377}, + {26.7, 77.9, 1377}, + {34.3, 77.5, 1377}, + {19.6, 77.4, 1377}, + {27.4, 77.2, 1377}, + {32.1, 77.1, 1377}, + {23.2, 77.1, 1377}, + {66.6, 77.1, 1377}, + {29.3, 77, 1377}, + {22.6, 77, 1377}, + {25.5, 76.9, 1377}, + {28, 76.9, 1377}, + {20.2, 76.9, 1377}, + {65.3, 76.8, 1377}, + {63.5, 76.8, 1377}, + {42.1, 76.8, 1377}, + {25, 76.6, 1377}, + {43.3, 76.6, 1377}, + {67.6, 76.1, 1377}, + {33.7, 75.9, 1377}, + {38.5, 75.8, 1377}, + {45.3, 75.7, 1377}, + {36, 75.7, 1377}, + {65.6, 75.6, 1377}, + {31.2, 75.5, 1377}, + {42.5, 75.5, 1377}, + {33.2, 75.3, 1377}, + {29.7, 75.3, 1377}, + {27.2, 75.3, 1377}, + {45.6, 74.6, 1377}, + {26.1, 74.6, 1377}, + {32.3, 74.5, 1377}, + {30.9, 74.5, 1377}, + {64.7, 74.3, 1377}, + {36, 74.3, 1377}, + {66.5, 74.3, 1377}, + {39.2, 74.3, 1377}, + {28.4, 74.1, 1377}, + {43.1, 73.9, 1377}, + {33.6, 73.9, 1377}, + {62.7, 73.8, 1377}, + {36.7, 73.7, 1377}, + {34.9, 73.5, 1377}, + {40.9, 73.3, 1377}, + {33.1, 72.9, 1377}, + {45.2, 72.9, 1377}, + {65.4, 72.7, 1377}, + {45, 72.6, 1377}, + {31.6, 72.5, 1377}, + {34.1, 72.5, 1377}, + {63.4, 72.5, 1377}, + {45.7, 71.5, 1377}, + {33.1, 71.4, 1377}, + {39.7, 71.3, 1377}, + {33.4, 71.2, 1377}, + {48.9, 70.9, 1377}, + {35.4, 70.9, 1377}, + {63.8, 70.4, 1377}, + {64.7, 70.4, 1377}, + {44.9, 70.2, 1377}, + {38.7, 70, 1377}, + {36.9, 70, 1377}, + {41.4, 69.9, 1377}, + {47.5, 69.9, 1377}, + {35.1, 69.8, 1377}, + {47.7, 69.8, 1377}, + {44.6, 69.8, 1377}, + {41.2, 69.7, 1377}, + {48.3, 69.6, 1377}, + {62.3, 69.3, 1377}, + {36, 68.7, 1377}, + {41.9, 68.7, 1377}, + {39.5, 68.6, 1377}, + {33, 67.8, 1377}, + {31.8, 67.8, 1377}, + {41.8, 67.5, 1377}, + {39.3, 67.2, 1377}, + {48.6, 67.2, 1377}, + {63.8, 67.2, 1377}, + {34, 67.1, 1377}, + {61.5, 67.1, 1377}, + {40.4, 66.7, 1377}, + {64.2, 66.6, 1377}, + {49.5, 66.6, 1377}, + {37.1, 66.1, 1377}, + {35.9, 74, 1377}, + {63.4, 2.4, 3478}, + {64.9, 1.2, 3478}, + {63.1, 0.5, 3478}, + {53.1, 0.3, 3478}, + }, + [11921] = { + {52, 73.9, 406}, + {53.8, 73, 406}, + }, + [12021] = { + {45.2, 34.7, 493}, + }, + [12027] = { + {24.9, 71.8, 405}, + }, + [12029] = { + {53.3, 42.7, 493}, + }, + [12246] = { + {40.5, 79.3, 405}, + }, + [12431] = { + {59.5, 7.6, 130}, + }, + [12432] = { + {53.8, 51.9, 130}, + }, + [12433] = { + {24, 79.7, 85}, + }, + [12959] = { + {43.3, 7.7, 490}, + }, + [12960] = { + {66.6, 7, 405}, + {41.1, 82.1, 406}, + {45, 79.8, 406}, + }, + [13216] = { + {44.3, 18.2, 2597}, + }, + [13217] = { + {39.4, 81.7, 36}, + {43.4, 19.9, 267}, + }, + [13218] = { + {49.3, 82.5, 2597}, + }, + [13219] = { + {62.8, 59.3, 36}, + }, + [13599] = { + {20.2, 88.2, 2100}, + {22.5, 87.1, 2100}, + {18.4, 86.4, 2100}, + {17.4, 84.1, 2100}, + {17.6, 81.1, 2100}, + {22.6, 79, 2100}, + {19, 78.6, 2100}, + {20.8, 77.9, 2100}, + {25, 51.2, 2100}, + {25.1, 51, 2100}, + {25.3, 50.9, 2100}, + {25, 50.9, 2100}, + {25.1, 50.8, 2100}, + {19.6, 50.3, 2100}, + {24.6, 49.7, 2100}, + {22.2, 49.5, 2100}, + {22.3, 45.5, 2100}, + {17.9, 45.2, 2100}, + {19.7, 45.1, 2100}, + {19.9, 45.1, 2100}, + {17.7, 44.9, 2100}, + {19.6, 44.9, 2100}, + {19.7, 44.7, 2100}, + {21.1, 44.2, 2100}, + {23, 42.9, 2100}, + {21.7, 41.2, 2100}, + }, + [14123] = { + {73.3, 43, 440}, + {72.5, 42.3, 440}, + {73, 42.3, 440}, + {71.8, 41.2, 440}, + {70.8, 41.1, 440}, + {71.5, 39.9, 440}, + {68.3, 39.7, 440}, + {70.4, 39.3, 440}, + {67.4, 39.3, 440}, + {68.8, 39.1, 440}, + {67.8, 38.6, 440}, + {67, 38, 440}, + {68.1, 37.8, 440}, + {67.7, 37.3, 440}, + {68.4, 37, 440}, + {67.5, 36.2, 440}, + {68.3, 35.8, 440}, + {68.5, 35.3, 440}, + {68.1, 34.3, 440}, + {68.5, 33.5, 440}, + {68.5, 32.7, 440}, + {68.5, 30.1, 440}, + {67.8, 29.7, 440}, + {68, 28.5, 440}, + {67.1, 28.3, 440}, + {67.1, 27.7, 440}, + {68.3, 27.5, 440}, + {67.6, 26.3, 440}, + {68, 25.8, 440}, + {65, 25.1, 440}, + {67.6, 24.9, 440}, + {68.1, 23.8, 440}, + {65.8, 22.3, 440}, + {68.2, 21.8, 440}, + {67.8, 21.5, 440}, + {66.2, 20.8, 440}, + {67.3, 20.2, 440}, + {68.2, 19.6, 440}, + {67.8, 19.6, 440}, + {67, 18.9, 440}, + {67.2, 18.2, 440}, + {67.3, 17.9, 440}, + {67.7, 17.4, 440}, + {67.3, 39.2, 440}, + {68.6, 39.1, 440}, + {67.9, 38.6, 440}, + {67.2, 38, 440}, + {67.7, 37.2, 440}, + {67.4, 30.3, 440}, + {67.7, 29.5, 440}, + {67.9, 29.1, 440}, + {67.1, 28.7, 440}, + {68.1, 28.6, 440}, + {66.5, 27.6, 440}, + {67.5, 24.9, 440}, + {65.8, 22.2, 440}, + {3, 30.6, 5121}, + {1.3, 29.1, 5121}, + {2.4, 29.1, 5121}, + }, + [14228] = { + {61.3, 28.9, 405}, + }, + [14232] = { + {48.1, 16, 15}, + }, + [14233] = { + {49.2, 57.4, 15}, + {42, 55.3, 15}, + }, + [14266] = { + {78.1, 52.4, 38}, + {38.5, 71.1, 5602}, + }, + [14268] = { + {77.7, 74.9, 38}, + {68.6, 76.4, 38}, + {33.7, 83.4, 5602}, + }, + [14279] = { + {38.6, 58.4, 267}, + {93.3, 19.9, 5179}, + }, + [14280] = { + {86, 39.9, 267}, + }, + [14283] = { + {51.3, 25.8, 2597}, + {51.2, 25.8, 2597}, + {53.7, 7.3, 2597}, + {53.7, 7.2, 2597}, + {54.1, 5.4, 2597}, + {54.2, 5.3, 2597}, + {49.7, 42.2, 2597}, + {49.7, 42.1, 2597}, + {48.7, 37.4, 2597}, + {48.7, 37.2, 2597}, + }, + [14301] = { + {12, 78.4, 16}, + }, + [14339] = { + {50, 77.1, 361}, + {4.8, 90.9, 616}, + }, + [14343] = { + {50.2, 32.8, 361}, + {5.1, 11.6, 616}, + }, + [14344] = { + {42.1, 75.2, 361}, + }, + [14472] = { + {39.3, 55.4, 1377}, + }, + [14476] = { + {63.2, 16.7, 1377}, + }, + [14491] = { + {37.8, 61.2, 33}, + {35.4, 56.6, 33}, + }, + [14532] = { + {49.5, 81.6, 1977}, + {47.4, 81.6, 1977}, + {47.1, 81.4, 1977}, + {49.3, 81.1, 1977}, + {49.7, 80.6, 1977}, + {49.5, 80.6, 1977}, + {50, 80.6, 1977}, + {47.8, 80.4, 1977}, + {49.8, 80.3, 1977}, + {47, 79.9, 1977}, + {49.8, 79.9, 1977}, + {47.4, 79.8, 1977}, + {46.9, 79.7, 1977}, + {50.6, 79.5, 1977}, + {46.7, 79.3, 1977}, + {46.3, 78.7, 1977}, + {50.6, 76.8, 1977}, + {50.9, 76.1, 1977}, + {50.3, 75.9, 1977}, + {47.9, 75.7, 1977}, + {50.5, 75.6, 1977}, + {48.6, 75.6, 1977}, + {47.7, 75.5, 1977}, + {50.5, 75.4, 1977}, + {48, 75.3, 1977}, + {48.6, 75.3, 1977}, + {48.3, 75.1, 1977}, + {48.9, 74.1, 1977}, + {48.7, 73.6, 1977}, + {40, 30.4, 1977}, + {39.5, 30.3, 1977}, + }, + [14624] = { + {38.8, 28.5, 51}, + }, + [14821] = { + {57.4, 83.2, 1977}, + {57.8, 83.2, 1977}, + {61.1, 82.7, 1977}, + {57.8, 82.4, 1977}, + {60.7, 81.9, 1977}, + {57.5, 80.9, 1977}, + {62.2, 80.8, 1977}, + {56.8, 80.5, 1977}, + {59.5, 80.4, 1977}, + {62.5, 80.1, 1977}, + {62.1, 79.5, 1977}, + {58.4, 75.9, 1977}, + {60.7, 72.5, 1977}, + {61.1, 72.4, 1977}, + {60.8, 72.2, 1977}, + {61.3, 72, 1977}, + {60.5, 71.9, 1977}, + {60.8, 71.7, 1977}, + {60.6, 68.4, 1977}, + {60.9, 67.9, 1977}, + {60.4, 67.9, 1977}, + {60.6, 67.5, 1977}, + {61, 67.5, 1977}, + {60.7, 67, 1977}, + {63.2, 66.1, 1977}, + {63.6, 65.9, 1977}, + {62.8, 65.8, 1977}, + {63.9, 65.7, 1977}, + {63, 65.7, 1977}, + {62.7, 65.2, 1977}, + {43.8, 30.6, 1977}, + {41.5, 21.6, 1977}, + }, + [15043] = { + {41.6, 48.6, 1977}, + {53.1, 48.3, 1977}, + {52.7, 48.2, 1977}, + {41.4, 47.9, 1977}, + {41.9, 47.9, 1977}, + {52.8, 47.8, 1977}, + {53.1, 47.8, 1977}, + {53.1, 47.5, 1977}, + {42, 47.4, 1977}, + {41.8, 47.2, 1977}, + {50.6, 46.4, 1977}, + {50.8, 46, 1977}, + {49.1, 45.9, 1977}, + {51.1, 45.9, 1977}, + {48.2, 45.8, 1977}, + {47.4, 45.8, 1977}, + {47.7, 45.8, 1977}, + {48.6, 45.7, 1977}, + {51.7, 45.6, 1977}, + {54.1, 43.4, 1977}, + {54.5, 43.1, 1977}, + {54.2, 43, 1977}, + {42.9, 42.8, 1977}, + {42, 42.6, 1977}, + {54.3, 42.3, 1977}, + {42.5, 41.9, 1977}, + {41.9, 41.7, 1977}, + {36.9, 39.9, 1977}, + {37.3, 39.6, 1977}, + {37, 39.3, 1977}, + {36.6, 39.2, 1977}, + {37.2, 39, 1977}, + {53.3, 35.7, 1977}, + {53, 35.4, 1977}, + {52.7, 35.2, 1977}, + {52.6, 34.4, 1977}, + {49.8, 33.8, 1977}, + {49.2, 33.6, 1977}, + {49.6, 33.5, 1977}, + {49.2, 33.1, 1977}, + {48.7, 32.8, 1977}, + }, + [15174] = { + {51.9, 39.2, 1377}, + }, + [16117] = { + {18.1, 35.3, 139}, + {17.9, 34.8, 139}, + {17.7, 34.4, 139}, + {18.9, 34.3, 139}, + {18.8, 33.8, 139}, + {14.1, 33.7, 139}, + {14.7, 33.7, 139}, + {15.5, 33.2, 139}, + {18.2, 33.1, 139}, + {15.1, 33, 139}, + {13.8, 33, 139}, + {18.3, 32.1, 139}, + {14.9, 31.9, 139}, + {17.1, 31.2, 139}, + {17.9, 30.5, 139}, + {16.2, 30.3, 139}, + {16.4, 29.6, 139}, + {60.8, 99.5, 5225}, + {60.5, 99, 5225}, + {62, 98.9, 5225}, + {61.9, 98.3, 5225}, + {56, 98.2, 5225}, + {56.7, 98.1, 5225}, + {57.8, 97.5, 5225}, + {61.1, 97.3, 5225}, + {57.2, 97.2, 5225}, + {55.6, 97.2, 5225}, + {61.2, 96.1, 5225}, + {57, 95.9, 5225}, + {59.8, 95, 5225}, + {60.8, 94.1, 5225}, + {58.7, 93.8, 5225}, + {58.9, 92.9, 5225}, + }, + [17598] = { + {76.9, 81.4, 47}, + }, + [51656] = { + {68.2, 19.1, 148}, + }, + [60456] = { + {42, 79.2, 33}, + }, + [60494] = { + {70.6, 31.4, 8}, + }, + [60646] = { + {37.9, 72, 14}, + {65, 34.7, 17}, + }, + [60653] = { + {29, 45, 28}, + {85.5, 58.5, 85}, + }, + [60663] = { + {48.9, 47.8, 8}, + }, + [60740] = { + {34.2, 21.2, 28}, + {90.5, 35.8, 85}, + }, + [60766] = { + {93.2, 23, 46}, + }, + [60790] = { + {21.4, 63.4, 45}, + }, + [60803] = { + {19.5, 65.8, 46}, + {95.5, 92.2, 5581}, + }, + [60813] = { + {40, 78.2, 215}, + }, + [60848] = { + {38, 35.7, 17}, + {37.7, 35.7, 17}, + {37.5, 35.2, 17}, + {36.5, 35.2, 17}, + {37.2, 35.2, 17}, + {63.7, 15.4, 215}, + {63.1, 15.4, 215}, + {62.7, 14.5, 215}, + {60.9, 14.4, 215}, + {62.1, 14.4, 215}, + }, + [60966] = { + {37, 51.3, 406}, + }, + [60989] = { + {34, 10, 17}, + {79.3, 62.4, 406}, + }, + [61058] = { + {36.2, 22.5, 17}, + {83, 83.5, 406}, + }, + [61074] = { + {49.7, 58, 5121}, + {47.7, 58, 5121}, + {46.1, 57, 5121}, + {48.1, 56.2, 5121}, + {42.6, 55.3, 5121}, + {45.1, 54.6, 5121}, + {46.4, 54.5, 5121}, + {41, 54.4, 5121}, + {39.9, 53.9, 5121}, + {48.7, 53.1, 5121}, + {45.9, 52.7, 5121}, + {43.6, 52.6, 5121}, + {41.6, 52.1, 5121}, + {47.1, 51.7, 5121}, + {45.5, 50.8, 5121}, + {41, 50.5, 5121}, + {48.8, 50, 5121}, + {43.9, 49.6, 5121}, + {46.5, 48.3, 5121}, + {44, 47.7, 5121}, + {45.6, 47.2, 5121}, + {48.8, 45.7, 5121}, + {47.5, 44.2, 5121}, + {48.6, 43.4, 5121}, + {51.3, 40.8, 5121}, + {50, 40.2, 5121}, + {56.1, 37.4, 5121}, + {53.9, 36.7, 5121}, + {52.8, 35.9, 5121}, + {55.9, 34.6, 5121}, + {48.6, 33.9, 5121}, + }, + [61075] = { + {36.9, 53.5, 5121}, + {38.5, 51.3, 5121}, + {39.1, 47, 5121}, + {62.5, 46.2, 5121}, + {38.2, 45.3, 5121}, + {40.8, 44.6, 5121}, + {63.1, 44.3, 5121}, + {39.6, 44, 5121}, + {41.3, 42.7, 5121}, + {63.1, 40.8, 5121}, + {38.2, 40.1, 5121}, + {39.3, 39.9, 5121}, + {61.3, 37.3, 5121}, + {58.9, 36.4, 5121}, + {39.1, 34.2, 5121}, + {58.2, 33.7, 5121}, + {57, 33.7, 5121}, + {55.9, 33.1, 5121}, + {53.9, 32.9, 5121}, + {54.9, 32.5, 5121}, + {45.7, 32.5, 5121}, + {56.9, 32.5, 5121}, + {49.4, 31.7, 5121}, + {42.8, 31.6, 5121}, + {55, 31.3, 5121}, + {47.7, 31.2, 5121}, + {57.6, 30.7, 5121}, + {53, 30.6, 5121}, + {52, 30.5, 5121}, + {49.4, 29.8, 5121}, + {56.4, 28.9, 5121}, + {45.7, 27.9, 5121}, + {48.6, 27.5, 5121}, + {42.1, 26.7, 5121}, + {51.4, 25.9, 5121}, + {51.4, 22.7, 5121}, + {43.2, 22.4, 5121}, + {43.1, 20.9, 5121}, + {52.8, 17.2, 5121}, + {54.7, 14.6, 5121}, + {52.5, 13.8, 5121}, + {48.8, 12.9, 5121}, + {53.3, 12.9, 5121}, + {53.3, 12.3, 5121}, + {58.7, 10.1, 5121}, + {54.5, 8.4, 5121}, + {56.2, 7.5, 5121}, + {55.1, 7.3, 5121}, + }, + [61076] = { + {46.9, 89.9, 5121}, + {47, 88.7, 5121}, + {45.1, 88.4, 5121}, + {53, 87.2, 5121}, + {39.7, 85.6, 5121}, + {53.8, 85, 5121}, + {39.6, 81.6, 5121}, + {57.4, 79.8, 5121}, + {60.5, 78.5, 5121}, + {53.4, 76.7, 5121}, + {40.2, 75.9, 5121}, + {60.9, 74.7, 5121}, + {55.3, 72.9, 5121}, + {40.5, 71.1, 5121}, + {40.6, 67.1, 5121}, + {63.8, 66.7, 5121}, + {39.5, 62.5, 5121}, + {42.3, 62.5, 5121}, + {64.2, 60, 5121}, + {65.5, 58, 5121}, + {63.4, 51.9, 5121}, + }, + [61077] = { + {51.9, 58.2, 5121}, + {54.6, 56.5, 5121}, + {57.3, 55.2, 5121}, + {50.7, 55.2, 5121}, + {52, 54.8, 5121}, + {52.7, 54.6, 5121}, + {57.4, 54.6, 5121}, + {52.4, 53.9, 5121}, + {41.6, 53.9, 5121}, + {40.5, 53.4, 5121}, + {40.7, 53.1, 5121}, + {41.1, 52.6, 5121}, + {55.9, 51.6, 5121}, + {57.3, 50.4, 5121}, + {53.1, 50.1, 5121}, + {55.1, 49.1, 5121}, + {58.4, 43.4, 5121}, + {57.4, 40.2, 5121}, + }, + [61078] = { + {51.3, 85.8, 5121}, + {50.6, 84.7, 5121}, + {51.2, 84.4, 5121}, + {42.9, 82.6, 5121}, + {48.7, 78.6, 5121}, + {49.2, 78.3, 5121}, + {54.4, 55.2, 5121}, + {52, 54.5, 5121}, + {50.7, 54.4, 5121}, + {57.7, 53.9, 5121}, + {40.9, 53.7, 5121}, + {41.4, 53.2, 5121}, + {41.9, 53.1, 5121}, + {40.5, 52.8, 5121}, + {52.3, 52.7, 5121}, + {41.3, 52, 5121}, + {55.2, 51.4, 5121}, + {44, 51, 5121}, + {57.7, 50.9, 5121}, + {55.4, 49.2, 5121}, + {53.7, 48.1, 5121}, + {56.4, 40, 5121}, + {55.3, 39.9, 5121}, + }, + [61080] = { + {53.3, 58.6, 5121}, + {52, 57, 5121}, + {56.8, 56.5, 5121}, + {55.2, 56.4, 5121}, + {52.9, 55.6, 5121}, + {55.6, 54.8, 5121}, + {56.2, 54.5, 5121}, + {50.1, 54.3, 5121}, + {59.4, 53.8, 5121}, + {54.1, 53.8, 5121}, + {54.4, 53.3, 5121}, + {54.5, 52.6, 5121}, + {50.7, 52.4, 5121}, + {57.2, 51.6, 5121}, + {59.5, 51.5, 5121}, + {55.6, 51.4, 5121}, + {57.1, 51, 5121}, + {55.4, 50.3, 5121}, + {56.7, 50, 5121}, + {55, 49.9, 5121}, + {51, 49.2, 5121}, + {53.2, 49, 5121}, + {57, 48.2, 5121}, + {49.9, 47.9, 5121}, + {55, 47.8, 5121}, + {51.3, 47.6, 5121}, + {52.8, 47.2, 5121}, + {50.1, 46.2, 5121}, + {51.6, 44.7, 5121}, + {55.9, 44.6, 5121}, + {53.5, 43.5, 5121}, + {56.6, 43.4, 5121}, + {52.4, 43.1, 5121}, + {53.5, 42.6, 5121}, + {57.2, 42.2, 5121}, + }, + [61082] = { + {55.2, 58.2, 5121}, + {56.1, 54.3, 5121}, + {52.6, 54.3, 5121}, + {52.2, 54.2, 5121}, + {54.3, 54.2, 5121}, + {56.5, 54, 5121}, + {53.4, 54, 5121}, + {55.3, 53, 5121}, + {57.2, 52.7, 5121}, + {58.9, 52.5, 5121}, + {56.9, 52.2, 5121}, + {55, 51.4, 5121}, + {57.3, 51, 5121}, + {53.9, 50.4, 5121}, + {52.7, 49.3, 5121}, + {54.8, 48.9, 5121}, + {55.1, 48.8, 5121}, + {55.4, 48.4, 5121}, + {53.2, 47.9, 5121}, + {54.3, 47.2, 5121}, + {56.8, 46, 5121}, + {55.5, 42.6, 5121}, + {55.1, 41.8, 5121}, + }, + [61090] = { + {55.9, 69.3, 5121}, + {56.9, 68.3, 5121}, + {57.4, 66.8, 5121}, + {56.4, 66.3, 5121}, + {54.1, 66.1, 5121}, + {55.4, 65.2, 5121}, + {60.9, 64.9, 5121}, + {62.5, 64.7, 5121}, + {51.2, 63.8, 5121}, + {59.7, 63.4, 5121}, + {60.7, 63, 5121}, + {52.4, 62.9, 5121}, + {51.1, 62.4, 5121}, + {62.6, 62.4, 5121}, + {57.8, 62.3, 5121}, + {59.4, 62, 5121}, + {44.7, 61.8, 5121}, + {60, 61.3, 5121}, + {49, 61, 5121}, + {61.7, 60.3, 5121}, + {45.2, 60.2, 5121}, + {56.1, 60.1, 5121}, + {58.6, 59.5, 5121}, + {43.5, 58.7, 5121}, + {63, 58.6, 5121}, + {42.5, 57.7, 5121}, + {62.5, 57.5, 5121}, + {43.6, 57.2, 5121}, + {61.4, 56.9, 5121}, + {62.7, 54.4, 5121}, + {62.3, 50.4, 5121}, + {62.5, 49, 5121}, + {61.7, 46.5, 5121}, + }, + [61091] = { + {49.4, 86.4, 5121}, + {47.6, 85.6, 5121}, + {46.3, 85.3, 5121}, + {44.1, 85, 5121}, + {52.8, 84.4, 5121}, + {49, 84.2, 5121}, + {46.4, 82.9, 5121}, + {41.7, 81.8, 5121}, + {50.8, 78.6, 5121}, + {47.3, 78.4, 5121}, + {52.6, 78.1, 5121}, + {51.6, 75.3, 5121}, + {46.6, 74.9, 5121}, + {49.5, 74.5, 5121}, + {42.4, 72.6, 5121}, + }, + [61095] = { + {47.3, 87.3, 5121}, + {44.5, 82.4, 5121}, + {51, 80.6, 5121}, + {49.1, 80.2, 5121}, + {47.5, 76.9, 5121}, + {45.9, 76.5, 5121}, + {44.9, 74.8, 5121}, + {48.2, 74.5, 5121}, + }, + [61098] = { + {53.9, 48.6, 5121}, + }, + [61115] = { + {42.9, 79.3, 5121}, + }, + [61140] = { + {87.3, 45.7, 331}, + }, + [61206] = { + {65.3, 66.4, 3457}, + {58.9, 61.2, 3457}, + {58.9, 59, 3457}, + {66.7, 52.3, 3457}, + {55.1, 45.6, 3457}, + {54.6, 45.6, 3457}, + {59.5, 45.1, 3457}, + {54, 45, 3457}, + {54.6, 44.5, 3457}, + {55.3, 44.5, 3457}, + {59.6, 40.1, 3457}, + {60, 39.8, 3457}, + {72.4, 39.5, 3457}, + {59.2, 39.2, 3457}, + {67.1, 34.4, 3457}, + {60.8, 33.3, 3457}, + {54.5, 32.6, 3457}, + {59.8, 32.1, 3457}, + {55.5, 31.6, 3457}, + {18.2, 23.1, 3457}, + {18.8, 22.4, 3457}, + }, + [61218] = { + {45.9, 36.2, 17}, + {46.8, 34.7, 17}, + {22.9, 89.5, 718}, + {39, 63.3, 718}, + {86, 77.4, 718}, + }, + [61227] = { + {47, 91.4, 130}, + {46, 91, 130}, + {52.4, 41.3, 5179}, + {46.9, 40.2, 5179}, + {51.4, 40.2, 5179}, + {37.4, 40.1, 5179}, + {48.9, 39.9, 5179}, + {52.7, 39.9, 5179}, + {46.3, 39.6, 5179}, + {43.9, 39.6, 5179}, + {45.3, 39.6, 5179}, + {51.9, 39.5, 5179}, + {50.1, 37.9, 5179}, + {51.1, 37.8, 5179}, + {49.4, 37.8, 5179}, + {37, 37.3, 5179}, + {50.3, 37, 5179}, + {50, 36.3, 5179}, + {50.8, 36.2, 5179}, + {36.7, 35.5, 5179}, + {55.8, 35.4, 5179}, + {35.5, 35, 5179}, + {46.5, 34.4, 5179}, + {42, 34.2, 5179}, + {47.8, 34, 5179}, + {43.1, 33.7, 5179}, + {56.6, 33.1, 5179}, + {46.8, 32.8, 5179}, + {42.6, 32.5, 5179}, + {38.7, 31.2, 5179}, + {42.4, 29.1, 5179}, + {40.9, 29, 5179}, + {46.3, 28, 5179}, + {40.3, 27.7, 5179}, + {48.5, 27.1, 5179}, + {44.8, 25.7, 5179}, + {47.3, 25.1, 5179}, + {48.8, 24.5, 5179}, + {44.2, 24.5, 5179}, + {46.8, 23.7, 5179}, + {48.5, 21.9, 5179}, + {47.4, 21.4, 5179}, + {30.9, 33, 5208}, + {28.1, 30, 5208}, + {34.8, 27.6, 5208}, + {24.5, 27, 5208}, + {33.9, 25.6, 5208}, + {23.1, 24.5, 5208}, + {24.2, 23.7, 5208}, + {36, 23.4, 5208}, + {32.5, 21.5, 5208}, + {26.1, 17.8, 5208}, + {26.9, 15.3, 5208}, + }, + [61228] = { + {56.5, 76.1, 5179}, + {53.1, 75.6, 5179}, + {54.8, 75.5, 5179}, + {51.2, 74.6, 5179}, + {53, 73.6, 5179}, + {48.3, 72.8, 5179}, + {54.7, 71.1, 5179}, + {56.7, 70.9, 5179}, + {46.1, 70.5, 5179}, + {48, 70.4, 5179}, + {56.2, 69.4, 5179}, + {41, 63.5, 5179}, + {36.7, 63.3, 5179}, + {50.1, 62.5, 5179}, + {40.8, 61.2, 5179}, + {36.8, 61.1, 5179}, + {39.2, 61, 5179}, + {35.6, 60.7, 5179}, + {42.6, 58.5, 5179}, + {41.4, 58.2, 5179}, + {37.8, 57.8, 5179}, + {36.1, 56.9, 5179}, + {51, 56.6, 5179}, + {39.6, 56.6, 5179}, + {36.3, 55.3, 5179}, + {42.3, 55.2, 5179}, + {31.5, 55.1, 5179}, + {51.3, 53.7, 5179}, + {37.4, 52.9, 5179}, + }, + [61229] = { + {26.4, 74.1, 5179}, + {27.4, 73.5, 5179}, + {24.5, 73, 5179}, + {26, 73, 5179}, + {31.6, 72.1, 5179}, + {21.2, 71.5, 5179}, + {22, 71.2, 5179}, + {30.6, 71, 5179}, + {24.9, 70.7, 5179}, + {31.7, 70.3, 5179}, + {20.9, 69.8, 5179}, + {27.9, 69.7, 5179}, + {24.9, 69.5, 5179}, + {27.1, 68.7, 5179}, + {28.1, 67.9, 5179}, + {27, 67.4, 5179}, + {19.6, 66.7, 5179}, + {28.8, 66.4, 5179}, + {17.9, 64.8, 5179}, + {26.3, 63.7, 5179}, + {17.9, 63.1, 5179}, + {17, 62.9, 5179}, + {24.7, 60.6, 5179}, + {23.4, 56.8, 5179}, + {22.6, 56.2, 5179}, + {21.1, 53.8, 5179}, + {14.7, 52.9, 5179}, + {15.4, 51.5, 5179}, + {14.8, 50.6, 5179}, + {13.8, 49.8, 5179}, + {14.9, 49.6, 5179}, + {14.6, 47.9, 5179}, + {13.3, 47.1, 5179}, + {12.3, 46.2, 5179}, + {13.8, 45.9, 5179}, + {12, 44.3, 5179}, + {15.6, 43.7, 5179}, + {14.7, 43.5, 5179}, + {12.6, 42.4, 5179}, + {13.9, 40.9, 5179}, + {14.7, 39.8, 5179}, + }, + [61230] = { + {44.3, 90.1, 130}, + {44.3, 88.5, 130}, + {51.2, 44.7, 5179}, + {49.2, 43.4, 5179}, + {52.9, 43, 5179}, + {50.1, 41.8, 5179}, + {32.1, 41.5, 5179}, + {33.5, 41, 5179}, + {39.6, 40.4, 5179}, + {32.5, 39.7, 5179}, + {34, 39.5, 5179}, + {46.6, 38.7, 5179}, + {44.2, 38.6, 5179}, + {52.8, 38.3, 5179}, + {53.5, 38.1, 5179}, + {54.2, 37.2, 5179}, + {53.8, 36.2, 5179}, + {54.7, 36.2, 5179}, + {45.4, 36.2, 5179}, + {46, 36.1, 5179}, + {43.3, 35.9, 5179}, + {36.9, 34.4, 5179}, + {56.4, 34.2, 5179}, + {44.4, 34.2, 5179}, + {49.7, 33.7, 5179}, + {48.7, 33.6, 5179}, + {49.8, 32.9, 5179}, + {47.9, 32.9, 5179}, + {37, 32.4, 5179}, + {50.2, 32.1, 5179}, + {48.9, 32.1, 5179}, + {48.3, 31.8, 5179}, + {47.8, 31, 5179}, + {48.7, 30.8, 5179}, + {47.9, 30, 5179}, + {49.4, 29.9, 5179}, + {32.3, 29.9, 5179}, + {48.6, 29.7, 5179}, + {46.8, 29.7, 5179}, + {48.9, 29, 5179}, + {48.1, 28.7, 5179}, + {47.3, 28.3, 5179}, + {47.4, 27.1, 5179}, + {41.8, 26.8, 5179}, + {46.4, 26.1, 5179}, + {34.7, 26, 5179}, + {48.4, 25.2, 5179}, + {47.7, 23.5, 5179}, + {45.4, 20.3, 5179}, + {45.4, 18.6, 5179}, + }, + [61231] = { + {40.3, 65.4, 5179}, + {31.9, 64.4, 5179}, + {39.7, 63.5, 5179}, + {35.4, 63.1, 5179}, + {38.2, 62.7, 5179}, + {33, 61.3, 5179}, + {42.5, 60.3, 5179}, + {40.5, 59.5, 5179}, + {39.4, 58.5, 5179}, + {37.1, 58.2, 5179}, + {32.9, 57.8, 5179}, + {43.9, 56.5, 5179}, + {38.2, 56.1, 5179}, + {33.9, 52.2, 5179}, + {58.1, 49.5, 5179}, + {55.9, 49, 5179}, + {56.8, 47.4, 5179}, + {54.2, 46.4, 5179}, + {57.5, 46.3, 5179}, + {56.5, 45.8, 5179}, + }, + [61232] = { + {46.7, 78.2, 5179}, + {41.7, 77.2, 5179}, + {40.3, 76.8, 5179}, + {45.7, 76.7, 5179}, + {38.1, 76.6, 5179}, + {44, 76.3, 5179}, + {34.6, 76, 5179}, + {36.3, 75.6, 5179}, + {41.6, 74.8, 5179}, + {45.8, 74.4, 5179}, + {42.9, 74, 5179}, + {47.2, 73.9, 5179}, + {39.7, 73.3, 5179}, + {41.3, 73.3, 5179}, + {38.7, 72.9, 5179}, + {43.7, 71.6, 5179}, + }, + [61233] = { + {43.4, 77.8, 5179}, + {39.5, 77.7, 5179}, + {34.8, 77.6, 5179}, + {46.6, 76.6, 5179}, + {37.9, 74.8, 5179}, + {40.1, 74.5, 5179}, + {43.9, 74.1, 5179}, + {35.6, 73.7, 5179}, + {43.9, 72.6, 5179}, + {45.6, 72.4, 5179}, + }, + [61272] = { + {59.6, 71.7, 5179}, + }, + [61288] = { + {56.8, 38.8, 5179}, + }, + [61336] = { + {63, 87.6, 616}, + {53.5, 87.5, 616}, + {71.2, 83.3, 616}, + {61.8, 82.6, 616}, + {67.2, 82.2, 616}, + {55.6, 81.9, 616}, + {31.1, 81.3, 616}, + {68.8, 81.1, 616}, + {33.5, 79.9, 616}, + {64.5, 75.7, 616}, + {26.1, 74.7, 616}, + {28, 73.1, 616}, + {83.2, 70.9, 616}, + {64.2, 70.6, 616}, + {29.5, 70.4, 616}, + {81.3, 70.2, 616}, + {78.5, 69.4, 616}, + {87.7, 68.5, 616}, + {77.9, 68.2, 616}, + {15.7, 67.8, 616}, + {78.8, 67.2, 616}, + {80.9, 66.5, 616}, + {33.9, 66.5, 616}, + {82.7, 66.4, 616}, + {78.1, 66.3, 616}, + {88.1, 66, 616}, + {82, 65.7, 616}, + {15, 65.3, 616}, + {78.8, 65.3, 616}, + {82.3, 64.5, 616}, + {80.4, 64, 616}, + {35.2, 63.9, 616}, + {83.4, 63.9, 616}, + {56.5, 55.2, 616}, + {53.3, 51.1, 616}, + {49.3, 50.9, 616}, + {46.3, 50.4, 616}, + {48.3, 82.9, 618}, + {47.5, 82.7, 618}, + {46.2, 82.3, 618}, + {50.4, 81.9, 618}, + {45.9, 81.7, 618}, + {47.3, 81, 618}, + {48.1, 80.9, 618}, + {50.5, 80.8, 618}, + {47.8, 80.6, 618}, + {47.9, 80.1, 618}, + {47, 79.8, 618}, + {48.4, 79.8, 618}, + }, + [61337] = { + {71.2, 48.7, 616}, + {67.9, 43.3, 616}, + {79.1, 42.3, 616}, + {24.4, 38.8, 616}, + {73.2, 35.6, 616}, + {79.6, 34.8, 616}, + {42.2, 34.2, 616}, + {42.9, 32.7, 616}, + {42.3, 32.3, 616}, + {41.4, 32, 616}, + {86.7, 31, 616}, + {42, 30.6, 616}, + {71.5, 29.8, 616}, + {87.9, 28.3, 616}, + {73.2, 27.7, 616}, + {84.2, 23.3, 616}, + }, + [61369] = { + {14.6, 34.8, 5179}, + }, + [61401] = { + {54, 89.8, 5179}, + {52.4, 89.8, 5179}, + {50.8, 88.5, 5179}, + {56.2, 88.3, 5179}, + {54.2, 87.3, 5179}, + {51.5, 86.1, 5179}, + {55.1, 85.1, 5179}, + {52.7, 84.9, 5179}, + {57.4, 83.4, 5179}, + {51.3, 83.3, 5179}, + {49.8, 82.3, 5179}, + {57, 79.4, 5179}, + {49.4, 79.2, 5179}, + {52.4, 78.1, 5179}, + {51.1, 77.9, 5179}, + {50.1, 75.2, 5179}, + }, + [61440] = { + {25.2, 30.7, 1}, + {81.5, 20.3, 721}, + }, + [61443] = { + {25.4, 30.6, 1}, + {83.7, 19.9, 721}, + }, + [61445] = { + {25.4, 30.7, 1}, + {83.5, 21.1, 721}, + }, + [61488] = { + {23, 37.1, 616}, + }, + [61523] = { + {82.8, 29.2, 616}, + }, + [61563] = { + {54.9, 85.4, 616}, + }, + [61599] = { + {84, 38.4, 357}, + {84.7, 37.6, 357}, + {84.4, 37.1, 357}, + {83.6, 35.5, 357}, + {84, 35.2, 357}, + {85, 34.9, 357}, + {83.2, 34.8, 357}, + {84.3, 34.8, 357}, + {83.7, 34, 357}, + {83.1, 33.7, 357}, + {85, 33.4, 357}, + {83.9, 32.9, 357}, + {84.6, 32.3, 357}, + }, + [61651] = { + {73, 58.1, 331}, + }, + [61656] = { + {99.9, 53.5, 14}, + {99.6, 51.3, 14}, + {44.2, 61.6, 5536}, + {52.2, 61.5, 5536}, + {51.6, 58.5, 5536}, + {55, 57.9, 5536}, + {56.5, 56.1, 5536}, + {50.3, 55.3, 5536}, + {39.4, 52.6, 5536}, + {42.8, 52.4, 5536}, + {42.2, 50.3, 5536}, + {48.7, 50.1, 5536}, + {38.6, 47.9, 5536}, + {53.6, 47.2, 5536}, + {51.5, 45.8, 5536}, + {40.5, 44.3, 5536}, + {45.5, 41.8, 5536}, + {49.8, 39.8, 5536}, + {48.9, 39.1, 5536}, + {50.6, 38.1, 5536}, + {45, 37.3, 5536}, + {48.3, 33.8, 5536}, + {44.7, 31.2, 5536}, + }, + [61657] = { + {57, 72.7, 5536}, + {58.1, 72.7, 5536}, + {56.2, 72.1, 5536}, + {57.4, 71, 5536}, + {55.4, 67.5, 5536}, + {57.7, 64.8, 5536}, + {55.6, 62.6, 5536}, + {59, 55.5, 5536}, + {59.1, 55.1, 5536}, + {57, 53, 5536}, + {60.3, 51.7, 5536}, + {60.9, 50.3, 5536}, + {59.8, 49.5, 5536}, + {59.3, 48.8, 5536}, + {60.6, 48.5, 5536}, + {61.6, 47.9, 5536}, + {60.7, 47.7, 5536}, + {55.6, 44.4, 5536}, + {55.6, 43.5, 5536}, + {54.6, 43.4, 5536}, + {58.6, 41.8, 5536}, + {63.6, 39.8, 5536}, + {58.5, 39.2, 5536}, + {61.6, 38.8, 5536}, + {63.6, 37.3, 5536}, + {63.9, 34.8, 5536}, + {63, 32.8, 5536}, + {61.9, 31.4, 5536}, + {63, 31, 5536}, + {51.7, 26.8, 5536}, + {57.1, 26.7, 5536}, + {54.2, 24, 5536}, + }, + [61658] = { + {45.1, 46.4, 5536}, + {52.8, 44.5, 5536}, + {51.5, 42.8, 5536}, + {55, 42.5, 5536}, + {53.6, 42.2, 5536}, + {42.5, 40.9, 5536}, + {47.5, 40.8, 5536}, + {45.9, 39.2, 5536}, + {52.2, 39.1, 5536}, + {41.6, 38.6, 5536}, + {43.2, 37.5, 5536}, + {48.2, 36.8, 5536}, + {46.1, 36.2, 5536}, + {45, 34.5, 5536}, + {43.9, 33.9, 5536}, + }, + [61659] = { + {59.8, 52.4, 5536}, + {63, 51.4, 5536}, + {58.9, 48.1, 5536}, + {63.2, 47.1, 5536}, + {57.6, 47.1, 5536}, + {59.1, 44.6, 5536}, + {57.2, 43.4, 5536}, + {46.2, 31.5, 5536}, + {52, 23.5, 5536}, + {53.7, 21.6, 5536}, + {55.4, 21.4, 5536}, + }, + [61660] = { + {99.4, 53, 14}, + {99.9, 52.3, 14}, + {99.7, 50.1, 14}, + {43.6, 75, 5536}, + {50, 58.2, 5536}, + {52.2, 57.5, 5536}, + {49.2, 57.3, 5536}, + {54.2, 56.3, 5536}, + {48.2, 54, 5536}, + {47.3, 52, 5536}, + {38.2, 51.6, 5536}, + {43.6, 50.4, 5536}, + {39.3, 50.2, 5536}, + {41.4, 49.5, 5536}, + {50.4, 48.3, 5536}, + {43.3, 48.2, 5536}, + {48.3, 47.8, 5536}, + {40.8, 47.4, 5536}, + {38.9, 45.4, 5536}, + {43.7, 43.8, 5536}, + {39.5, 42.3, 5536}, + }, + [61661] = { + {46.9, 76.6, 5536}, + {45.5, 74.7, 5536}, + {48.6, 73.5, 5536}, + {56.3, 51.7, 5536}, + }, + [61662] = { + {55.9, 75.6, 5536}, + {51.3, 73.3, 5536}, + {53.2, 72.2, 5536}, + {58.2, 71.3, 5536}, + {56.7, 70.4, 5536}, + {56.4, 68.7, 5536}, + {59.6, 68.1, 5536}, + {56.5, 65.5, 5536}, + {54.9, 64.7, 5536}, + {54.9, 60.8, 5536}, + {53.2, 60.1, 5536}, + {56.4, 58.2, 5536}, + }, + [61680] = { + {98.8, 37.9, 14}, + {54.6, 91.8, 5536}, + {57.7, 90.9, 5536}, + {50.6, 90.1, 5536}, + {47.5, 87.5, 5536}, + {44.9, 82.9, 5536}, + {49.8, 82.6, 5536}, + {53.1, 81.2, 5536}, + {46, 80, 5536}, + {56.3, 79.5, 5536}, + {63.9, 75.7, 5536}, + {61, 73.4, 5536}, + {63.1, 69.3, 5536}, + {60.9, 69.1, 5536}, + {67.1, 68.9, 5536}, + {71.2, 65.9, 5536}, + {66.5, 65.2, 5536}, + {72.5, 65, 5536}, + {70, 62.9, 5536}, + {70.4, 57.4, 5536}, + {68.4, 56, 5536}, + {77.2, 54, 5536}, + {72.1, 51.5, 5536}, + {75.8, 48.1, 5536}, + {78.2, 46.5, 5536}, + {69, 46.1, 5536}, + {71.9, 43.1, 5536}, + {79.1, 41.7, 5536}, + {69.5, 40.9, 5536}, + {72.8, 39.9, 5536}, + {78.9, 37.5, 5536}, + {73.5, 35.6, 5536}, + {71.3, 34.5, 5536}, + {76.1, 33.9, 5536}, + {69.2, 33.6, 5536}, + {71.4, 31.4, 5536}, + {78.5, 30.5, 5536}, + {46.6, 29, 5536}, + {73.9, 26.6, 5536}, + {78, 25.5, 5536}, + {41.6, 25.1, 5536}, + {45.5, 24, 5536}, + {69, 23.6, 5536}, + {43.2, 22.6, 5536}, + {48.1, 21.8, 5536}, + {69.8, 20.9, 5536}, + {76.5, 20.8, 5536}, + {37, 19.6, 5536}, + {63, 19.5, 5536}, + {48, 19.3, 5536}, + {53.1, 17.7, 5536}, + {67.5, 17.4, 5536}, + {70.6, 16.8, 5536}, + {55.1, 16.8, 5536}, + {44.2, 16.8, 5536}, + {62.2, 16.2, 5536}, + {46, 16, 5536}, + {51.7, 15.3, 5536}, + {48.8, 15.1, 5536}, + {45.4, 13.4, 5536}, + {55.3, 13.1, 5536}, + {40.8, 12.8, 5536}, + {69.8, 12, 5536}, + {44.2, 11.7, 5536}, + {60.4, 11.6, 5536}, + {46.6, 10.5, 5536}, + {63, 10.4, 5536}, + {51.8, 10.3, 5536}, + {49.5, 8.7, 5536}, + {59, 7.9, 5536}, + }, + [61681] = { + {95.8, 67.9, 14}, + {96.8, 67.6, 14}, + {99.7, 67.5, 14}, + {98.3, 67.3, 14}, + {95.2, 66.9, 14}, + {97.3, 66.8, 14}, + {97, 64.7, 14}, + {95.4, 63.5, 14}, + {94.6, 61.8, 14}, + {95.2, 60.1, 14}, + {93.5, 60, 14}, + {94.2, 59.3, 14}, + {94.9, 58.7, 14}, + {93.7, 58.5, 14}, + {95.5, 57.5, 14}, + {94, 56.9, 14}, + {96.3, 56.3, 14}, + {95.2, 55.2, 14}, + {94.2, 55, 14}, + {94, 53.8, 14}, + {94.6, 52.5, 14}, + {96.7, 51.8, 14}, + {98.2, 48.8, 14}, + {95.7, 48.7, 14}, + {99.9, 46.1, 14}, + {96.5, 45.5, 14}, + {98.7, 45.4, 14}, + {98, 44.1, 14}, + {97.4, 42.2, 14}, + {97.2, 40.3, 14}, + {99.7, 37.8, 14}, + {98.2, 36.4, 14}, + {99.6, 35.3, 14}, + {30.6, 83.1, 5536}, + {32.7, 82.6, 5536}, + {38.8, 82.2, 5536}, + {35.9, 82, 5536}, + {29.2, 81, 5536}, + {33.6, 80.8, 5536}, + {39.7, 79.5, 5536}, + {33, 76.4, 5536}, + {29.7, 73.9, 5536}, + {28, 70.2, 5536}, + {29.2, 66.7, 5536}, + {25.7, 66.3, 5536}, + {27.1, 65, 5536}, + {28.6, 63.7, 5536}, + {26, 63.2, 5536}, + {30, 61.2, 5536}, + {26.6, 59.8, 5536}, + {31.5, 58.6, 5536}, + {29.3, 56.2, 5536}, + {27.1, 55.9, 5536}, + {26.6, 53.2, 5536}, + {27.9, 50.5, 5536}, + {32.5, 49, 5536}, + {35.6, 42.8, 5536}, + {30.3, 42.4, 5536}, + {39.2, 37, 5536}, + {41.8, 35.9, 5536}, + {31.9, 35.6, 5536}, + {36.7, 35.5, 5536}, + {35.2, 32.8, 5536}, + {40.6, 32, 5536}, + {41.6, 31.5, 5536}, + {33.9, 28.7, 5536}, + {33.4, 24.6, 5536}, + {40, 23.4, 5536}, + {38.8, 19.5, 5536}, + {35.6, 16.4, 5536}, + {38.7, 14.1, 5536}, + }, + [61686] = { + {99.4, 65.2, 14}, + {98, 65.2, 14}, + {98.4, 63.1, 14}, + {96.8, 63.1, 14}, + {99.1, 63, 14}, + {100, 62.8, 14}, + {99, 62.2, 14}, + {98, 62.2, 14}, + {99.9, 61.5, 14}, + {98.1, 61, 14}, + {96.5, 59.3, 14}, + {97.3, 59, 14}, + {96.8, 57.5, 14}, + {97.8, 56, 14}, + {96.9, 55.6, 14}, + {99.9, 55.5, 14}, + {99.7, 54.8, 14}, + {38.2, 77.5, 5536}, + {35.2, 77.3, 5536}, + {36.1, 73.1, 5536}, + {32.7, 73, 5536}, + {37.6, 72.8, 5536}, + {39.5, 72.3, 5536}, + {41.8, 72.2, 5536}, + {37.2, 71, 5536}, + {35.1, 71, 5536}, + {40.7, 70.3, 5536}, + {39.2, 69.6, 5536}, + {35.5, 68.6, 5536}, + {31.9, 65, 5536}, + {33.7, 64.3, 5536}, + {32.6, 61.2, 5536}, + {41.5, 58.6, 5536}, + {34.8, 57.8, 5536}, + {40.2, 57.2, 5536}, + {32.9, 57, 5536}, + {39.2, 56.9, 5536}, + {38.8, 55.3, 5536}, + }, + [61689] = { + {6, 27.9, 139}, + {6.9, 27.6, 139}, + {6.1, 27.6, 139}, + {7.9, 27.2, 139}, + {5.2, 26.7, 139}, + {8.2, 26.6, 139}, + {5.6, 26.3, 139}, + {6.3, 26.3, 139}, + {9.4, 26.1, 139}, + {7.9, 26, 139}, + {7.5, 25.7, 139}, + {10, 25.6, 139}, + {9.6, 25.5, 139}, + {7.9, 25.1, 139}, + {7.6, 25.1, 139}, + {10.1, 25, 139}, + {10.7, 23.9, 139}, + {10.7, 23.4, 139}, + {10.3, 22.5, 139}, + {11, 22.4, 139}, + {9.9, 21.5, 139}, + {10.8, 21.5, 139}, + {11.4, 20.8, 139}, + {8.7, 20.1, 139}, + {8, 20.1, 139}, + {8.3, 19.9, 139}, + {10.6, 19.8, 139}, + {10.2, 19.8, 139}, + {9.8, 19.7, 139}, + {11.7, 19.7, 139}, + {9.7, 19.2, 139}, + {9.3, 18.5, 139}, + {9.5, 18, 139}, + {45.8, 90.8, 5225}, + {47, 90.5, 5225}, + {46, 90.4, 5225}, + {48.2, 90, 5225}, + {44.9, 89.3, 5225}, + {48.6, 89.2, 5225}, + {45.3, 88.9, 5225}, + {46.3, 88.9, 5225}, + {50.1, 88.6, 5225}, + {48.2, 88.4, 5225}, + {43.8, 88.2, 5225}, + {47.8, 88.1, 5225}, + {50.8, 88, 5225}, + {50.4, 87.9, 5225}, + {48.2, 87.3, 5225}, + {47.8, 87.3, 5225}, + {44.9, 87.3, 5225}, + {50.9, 87.2, 5225}, + {44.4, 87, 5225}, + {51.7, 85.9, 5225}, + {45, 85.3, 5225}, + {43.9, 85.2, 5225}, + {51.7, 85.2, 5225}, + {44.2, 84.6, 5225}, + {51.2, 84, 5225}, + {52.1, 84, 5225}, + {44.9, 83, 5225}, + {50.7, 82.9, 5225}, + {51.8, 82.9, 5225}, + {46, 82.4, 5225}, + {45.5, 82.2, 5225}, + {44.8, 82.2, 5225}, + {52.6, 81.9, 5225}, + {47.5, 81.3, 5225}, + {49.3, 81.1, 5225}, + {48.3, 81, 5225}, + {48.7, 80.8, 5225}, + {51.6, 80.7, 5225}, + {51.1, 80.7, 5225}, + {50.6, 80.6, 5225}, + {52.9, 80.6, 5225}, + {47.5, 80.4, 5225}, + {46.1, 80.3, 5225}, + {45.3, 80.1, 5225}, + {50.5, 79.9, 5225}, + {45.2, 79.6, 5225}, + {50, 79.1, 5225}, + {50.3, 78.4, 5225}, + {48.2, 78.4, 5225}, + {46.8, 77.1, 5225}, + {45.8, 77, 5225}, + {46.9, 75.3, 5225}, + {45.8, 75, 5225}, + {49.7, 75, 5225}, + {48.9, 73, 5225}, + {46.9, 72.4, 5225}, + {49.7, 72, 5225}, + {47.8, 71.8, 5225}, + }, + [61690] = { + {8.9, 27.7, 139}, + {7.8, 27.6, 139}, + {5.4, 27.3, 139}, + {8.6, 27.1, 139}, + {5.5, 27, 139}, + {7.4, 26.8, 139}, + {4.6, 26.5, 139}, + {6.9, 26.5, 139}, + {9.7, 26.3, 139}, + {7.1, 26, 139}, + {10.2, 25.6, 139}, + {7.8, 24.9, 139}, + {5.6, 24, 139}, + {10.5, 23.9, 139}, + {9.7, 23.8, 139}, + {10.4, 23.1, 139}, + {11.4, 22.7, 139}, + {9.8, 22.1, 139}, + {11.2, 21.9, 139}, + {10.3, 21.8, 139}, + {10.6, 20.6, 139}, + {7.8, 20.4, 139}, + {9.3, 19.8, 139}, + {11.1, 19.6, 139}, + {10.1, 19.2, 139}, + {10, 18.6, 139}, + {9.7, 18.2, 139}, + {9.6, 16.6, 139}, + {49.4, 90.6, 5225}, + {48.1, 90.5, 5225}, + {45.1, 90.1, 5225}, + {49.1, 89.8, 5225}, + {45.2, 89.8, 5225}, + {47.6, 89.5, 5225}, + {44.1, 89.1, 5225}, + {47, 89, 5225}, + {50.5, 88.8, 5225}, + {47.2, 88.5, 5225}, + {51.2, 87.9, 5225}, + {48.1, 87, 5225}, + {43.8, 86.3, 5225}, + {44, 86.2, 5225}, + {45.3, 86, 5225}, + {51.4, 85.8, 5225}, + {50.5, 85.7, 5225}, + {51.4, 84.8, 5225}, + {52.7, 84.4, 5225}, + {50.6, 83.6, 5225}, + {44.4, 83.4, 5225}, + {52.4, 83.3, 5225}, + {51.2, 83.2, 5225}, + {45.1, 82.8, 5225}, + {47.2, 82, 5225}, + {51.6, 81.7, 5225}, + {48.1, 81.4, 5225}, + {47, 81.1, 5225}, + {49.9, 80.7, 5225}, + {45.5, 80.5, 5225}, + {52.2, 80.5, 5225}, + {51, 80, 5225}, + {50.8, 79.1, 5225}, + {50.5, 78.7, 5225}, + {45.7, 78.1, 5225}, + {50.3, 76.7, 5225}, + {48, 76.5, 5225}, + {46.6, 76.3, 5225}, + {50, 76.1, 5225}, + {45.6, 75.8, 5225}, + {49.4, 75.6, 5225}, + {50.1, 75.1, 5225}, + {47.5, 74.3, 5225}, + {49.8, 74.1, 5225}, + {48.1, 74.1, 5225}, + {45.2, 74, 5225}, + {46.1, 73.8, 5225}, + {47.9, 73.1, 5225}, + {48.4, 72.2, 5225}, + }, + [61691] = { + {10.7, 18.5, 139}, + {45.5, 79.6, 5225}, + {51.7, 79, 5225}, + {46.1, 76.1, 5225}, + {47.6, 75.9, 5225}, + {48.6, 75.2, 5225}, + {44.6, 73.7, 5225}, + {47.4, 73.7, 5225}, + {50.5, 72.9, 5225}, + {42.7, 71.9, 5225}, + {46.8, 71.2, 5225}, + {51.7, 71.1, 5225}, + {48.6, 71, 5225}, + {42.1, 70.8, 5225}, + {50.2, 70.7, 5225}, + {46.2, 70.5, 5225}, + {47.9, 69.9, 5225}, + {45.1, 69.4, 5225}, + {44.2, 68.5, 5225}, + {49.1, 67.8, 5225}, + {50.9, 67.6, 5225}, + {45.4, 67.1, 5225}, + {43, 66.9, 5225}, + {47.4, 66.2, 5225}, + }, + [61692] = { + {48.7, 76, 5225}, + {45.3, 74.8, 5225}, + {50.1, 73.6, 5225}, + {44.6, 73.5, 5225}, + {49.4, 73.1, 5225}, + {44.2, 72.9, 5225}, + {47, 72.8, 5225}, + {43.1, 71.8, 5225}, + {43.8, 71.5, 5225}, + {41.3, 70.4, 5225}, + {49.6, 70.4, 5225}, + {43.7, 70.2, 5225}, + {51.7, 70.2, 5225}, + {50.5, 69.6, 5225}, + {42.6, 69.4, 5225}, + {51.8, 69.1, 5225}, + {48.6, 69, 5225}, + {48.5, 68.2, 5225}, + {47.3, 68.1, 5225}, + {44, 67.2, 5225}, + {44.4, 66.4, 5225}, + {45.6, 65.5, 5225}, + }, + [61693] = { + {9.7, 16.2, 139}, + {47.5, 76.6, 5225}, + {50.4, 76.2, 5225}, + {48.7, 74, 5225}, + {42.4, 73.1, 5225}, + {48.4, 72.9, 5225}, + {44.6, 72.7, 5225}, + {51.1, 71.3, 5225}, + {52.7, 70.6, 5225}, + {48.3, 70.5, 5225}, + {52.4, 70.4, 5225}, + {46.9, 69.5, 5225}, + {41.8, 69.3, 5225}, + {52.5, 69.3, 5225}, + {49.2, 68.8, 5225}, + {43.3, 68.7, 5225}, + {43.1, 68.4, 5225}, + {50.9, 68.4, 5225}, + {42.6, 67.4, 5225}, + {45.9, 67.2, 5225}, + {44.8, 66.3, 5225}, + {46.2, 65.3, 5225}, + }, + [61695] = { + {16.9, 12.5, 139}, + {18.8, 11.4, 139}, + {20.5, 9.7, 139}, + {19.9, 7.5, 139}, + {16.8, 94.9, 2040}, + {31.5, 94.4, 2040}, + {28.7, 94, 2040}, + {20.8, 92.3, 2040}, + {12, 91.8, 2040}, + {28.9, 91.5, 2040}, + {39.9, 90.7, 2040}, + {23.6, 89.9, 2040}, + {14.4, 89, 2040}, + {22.9, 88.7, 2040}, + {28, 85.6, 2040}, + {28.8, 82.4, 2040}, + {39.2, 71.6, 5225}, + {59.5, 71.6, 5225}, + {61.9, 70.2, 5225}, + {54.9, 69.8, 5225}, + {53.3, 68.6, 5225}, + {64, 68.1, 5225}, + {51.7, 68, 5225}, + {56, 66.4, 5225}, + {51.5, 66.2, 5225}, + {60.4, 65.9, 5225}, + {48.2, 65.8, 5225}, + {49.2, 65.8, 5225}, + {63.3, 65.3, 5225}, + {42.2, 64.8, 5225}, + {57.6, 64.7, 5225}, + {45.2, 64, 5225}, + {46, 63.7, 5225}, + {49.6, 62.8, 5225}, + {51.4, 62.5, 5225}, + {45.5, 61, 5225}, + {50.1, 59.7, 5225}, + {43.6, 59.2, 5225}, + {47.4, 57.1, 5225}, + {52.3, 56, 5225}, + {43.8, 55.6, 5225}, + {46.8, 55.2, 5225}, + {48.6, 54.9, 5225}, + {51.7, 53.3, 5225}, + {45.4, 53.1, 5225}, + {43.3, 50, 5225}, + {50.6, 50, 5225}, + {45.8, 47.1, 5225}, + {52.8, 46.9, 5225}, + {51.5, 46.7, 5225}, + {47.7, 45.9, 5225}, + {43.5, 45.6, 5225}, + {51.5, 45.5, 5225}, + {56.8, 45.1, 5225}, + {49, 44.8, 5225}, + {44.6, 44.3, 5225}, + {48.7, 44.2, 5225}, + {51.1, 42.7, 5225}, + {51.5, 41.2, 5225}, + }, + [61696] = { + {13.5, 12.9, 139}, + {14.5, 12.1, 139}, + {15.9, 11.8, 139}, + {12.8, 98.8, 2040}, + {9.8, 97.3, 2040}, + {15.5, 96.6, 2040}, + {18.4, 94.9, 2040}, + {15.6, 92.8, 2040}, + {34, 76.4, 5225}, + {37.7, 73.5, 5225}, + {55.3, 72, 5225}, + {54.2, 71.4, 5225}, + {36.8, 71.3, 5225}, + {56.5, 71.1, 5225}, + {58.3, 70.7, 5225}, + {34.3, 70.2, 5225}, + {34.3, 68.3, 5225}, + {56, 68.1, 5225}, + {41.8, 67.9, 5225}, + {54, 67.9, 5225}, + {40, 67.4, 5225}, + {37, 66.7, 5225}, + {56.5, 65.8, 5225}, + {39.7, 65.7, 5225}, + {58.8, 65.3, 5225}, + {39, 65.1, 5225}, + {42.8, 65.1, 5225}, + {54.9, 65, 5225}, + {43.6, 64.4, 5225}, + {50.5, 63.8, 5225}, + {55.3, 63.3, 5225}, + {47.5, 63.1, 5225}, + {52.1, 63, 5225}, + {42.2, 63, 5225}, + {45, 62.7, 5225}, + {53.5, 61.8, 5225}, + {48.4, 60.8, 5225}, + {44.6, 60.8, 5225}, + {49.3, 60.4, 5225}, + {45.4, 58.5, 5225}, + {46.6, 58.3, 5225}, + {50.8, 58.1, 5225}, + {53.2, 55.2, 5225}, + {50.2, 55, 5225}, + {44.9, 54.9, 5225}, + {49.7, 54.2, 5225}, + {48, 53.9, 5225}, + {53.3, 53.2, 5225}, + {46.8, 53.2, 5225}, + {46.2, 53.1, 5225}, + {50.2, 51.6, 5225}, + {43.6, 51.6, 5225}, + {45.3, 51.2, 5225}, + {46, 50, 5225}, + {43.9, 49, 5225}, + {42.4, 48.3, 5225}, + {45.2, 47.9, 5225}, + {46.5, 47.1, 5225}, + {45.2, 46.1, 5225}, + }, + [61697] = { + {18.9, 12.2, 139}, + {16.6, 11.9, 139}, + {20.2, 11.4, 139}, + {17.4, 11.2, 139}, + {19.9, 10.4, 139}, + {17.3, 10.3, 139}, + {18.1, 10, 139}, + {18.6, 9.8, 139}, + {18.5, 9.7, 139}, + {16.9, 9.7, 139}, + {19.7, 9.4, 139}, + {21.1, 8.9, 139}, + {19.2, 8.9, 139}, + {19.4, 8.2, 139}, + {20.8, 7.8, 139}, + {62, 71.2, 5225}, + {59.1, 70.8, 5225}, + {63.7, 70.1, 5225}, + {60.2, 69.9, 5225}, + {63.3, 68.9, 5225}, + {60.1, 68.8, 5225}, + {61.1, 68.4, 5225}, + {57.2, 68.3, 5225}, + {61.6, 68.2, 5225}, + {61.5, 68, 5225}, + {59.5, 68, 5225}, + {63, 67.6, 5225}, + {64.8, 67, 5225}, + {62.5, 67, 5225}, + {56.8, 66.9, 5225}, + {58.7, 66.9, 5225}, + {60.6, 66.4, 5225}, + {62.7, 66.2, 5225}, + {64.4, 65.7, 5225}, + {58.1, 65.3, 5225}, + {60.9, 65.2, 5225}, + {62.2, 64.7, 5225}, + {58.5, 64.4, 5225}, + {59.6, 64.3, 5225}, + {60.5, 63.8, 5225}, + }, + [61698] = { + {18.2, 11.5, 139}, + {20.1, 10.9, 139}, + {17.5, 10.7, 139}, + {20.3, 9.4, 139}, + {17.9, 8.9, 139}, + {20.4, 8.4, 139}, + {19.2, 7.7, 139}, + {61.1, 70.2, 5225}, + {63.5, 69.5, 5225}, + {60.2, 69.3, 5225}, + {63.7, 67.6, 5225}, + {60.7, 67, 5225}, + {63.9, 66.4, 5225}, + {62.4, 65.5, 5225}, + {63.2, 63.8, 5225}, + }, + [61700] = { + {35.2, 98.1, 2040}, + {24.7, 94.6, 2040}, + {21.6, 94.5, 2040}, + {0.3, 94.3, 2040}, + {27.5, 93.5, 2040}, + {17.8, 92.3, 2040}, + {5, 90.3, 2040}, + {37.3, 88.7, 2040}, + {39.6, 87.8, 2040}, + {26.1, 87.2, 2040}, + {9.7, 85.8, 2040}, + {26.7, 60.3, 5225}, + {30.1, 55.5, 5225}, + {33, 52.4, 5225}, + {29.3, 52.2, 5225}, + {53.4, 52.1, 5225}, + {34.1, 51.1, 5225}, + {37, 50.3, 5225}, + {53.2, 50.2, 5225}, + {54.5, 48.6, 5225}, + {36.8, 48.5, 5225}, + {30.1, 48.5, 5225}, + {49.5, 47, 5225}, + {48.1, 47, 5225}, + {37.9, 46.8, 5225}, + {33.8, 46.6, 5225}, + {50.9, 46.5, 5225}, + {46.2, 45.9, 5225}, + {40.1, 44.9, 5225}, + {55.6, 44.2, 5225}, + {56.6, 43.8, 5225}, + {50.2, 43.5, 5225}, + {42.4, 42.8, 5225}, + }, + [61704] = { + {17.7, 86.9, 2040}, + {13.5, 84.2, 2040}, + {11.6, 83.2, 2040}, + {5.2, 82.4, 2040}, + {0.9, 82, 2040}, + {9.1, 80, 2040}, + {2.1, 68.7, 2040}, + {6.4, 64.9, 2040}, + {7.3, 60.5, 2040}, + {28, 50.2, 5225}, + {28.1, 48.9, 5225}, + {30.7, 47.2, 5225}, + {29.1, 46.7, 5225}, + {31, 45.8, 5225}, + {31.6, 45.8, 5225}, + {31.9, 45.1, 5225}, + {33.7, 44.2, 5225}, + {46.2, 43.3, 5225}, + {35.2, 43.3, 5225}, + {32.7, 43.2, 5225}, + {34.3, 43.2, 5225}, + {36.1, 42.3, 5225}, + {44.2, 42, 5225}, + {43.3, 41.6, 5225}, + {32.9, 41.4, 5225}, + {32.1, 41.4, 5225}, + {31.5, 41.3, 5225}, + {40.3, 41.2, 5225}, + {38.2, 41, 5225}, + {35.6, 40.5, 5225}, + {42.1, 40.1, 5225}, + {30.5, 39.9, 5225}, + {30.2, 39.7, 5225}, + {37.2, 39.5, 5225}, + {34.9, 39.2, 5225}, + {29.7, 39, 5225}, + {28.7, 38.1, 5225}, + {27.9, 37.9, 5225}, + {35.8, 37.2, 5225}, + {27.1, 37, 5225}, + {37.1, 36.2, 5225}, + {38.8, 34.7, 5225}, + {40.8, 32.9, 5225}, + {41.2, 30.8, 5225}, + }, + [61716] = { + {0.5, 88.8, 2040}, + {6.4, 86.5, 2040}, + {2.2, 85.7, 2040}, + {0.8, 61.7, 2040}, + {4.7, 60.2, 2040}, + {5.3, 53.7, 2040}, + {0.9, 45.7, 2040}, + {3.1, 41.1, 2040}, + {29.4, 58.9, 5225}, + {27.2, 56.2, 5225}, + {32.4, 51.5, 5225}, + {34.8, 51.4, 5225}, + {29.5, 50.8, 5225}, + {31, 50.1, 5225}, + {37.7, 49.2, 5225}, + {31.8, 48.4, 5225}, + {33.7, 47.9, 5225}, + {35.1, 45.5, 5225}, + {38, 44.2, 5225}, + {36.7, 43.5, 5225}, + {40.8, 43.1, 5225}, + {38.8, 42.8, 5225}, + {29.2, 37, 5225}, + {31.1, 34.6, 5225}, + {34.4, 34.1, 5225}, + {33.5, 33.9, 5225}, + {29.3, 32.8, 5225}, + {38.1, 31.4, 5225}, + {32.8, 30.8, 5225}, + {40, 30.7, 5225}, + {26.8, 30.2, 5225}, + {25.5, 29.3, 5225}, + {40.3, 27.6, 5225}, + {32, 27.4, 5225}, + {28.8, 27.4, 5225}, + {35, 27.3, 5225}, + {32, 25.5, 5225}, + {31.3, 24.6, 5225}, + {38.2, 23.8, 5225}, + {32.4, 22.5, 5225}, + {39.2, 21.6, 5225}, + }, + [61717] = { + {0.2, 64.3, 2040}, + {0.7, 56.6, 2040}, + {4.4, 50.6, 2040}, + {0.4, 48.2, 2040}, + {5.1, 41.9, 2040}, + {34.7, 36.7, 5225}, + {32.8, 36.5, 5225}, + {37.5, 33.6, 5225}, + {37.9, 32.7, 5225}, + {27.7, 31.3, 5225}, + {29.9, 30.8, 5225}, + {29.1, 30.6, 5225}, + {38.1, 29, 5225}, + {27.8, 28.4, 5225}, + {39.8, 26.2, 5225}, + {37.9, 25, 5225}, + {36.1, 25, 5225}, + {28.9, 23.9, 5225}, + {31.4, 22.3, 5225}, + {40.2, 22, 5225}, + {28.3, 21.4, 5225}, + }, + [61742] = { + {49.7, 64.3, 5536}, + }, + [61743] = { + {48.4, 69.2, 5536}, + }, + [61751] = { + {61.4, 81.6, 5536}, + }, + [61774] = { + {11, 97.4, 2040}, + {18.3, 97, 2040}, + {19.4, 95.1, 2040}, + {19.5, 92.5, 2040}, + {18.6, 89.2, 2040}, + {53.1, 69.3, 5225}, + {38.2, 67.5, 5225}, + {54.5, 67.5, 5225}, + {53.1, 66.5, 5225}, + {42.3, 66.2, 5225}, + {38.3, 65.5, 5225}, + {56.3, 64.9, 5225}, + {40.8, 64.7, 5225}, + {44, 64.1, 5225}, + {42, 63.9, 5225}, + {46.5, 62.3, 5225}, + {42.7, 62.2, 5225}, + {52.8, 60.3, 5225}, + {45.3, 60.2, 5225}, + {46.1, 59.8, 5225}, + {47.4, 59.1, 5225}, + {50.1, 58.5, 5225}, + {51.8, 57.1, 5225}, + {47.9, 55.9, 5225}, + {52.7, 55.6, 5225}, + {51, 55, 5225}, + {44, 54.6, 5225}, + {47, 53.8, 5225}, + {44.9, 53, 5225}, + {50.8, 52.8, 5225}, + {45.9, 51.8, 5225}, + {49.5, 51.7, 5225}, + {49.7, 49.9, 5225}, + {43, 48.3, 5225}, + {46.5, 48.1, 5225}, + {47, 47.2, 5225}, + {47.1, 46, 5225}, + {46.6, 44.4, 5225}, + }, + [61775] = { + {1.3, 95.4, 2040}, + {7.1, 93, 2040}, + {9.1, 87.8, 2040}, + {4.6, 85.1, 2040}, + {7.4, 84.7, 2040}, + {26.1, 59.4, 5225}, + {28.6, 58.5, 5225}, + {25.8, 56.8, 5225}, + {29, 55.1, 5225}, + {29.4, 53.7, 5225}, + {31.5, 52.8, 5225}, + {30.7, 51.9, 5225}, + {28.8, 51.6, 5225}, + {36.4, 51.5, 5225}, + {36, 49, 5225}, + {31, 48.7, 5225}, + {29.7, 48.5, 5225}, + {38.4, 47.3, 5225}, + {32.5, 47.3, 5225}, + {32.6, 46.4, 5225}, + {41.1, 46.2, 5225}, + {36.2, 44.9, 5225}, + {42.1, 43.8, 5225}, + {40, 42.5, 5225}, + {41.3, 42.3, 5225}, + }, + [61776] = { + {35.7, 76.6, 5225}, + {33.9, 75.4, 5225}, + {36.6, 75.1, 5225}, + {35, 74.9, 5225}, + {35.2, 74.2, 5225}, + {34.2, 73.6, 5225}, + {37.6, 73.2, 5225}, + {38, 72.5, 5225}, + {33.9, 71.6, 5225}, + {37.8, 71.4, 5225}, + {33.9, 70.5, 5225}, + {37.9, 70.2, 5225}, + {36, 70.1, 5225}, + {33.9, 69.1, 5225}, + {32.4, 68.5, 5225}, + {37.5, 68.5, 5225}, + {33.4, 67.9, 5225}, + {36.1, 67, 5225}, + }, + [61789] = { + {26.5, 58.4, 5225}, + {35.3, 48.2, 5225}, + }, + [61812] = { + {53.6, 51.2, 2040}, + {63.3, 26.4, 5225}, + }, + [62086] = { + {51.8, 53.7, 2040}, + {62.4, 27.6, 5225}, + }, + [62088] = { + {40.9, 68.1, 2040}, + {57.3, 34.4, 5225}, + }, + [62096] = { + {51.6, 64.6, 5225}, + }, + [62121] = { + {40.5, 71.7, 5581}, + {38.6, 71.6, 5581}, + {50.9, 71.3, 5581}, + {52.1, 70.8, 5581}, + {41.6, 70.6, 5581}, + {47.7, 69.3, 5581}, + {54.6, 68.6, 5581}, + {56.2, 68.3, 5581}, + {39, 68.1, 5581}, + {59.5, 67.9, 5581}, + {62.8, 67.9, 5581}, + {51.1, 67.7, 5581}, + {60.7, 67.4, 5581}, + {44.6, 67, 5581}, + {47.3, 67, 5581}, + {61.6, 66.9, 5581}, + {47, 65.5, 5581}, + {45.6, 65.3, 5581}, + {49.7, 65.1, 5581}, + {47.4, 64.8, 5581}, + {54.8, 64.1, 5581}, + {52.3, 63.8, 5581}, + {51.1, 62.7, 5581}, + {52.9, 61.8, 5581}, + {54.7, 61.1, 5581}, + {52.4, 57.5, 5581}, + {50.8, 56.3, 5581}, + {51, 53, 5581}, + {42.2, 48.1, 5581}, + {34.1, 48.1, 5581}, + {41, 47.7, 5581}, + {42.9, 46.4, 5581}, + {44.7, 45.4, 5581}, + {43.5, 45.1, 5581}, + {33.5, 44, 5581}, + {43.2, 43.5, 5581}, + {44.3, 42.8, 5581}, + {34.9, 42.6, 5581}, + {43.5, 41.5, 5581}, + {25.6, 39.8, 5581}, + {33.3, 37.5, 5581}, + }, + [62122] = { + {23.9, 93.4, 1}, + {20.8, 93.2, 1}, + {57.5, 75.5, 5581}, + {75, 73.5, 5581}, + {74, 72.7, 5581}, + {72.6, 71.6, 5581}, + {73.9, 71.3, 5581}, + {32.9, 69.8, 5581}, + {72.8, 69.6, 5581}, + {34.7, 69.1, 5581}, + {31.2, 68, 5581}, + {33.6, 67.7, 5581}, + {76.8, 67.6, 5581}, + {29, 66, 5581}, + {73.8, 65.9, 5581}, + {70.4, 64.4, 5581}, + {71.6, 63.8, 5581}, + {35.8, 63.4, 5581}, + {37.3, 63.1, 5581}, + {74.3, 62.7, 5581}, + {76, 62.5, 5581}, + {63.8, 62, 5581}, + {76.7, 61.1, 5581}, + {61.7, 60.6, 5581}, + {68.8, 59.7, 5581}, + {74.7, 58.3, 5581}, + {60, 58.2, 5581}, + {73.5, 57, 5581}, + {67.4, 56.6, 5581}, + {70.8, 55.6, 5581}, + {55, 54.6, 5581}, + {61.5, 53.1, 5581}, + {75.1, 51.9, 5581}, + {67.8, 51.8, 5581}, + {76.6, 51.3, 5581}, + {71.8, 50.4, 5581}, + {64.5, 50.1, 5581}, + {79.3, 49.8, 5581}, + {65.7, 48.5, 5581}, + {57.2, 48.1, 5581}, + {81.1, 48.1, 5581}, + {77.1, 47.6, 5581}, + {72.2, 45.3, 5581}, + {76.8, 44.9, 5581}, + {49.5, 44.6, 5581}, + {75.3, 44.5, 5581}, + {78.6, 44, 5581}, + {47, 43.9, 5581}, + {74.6, 42.9, 5581}, + {71.3, 42.3, 5581}, + {76.2, 41.3, 5581}, + {49.4, 40.9, 5581}, + {45.9, 40.3, 5581}, + {50.6, 39.3, 5581}, + {66, 39.2, 5581}, + {72.9, 39, 5581}, + {58, 38.9, 5581}, + {64.3, 37.8, 5581}, + {77.2, 37.3, 5581}, + {60.3, 37.2, 5581}, + {78.2, 36.7, 5581}, + {55.3, 36.6, 5581}, + {72.9, 36.1, 5581}, + {68.5, 35.8, 5581}, + {53.4, 35.1, 5581}, + {63.1, 34.8, 5581}, + {48.1, 34.5, 5581}, + {57.4, 34.4, 5581}, + {61, 33.8, 5581}, + {47.7, 31.7, 5581}, + {45.4, 31.6, 5581}, + {70.8, 31.3, 5581}, + {49.2, 30.4, 5581}, + {44.4, 29, 5581}, + {47.5, 28.8, 5581}, + {50.4, 28.6, 5581}, + {45.6, 28.3, 5581}, + {69, 27.6, 5581}, + {71.2, 26.1, 5581}, + }, + [62123] = { + {41.7, 72.5, 5581}, + {56.3, 71.7, 5581}, + {43.2, 71, 5581}, + {55.2, 70.2, 5581}, + {60, 70.1, 5581}, + {45.3, 69.7, 5581}, + {38.4, 69.2, 5581}, + {36.6, 69.1, 5581}, + {52.9, 68.7, 5581}, + {58.4, 67.3, 5581}, + {63, 66.5, 5581}, + {53.4, 66.2, 5581}, + {61.3, 64.9, 5581}, + {53.8, 62.9, 5581}, + {53.3, 59.1, 5581}, + {32.3, 48.6, 5581}, + {29, 47.6, 5581}, + {35.7, 41.1, 5581}, + {33.6, 40.2, 5581}, + {32.6, 39.1, 5581}, + }, + [62124] = { + {56, 75.9, 5581}, + {58.3, 74.8, 5581}, + {54.4, 73.5, 5581}, + {32.4, 68.3, 5581}, + {75.1, 68.3, 5581}, + {30, 68, 5581}, + {71.8, 67.9, 5581}, + {72.8, 66.2, 5581}, + {28, 66.1, 5581}, + {30.1, 66, 5581}, + {75, 64.5, 5581}, + {62.5, 62.6, 5581}, + {71.9, 61.9, 5581}, + {74.5, 60, 5581}, + {71.8, 59.7, 5581}, + {65.8, 59.3, 5581}, + {72.7, 58.4, 5581}, + {61.7, 58.4, 5581}, + {69.9, 58.2, 5581}, + {69.6, 56.6, 5581}, + {55, 56, 5581}, + {57.7, 54.5, 5581}, + {75.8, 54.1, 5581}, + {53.1, 53.9, 5581}, + {77.1, 52.7, 5581}, + {62, 51.9, 5581}, + {69.1, 51.6, 5581}, + {66.5, 50.7, 5581}, + {57.8, 50, 5581}, + {72.6, 49.3, 5581}, + {59.6, 49, 5581}, + {74.8, 48.5, 5581}, + {70.9, 47.4, 5581}, + {61.3, 47.1, 5581}, + {55.9, 46.9, 5581}, + {69.6, 46.1, 5581}, + {62.6, 45.6, 5581}, + {79.1, 45.5, 5581}, + {77.5, 45.4, 5581}, + {59.1, 44.4, 5581}, + {79.5, 43.8, 5581}, + {48.6, 42.6, 5581}, + {76, 42.5, 5581}, + {72.5, 42.4, 5581}, + {60.3, 42.3, 5581}, + {67.4, 42.3, 5581}, + {57.6, 42.1, 5581}, + {64, 41.8, 5581}, + {61.4, 41.6, 5581}, + {47.6, 41.4, 5581}, + {55.5, 40.7, 5581}, + {71.7, 39.8, 5581}, + {69.9, 39.2, 5581}, + {53, 39.2, 5581}, + {75.2, 38.9, 5581}, + {77, 38.6, 5581}, + {46.7, 38.5, 5581}, + {56.9, 38.1, 5581}, + {67, 38, 5581}, + {48, 37.3, 5581}, + {70.5, 36.8, 5581}, + {78.9, 35.9, 5581}, + {28.9, 35.2, 5581}, + {58.8, 35.2, 5581}, + {31.5, 34.6, 5581}, + {32.3, 34.1, 5581}, + {33.6, 34, 5581}, + {30.8, 33.6, 5581}, + {63.7, 32.9, 5581}, + {59.6, 32.9, 5581}, + {29.2, 32.7, 5581}, + {34.1, 32.6, 5581}, + {35, 31.8, 5581}, + {27.7, 31.5, 5581}, + {57.5, 31.4, 5581}, + {68.7, 30.7, 5581}, + {33.7, 30.7, 5581}, + {71.1, 28.3, 5581}, + {68.9, 25.3, 5581}, + {72.6, 23.7, 5581}, + {69.1, 20.6, 5581}, + {71.2, 20.2, 5581}, + }, + [62125] = { + {57.7, 72.1, 5581}, + {53.5, 71.3, 5581}, + {53.8, 69.7, 5581}, + {58.1, 68.9, 5581}, + {61.5, 68.6, 5581}, + {35.4, 67.8, 5581}, + {56.7, 66.7, 5581}, + {37.6, 66.5, 5581}, + {36.4, 66.2, 5581}, + {51, 66, 5581}, + {59.2, 64.7, 5581}, + {53.2, 64.5, 5581}, + {53.8, 61, 5581}, + {52.6, 60.6, 5581}, + {34.2, 50.3, 5581}, + {31.3, 49.5, 5581}, + {28.9, 49.3, 5581}, + {33.6, 41.5, 5581}, + {35.9, 39.4, 5581}, + {32, 37.1, 5581}, + {42.1, 35.8, 5581}, + {43, 35.7, 5581}, + {41.4, 34.7, 5581}, + {40.8, 34.3, 5581}, + {40.7, 33, 5581}, + {39.9, 33, 5581}, + {38.5, 32.5, 5581}, + {37.4, 32, 5581}, + {39.1, 31.7, 5581}, + {40, 31.7, 5581}, + {38.1, 31.3, 5581}, + {39.2, 30.9, 5581}, + }, + [62126] = { + {58.9, 76.3, 5581}, + {54.9, 76.1, 5581}, + {31.8, 69.8, 5581}, + {30.2, 69.3, 5581}, + {72.8, 68, 5581}, + {70.9, 66.6, 5581}, + {38.4, 64.5, 5581}, + {28.9, 64.4, 5581}, + {73.2, 64.4, 5581}, + {64.2, 64.3, 5581}, + {60.3, 62.7, 5581}, + {73.4, 61.7, 5581}, + {73.5, 60.2, 5581}, + {79.1, 58.9, 5581}, + {71.3, 57.7, 5581}, + {79.1, 57.2, 5581}, + {54, 56.9, 5581}, + {59.3, 56.1, 5581}, + {72.6, 55.6, 5581}, + {77.5, 55.5, 5581}, + {67.2, 54.7, 5581}, + {71.2, 54.2, 5581}, + {55.6, 52.3, 5581}, + {63.6, 51.8, 5581}, + {78.7, 51.2, 5581}, + {69.8, 50.2, 5581}, + {62.6, 49.9, 5581}, + {56.1, 49.3, 5581}, + {68.4, 48.4, 5581}, + {82.3, 48.2, 5581}, + {58.5, 47.7, 5581}, + {72.3, 47.5, 5581}, + {78.9, 47, 5581}, + {74.8, 46.8, 5581}, + {76.2, 46.7, 5581}, + {60.3, 45.6, 5581}, + {57.3, 44.3, 5581}, + {63.4, 44.1, 5581}, + {61.9, 43.5, 5581}, + {68.6, 43, 5581}, + {55.5, 42.2, 5581}, + {58.8, 42.1, 5581}, + {65.2, 41.6, 5581}, + {75.1, 41.2, 5581}, + {62.7, 40.8, 5581}, + {74.5, 39.9, 5581}, + {68.6, 39.3, 5581}, + {44, 38.7, 5581}, + {71.9, 37.6, 5581}, + {75.8, 37.3, 5581}, + {80.1, 36.6, 5581}, + {71.8, 35.4, 5581}, + {69.9, 33.5, 5581}, + {67, 32, 5581}, + {37.9, 31.1, 5581}, + {39.4, 30.8, 5581}, + {38.7, 30.8, 5581}, + {65.1, 30.6, 5581}, + {67.6, 28.8, 5581}, + {68.9, 22.4, 5581}, + {67.7, 21.8, 5581}, + }, + [62148] = { + {46.1, 60.6, 5581}, + }, + [62223] = { + {49.8, 67.5, 5602}, + {48.5, 67.5, 5602}, + {49.3, 66.9, 5602}, + {49.4, 65.5, 5602}, + {48.1, 65, 5602}, + {47.6, 64.9, 5602}, + {47.1, 64.5, 5602}, + {48, 64, 5602}, + {48.8, 63.8, 5602}, + {49.3, 62.7, 5602}, + {47.5, 62.4, 5602}, + {48.8, 62.3, 5602}, + {49.6, 61.4, 5602}, + {48.2, 60.1, 5602}, + {49.1, 60, 5602}, + {48.2, 59.9, 5602}, + {48.8, 59.6, 5602}, + {49.2, 59.4, 5602}, + {46.6, 59.3, 5602}, + {46.6, 59.2, 5602}, + {47, 58.8, 5602}, + {48.4, 58.2, 5602}, + {48.7, 57.8, 5602}, + {46.9, 55.8, 5602}, + {57.8, 55.6, 5602}, + {49.4, 55.1, 5602}, + {52.4, 52.5, 5602}, + }, + [62224] = { + {49.4, 66.9, 5602}, + {47.7, 66.1, 5602}, + {47.7, 66, 5602}, + {49.5, 65.5, 5602}, + {48.9, 64.6, 5602}, + {48.9, 64.5, 5602}, + {47.9, 64, 5602}, + {47.3, 63, 5602}, + {49.8, 62.7, 5602}, + {48.2, 62, 5602}, + {48.7, 60.6, 5602}, + {49.2, 59.3, 5602}, + {48.1, 58.9, 5602}, + {46.6, 58.1, 5602}, + }, + [62225] = { + {55.1, 37.7, 5602}, + {53.9, 37.4, 5602}, + {55.3, 35.6, 5602}, + {56.1, 35.4, 5602}, + {55.4, 35.2, 5602}, + {51.9, 34.2, 5602}, + {52.7, 33.3, 5602}, + {52.6, 33.1, 5602}, + {54.2, 33, 5602}, + {51.3, 32.5, 5602}, + {53.5, 32.1, 5602}, + {55, 31.8, 5602}, + {56.4, 31.7, 5602}, + {52.1, 30.5, 5602}, + {55.4, 30.3, 5602}, + {54.3, 30.2, 5602}, + {52.6, 29.6, 5602}, + {55.5, 29.5, 5602}, + {55.5, 29.3, 5602}, + {48.9, 27.2, 5602}, + {50.5, 26.6, 5602}, + {49.5, 26.4, 5602}, + {52, 26.2, 5602}, + {49.6, 26.1, 5602}, + {57, 25.8, 5602}, + {53.5, 23.9, 5602}, + {56.1, 23.8, 5602}, + }, + [62226] = { + {95.4, 68.5, 11}, + {95.9, 65.9, 11}, + {97.2, 63.7, 11}, + {95.2, 56.2, 11}, + {56.7, 65.2, 5602}, + {57.7, 64.8, 5602}, + {52.3, 63.9, 5602}, + {50.6, 61.9, 5602}, + {55.7, 61.4, 5602}, + {54.7, 60.6, 5602}, + {51, 60, 5602}, + {57.6, 59.5, 5602}, + {45.7, 59.1, 5602}, + {50.4, 57.9, 5602}, + {58.1, 57.3, 5602}, + {45.2, 56.8, 5602}, + {50.7, 55.3, 5602}, + {45.8, 55.3, 5602}, + {56.6, 55.1, 5602}, + {54.1, 54.9, 5602}, + {48.1, 54.6, 5602}, + {58.4, 54.3, 5602}, + {51.8, 53.8, 5602}, + {46.6, 53.6, 5602}, + {56.3, 52.8, 5602}, + {48.2, 52.6, 5602}, + {49.3, 51.9, 5602}, + {47.1, 51.8, 5602}, + {55.2, 51.5, 5602}, + {51, 51.4, 5602}, + {61.2, 51.3, 5602}, + {45.4, 50.4, 5602}, + {46.6, 50, 5602}, + {61, 49.6, 5602}, + {63.1, 49.2, 5602}, + {62, 49.1, 5602}, + {47.7, 48.9, 5602}, + {62.4, 48.1, 5602}, + {59.5, 47.7, 5602}, + {58.8, 47.1, 5602}, + {59.2, 45.9, 5602}, + {48.7, 44.9, 5602}, + {58.6, 44.8, 5602}, + {64.4, 44.8, 5602}, + {50.3, 43.6, 5602}, + {57.7, 43.4, 5602}, + {53.6, 43.2, 5602}, + {47.7, 43.2, 5602}, + {54.5, 43.2, 5602}, + {48.4, 43.1, 5602}, + {55.8, 42, 5602}, + {55, 41.1, 5602}, + {56.8, 40.3, 5602}, + {53.3, 40.2, 5602}, + {49.1, 39.8, 5602}, + {54.6, 39.8, 5602}, + {44.4, 39.5, 5602}, + {48.2, 39.4, 5602}, + {46.4, 38.9, 5602}, + {50.5, 38.3, 5602}, + {51.8, 38.2, 5602}, + {49.8, 37.1, 5602}, + {49.1, 37, 5602}, + {47.6, 36.6, 5602}, + {44.2, 36.5, 5602}, + {45.6, 35.9, 5602}, + {43.5, 34.6, 5602}, + {44.5, 34.3, 5602}, + {43.9, 33.4, 5602}, + {46.8, 33.2, 5602}, + {42, 31.6, 5602}, + {43.2, 30.6, 5602}, + {47.1, 30.1, 5602}, + {42.4, 29.6, 5602}, + {45.8, 28.2, 5602}, + {43.4, 27.9, 5602}, + {44.7, 27.6, 5602}, + {44.5, 26, 5602}, + {46.7, 24.8, 5602}, + {47.5, 22.5, 5602}, + {41.8, 22.1, 5602}, + {45.4, 18.4, 5602}, + }, + [62227] = { + {63.2, 77, 5602}, + {62.3, 76.6, 5602}, + {61.7, 75.1, 5602}, + {59.3, 74, 5602}, + {60.6, 73.8, 5602}, + {54.3, 73.4, 5602}, + {61.1, 72.9, 5602}, + {57.5, 72.6, 5602}, + {53.7, 72.2, 5602}, + {56.6, 71.6, 5602}, + {55.5, 71.3, 5602}, + {52.3, 71.2, 5602}, + {57.5, 70.8, 5602}, + {50.5, 70.3, 5602}, + {51.9, 69.5, 5602}, + {52.6, 69.2, 5602}, + {54.2, 68.5, 5602}, + {48.9, 68.4, 5602}, + {50.3, 68, 5602}, + {54.9, 67.7, 5602}, + {51.1, 67.6, 5602}, + {50.5, 67.1, 5602}, + {52.6, 66, 5602}, + {51.3, 65, 5602}, + {65, 41, 5602}, + {58.4, 39.2, 5602}, + {54.6, 38.3, 5602}, + {64.5, 38.1, 5602}, + {60.6, 38.1, 5602}, + {61.5, 37.6, 5602}, + {58.5, 37.2, 5602}, + {57.5, 37, 5602}, + {55.6, 36.8, 5602}, + {63.5, 36.4, 5602}, + {60.2, 36.3, 5602}, + {59.1, 34.8, 5602}, + {53.1, 34.4, 5602}, + {54.1, 34.4, 5602}, + {63.4, 33.9, 5602}, + {55.8, 33.6, 5602}, + {62.2, 33.5, 5602}, + {59.7, 32.3, 5602}, + {59.1, 31.6, 5602}, + {49.9, 31.6, 5602}, + {63.1, 31.5, 5602}, + {61.5, 31.3, 5602}, + {53.1, 30.9, 5602}, + {49.2, 30.5, 5602}, + {50.7, 30.5, 5602}, + {56.6, 29.8, 5602}, + {50.1, 29.4, 5602}, + {57.7, 29, 5602}, + {61.6, 28.5, 5602}, + {58.6, 28.3, 5602}, + {55.3, 27.8, 5602}, + {51.5, 27.8, 5602}, + {60, 27.5, 5602}, + {56.5, 27.4, 5602}, + {59.2, 27.1, 5602}, + {49.9, 27.1, 5602}, + {62.1, 27.1, 5602}, + {55.2, 26.5, 5602}, + {61.2, 25.8, 5602}, + {52.4, 24.1, 5602}, + {60.9, 23.9, 5602}, + {57, 23.4, 5602}, + {61.3, 22, 5602}, + {61.4, 20.8, 5602}, + {59.4, 20.1, 5602}, + {56, 19.6, 5602}, + {56.3, 18.6, 5602}, + {58.5, 17.6, 5602}, + {57.9, 16.3, 5602}, + {58.2, 14.4, 5602}, + {58.8, 12.8, 5602}, + {59.8, 11.5, 5602}, + }, + [62247] = { + {54.6, 66, 5602}, + {51.1, 63.5, 5602}, + {56.6, 58.8, 5602}, + {45, 57.3, 5602}, + {59.2, 56.7, 5602}, + {49.3, 56.6, 5602}, + {53.9, 52.7, 5602}, + {48.6, 52.6, 5602}, + {45.1, 51.6, 5602}, + {56.4, 50.8, 5602}, + {63.8, 50.7, 5602}, + {60.6, 48.4, 5602}, + {64.4, 46.4, 5602}, + {57, 44.3, 5602}, + {58.6, 43.6, 5602}, + {54.5, 42.2, 5602}, + {57.9, 41.5, 5602}, + {48.6, 41.4, 5602}, + {59.7, 38.7, 5602}, + {53.2, 38.3, 5602}, + {61.9, 36.2, 5602}, + {50.6, 35.9, 5602}, + {49, 35.3, 5602}, + {62.9, 35.3, 5602}, + {60.3, 34.8, 5602}, + {57.9, 34.6, 5602}, + {60.6, 32.2, 5602}, + {62.9, 31.6, 5602}, + {58.3, 30, 5602}, + {61.4, 29.5, 5602}, + {60.2, 21.5, 5602}, + {57.9, 20.1, 5602}, + {58.8, 11, 5602}, + }, + [62248] = { + {46.6, 93.9, 5602}, + {48.2, 92.9, 5602}, + {45.8, 92.9, 5602}, + {46.9, 92.6, 5602}, + {50.1, 90.5, 5602}, + {48.3, 90.5, 5602}, + {49, 89.8, 5602}, + {57, 89.8, 5602}, + {46.2, 89.7, 5602}, + {58.1, 89.6, 5602}, + {47.1, 89.4, 5602}, + {58.6, 88.7, 5602}, + {51.6, 88.3, 5602}, + {52.8, 87.9, 5602}, + {57.5, 87.7, 5602}, + {46.2, 87.4, 5602}, + {49.9, 87.3, 5602}, + {58.9, 86.7, 5602}, + {53.2, 86.5, 5602}, + {49.4, 86.3, 5602}, + {48.5, 86.3, 5602}, + {50.1, 84.7, 5602}, + {54.5, 84.1, 5602}, + {61.4, 83, 5602}, + {56.7, 80.7, 5602}, + {55.9, 80.6, 5602}, + {60.8, 80.5, 5602}, + {63.4, 79.9, 5602}, + {62, 79, 5602}, + {56.9, 78.6, 5602}, + {56.1, 78, 5602}, + {54.2, 76.8, 5602}, + {59.3, 76.4, 5602}, + {55.3, 73.9, 5602}, + {58.6, 72.4, 5602}, + {54.8, 71.9, 5602}, + {51.4, 70.6, 5602}, + {50.9, 69.4, 5602}, + {49.7, 69.1, 5602}, + {51.9, 68.1, 5602}, + {54.1, 67, 5602}, + {51.4, 66.2, 5602}, + }, + [62256] = { + {58.4, 53.2, 5602}, + {53.1, 51.8, 5602}, + {54.6, 51.7, 5602}, + {57.6, 51.2, 5602}, + {51.8, 51.2, 5602}, + {58.5, 51.1, 5602}, + {59.4, 50.4, 5602}, + {55.6, 50.3, 5602}, + {53.1, 49.9, 5602}, + {56.6, 49.7, 5602}, + {55, 49.4, 5602}, + {57.9, 49.3, 5602}, + {51.8, 48.9, 5602}, + {55.3, 48, 5602}, + {44.6, 47.5, 5602}, + {53.7, 47.4, 5602}, + {52, 47.3, 5602}, + {47.8, 47.1, 5602}, + {45.4, 47, 5602}, + {44.7, 46.7, 5602}, + {49.7, 46.7, 5602}, + {48.6, 46.4, 5602}, + {52.9, 46.2, 5602}, + {54.7, 46.2, 5602}, + {55.9, 46, 5602}, + {46.8, 45.3, 5602}, + {45.9, 45.3, 5602}, + {46.2, 44.2, 5602}, + }, + [62257] = { + {46.2, 91.7, 5602}, + {48.1, 88.3, 5602}, + {49.3, 88.1, 5602}, + {58, 87.3, 5602}, + {47.1, 86.9, 5602}, + {56.9, 86.4, 5602}, + {51.4, 86.3, 5602}, + {50.3, 85.5, 5602}, + {48.6, 85.4, 5602}, + {58.5, 84.7, 5602}, + {55.4, 84.1, 5602}, + {62.2, 83.7, 5602}, + {53.4, 83.1, 5602}, + {57.7, 82.5, 5602}, + {56.5, 82.2, 5602}, + {61.1, 81.5, 5602}, + {63, 81.2, 5602}, + {53.5, 80.1, 5602}, + {58.3, 79.6, 5602}, + {54.1, 79.1, 5602}, + {59.2, 78.7, 5602}, + {63.1, 78.4, 5602}, + {54.4, 78.1, 5602}, + {57.2, 77.1, 5602}, + {60.1, 77, 5602}, + {55.8, 76.9, 5602}, + {61.7, 76.9, 5602}, + {54.9, 75.6, 5602}, + {58.5, 75.6, 5602}, + {59.3, 74.7, 5602}, + {57.2, 74.6, 5602}, + {52.9, 73.9, 5602}, + }, + [62258] = { + {59.4, 51.5, 5602}, + {54.7, 51.1, 5602}, + {52.4, 51.1, 5602}, + {57.3, 50.2, 5602}, + {58.7, 49.5, 5602}, + {52.4, 49.1, 5602}, + {54.4, 49.1, 5602}, + {56, 48.7, 5602}, + {49.9, 47.9, 5602}, + {52.9, 47.6, 5602}, + {55.3, 46, 5602}, + {46.1, 45.8, 5602}, + {47.6, 45.6, 5602}, + {45.2, 45.5, 5602}, + {53.7, 45.2, 5602}, + }, + [62259] = { + {50.7, 87.8, 5602}, + {54.1, 86.4, 5602}, + {47.1, 85.4, 5602}, + {53.1, 84.4, 5602}, + {54.6, 83.2, 5602}, + {58.7, 82.7, 5602}, + {53.6, 81.5, 5602}, + {57.4, 81.2, 5602}, + {60, 80.6, 5602}, + {58.1, 78.1, 5602}, + {62, 77.6, 5602}, + {55, 77.6, 5602}, + {56.4, 76.6, 5602}, + {59.6, 76.2, 5602}, + {61, 76.1, 5602}, + {57.2, 76, 5602}, + {57.8, 74.3, 5602}, + {53.9, 73.8, 5602}, + }, + [62260] = { + {63.4, 99.3, 36}, + {36, 99.3, 36}, + {45.6, 95.2, 36}, + {27.7, 93.3, 36}, + {53.4, 92.5, 36}, + {41.4, 61.3, 267}, + {33.2, 60.8, 267}, + {24.2, 54.9, 267}, + {39.6, 54.1, 267}, + {72.7, 45.9, 267}, + {43.4, 45.7, 267}, + {48.8, 44.9, 267}, + {64.3, 35.3, 267}, + {40.4, 35.3, 267}, + {48.7, 31.7, 267}, + {33.1, 30.1, 267}, + {55.6, 29.4, 267}, + {95.8, 22.5, 5179}, + {88.7, 22.1, 5179}, + {80.8, 16.9, 5179}, + {94.2, 16.2, 5179}, + {97.5, 8.9, 5179}, + }, + [62261] = { + {7.5, 52.2, 45}, + {68.9, 83.7, 267}, + {67.9, 77, 267}, + {71.4, 71.2, 267}, + {62.3, 67.3, 267}, + {72.8, 63.8, 267}, + {56.4, 58.3, 267}, + {22.5, 43.2, 267}, + {79.3, 6.6, 5179}, + }, + [62262] = { + {42.5, 94.8, 17}, + {44.8, 93.7, 17}, + {41.4, 92.8, 17}, + {28.5, 28.1, 400}, + {33.7, 25.6, 400}, + {26, 23.6, 400}, + }, + [62271] = { + {60.1, 14.4, 1519}, + {56.9, 14.2, 1519}, + {66.4, 12.7, 1519}, + {53.8, 12.3, 1519}, + {57.6, 11.8, 1519}, + {69.8, 11.5, 1519}, + {62, 11.2, 1519}, + {59.2, 8.5, 1519}, + {69.5, 7.2, 1519}, + {67.6, 6.4, 1519}, + {64.9, 6.2, 1519}, + {61.5, 2.8, 1519}, + {64.9, 2.2, 1519}, + {48.7, 85.1, 5581}, + {47, 85, 5581}, + {52.1, 84.2, 5581}, + {45.4, 84, 5581}, + {47.4, 83.7, 5581}, + {53.9, 83.5, 5581}, + {49.7, 83.4, 5581}, + {48.3, 82, 5581}, + {53.8, 81.2, 5581}, + {52.7, 80.8, 5581}, + {51.3, 80.7, 5581}, + {49.5, 78.9, 5581}, + {51.3, 78.5, 5581}, + }, + [62272] = { + {64.1, 11.4, 1519}, + {60.2, 11.4, 1519}, + {67.9, 9.7, 1519}, + {65.6, 8.4, 1519}, + {50.8, 8.1, 1519}, + {62.8, 7.1, 1519}, + {61.1, 4.9, 1519}, + {67.9, 4.2, 1519}, + {66.2, 3.7, 1519}, + {50.9, 83.5, 5581}, + {48.8, 83.5, 5581}, + {52.9, 82.6, 5581}, + {51.7, 81.9, 5581}, + {43.7, 81.7, 5581}, + {50.2, 81.2, 5581}, + {49.3, 80, 5581}, + {52.9, 79.6, 5581}, + {52, 79.4, 5581}, + }, + [62307] = { + {33.6, 58.8, 5581}, + }, + [62338] = { + {33.3, 79, 5561}, + {32.8, 76.6, 5561}, + {31.2, 76, 5561}, + {29.9, 73.2, 5561}, + {31.1, 71, 5561}, + {30, 69.8, 5561}, + {46.1, 49, 5561}, + {47.1, 48.2, 5561}, + {47.9, 46.8, 5561}, + {49.6, 46.6, 5561}, + {45.4, 46.6, 5561}, + {46.1, 46.2, 5561}, + {48.6, 45.7, 5561}, + {44.3, 45.5, 5561}, + {47.7, 44.9, 5561}, + {44.9, 44.8, 5561}, + {43.5, 44.5, 5561}, + {48.9, 44.5, 5561}, + {45.3, 43, 5561}, + {48.9, 42.7, 5561}, + {48, 42.6, 5561}, + {46.5, 42, 5561}, + }, + [62339] = { + {29.1, 60.9, 5561}, + {28.8, 58.7, 5561}, + {29.4, 57.4, 5561}, + {27.9, 53.3, 5561}, + {28.7, 52.4, 5561}, + {48.8, 46.7, 5561}, + {47.5, 43.5, 5561}, + {46.3, 43.2, 5561}, + }, + [62340] = { + {47.2, 91.2, 5561}, + {46.4, 90.6, 5561}, + {52.7, 90.5, 5561}, + {50.8, 90.2, 5561}, + {54.2, 89.7, 5561}, + {49, 87.5, 5561}, + {45.1, 87.5, 5561}, + {45.4, 87.3, 5561}, + {51.4, 87.2, 5561}, + {47.9, 87, 5561}, + {55.4, 86.3, 5561}, + {52.7, 86.2, 5561}, + {47.3, 86.1, 5561}, + {51.8, 85.4, 5561}, + {50.3, 84.8, 5561}, + {54.1, 84.7, 5561}, + }, + [62341] = { + {0.7, 1.8, 40}, + {46.1, 90.8, 5561}, + {56, 90.2, 5561}, + {45.5, 90, 5561}, + {49.4, 89.8, 5561}, + {48, 89.3, 5561}, + {46.4, 89, 5561}, + {47.8, 88.8, 5561}, + {45.7, 88.5, 5561}, + {47.3, 87.5, 5561}, + {49.2, 87.2, 5561}, + {56.7, 87.2, 5561}, + {50.9, 87.1, 5561}, + {46.9, 84.9, 5561}, + {48.3, 64, 5561}, + {50.2, 63.3, 5561}, + {52.1, 61.9, 5561}, + {52.9, 60.9, 5561}, + {53.6, 60.9, 5561}, + {54.1, 59.9, 5561}, + {55.2, 59.5, 5561}, + }, + [62342] = { + {31.8, 56, 5561}, + {31, 55.9, 5561}, + {31.3, 53.8, 5561}, + {32.4, 53, 5561}, + {29.9, 52.7, 5561}, + {31.3, 52.4, 5561}, + {29.4, 51.7, 5561}, + {29.5, 50.2, 5561}, + {30.8, 50, 5561}, + {33.6, 50, 5561}, + {32.3, 48.8, 5561}, + {30.4, 48.7, 5561}, + {33.8, 46.5, 5561}, + {35.9, 45.7, 5561}, + {34.1, 45.5, 5561}, + {34, 44.6, 5561}, + {37.9, 44.2, 5561}, + {35.3, 44, 5561}, + {36.3, 43.5, 5561}, + {37.1, 43, 5561}, + {35.6, 41.8, 5561}, + {34.8, 41.6, 5561}, + {34.1, 40.1, 5561}, + {34.3, 38, 5561}, + {33.5, 37, 5561}, + {33.8, 35.5, 5561}, + {34.2, 34.5, 5561}, + {33.6, 33.5, 5561}, + }, + [62343] = { + {30.6, 54.7, 5561}, + {32.1, 54.6, 5561}, + {30.5, 53.2, 5561}, + {31.8, 53.1, 5561}, + {33.4, 52.5, 5561}, + {33.8, 51.5, 5561}, + {31.4, 51.2, 5561}, + {30.2, 50.6, 5561}, + {33.2, 49, 5561}, + {31, 48.5, 5561}, + {31.8, 48.1, 5561}, + {33.9, 47.8, 5561}, + {34.6, 39.2, 5561}, + }, + [62344] = { + {31.2, 53.2, 5561}, + }, + [62402] = { + {53.6, 57.3, 5602}, + }, + [62403] = { + {52.1, 58.7, 5602}, + }, + [62404] = { + {52.1, 58.9, 5602}, + }, + [62409] = { + {54.8, 56.1, 5602}, + }, + [62436] = { + {59.3, 29.4, 5602}, + }, + [62437] = { + {60.2, 30.2, 5602}, + }, + [62462] = { + {70.9, 76.9, 5561}, + }, + [62469] = { + {28.5, 10.5, 5561}, + }, + [62628] = { + {25.3, 73.4, 440}, + }, + [62642] = { + {49.1, 67.2, 5602}, + }, + [62739] = { + {66.6, 85.7, 618}, + }, + [65002] = { + {63.8, 57.5, 440}, + {68.8, 55.7, 1941}, + }, + [65003] = { + {63.8, 57.7, 440}, + {68.4, 56.8, 1941}, + }, + [80102] = { + {98.6, 57.7, 14}, + {36.5, 61.6, 5536}, + }, + [80214] = { + {7.2, 22.5, 139}, + {47.4, 84.1, 5225}, + }, + [80252] = { + {75.7, 60.2, 1}, + {65.7, 60.1, 1}, + {79.4, 58.9, 1}, + {66.2, 57.7, 1}, + {66.6, 57.7, 1}, + {76.3, 57.4, 1}, + {67, 56.5, 1}, + {74.1, 55.9, 1}, + {71.9, 55.9, 1}, + {74.8, 55.3, 1}, + {78.9, 54.4, 1}, + {66.9, 53.9, 1}, + {70.4, 53.5, 1}, + {74.3, 52.5, 1}, + {65.5, 52, 1}, + {71.1, 51.7, 1}, + {68.2, 50.7, 1}, + {74.6, 50.5, 1}, + {77.7, 50.3, 1}, + {76.9, 50.3, 1}, + {75.8, 50.1, 1}, + {74.5, 48.4, 1}, + {75.4, 48, 1}, + {78.8, 46.5, 1}, + {56.3, 69.9, 5130}, + {62.4, 67.8, 5130}, + {33.7, 48.2, 5130}, + {0.7, 81.1, 5602}, + {0.2, 77, 5602}, + {0.1, 69.7, 5602}, + }, + [80254] = { + {21.6, 88.9, 12}, + {21.2, 87.3, 12}, + {20, 85.3, 12}, + {18.1, 79.2, 12}, + {18.1, 76.8, 12}, + {66.8, 51.6, 40}, + {65.6, 49.3, 40}, + {64.7, 47.6, 40}, + {63.8, 44.8, 40}, + {63.8, 42.2, 40}, + {63.4, 41.8, 40}, + {63.1, 40.7, 40}, + {64.1, 40.6, 40}, + {62.4, 37.6, 40}, + {62.5, 30.6, 40}, + {63, 29.5, 40}, + {63.5, 26.3, 40}, + {63.8, 25.6, 40}, + {63.3, 24, 40}, + {62.2, 22, 40}, + {60.3, 16, 40}, + {60.3, 13.6, 40}, + }, + [80256] = { + {44.1, 76.7, 10}, + {48.2, 76.3, 10}, + {47, 74.8, 10}, + {43.5, 73.6, 10}, + {42.3, 71, 10}, + {45.3, 70.5, 10}, + {45.8, 68.6, 10}, + {42.9, 68.6, 10}, + {47.8, 68.6, 10}, + {27.4, 68.4, 10}, + {40.1, 67.3, 10}, + {28.1, 66.5, 10}, + {40.1, 65.7, 10}, + {35.6, 65.4, 10}, + {31.6, 64.7, 10}, + {36.9, 64.5, 10}, + {38.9, 64, 10}, + {27.3, 63.2, 10}, + {33.2, 62, 10}, + {30.8, 60.5, 10}, + }, + [80258] = { + {39.1, 29.9, 85}, + {50.6, 28.8, 130}, + {49.2, 28.4, 130}, + {53.7, 27.1, 130}, + {50, 24.3, 130}, + {53.8, 24, 130}, + {51.9, 23.8, 130}, + {52.8, 23.8, 130}, + {49.2, 23.4, 130}, + {52.2, 23.1, 130}, + {51, 22.8, 130}, + }, + [80267] = { + {7.3, 22.9, 139}, + {47.4, 84.6, 5225}, + }, + [80466] = { + {62.6, 58.9, 85}, + {65.4, 58.1, 85}, + {59.9, 58, 85}, + {69.8, 57.8, 85}, + {58.9, 57.5, 85}, + {71.3, 57.4, 85}, + {67.6, 56.6, 85}, + {57.8, 56.2, 85}, + {71.6, 55.6, 85}, + {56.6, 55.2, 85}, + {71, 53.8, 85}, + {55.8, 53.4, 85}, + {55.5, 51, 85}, + {71, 50.5, 85}, + {54.8, 49.3, 85}, + {71.9, 47.9, 85}, + {55.6, 47.7, 85}, + {56.7, 47, 85}, + {56.2, 47, 85}, + {57.5, 46.3, 85}, + {58.1, 46, 85}, + {72.1, 45.8, 85}, + {61.4, 44.9, 85}, + {59.3, 44.6, 85}, + {61.3, 43, 85}, + {73.6, 42.4, 85}, + {63.4, 42.4, 85}, + {62.3, 42.3, 85}, + {62.9, 42.3, 85}, + {63.4, 41.5, 85}, + {63.3, 40.9, 85}, + {73.7, 39.3, 85}, + {64.2, 38.1, 85}, + {65, 36.9, 85}, + {63.3, 36.5, 85}, + {72.3, 35.5, 85}, + {74.6, 34.5, 85}, + {76.1, 32.8, 85}, + {77, 30.3, 85}, + }, + [80808] = { + {25.2, 13.9, 406}, + }, + [80815] = { + {36.4, 18.3, 406}, + {24.5, 15.3, 406}, + {25.4, 12.9, 406}, + {23.7, 12.7, 406}, + {22.6, 11.9, 406}, + {23.2, 10.8, 406}, + {26.1, 7.4, 406}, + }, + [80816] = { + {21.2, 56.8, 47}, + {27.5, 56.3, 47}, + {20.7, 51.8, 47}, + {29.5, 50.7, 47}, + }, + [81035] = { + {61.2, 52.7, 85}, + }, + [91246] = { + {58.9, 26.3, 14}, + }, + [91248] = { + {58.4, 25.1, 14}, + }, + [91403] = { + {48.1, 26.4, 45}, + }, + [91725] = { + {22, 69.2, 85}, + }, + [91819] = { + {30, 6, 408}, + {30.1, 4.2, 408}, + {49.1, 83.9, 409}, + {49.3, 82, 409}, + {45.8, 76.6, 409}, + {44.5, 76.3, 409}, + {46, 74.8, 409}, + {46.8, 74.4, 409}, + {40.1, 73.8, 409}, + {47.4, 73.6, 409}, + }, + [91825] = { + {59.6, 68.5, 409}, + {68.7, 65.3, 409}, + {67.9, 65.1, 409}, + {66.6, 64, 409}, + {68.1, 63.6, 409}, + {67.4, 63.4, 409}, + {64.6, 62.8, 409}, + {63.8, 62.7, 409}, + {68.5, 62.6, 409}, + {63.5, 61.5, 409}, + {62.5, 61.4, 409}, + {65.1, 61.3, 409}, + {67, 60.3, 409}, + {62.5, 60.2, 409}, + {65.4, 60.1, 409}, + {67.9, 59.6, 409}, + {65.9, 59.2, 409}, + {62.6, 58.5, 409}, + {49.7, 45.4, 409}, + {51.1, 44.9, 409}, + {53.1, 42, 409}, + }, + [91826] = { + {39.6, 56.6, 409}, + {40.9, 54.8, 409}, + {39.2, 53.4, 409}, + {40.4, 52.5, 409}, + }, + [91827] = { + {59.7, 71.8, 409}, + {58.5, 71.3, 409}, + {59, 69.9, 409}, + {57.3, 69.7, 409}, + {59.5, 68.4, 409}, + {58.6, 66.4, 409}, + {59.3, 65.2, 409}, + {57.7, 63.4, 409}, + {49.6, 56.6, 409}, + {46.6, 55.2, 409}, + {45.7, 53.5, 409}, + {44.7, 53.4, 409}, + {43.7, 51.2, 409}, + {48.1, 48.2, 409}, + {47.7, 46.4, 409}, + }, + [91828] = { + {41.4, 59.9, 409}, + {40.5, 58.6, 409}, + {37.1, 57.4, 409}, + {38.7, 57.3, 409}, + {42.6, 53.6, 409}, + {37.4, 51.7, 409}, + {38.5, 51.7, 409}, + {40.5, 50.8, 409}, + }, + [91829] = { + {62, 64.9, 409}, + {63.1, 64.8, 409}, + {63.7, 64.6, 409}, + {62.6, 64.3, 409}, + {62.1, 63.9, 409}, + {44.5, 49.8, 409}, + {46.1, 49.7, 409}, + {45.3, 49, 409}, + {46.4, 47.7, 409}, + {45.1, 47.6, 409}, + {46.9, 47.5, 409}, + }, + [91830] = { + {40.4, 63.7, 409}, + {39.3, 61.5, 409}, + {38.5, 59.3, 409}, + {41.2, 56.9, 409}, + {37.9, 55.1, 409}, + }, + [91831] = { + {29.6, 70.3, 409}, + {32.6, 69.1, 409}, + {32.4, 66.7, 409}, + {30.4, 66.5, 409}, + {34.8, 66.2, 409}, + {26.8, 64.9, 409}, + {29.2, 64.3, 409}, + {34, 63.9, 409}, + {32.3, 63.7, 409}, + {32.6, 61.8, 409}, + {33.7, 61.6, 409}, + {35.3, 61.4, 409}, + {32.3, 60, 409}, + {32.6, 58.7, 409}, + {31.6, 58.3, 409}, + {30.9, 57.3, 409}, + {31.4, 55.7, 409}, + {32.7, 55.5, 409}, + {27.1, 55.3, 409}, + {29.3, 55.2, 409}, + {30.3, 54.2, 409}, + {33.1, 53.8, 409}, + {31.7, 53, 409}, + {27.5, 52.4, 409}, + {29.4, 52, 409}, + {31, 51.6, 409}, + {32.2, 51.6, 409}, + {33.1, 50.2, 409}, + {26.7, 48.8, 409}, + {30.5, 43.6, 409}, + {28.6, 42.1, 409}, + {27.8, 38.7, 409}, + }, + [91832] = { + {62.1, 17.8, 409}, + {64.3, 17.6, 409}, + {59.4, 17.5, 409}, + {55.8, 17, 409}, + {52.9, 16.2, 409}, + {50.9, 15.6, 409}, + {54.3, 15.5, 409}, + {54.9, 13.8, 409}, + {50.1, 13.6, 409}, + {51.4, 13.2, 409}, + {52.7, 13, 409}, + {50.8, 11.1, 409}, + {52.8, 10, 409}, + }, + [91833] = { + {3.9, 97, 40}, + {4.9, 96.6, 40}, + {4.1, 95.8, 40}, + {4.6, 95.5, 40}, + {3.4, 95.4, 40}, + {3.4, 94.1, 40}, + {4.6, 93.6, 40}, + {5.2, 92.6, 40}, + {5.4, 92.2, 40}, + {2.9, 92, 40}, + {6.5, 91.5, 40}, + {7.1, 91.3, 40}, + {4.4, 91, 40}, + {70.8, 32.4, 409}, + {71.9, 31.9, 409}, + {71, 30.9, 409}, + {71.6, 30.5, 409}, + {70.2, 30.4, 409}, + {70.1, 28.8, 409}, + {71.7, 28.3, 409}, + {72.3, 27, 409}, + {72.6, 26.5, 409}, + {69.6, 26.3, 409}, + {73.9, 25.6, 409}, + {74.6, 25.4, 409}, + {71.4, 25, 409}, + }, + [91838] = { + {41.8, 89.3, 408}, + {44.2, 85.9, 408}, + {42.9, 84.8, 408}, + {45.9, 84, 408}, + {45.4, 82.3, 408}, + {42.6, 82.1, 408}, + {47.5, 80.3, 408}, + {40, 80.2, 408}, + {52.4, 80.2, 408}, + {44.7, 79.7, 408}, + {35.5, 79.3, 408}, + {41.5, 79.2, 408}, + {42.8, 78.9, 408}, + {46.6, 78.6, 408}, + {49.5, 78, 408}, + {48, 76.9, 408}, + }, + [91868] = { + {53.4, 49.4, 409}, + }, + [91956] = { + {60.1, 39.6, 409}, + }, + [91960] = { + {66.1, 63.4, 408}, + {67.7, 62.6, 408}, + {65.4, 62.3, 408}, + {66.8, 62.2, 408}, + {68.7, 62.1, 408}, + {68.4, 60.9, 408}, + {65.2, 60, 408}, + {65.8, 59.1, 408}, + {67.6, 57.8, 408}, + {63.3, 57, 408}, + {66.3, 56.5, 408}, + {65.6, 56.4, 408}, + {67.6, 56, 408}, + {61.2, 55.6, 408}, + {64.3, 55.5, 408}, + {62.5, 54.2, 408}, + {61.3, 54.2, 408}, + {66.4, 53.6, 408}, + {62.9, 53, 408}, + {64, 52.1, 408}, + {62.1, 52, 408}, + {66.3, 51.7, 408}, + {64.8, 51.5, 408}, + {60.8, 51.5, 408}, + {65.6, 51.3, 408}, + {62.2, 50.9, 408}, + {61.5, 50.3, 408}, + {66.2, 50.1, 408}, + {61.1, 48.2, 408}, + }, + [91961] = { + {52.9, 78.4, 408}, + {52.8, 77, 408}, + {51.1, 76.5, 408}, + {54.6, 75.5, 408}, + {52.6, 74.9, 408}, + {51.4, 74.8, 408}, + {54.5, 74.5, 408}, + {50.9, 74, 408}, + {51.6, 73.1, 408}, + {55, 71.9, 408}, + {53.1, 71.5, 408}, + {55.9, 71.2, 408}, + {54.3, 70.3, 408}, + {50.7, 69.8, 408}, + {53.2, 69.6, 408}, + {55.9, 69.6, 408}, + {51.6, 68.9, 408}, + {58, 68.6, 408}, + {56.1, 67.4, 408}, + {57.7, 66.3, 408}, + {57.4, 64.3, 408}, + {57.1, 64, 408}, + {43.1, 46.5, 408}, + {49.1, 45.5, 408}, + {43.3, 45, 408}, + {49.2, 44.2, 408}, + {44.2, 43.2, 408}, + {47.1, 41.8, 408}, + }, + [91962] = { + {72.6, 67.4, 408}, + {72, 65.6, 408}, + {72.6, 62.3, 408}, + {71.4, 60.8, 408}, + {69.8, 59.3, 408}, + {69.8, 56.9, 408}, + {69.7, 53.6, 408}, + {68.6, 53.4, 408}, + {67.7, 50.8, 408}, + {67.6, 48.4, 408}, + {64.5, 48.2, 408}, + {63.6, 46.3, 408}, + }, + [91966] = { + {56.7, 57.4, 408}, + {54.4, 57.1, 408}, + {59.6, 56.3, 408}, + {52.8, 56, 408}, + {55, 54.8, 408}, + {53.7, 54.8, 408}, + {55.4, 54.4, 408}, + {53.9, 53.8, 408}, + {54.1, 52.9, 408}, + {52.6, 52.7, 408}, + {55.1, 52, 408}, + {53.8, 51.9, 408}, + {53.9, 51, 408}, + {54.8, 50.2, 408}, + {54, 49.9, 408}, + {55.1, 49.6, 408}, + {54.6, 48.9, 408}, + {48, 45, 408}, + {45.5, 44.9, 408}, + {44.9, 44.9, 408}, + {47.1, 44.3, 408}, + {44.4, 44.2, 408}, + {46.3, 43.7, 408}, + {46.8, 43.3, 408}, + }, + [91977] = { + {13.9, 55.1, 85}, + }, + [91984] = { + {23.6, 68, 85}, + {22.8, 67.2, 85}, + {24.6, 66.6, 85}, + {16.5, 64.8, 85}, + {21.6, 64.6, 85}, + {23.4, 64.5, 85}, + {22.4, 64.5, 85}, + {23.2, 62.6, 85}, + {17.8, 60, 85}, + {17.9, 57.4, 85}, + {17.2, 55.7, 85}, + }, + [91985] = { + {23.7, 68.1, 85}, + {23.3, 62.7, 85}, + {17.2, 55.9, 85}, + }, + [91987] = { + {23.9, 79.6, 85}, + {22.9, 78.2, 85}, + {20.7, 74.9, 85}, + {20, 74.2, 85}, + {21.1, 73.8, 85}, + {21.2, 72.3, 85}, + {19.4, 72.1, 85}, + {20.1, 71.9, 85}, + {20.6, 71.2, 85}, + {21.5, 71.1, 85}, + {20.1, 69.7, 85}, + {19.6, 68.5, 85}, + {17.6, 64.4, 85}, + {16.4, 63.6, 85}, + {16.3, 63.4, 85}, + {19.5, 62.3, 85}, + {13.9, 62.3, 85}, + {18.1, 61.2, 85}, + {14.3, 61.1, 85}, + {18.4, 58.5, 85}, + {18.6, 56.7, 85}, + {17.7, 54.9, 85}, + {18.8, 54, 85}, + {20.1, 52.5, 85}, + {21.4, 51.8, 85}, + {19.1, 50.5, 85}, + {21.1, 50.4, 85}, + {21, 49, 85}, + {19.5, 49, 85}, + {20.5, 47.3, 85}, + }, + [91989] = { + {19.9, 76.4, 85}, + {19.3, 76.4, 85}, + {19.6, 75.9, 85}, + {20.5, 61.1, 85}, + {26, 60, 85}, + {21.5, 59.6, 85}, + {26.8, 59.5, 85}, + {27.8, 59, 85}, + {25, 58.7, 85}, + {20.4, 58.6, 85}, + {28.4, 58.3, 85}, + {25.9, 58.3, 85}, + {26.7, 58, 85}, + {28.1, 57.7, 85}, + {27.4, 57.6, 85}, + {24.8, 57.5, 85}, + {22.4, 57.3, 85}, + {26.9, 56.9, 85}, + {25.3, 56, 85}, + {25.3, 55.8, 85}, + {21.1, 55.8, 85}, + {22.2, 55.5, 85}, + {24.3, 55.2, 85}, + {23.7, 54.6, 85}, + {22.6, 54.4, 85}, + {23.7, 53.2, 85}, + {22.2, 49.1, 85}, + {20.5, 48.2, 85}, + }, + [91990] = { + {27, 58.4, 85}, + }, + [92115] = { + {47, 48.9, 5077}, + {44.6, 47.5, 5077}, + {47.1, 47.3, 5077}, + {40.1, 44.9, 5077}, + {46.6, 44.3, 5077}, + {38.2, 43.7, 5077}, + {47.8, 43.3, 5077}, + {39.9, 42.7, 5077}, + {36.2, 42.3, 5077}, + {38.1, 42.3, 5077}, + {35.5, 41.4, 5077}, + {36, 41.3, 5077}, + {47.9, 41.1, 5077}, + {46.6, 40.3, 5077}, + {36.2, 40.2, 5077}, + {47.5, 39.6, 5077}, + {47.7, 38.1, 5077}, + {35.9, 37.7, 5077}, + {47.4, 37.6, 5077}, + {34.7, 36.9, 5077}, + {46, 36.8, 5077}, + {44.7, 36.3, 5077}, + {41.7, 34.9, 5077}, + {41.7, 22.3, 5077}, + }, + [92146] = { + {0.9, 66.3, 33}, + {1.1, 64.9, 33}, + {0.8, 64.7, 33}, + {1, 63.9, 33}, + {1.1, 62.8, 33}, + {0.7, 61.7, 33}, + {1.1, 61.5, 33}, + {0.8, 61.1, 33}, + {0.6, 59.5, 33}, + {1, 59.4, 33}, + {0.9, 58.8, 33}, + {1.1, 57.5, 33}, + {0.9, 56.6, 33}, + {0.5, 56.5, 33}, + {0.8, 55.3, 33}, + {73.5, 71.4, 408}, + {73.8, 68.5, 408}, + {73.3, 68.1, 408}, + {72.1, 66.9, 408}, + {73.7, 66.3, 408}, + {72.2, 65.3, 408}, + {73.9, 64, 408}, + {72, 63.8, 408}, + {70.7, 62.7, 408}, + {73, 61.9, 408}, + {73.8, 61.4, 408}, + {73.4, 60.6, 408}, + {70.3, 58.6, 408}, + {72.4, 58.2, 408}, + {72.8, 57.3, 408}, + {73.6, 57, 408}, + {72.1, 56.3, 408}, + {73.5, 55.8, 408}, + {69.5, 55.7, 408}, + {71.2, 54.5, 408}, + {71.9, 53.6, 408}, + {74, 53.1, 408}, + {68.6, 52, 408}, + {70.2, 51.5, 408}, + {73.5, 51.2, 408}, + {72.6, 50.9, 408}, + {68.8, 50.2, 408}, + {67.2, 49.4, 408}, + {72.3, 49.4, 408}, + {68, 49.3, 408}, + {70.8, 49.3, 408}, + {66.7, 48.5, 408}, + {73.3, 48.4, 408}, + {64.1, 48, 408}, + {70.6, 47.6, 408}, + {68.3, 47.3, 408}, + {71.8, 47.2, 408}, + {65.3, 47.1, 408}, + {64.1, 46.7, 408}, + {68.1, 45.7, 408}, + {65.5, 44.6, 408}, + {64.4, 44.5, 408}, + {63.1, 44, 408}, + {63.4, 42.4, 408}, + {65.8, 41.9, 408}, + {64.6, 40.5, 408}, + }, + [92147] = { + {42.5, 85.6, 408}, + {40, 85.3, 408}, + {40.9, 85.2, 408}, + {42.1, 84.9, 408}, + {37.5, 84.8, 408}, + {43.8, 83.4, 408}, + {39.1, 83.4, 408}, + {40.9, 82.9, 408}, + {36, 82.2, 408}, + {44.6, 81.8, 408}, + {43.8, 81.6, 408}, + {39.5, 81.5, 408}, + {37.5, 80.6, 408}, + {41.5, 80.5, 408}, + {45.2, 79.4, 408}, + {34.6, 79, 408}, + {38.8, 78.8, 408}, + {35.3, 76.9, 408}, + {33.3, 75.6, 408}, + {29, 74.4, 408}, + {34.5, 73.9, 408}, + {32.6, 72.9, 408}, + {30.3, 72.6, 408}, + {33.8, 72.1, 408}, + {35.6, 70.4, 408}, + {34, 68.9, 408}, + {28.8, 66.8, 408}, + {34.5, 66.7, 408}, + {35.7, 66.2, 408}, + {32.8, 66.1, 408}, + {29, 64, 408}, + {33.7, 62.8, 408}, + {31.5, 60.9, 408}, + {35.4, 59.5, 408}, + {34.7, 57, 408}, + {36.2, 56.8, 408}, + }, + [92169] = { + {50.3, 40.7, 408}, + }, + [92177] = { + {69, 76.4, 408}, + }, + [92200] = { + {53.4, 10.9, 16}, + }, + [92217] = { + {45.1, 91.3, 16}, + }, +} + +MTH_DS_UnitCoordsMeta = { + generated = "2026-02-15", + beastIds = 627, + vendorIds = 193, + dropCreatureIds = 21, + needed = 841, + covered = 818, + missing = 23, +} diff --git a/data/ds-vendors-extra.lua b/data/ds-vendors-extra.lua new file mode 100644 index 0000000..4106390 --- /dev/null +++ b/data/ds-vendors-extra.lua @@ -0,0 +1,1930 @@ +-- MetaHunt Vendor Extras (generated from pfQuest/pfQuest-turtle) +-- Adds missing vendors referenced by imported item sources +if not MTH_DS then MTH_DS = {} end +if not MTH_DS_Vendors then MTH_DS_Vendors = {} end + +if not MTH_DS_Vendors[7] then + MTH_DS_Vendors[7] = { + ["name"] = '*', + ["lvl"] = '1', + ["fac"] = 'AH', + ["meta"] = { + ["vendor"] = 'AH', + }, + ["coords"] = { + }, + } +end + +if not MTH_DS_Vendors[3534] then + MTH_DS_Vendors[3534] = { + ["name"] = 'Wallace the Blind', + ["lvl"] = '19', + ["fac"] = 'AH', + ["meta"] = { + ["vendor"] = 'AH', + }, + ["coords"] = { + {46.5, 86.5, 130}, + {47.9, 16.2, 5179}, + }, + } +end + +if not MTH_DS_Vendors[5814] then + MTH_DS_Vendors[5814] = { + ["name"] = 'Innkeeper Thulbek', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["vendor"] = 'H', + }, + ["coords"] = { + {31.5, 29.8, 33}, + }, + } +end + +if not MTH_DS_Vendors[12782] then + MTH_DS_Vendors[12782] = { + ["name"] = "Captain O\'Neal", + ["lvl"] = '55', + ["fac"] = 'A', + ["meta"] = { + ["vendor"] = 'A', + }, + ["coords"] = { + }, + } +end + +if not MTH_DS_Vendors[14581] then + MTH_DS_Vendors[14581] = { + ["name"] = 'Sergeant Thunderhorn', + ["lvl"] = '55', + ["fac"] = 'AH', + ["meta"] = { + ["vendor"] = 'AH', + }, + ["coords"] = { + }, + } +end + +if not MTH_DS_Vendors[14753] then + MTH_DS_Vendors[14753] = { + ["name"] = 'Illiyana Moonblaze', + ["lvl"] = '55', + ["fac"] = 'A', + ["meta"] = { + ["vendor"] = 'A', + }, + ["coords"] = { + {61.5, 83.9, 331}, + }, + } +end + +if not MTH_DS_Vendors[14754] then + MTH_DS_Vendors[14754] = { + ["name"] = 'Kelm Hargunth', + ["lvl"] = '55', + ["fac"] = 'H', + ["meta"] = { + ["vendor"] = 'H', + }, + ["coords"] = { + {46.7, 8.4, 17}, + }, + } +end + +if not MTH_DS_Vendors[14846] then + MTH_DS_Vendors[14846] = { + ["name"] = 'Lhara', + ["lvl"] = '35', + ["fac"] = 'AH', + ["meta"] = { + ["vendor"] = 'AH', + }, + ["coords"] = { + {41.2, 69.9, 12}, + {36.5, 37.9, 215}, + }, + } +end + +if not MTH_DS_Vendors[80266] then + MTH_DS_Vendors[80266] = { + ["name"] = 'Soalara Dawnstar', + ["lvl"] = '11', + ["fac"] = 'A', + ["meta"] = { + ["vendor"] = 'A', + }, + ["coords"] = { + {60, 61.6, 2040}, + {66.4, 31.4, 5225}, + }, + } +end + +if not MTH_DS_Vendors[80807] then + MTH_DS_Vendors[80807] = { + ["name"] = 'Reolis Riptusk', + ["lvl"] = '11', + ["fac"] = 'H', + ["meta"] = { + ["vendor"] = 'H', + }, + ["coords"] = { + {25, 12.2, 406}, + }, + } +end + +if not MTH_DS_Vendors[80915] then + MTH_DS_Vendors[80915] = { + ["name"] = "Tan\'Pogo", + ["lvl"] = '11', + ["fac"] = 'H', + ["meta"] = { + ["vendor"] = 'H', + }, + ["coords"] = { + {25.1, 12, 406}, + }, + } +end + +if not MTH_DS_Vendors[80941] then + MTH_DS_Vendors[80941] = { + ["name"] = 'Earthcaller Jalyssa', + ["lvl"] = '11', + ["fac"] = 'AH', + ["meta"] = { + ["vendor"] = 'AH', + }, + ["coords"] = { + {36.5, 81.2, 405}, + }, + } +end + +if not MTH_DS_Vendors[80942] then + MTH_DS_Vendors[80942] = { + ["name"] = 'Deathcaller Aisha', + ["lvl"] = '11', + ["fac"] = 'AH', + ["meta"] = { + ["vendor"] = 'AH', + }, + ["coords"] = { + {73.9, 73.8, 405}, + }, + } +end + +if not MTH_DS_Vendors[80943] then + MTH_DS_Vendors[80943] = { + ["name"] = 'Dronormu', + ["lvl"] = '57', + ["fac"] = 'AH', + ["meta"] = { + ["vendor"] = 'AH', + }, + ["coords"] = { + {63.5, 57.7, 440}, + {67.2, 56.8, 1941}, + }, + } +end + + +-- Hunter service NPCs (stable master / hunter trainer / pet trainer) +-- Generated from cached Turtle search pages + pfQuest/pfQuest-turtle lookups +if not MTH_DS_Vendors[543] then + MTH_DS_Vendors[543] = { + ["name"] = 'Nalesette Wildbringer', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["pettrainer"] = 'A', + }, + ["coords"] = { + {62.2, 24.4, 361}, + }, + } +end + +if not MTH_DS_Vendors[895] then + MTH_DS_Vendors[895] = { + ["name"] = 'Thorgas Grimson', + ["lvl"] = '5', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {29.2, 67.5, 1}, + }, + } +end + +if not MTH_DS_Vendors[987] then + MTH_DS_Vendors[987] = { + ["name"] = 'Ogromm', + ["lvl"] = '50', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {47.3, 53.4, 8}, + }, + } +end + +if not MTH_DS_Vendors[1231] then + MTH_DS_Vendors[1231] = { + ["name"] = 'Grif Wildheart', + ["lvl"] = '8-12', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {45.8, 53, 1}, + }, + } +end + +if not MTH_DS_Vendors[1404] then + MTH_DS_Vendors[1404] = { + ["name"] = 'Kragg', + ["lvl"] = '40', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {31.2, 28.7, 33}, + }, + } +end + +if not MTH_DS_Vendors[2878] then + MTH_DS_Vendors[2878] = { + ["name"] = 'Peria Lamenur', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["pettrainer"] = 'A', + }, + ["coords"] = { + {46.7, 54, 1}, + {71, 38, 1}, + }, + } +end + +if not MTH_DS_Vendors[2879] then + MTH_DS_Vendors[2879] = { + ["name"] = 'Karrina Mekenda', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["pettrainer"] = 'A', + }, + ["coords"] = { + {67.3, 36.8, 1519}, + {52.6, 97.1, 5581}, + }, + } +end + +if not MTH_DS_Vendors[3038] then + MTH_DS_Vendors[3038] = { + ["name"] = 'Kary Thunderhorn', + ["lvl"] = '50', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {41.7, 34.8, 215}, + {58.5, 88.3, 1638}, + }, + } +end + +if not MTH_DS_Vendors[3039] then + MTH_DS_Vendors[3039] = { + ["name"] = 'Holt Thunderhorn', + ["lvl"] = '60', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {41.4, 35.1, 215}, + {57.3, 89.8, 1638}, + }, + } +end + +if not MTH_DS_Vendors[3040] then + MTH_DS_Vendors[3040] = { + ["name"] = 'Urek Thunderhorn', + ["lvl"] = '40', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {41.8, 34.5, 215}, + {59.1, 86.9, 1638}, + }, + } +end + +if not MTH_DS_Vendors[3061] then + MTH_DS_Vendors[3061] = { + ["name"] = 'Lanka Farshot', + ["lvl"] = '11', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {44.3, 75.7, 215}, + }, + } +end + +if not MTH_DS_Vendors[3065] then + MTH_DS_Vendors[3065] = { + ["name"] = 'Yaw Sharpmane', + ["lvl"] = '11', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {47.8, 55.7, 215}, + }, + } +end + +if not MTH_DS_Vendors[3154] then + MTH_DS_Vendors[3154] = { + ["name"] = 'Jen\'shan', + ["lvl"] = '8', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {42.8, 69.3, 14}, + }, + } +end + +if not MTH_DS_Vendors[3171] then + MTH_DS_Vendors[3171] = { + ["name"] = 'Thotar', + ["lvl"] = '16', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {51.8, 43.5, 14}, + }, + } +end + +if not MTH_DS_Vendors[3306] then + MTH_DS_Vendors[3306] = { + ["name"] = 'Keldas', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["pettrainer"] = 'A', + }, + ["coords"] = { + {56.8, 59.8, 141}, + }, + } +end + +if not MTH_DS_Vendors[3352] then + MTH_DS_Vendors[3352] = { + ["name"] = 'Ormak Grimshot', + ["lvl"] = '60', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {66, 18.5, 1637}, + }, + } +end + +if not MTH_DS_Vendors[3406] then + MTH_DS_Vendors[3406] = { + ["name"] = 'Xor\'juul', + ["lvl"] = '50', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {67.2, 20.2, 1637}, + }, + } +end + +if not MTH_DS_Vendors[3407] then + MTH_DS_Vendors[3407] = { + ["name"] = 'Sian\'dur', + ["lvl"] = '40', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {68, 17.8, 1637}, + }, + } +end + +if not MTH_DS_Vendors[3545] then + MTH_DS_Vendors[3545] = { + ["name"] = 'Claude Erksine', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["pettrainer"] = 'A', + }, + ["coords"] = { + {82.2, 62.8, 38}, + {40.6, 76.4, 5602}, + }, + } +end + +if not MTH_DS_Vendors[3596] then + MTH_DS_Vendors[3596] = { + ["name"] = 'Ayanna Everstride', + ["lvl"] = '10', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {58.7, 40.4, 141}, + }, + } +end + +if not MTH_DS_Vendors[3601] then + MTH_DS_Vendors[3601] = { + ["name"] = 'Dazalar', + ["lvl"] = '20', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {56.7, 59.5, 141}, + }, + } +end + +if not MTH_DS_Vendors[3620] then + MTH_DS_Vendors[3620] = { + ["name"] = 'Harruk', + ["lvl"] = '40', + ["fac"] = 'H', + ["meta"] = { + ["pettrainer"] = 'H', + }, + ["coords"] = { + {52, 43.5, 14}, + }, + } +end + +if not MTH_DS_Vendors[3622] then + MTH_DS_Vendors[3622] = { + ["name"] = 'Grokor', + ["lvl"] = '40', + ["fac"] = 'H', + ["meta"] = { + ["pettrainer"] = 'H', + }, + ["coords"] = { + {47.4, 52.9, 8}, + }, + } +end + +if not MTH_DS_Vendors[3624] then + MTH_DS_Vendors[3624] = { + ["name"] = 'Zudd', + ["lvl"] = '40', + ["fac"] = 'H', + ["meta"] = { + ["pettrainer"] = 'H', + }, + ["coords"] = { + {31.1, 28.9, 33}, + }, + } +end + +if not MTH_DS_Vendors[3688] then + MTH_DS_Vendors[3688] = { + ["name"] = 'Reban Freerunner', + ["lvl"] = '40', + ["fac"] = 'H', + ["meta"] = { + ["pettrainer"] = 'H', + }, + ["coords"] = { + {47.7, 55.7, 215}, + }, + } +end + +if not MTH_DS_Vendors[3698] then + MTH_DS_Vendors[3698] = { + ["name"] = 'Bolyun', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["pettrainer"] = 'A', + }, + ["coords"] = { + {18, 60, 331}, + {54.6, 26.8, 406}, + }, + } +end + +if not MTH_DS_Vendors[3963] then + MTH_DS_Vendors[3963] = { + ["name"] = 'Danlaar Nightstride', + ["lvl"] = '35', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {50.1, 67.9, 331}, + }, + } +end + +if not MTH_DS_Vendors[4138] then + MTH_DS_Vendors[4138] = { + ["name"] = 'Jeen\'ra Nightrunner', + ["lvl"] = '60', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {25.5, 48.1, 141}, + {39.7, 5.4, 1657}, + }, + } +end + +if not MTH_DS_Vendors[4146] then + MTH_DS_Vendors[4146] = { + ["name"] = 'Jocaste', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {25.6, 48.7, 141}, + {40.4, 8.5, 1657}, + }, + } +end + +if not MTH_DS_Vendors[4205] then + MTH_DS_Vendors[4205] = { + ["name"] = 'Dorion', + ["lvl"] = '50', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {26, 48.4, 141}, + {42.2, 7.3, 1657}, + }, + } +end + +if not MTH_DS_Vendors[4320] then + MTH_DS_Vendors[4320] = { + ["name"] = 'Caelyb', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["pettrainer"] = 'A', + }, + ["coords"] = { + {49.7, 67, 331}, + }, + } +end + +if not MTH_DS_Vendors[5115] then + MTH_DS_Vendors[5115] = { + ["name"] = 'Daera Brightspear', + ["lvl"] = '60', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {71, 89.8, 1537}, + }, + } +end + +if not MTH_DS_Vendors[5116] then + MTH_DS_Vendors[5116] = { + ["name"] = 'Olmin Burningbeard', + ["lvl"] = '50', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {70.9, 83.6, 1537}, + }, + } +end + +if not MTH_DS_Vendors[5117] then + MTH_DS_Vendors[5117] = { + ["name"] = 'Regnus Thundergranite', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {69.9, 82.9, 1537}, + }, + } +end + +if not MTH_DS_Vendors[5501] then + MTH_DS_Vendors[5501] = { + ["name"] = 'Kaerbrus', + ["lvl"] = '57', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {61.9, 23.6, 361}, + }, + } +end + +if not MTH_DS_Vendors[5515] then + MTH_DS_Vendors[5515] = { + ["name"] = 'Einris Brightspear', + ["lvl"] = '60', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {67.4, 36.3, 1519}, + {52.6, 96.8, 5581}, + }, + } +end + +if not MTH_DS_Vendors[5516] then + MTH_DS_Vendors[5516] = { + ["name"] = 'Ulfir Ironbeard', + ["lvl"] = '50', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {67.6, 35.8, 1519}, + {52.7, 96.6, 5581}, + }, + } +end + +if not MTH_DS_Vendors[5517] then + MTH_DS_Vendors[5517] = { + ["name"] = 'Thorfin Stoneshield', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {68, 36, 1519}, + {53, 96.7, 5581}, + }, + } +end + +if not MTH_DS_Vendors[6749] then + MTH_DS_Vendors[6749] = { + ["name"] = 'Erma', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {42.9, 65.9, 12}, + }, + } +end + +if not MTH_DS_Vendors[8308] then + MTH_DS_Vendors[8308] = { + ["name"] = 'Alenndaar Lapidaar', + ["lvl"] = '27', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {18, 59.8, 331}, + {54.6, 26.6, 406}, + }, + } +end + +if not MTH_DS_Vendors[9976] then + MTH_DS_Vendors[9976] = { + ["name"] = 'Tharlidun', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {73.9, 33.1, 45}, + }, + } +end + +if not MTH_DS_Vendors[9977] then + MTH_DS_Vendors[9977] = { + ["name"] = 'Sylista', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {42.6, 64.1, 1519}, + }, + } +end + +if not MTH_DS_Vendors[9978] then + MTH_DS_Vendors[9978] = { + ["name"] = 'Wesley', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {50.4, 58.8, 267}, + }, + } +end + +if not MTH_DS_Vendors[9979] then + MTH_DS_Vendors[9979] = { + ["name"] = 'Sarah Goode', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {43.5, 41.2, 130}, + }, + } +end + +if not MTH_DS_Vendors[9980] then + MTH_DS_Vendors[9980] = { + ["name"] = 'Shelby Stoneflint', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {47, 52.7, 1}, + }, + } +end + +if not MTH_DS_Vendors[9981] then + MTH_DS_Vendors[9981] = { + ["name"] = 'Sikwa', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {51.7, 29.7, 17}, + }, + } +end + +if not MTH_DS_Vendors[9982] then + MTH_DS_Vendors[9982] = { + ["name"] = 'Penny', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {26.8, 46.6, 44}, + }, + } +end + +if not MTH_DS_Vendors[9983] then + MTH_DS_Vendors[9983] = { + ["name"] = 'Kelsuwa', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {45.3, 58.7, 17}, + }, + } +end + +if not MTH_DS_Vendors[9984] then + MTH_DS_Vendors[9984] = { + ["name"] = 'Ulbrek Firehand', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {69.3, 83.6, 1537}, + }, + } +end + +if not MTH_DS_Vendors[9985] then + MTH_DS_Vendors[9985] = { + ["name"] = 'Laziphus', + ["lvl"] = '30', + ["fac"] = 'AH', + ["meta"] = { + ["stablemaster"] = 'AH', + }, + ["coords"] = { + {52.3, 28.4, 440}, + }, + } +end + +if not MTH_DS_Vendors[9986] then + MTH_DS_Vendors[9986] = { + ["name"] = 'Shyrka Wolfrunner', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {74.5, 43.3, 357}, + }, + } +end + +if not MTH_DS_Vendors[9987] then + MTH_DS_Vendors[9987] = { + ["name"] = 'Shoja\'my', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {52, 41.8, 14}, + }, + } +end + +if not MTH_DS_Vendors[9988] then + MTH_DS_Vendors[9988] = { + ["name"] = 'Xon\'cha', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {70.4, 15, 1637}, + }, + } +end + +if not MTH_DS_Vendors[9989] then + MTH_DS_Vendors[9989] = { + ["name"] = 'Lina Hearthstove', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {34.6, 48.1, 38}, + {16.3, 68.9, 5602}, + }, + } +end + +if not MTH_DS_Vendors[10045] then + MTH_DS_Vendors[10045] = { + ["name"] = 'Kirk Maxwell', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {52.9, 53.1, 40}, + }, + } +end + +if not MTH_DS_Vendors[10046] then + MTH_DS_Vendors[10046] = { + ["name"] = 'Bethaine Flinthammer', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {10.5, 59.7, 11}, + }, + } +end + +if not MTH_DS_Vendors[10047] then + MTH_DS_Vendors[10047] = { + ["name"] = 'Michael', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {66, 45.5, 15}, + }, + } +end + +if not MTH_DS_Vendors[10048] then + MTH_DS_Vendors[10048] = { + ["name"] = 'Gereck', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {50.6, 62.9, 406}, + }, + } +end + +if not MTH_DS_Vendors[10049] then + MTH_DS_Vendors[10049] = { + ["name"] = 'Hekkru', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {45.6, 55.2, 8}, + }, + } +end + +if not MTH_DS_Vendors[10050] then + MTH_DS_Vendors[10050] = { + ["name"] = 'Seikwa', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {46.8, 60.4, 215}, + }, + } +end + +if not MTH_DS_Vendors[10051] then + MTH_DS_Vendors[10051] = { + ["name"] = 'Seriadne', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {56.6, 59.6, 141}, + }, + } +end + +if not MTH_DS_Vendors[10052] then + MTH_DS_Vendors[10052] = { + ["name"] = 'Maluressian', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {36.5, 50.4, 331}, + }, + } +end + +if not MTH_DS_Vendors[10053] then + MTH_DS_Vendors[10053] = { + ["name"] = 'Anya Maulray', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {67.4, 37.6, 1497}, + }, + } +end + +if not MTH_DS_Vendors[10054] then + MTH_DS_Vendors[10054] = { + ["name"] = 'Bulrug', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {39, 29.1, 215}, + {45.1, 60.2, 1638}, + }, + } +end + +if not MTH_DS_Vendors[10055] then + MTH_DS_Vendors[10055] = { + ["name"] = 'Morganus', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {60, 52.2, 85}, + }, + } +end + +if not MTH_DS_Vendors[10056] then + MTH_DS_Vendors[10056] = { + ["name"] = 'Alassin', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {25.4, 49, 141}, + {39.3, 10, 1657}, + }, + } +end + +if not MTH_DS_Vendors[10057] then + MTH_DS_Vendors[10057] = { + ["name"] = 'Theodore Mont Claire', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {61.1, 81.4, 36}, + {62.3, 19.7, 267}, + }, + } +end + +if not MTH_DS_Vendors[10058] then + MTH_DS_Vendors[10058] = { + ["name"] = 'Greth', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {3.7, 47.6, 3}, + {82.8, 38.9, 51}, + }, + } +end + +if not MTH_DS_Vendors[10059] then + MTH_DS_Vendors[10059] = { + ["name"] = 'Antarius', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {31.5, 43.1, 357}, + }, + } +end + +if not MTH_DS_Vendors[10060] then + MTH_DS_Vendors[10060] = { + ["name"] = 'Grimestack', + ["lvl"] = '46', + ["fac"] = 'AH', + ["meta"] = { + ["stablemaster"] = 'AH', + }, + ["coords"] = { + {27.3, 77.2, 33}, + }, + } +end + +if not MTH_DS_Vendors[10061] then + MTH_DS_Vendors[10061] = { + ["name"] = 'Killium Bouldertoe', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {14.4, 45.2, 47}, + {99.9, 4.4, 267}, + }, + } +end + +if not MTH_DS_Vendors[10062] then + MTH_DS_Vendors[10062] = { + ["name"] = 'Steven Black', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {74, 46.1, 10}, + }, + } +end + +if not MTH_DS_Vendors[10063] then + MTH_DS_Vendors[10063] = { + ["name"] = 'Reggifuz', + ["lvl"] = '35', + ["fac"] = 'AH', + ["meta"] = { + ["stablemaster"] = 'AH', + }, + ["coords"] = { + {62.2, 39.2, 17}, + }, + } +end + +if not MTH_DS_Vendors[10085] then + MTH_DS_Vendors[10085] = { + ["name"] = 'Jaelysia', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {37.4, 44.3, 148}, + }, + } +end + +if not MTH_DS_Vendors[10086] then + MTH_DS_Vendors[10086] = { + ["name"] = 'Hesuwa Thunderhorn', + ["lvl"] = '40', + ["fac"] = 'H', + ["meta"] = { + ["pettrainer"] = 'H', + }, + ["coords"] = { + {40.8, 33.9, 215}, + {54.1, 84, 1638}, + }, + } +end + +if not MTH_DS_Vendors[10088] then + MTH_DS_Vendors[10088] = { + ["name"] = 'Xao\'tsu', + ["lvl"] = '40', + ["fac"] = 'H', + ["meta"] = { + ["pettrainer"] = 'H', + }, + ["coords"] = { + {66.3, 14.8, 1637}, + }, + } +end + +if not MTH_DS_Vendors[10089] then + MTH_DS_Vendors[10089] = { + ["name"] = 'Silvaria', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["pettrainer"] = 'A', + }, + ["coords"] = { + {26, 48.8, 141}, + {42.5, 9.2, 1657}, + }, + } +end + +if not MTH_DS_Vendors[10090] then + MTH_DS_Vendors[10090] = { + ["name"] = 'Belia Thundergranite', + ["lvl"] = '40', + ["fac"] = 'A', + ["meta"] = { + ["pettrainer"] = 'A', + }, + ["coords"] = { + {70.9, 85.8, 1537}, + }, + } +end + +if not MTH_DS_Vendors[10930] then + MTH_DS_Vendors[10930] = { + ["name"] = 'Dargh Trueaim', + ["lvl"] = '25', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {35, 47.7, 38}, + {16.5, 68.7, 5602}, + }, + } +end + +if not MTH_DS_Vendors[11069] then + MTH_DS_Vendors[11069] = { + ["name"] = 'Jenova Stoneshield', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {67.2, 37.7, 1519}, + {52.6, 97.6, 5581}, + }, + } +end + +if not MTH_DS_Vendors[11104] then + MTH_DS_Vendors[11104] = { + ["name"] = 'Shelgrayn', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {65.6, 7.8, 405}, + {44.3, 80.4, 406}, + }, + } +end + +if not MTH_DS_Vendors[11105] then + MTH_DS_Vendors[11105] = { + ["name"] = 'Aboda', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {24.9, 68.7, 405}, + {11.4, 92.8, 2100}, + }, + } +end + +if not MTH_DS_Vendors[11117] then + MTH_DS_Vendors[11117] = { + ["name"] = 'Awenasa', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {45.8, 51.1, 400}, + }, + } +end + +if not MTH_DS_Vendors[11119] then + MTH_DS_Vendors[11119] = { + ["name"] = 'Azzleby', + ["lvl"] = '30', + ["fac"] = 'AH', + ["meta"] = { + ["stablemaster"] = 'AH', + }, + ["coords"] = { + {60.4, 37.9, 618}, + }, + } +end + +if not MTH_DS_Vendors[13616] then + MTH_DS_Vendors[13616] = { + ["name"] = 'Frostwolf Stable Master', + ["lvl"] = '60', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {57.1, 82.5, 2597}, + }, + } +end + +if not MTH_DS_Vendors[13617] then + MTH_DS_Vendors[13617] = { + ["name"] = 'Stormpike Stable Master', + ["lvl"] = '60', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {42.6, 16.8, 2597}, + }, + } +end + +if not MTH_DS_Vendors[14741] then + MTH_DS_Vendors[14741] = { + ["name"] = 'Huntsman Markhor', + ["lvl"] = '45', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {79.2, 79.5, 47}, + }, + } +end + +if not MTH_DS_Vendors[15131] then + MTH_DS_Vendors[15131] = { + ["name"] = 'Qeeju', + ["lvl"] = '45', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {73.4, 61, 331}, + }, + } +end + +if not MTH_DS_Vendors[15722] then + MTH_DS_Vendors[15722] = { + ["name"] = 'Squire Leoren Mal\'derath', + ["lvl"] = '60', + ["fac"] = 'AH', + ["meta"] = { + ["stablemaster"] = 'AH', + }, + ["coords"] = { + {49.3, 36.4, 1377}, + }, + } +end + +if not MTH_DS_Vendors[16094] then + MTH_DS_Vendors[16094] = { + ["name"] = 'Durik', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {31.9, 29.5, 33}, + }, + } +end + +if not MTH_DS_Vendors[16156] then + MTH_DS_Vendors[16156] = { + ["name"] = 'Dark Touched Warrior', + ["lvl"] = '61', + ["fac"] = 'AH', + ["meta"] = { + ["stablemaster"] = 'AH', + }, + ["coords"] = { + {38.7, 64.4, 3456}, + {38.5, 63.6, 3456}, + {37.9, 60.5, 3456}, + {41.9, 59.5, 3456}, + {47.2, 58.3, 3456}, + }, + } +end + +if not MTH_DS_Vendors[60483] then + MTH_DS_Vendors[60483] = { + ["name"] = 'Morpheus Ribcage', + ["lvl"] = '5', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {31, 64.3, 85}, + }, + } +end + +if not MTH_DS_Vendors[60484] then + MTH_DS_Vendors[60484] = { + ["name"] = 'Liott Maneskin', + ["lvl"] = '15', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {60.1, 51.5, 85}, + }, + } +end + +if not MTH_DS_Vendors[60485] then + MTH_DS_Vendors[60485] = { + ["name"] = 'Noel Bearfinger', + ["lvl"] = '60', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {61.4, 24.8, 1497}, + }, + } +end + +if not MTH_DS_Vendors[60486] then + MTH_DS_Vendors[60486] = { + ["name"] = 'Vaya Spidersbite', + ["lvl"] = '60', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {58.7, 30.9, 1497}, + }, + } +end + +if not MTH_DS_Vendors[60487] then + MTH_DS_Vendors[60487] = { + ["name"] = 'Valdos Madhound', + ["lvl"] = '60', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {62, 26.2, 1497}, + }, + } +end + +if not MTH_DS_Vendors[60488] then + MTH_DS_Vendors[60488] = { + ["name"] = 'Diane Willowfield', + ["lvl"] = '40', + ["fac"] = 'H', + ["meta"] = { + ["pettrainer"] = 'H', + }, + ["coords"] = { + {54.6, 37.2, 1497}, + }, + } +end + +if not MTH_DS_Vendors[60768] then + MTH_DS_Vendors[60768] = { + ["name"] = 'Herekk', + ["lvl"] = '48', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {93, 23.9, 46}, + }, + } +end + +if not MTH_DS_Vendors[61266] then + MTH_DS_Vendors[61266] = { + ["name"] = 'Alexandra', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {57.8, 69.5, 5179}, + }, + } +end + +if not MTH_DS_Vendors[61624] then + MTH_DS_Vendors[61624] = { + ["name"] = 'AJ Springberry', + ["lvl"] = '12', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {34.1, 72.1, 1519}, + }, + } +end + +if not MTH_DS_Vendors[61625] then + MTH_DS_Vendors[61625] = { + ["name"] = 'Daisy Windhelm', + ["lvl"] = '20', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {40.9, 65.9, 12}, + }, + } +end + +if not MTH_DS_Vendors[61626] then + MTH_DS_Vendors[61626] = { + ["name"] = 'Diane Cloverfield', + ["lvl"] = '35', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {34.1, 56.5, 5179}, + }, + } +end + +if not MTH_DS_Vendors[61627] then + MTH_DS_Vendors[61627] = { + ["name"] = 'Old Jonah', + ["lvl"] = '5', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {50.8, 40, 12}, + }, + } +end + +if not MTH_DS_Vendors[61628] then + MTH_DS_Vendors[61628] = { + ["name"] = 'Willhelm Rockdust', + ["lvl"] = '60', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {34.1, 71.2, 1519}, + }, + } +end + +if not MTH_DS_Vendors[61629] then + MTH_DS_Vendors[61629] = { + ["name"] = 'Marven', + ["lvl"] = '35', + ["fac"] = 'A', + ["meta"] = { + ["pettrainer"] = 'A', + }, + ["coords"] = { + {33.5, 71.8, 1519}, + }, + } +end + +if not MTH_DS_Vendors[61640] then + MTH_DS_Vendors[61640] = { + ["name"] = 'Marksman Rembrandt Olar', + ["lvl"] = '48', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + }, + } +end + +if not MTH_DS_Vendors[61722] then + MTH_DS_Vendors[61722] = { + ["name"] = 'Sazzlix', + ["lvl"] = '35', + ["fac"] = 'AH', + ["meta"] = { + ["stablemaster"] = 'AH', + }, + ["coords"] = { + {48.5, 65.7, 5536}, + }, + } +end + +if not MTH_DS_Vendors[62098] then + MTH_DS_Vendors[62098] = { + ["name"] = 'Gozzlek', + ["lvl"] = '51', + ["fac"] = 'AH', + ["meta"] = { + ["stablemaster"] = 'AH', + }, + ["coords"] = { + {42.4, 77.3, 5121}, + }, + } +end + +if not MTH_DS_Vendors[62099] then + MTH_DS_Vendors[62099] = { + ["name"] = 'Guzhek', + ["lvl"] = '35', + ["fac"] = 'AH', + ["meta"] = { + ["stablemaster"] = 'AH', + }, + ["coords"] = { + {37, 51.7, 406}, + }, + } +end + +if not MTH_DS_Vendors[62161] then + MTH_DS_Vendors[62161] = { + ["name"] = 'Leander Hering', + ["lvl"] = '45', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {45.7, 58.4, 5581}, + }, + } +end + +if not MTH_DS_Vendors[62401] then + MTH_DS_Vendors[62401] = { + ["name"] = 'Farwyn Barleynight', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {52.9, 57.7, 5602}, + }, + } +end + +if not MTH_DS_Vendors[80105] then + MTH_DS_Vendors[80105] = { + ["name"] = 'Mayten Boomrifle', + ["lvl"] = '8', + ["fac"] = 'AH', + ["meta"] = { + ["huntertrainer"] = 'AH', + }, + ["coords"] = { + {49.3, 63.1, 5536}, + }, + } +end + +if not MTH_DS_Vendors[80219] then + MTH_DS_Vendors[80219] = { + ["name"] = 'Ranger Rubinah Sunsworn', + ["lvl"] = '8', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {6.6, 22.7, 139}, + {46.5, 84.4, 5225}, + }, + } +end + +if not MTH_DS_Vendors[80245] then + MTH_DS_Vendors[80245] = { + ["name"] = 'Damilara Sunsorrow', + ["lvl"] = '60', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {40.7, 68.5, 2040}, + {57.2, 34.6, 5225}, + }, + } +end + +if not MTH_DS_Vendors[80457] then + MTH_DS_Vendors[80457] = { + ["name"] = 'Andrelas Thas\'danil', + ["lvl"] = '30', + ["fac"] = 'A', + ["meta"] = { + ["stablemaster"] = 'A', + }, + ["coords"] = { + {40.7, 68.3, 2040}, + {57.2, 34.5, 5225}, + }, + } +end + +if not MTH_DS_Vendors[80458] then + MTH_DS_Vendors[80458] = { + ["name"] = 'Ranger Canarah Kim\'Alah', + ["lvl"] = '55', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {65.2, 45.4, 1519}, + }, + } +end + +if not MTH_DS_Vendors[80810] then + MTH_DS_Vendors[80810] = { + ["name"] = 'Rinja Scenttusk', + ["lvl"] = '30', + ["fac"] = 'H', + ["meta"] = { + ["stablemaster"] = 'H', + }, + ["coords"] = { + {24.4, 12.8, 406}, + }, + } +end + +if not MTH_DS_Vendors[80855] then + MTH_DS_Vendors[80855] = { + ["name"] = 'Clover Spinpistol', + ["lvl"] = '8', + ["fac"] = 'A', + ["meta"] = { + ["huntertrainer"] = 'A', + }, + ["coords"] = { + {46.7, 54, 1}, + }, + } +end + +if not MTH_DS_Vendors[80856] then + MTH_DS_Vendors[80856] = { + ["name"] = 'Twinkie Boomstick', + ["lvl"] = '8', + ["fac"] = 'AH', + ["meta"] = { + ["huntertrainer"] = 'AH', + }, + ["coords"] = { + {42.4, 72.9, 15}, + }, + } +end + +if not MTH_DS_Vendors[80903] then + MTH_DS_Vendors[80903] = { + ["name"] = 'Viz Fizbeast', + ["lvl"] = '16', + ["fac"] = 'AH', + ["meta"] = { + ["huntertrainer"] = 'AH', + }, + ["coords"] = { + }, + } +end + +if not MTH_DS_Vendors[81050] then + MTH_DS_Vendors[81050] = { + ["name"] = 'Dark Ranger Lanissa', + ["lvl"] = '24', + ["fac"] = 'H', + ["meta"] = { + ["huntertrainer"] = 'H', + }, + ["coords"] = { + {60.7, 53.5, 85}, + }, + } +end + diff --git a/data/ds-vendors.lua b/data/ds-vendors.lua new file mode 100644 index 0000000..f84847e --- /dev/null +++ b/data/ds-vendors.lua @@ -0,0 +1,2229 @@ +-- MetaHunt Vendor Database +-- Vendors selling ammo and ranged weapons +-- Total: 193 vendors + +if not MTH_DS then MTH_DS = {} end + +MTH_DS_Vendors = { + [150] = { + ["name"] = "[UNUSED] Brother Milius", + ["lvl"] = "5", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + }, + }, + [151] = { + ["name"] = "Brog Hamfist", + ["lvl"] = "10", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {44, 65.9, 12}, + }, + }, + [152] = { + ["name"] = "Brother Danil", + ["lvl"] = "5", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {47.5, 41.6, 12}, + }, + }, + [227] = { + ["name"] = "Mabel Solaj", + ["lvl"] = "26", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {73.6, 45.3, 10}, + }, + }, + [228] = { + ["name"] = "Avette Fellwood", + ["lvl"] = "24", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {73, 44.4, 10}, + }, + }, + [491] = { + ["name"] = "Quartermaster Lewis", + ["lvl"] = "20", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {57, 47.2, 40}, + }, + }, + [734] = { + ["name"] = "Corporal Bluth", + ["lvl"] = "40", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {38, 3, 33}, + }, + }, + [789] = { + ["name"] = "Kimberly Hiett", + ["lvl"] = "20", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {27.1, 45.5, 44}, + }, + }, + [791] = { + ["name"] = "Lindsay Ashlock", + ["lvl"] = "20", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {28.8, 47.3, 44}, + }, + }, + [829] = { + ["name"] = "Adlin Pridedrift", + ["lvl"] = "5", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {30.1, 71.6, 1}, + }, + }, + [904] = { + ["name"] = "[UNUSED] Regna Khurn", + ["lvl"] = "5", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + }, + }, + [1149] = { + ["name"] = "Uthok", + ["lvl"] = "40", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {31.6, 28, 33}, + }, + }, + [1198] = { + ["name"] = "Rallic Finn", + ["lvl"] = "18", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {83.3, 66.1, 12}, + }, + }, + [1243] = { + ["name"] = "Hegnar Rumbleshot", + ["lvl"] = "8-12", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {40.7, 65.1, 1}, + }, + }, + [1250] = { + ["name"] = "Drake Lindgren", + ["lvl"] = "10", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {83.3, 66.7, 12}, + }, + }, + [1285] = { + ["name"] = "Thurman Mullby", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {64.8, 72.2, 1519}, + }, + }, + [1287] = { + ["name"] = "Marda Weller", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {64.1, 68.4, 1519}, + }, + }, + [1297] = { + ["name"] = "Lina Stover", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {58.7, 68.7, 1519}, + }, + }, + [1298] = { + ["name"] = "Frederick Stover", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {58.3, 69, 1519}, + }, + }, + [1448] = { + ["name"] = "Neal Allen", + ["lvl"] = "20", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {10.7, 56.8, 11}, + }, + }, + [1452] = { + ["name"] = "Gruham Rumdnul", + ["lvl"] = "25", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {12.1, 58, 11}, + }, + }, + [1455] = { + ["name"] = "[UNUSED] Grummar Thunk", + ["lvl"] = "1", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + }, + }, + [1459] = { + ["name"] = "Naela Trance", + ["lvl"] = "25", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {11.3, 58.4, 11}, + }, + }, + [1461] = { + ["name"] = "Murndan Derth", + ["lvl"] = "25", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {11.3, 59.6, 11}, + }, + }, + [1462] = { + ["name"] = "Edwina Monzor", + ["lvl"] = "25", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {11.1, 58.3, 11}, + }, + }, + [1469] = { + ["name"] = "Vrok Blunderblast", + ["lvl"] = "15", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {35.8, 43.5, 38}, + {16.9, 66.5, 5602}, + }, + }, + [1668] = { + ["name"] = "William MacGregor", + ["lvl"] = "15", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {57.7, 53.9, 40}, + }, + }, + [1682] = { + ["name"] = "Yanni Stoutheart", + ["lvl"] = "15", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {34.8, 48.6, 38}, + {16.3, 69.1, 5602}, + }, + }, + [1685] = { + ["name"] = "Xandar Goodbeard", + ["lvl"] = "15", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {82.3, 63.3, 38}, + {40.7, 76.7, 5602}, + }, + }, + [1686] = { + ["name"] = "Irene Sureshot", + ["lvl"] = "15", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {36.1, 45.1, 38}, + {17, 67.3, 5602}, + }, + }, + [1687] = { + ["name"] = "Cliff Hadin", + ["lvl"] = "15", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {37.3, 46.8, 38}, + {17.6, 68.2, 5602}, + }, + }, + [1691] = { + ["name"] = "Kreg Bilmn", + ["lvl"] = "10", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {47.2, 52.4, 1}, + }, + }, + [1859] = { + ["name"] = "[UNUSED] Nyein Longwind", + ["lvl"] = "4-7", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + }, + }, + [2084] = { + ["name"] = "Natheril Raincaller", + ["lvl"] = "57", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {51.5, 30.8, 618}, + }, + }, + [2115] = { + ["name"] = "Joshua Kien", + ["lvl"] = "5", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {32.3, 65.4, 85}, + }, + }, + [2134] = { + ["name"] = "Mrs. Winters", + ["lvl"] = "10", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {61.2, 52.6, 85}, + }, + }, + [2140] = { + ["name"] = "Edwin Harly", + ["lvl"] = "18-20", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {44, 39.9, 130}, + }, + }, + [2286] = { + ["name"] = "Bow Guy", + ["lvl"] = "1", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + }, + }, + [2401] = { + ["name"] = "Kayren Soothallow", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {61.4, 81.7, 36}, + {62.6, 19.9, 267}, + }, + }, + [2685] = { + ["name"] = "Mazk Snipeshot", + ["lvl"] = "36", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {28.5, 75.1, 33}, + }, + }, + [2803] = { + ["name"] = "Malygen", + ["lvl"] = "55", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {62.3, 25.6, 361}, + }, + }, + [2806] = { + ["name"] = "Bale", + ["lvl"] = "55", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {50.4, 74.2, 148}, + {34.8, 53.2, 361}, + }, + }, + [2808] = { + ["name"] = "Vikki Lonsav", + ["lvl"] = "36", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {46.4, 47.6, 45}, + }, + }, + [2820] = { + ["name"] = "Graud", + ["lvl"] = "31", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {74.1, 32.4, 45}, + }, + }, + [2839] = { + ["name"] = "Haren Kanmae", + ["lvl"] = "41", + ["fac"] = "Unknown", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {28.3, 74.6, 33}, + }, + }, + [2908] = { + ["name"] = "Grawl", + ["lvl"] = "44", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {3.1, 45.9, 3}, + {82.2, 37.1, 51}, + }, + }, + [2997] = { + ["name"] = "Jyn Stonehoof", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {38.2, 29.5, 215}, + {41.4, 62.4, 1638}, + }, + }, + [3015] = { + ["name"] = "Kuna Thunderhorn", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {39.4, 26.1, 215}, + {47, 45.7, 1638}, + }, + }, + [3018] = { + ["name"] = "Hogor Thunderhoof", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {41.1, 28.4, 215}, + {55.5, 57.1, 1638}, + }, + }, + [3053] = { + ["name"] = "Synge", + ["lvl"] = "30", + ["fac"] = "AH", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {80.4, 77, 400}, + }, + }, + [3072] = { + ["name"] = "Kawnie Softbreeze", + ["lvl"] = "8", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {45.3, 76.5, 215}, + }, + }, + [3076] = { + ["name"] = "Moorat Longstride", + ["lvl"] = "12", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {45.9, 57.7, 215}, + }, + }, + [3078] = { + ["name"] = "Kennah Hawkseye", + ["lvl"] = "10", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {45.5, 58.5, 215}, + }, + }, + [3088] = { + ["name"] = "Henry Chapal", + ["lvl"] = "25", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {23.8, 41.3, 44}, + }, + }, + [3158] = { + ["name"] = "Duokna", + ["lvl"] = "10", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {42.6, 67.3, 14}, + }, + }, + [3164] = { + ["name"] = "Jark", + ["lvl"] = "14", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {54.4, 42.2, 14}, + }, + }, + [3165] = { + ["name"] = "Ghrawt", + ["lvl"] = "13", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {53, 41, 14}, + }, + }, + [3186] = { + ["name"] = "K'waii", + ["lvl"] = "11", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {56.3, 73.4, 14}, + }, + }, + [3313] = { + ["name"] = "Trak'gen", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {45.3, 8.2, 14}, + {48.1, 80.5, 1637}, + }, + }, + [3322] = { + ["name"] = "Kaja", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {52.1, 62.1, 1637}, + }, + }, + [3350] = { + ["name"] = "Asoran", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {46.1, 40.9, 1637}, + }, + }, + [3409] = { + ["name"] = "Zendo'jian", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {81.2, 18.7, 1637}, + }, + }, + [3410] = { + ["name"] = "Jin'sora", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {78.1, 38.5, 1637}, + }, + }, + [3481] = { + ["name"] = "Barg", + ["lvl"] = "14", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {51.7, 29.9, 17}, + }, + }, + [3488] = { + ["name"] = "Uthrok", + ["lvl"] = "16", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {51.1, 29.1, 17}, + }, + }, + [3498] = { + ["name"] = "Jazzik", + ["lvl"] = "22", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {61.9, 38.8, 17}, + }, + }, + [3541] = { + ["name"] = "Sarah Raycroft", + ["lvl"] = "40", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {49.1, 55.1, 267}, + }, + }, + [3587] = { + ["name"] = "Lyrai", + ["lvl"] = "9", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {59.5, 40.9, 141}, + }, + }, + [3589] = { + ["name"] = "Keina", + ["lvl"] = "10", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {59.3, 41.1, 141}, + }, + }, + [3608] = { + ["name"] = "Aldia", + ["lvl"] = "17", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {55.5, 57.1, 141}, + }, + }, + [3610] = { + ["name"] = "Jeena Featherbow", + ["lvl"] = "19", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {55.9, 59.2, 141}, + }, + }, + [3625] = { + ["name"] = "Rarck", + ["lvl"] = "55", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {49.9, 82.1, 2597}, + }, + }, + [3951] = { + ["name"] = "Bhaldaran Ravenshade", + ["lvl"] = "34", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {50.3, 67.3, 331}, + }, + }, + [3962] = { + ["name"] = "Haljan Oakheart", + ["lvl"] = "26", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {34.8, 50.9, 331}, + }, + }, + [4082] = { + ["name"] = "Grawnal", + ["lvl"] = "32", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {48.9, 60.7, 406}, + }, + }, + [4084] = { + ["name"] = "Chylina", + ["lvl"] = "24", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {40.5, 18.1, 406}, + }, + }, + [4170] = { + ["name"] = "Ellandrieth", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {30.7, 58, 141}, + {64.7, 53, 1657}, + }, + }, + [4173] = { + ["name"] = "Landria", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {30.4, 60.7, 141}, + {63.3, 66.3, 1657}, + }, + }, + [4182] = { + ["name"] = "Dalmond", + ["lvl"] = "17", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {37.4, 40.5, 148}, + }, + }, + [4203] = { + ["name"] = "Ariyell Skyshadow", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {29.4, 56.2, 141}, + {58.8, 44.5, 1657}, + }, + }, + [4241] = { + ["name"] = "Mydrannul", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {31.9, 56.4, 141}, + {70.7, 45.4, 1657}, + }, + }, + [4555] = { + ["name"] = "Eleanor Rusk", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {69.2, 48.9, 1497}, + }, + }, + [4602] = { + ["name"] = "Benijah Fenner", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {58.8, 32.8, 1497}, + }, + }, + [4603] = { + ["name"] = "Nicholas Atwood", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {62.7, 26.8, 1497}, + }, + }, + [4604] = { + ["name"] = "Abigail Sawyer", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {54.7, 38.8, 1497}, + }, + }, + [4876] = { + ["name"] = "Jawn Highmesa", + ["lvl"] = "35", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {45.9, 51.5, 400}, + }, + }, + [4889] = { + ["name"] = "Torq Ironblast", + ["lvl"] = "40", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {67.5, 48, 15}, + }, + }, + [4892] = { + ["name"] = "Jensen Farran", + ["lvl"] = "37", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {67.9, 49.9, 15}, + }, + }, + [4896] = { + ["name"] = "Charity Mipsy", + ["lvl"] = "35", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {67.5, 51.7, 15}, + }, + }, + [5101] = { + ["name"] = "Bryllia Ironbrand", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {39.2, 74.5, 1537}, + }, + }, + [5122] = { + ["name"] = "Skolmin Goldfury", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {71.8, 66.7, 1537}, + }, + }, + [5123] = { + ["name"] = "Bretta Goldfury", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {72.2, 65.2, 1537}, + }, + }, + [5134] = { + ["name"] = "Jonivera Farmountain", + ["lvl"] = "55", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {43.1, 17.5, 2597}, + }, + }, + [5510] = { + ["name"] = "Thulman Flintcrag", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {61.9, 36.6, 1519}, + {49.7, 97, 5581}, + }, + }, + [6028] = { + ["name"] = "Burkrum", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {73.5, 60.3, 331}, + }, + }, + [7942] = { + ["name"] = "Faralorn", + ["lvl"] = "53", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {30.6, 43.4, 357}, + }, + }, + [7976] = { + ["name"] = "Thalgus Thunderfist", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {61.6, 89.4, 1537}, + }, + }, + [8131] = { + ["name"] = "Blizrik Buckshot", + ["lvl"] = "45", + ["fac"] = "Unknown", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {50.7, 27.5, 440}, + }, + }, + [8139] = { + ["name"] = "Jabbey", + ["lvl"] = "40", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {67, 22, 440}, + }, + }, + [8362] = { + ["name"] = "Kuruk", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {37.7, 30, 215}, + {38.9, 64.7, 1638}, + }, + }, + [9320] = { + ["name"] = "Pipoca the Searunner", + ["lvl"] = "55", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + }, + }, + [9548] = { + ["name"] = "Cawind Trueaim", + ["lvl"] = "52", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {74.9, 45.7, 357}, + }, + }, + [9549] = { + ["name"] = "Borand", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {48.5, 61.1, 406}, + }, + }, + [9551] = { + ["name"] = "Starn", + ["lvl"] = "37", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {44.9, 50.7, 400}, + }, + }, + [9552] = { + ["name"] = "Zanara", + ["lvl"] = "43", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {35.5, 30.1, 15}, + {53.9, 69.6, 17}, + }, + }, + [9553] = { + ["name"] = "Nadia Vernon", + ["lvl"] = "20", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {45, 39.3, 130}, + }, + }, + [9555] = { + ["name"] = "Mu'uta", + ["lvl"] = "35", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {72.5, 33.4, 45}, + }, + }, + [10369] = { + ["name"] = "Trayexir", + ["lvl"] = "12", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {56.5, 73.1, 14}, + }, + }, + [11038] = { + ["name"] = "Caretaker Alen", + ["lvl"] = "52", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {79.5, 63.9, 139}, + {38.4, 44.4, 4012}, + }, + }, + [11184] = { + ["name"] = "Wixxrak", + ["lvl"] = "53", + ["fac"] = "Unknown", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {61.7, 38, 618}, + }, + }, + [11555] = { + ["name"] = "Gorn One Eye", + ["lvl"] = "55", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "", + }, + ["coords"] = { + {65.2, 2.7, 361}, + }, + }, + [12021] = { + ["name"] = "Daeolyn Summerleaf", + ["lvl"] = "51", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {45.2, 34.7, 493}, + }, + }, + [12027] = { + ["name"] = "Tukk", + ["lvl"] = "30", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {24.9, 71.8, 405}, + }, + }, + [12029] = { + ["name"] = "Narianna", + ["lvl"] = "51", + ["fac"] = "Unknown", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {53.3, 42.7, 493}, + }, + }, + [12036] = { + ["name"] = "Aerie Peak General Goods", + ["lvl"] = "55", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + }, + }, + [12246] = { + ["name"] = "Super-Seller 680", + ["lvl"] = "36", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {40.5, 79.3, 405}, + }, + }, + [12959] = { + ["name"] = "Nergal", + ["lvl"] = "52", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {43.3, 7.7, 490}, + }, + }, + [12960] = { + ["name"] = "Christi Galvanis", + ["lvl"] = "40", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {66.6, 7, 405}, + {45, 79.8, 406}, + }, + }, + [13216] = { + ["name"] = "Gaelden Hammersmith", + ["lvl"] = "58", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {44.3, 18.2, 2597}, + }, + }, + [13217] = { + ["name"] = "Thanthaldis Snowgleam", + ["lvl"] = "58", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {39.4, 81.7, 36}, + {43.4, 19.9, 267}, + }, + }, + [13218] = { + ["name"] = "Grunnda Wolfheart", + ["lvl"] = "54-58", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {49.3, 82.5, 2597}, + }, + }, + [13219] = { + ["name"] = "Jekyll Flandring", + ["lvl"] = "58", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {62.8, 59.3, 36}, + }, + }, + [14301] = { + ["name"] = "Brinna Valanaar", + ["lvl"] = "45", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {12, 78.4, 16}, + }, + }, + [14624] = { + ["name"] = "Master Smith Burninate", + ["lvl"] = "52", + ["fac"] = "Unknown", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {38.8, 28.5, 51}, + }, + }, + [15174] = { + ["name"] = "Calandrath", + ["lvl"] = "54", + ["fac"] = "Unknown", + ["meta"] = { + ["innkeeper"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {51.9, 39.2, 1377}, + }, + }, + [17078] = { + ["name"] = "Jimmy McWeaksauce", + ["lvl"] = "10", + ["fac"] = "AH", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + }, + }, + [17598] = { + ["name"] = "Renn'az", + ["lvl"] = "46", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {76.9, 81.4, 47}, + }, + }, + [21002] = { + ["name"] = "Squire Boltfling", + ["lvl"] = "1", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + }, + }, + [51656] = { + ["name"] = "Rug", + ["lvl"] = "14", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {68.2, 19.1, 148}, + }, + }, + [60456] = { + ["name"] = "Belgrush Daggerfist", + ["lvl"] = "45", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "", + }, + ["coords"] = { + {42, 79.2, 33}, + }, + }, + [60641] = { + ["name"] = "Goma", + ["lvl"] = "24", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + }, + }, + [60646] = { + ["name"] = "Evatax", + ["lvl"] = "18", + ["fac"] = "AH", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {37.9, 72, 14}, + {65, 34.7, 17}, + }, + }, + [60653] = { + ["name"] = "Skeletal Blacksmith", + ["lvl"] = "30", + ["fac"] = "AH", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {29, 45, 28}, + {85.5, 58.5, 85}, + }, + }, + [60659] = { + ["name"] = "Cydas Moonshadow", + ["lvl"] = "55", + ["fac"] = "AH", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + }, + }, + [60663] = { + ["name"] = "Grin Harold", + ["lvl"] = "5", + ["fac"] = "AH", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {48.9, 47.8, 8}, + }, + }, + [60740] = { + ["name"] = "Bald'aan The Quiet", + ["lvl"] = "10", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {34.2, 21.2, 28}, + {90.5, 35.8, 85}, + }, + }, + [60766] = { + ["name"] = "Golsh", + ["lvl"] = "50", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {93.2, 23, 46}, + }, + }, + [60790] = { + ["name"] = "Mathilda Beckett", + ["lvl"] = "50", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {21.4, 63.4, 45}, + }, + }, + [60803] = { + ["name"] = "Tyrion Bouden", + ["lvl"] = "50", + ["fac"] = "AH", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {19.5, 65.8, 46}, + {95.5, 92.2, 5581}, + }, + }, + [60813] = { + ["name"] = "Ahnja Summerwind", + ["lvl"] = "20", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {40, 78.2, 215}, + }, + }, + [60966] = { + ["name"] = "Tanzig Wexlight", + ["lvl"] = "25", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {37, 51.3, 406}, + }, + }, + [60989] = { + ["name"] = "Borrin Dustshoulder", + ["lvl"] = "20", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {34, 10, 17}, + {79.3, 62.4, 406}, + }, + }, + [61058] = { + ["name"] = "Tazo Wirelight", + ["lvl"] = "19", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {36.2, 22.5, 17}, + {83, 83.5, 406}, + }, + }, + [61115] = { + ["name"] = "Wezzy Coppersaw", + ["lvl"] = "50", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {42.9, 79.3, 5121}, + }, + }, + [61140] = { + ["name"] = "Azmela Shadowcrest", + ["lvl"] = "30", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {87.3, 45.7, 331}, + }, + }, + [61272] = { + ["name"] = "Samantha Stewards", + ["lvl"] = "45", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {59.6, 71.7, 5179}, + }, + }, + [61288] = { + ["name"] = "Savenna Mosscage", + ["lvl"] = "36", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {56.8, 38.8, 5179}, + }, + }, + [61369] = { + ["name"] = "Anati Gapper", + ["lvl"] = "35", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {14.6, 34.8, 5179}, + }, + }, + [61440] = { + ["name"] = "Gnidabern Coilspinner", + ["lvl"] = "29", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {25.2, 30.7, 1}, + {81.5, 20.3, 721}, + }, + }, + [61443] = { + ["name"] = "Dimbil Stormshot", + ["lvl"] = "32", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {25.4, 30.6, 1}, + {83.7, 19.9, 721}, + }, + }, + [61445] = { + ["name"] = "Krilee Stormshot", + ["lvl"] = "36", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {25.4, 30.7, 1}, + {83.5, 21.1, 721}, + }, + }, + [61478] = { + ["name"] = "Talavana Andalorah", + ["lvl"] = "48", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + }, + }, + [61523] = { + ["name"] = "Naytha Grovelight", + ["lvl"] = "55", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {82.8, 29.2, 616}, + }, + }, + [61563] = { + ["name"] = "Arayna Softwind", + ["lvl"] = "60", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {54.9, 85.4, 616}, + }, + }, + [61651] = { + ["name"] = "Vi'cha Spirithowler", + ["lvl"] = "34", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {73, 58.1, 331}, + }, + }, + [61742] = { + ["name"] = "Mazin Forkblast", + ["lvl"] = "15", + ["fac"] = "Unknown", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {49.7, 64.3, 5536}, + }, + }, + [61743] = { + ["name"] = "Hazlor Torkfuse", + ["lvl"] = "25", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {48.4, 69.2, 5536}, + }, + }, + [61751] = { + ["name"] = "Kango Boltblast", + ["lvl"] = "15", + ["fac"] = "Unknown", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {61.4, 81.6, 5536}, + }, + }, + [61812] = { + ["name"] = "Celia Daywing", + ["lvl"] = "10", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {53.6, 51.2, 2040}, + {63.3, 26.4, 5225}, + }, + }, + [62086] = { + ["name"] = "Meldor Swiftlance", + ["lvl"] = "10", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {51.8, 53.7, 2040}, + {62.4, 27.6, 5225}, + }, + }, + [62088] = { + ["name"] = "Laithor Morningleaf", + ["lvl"] = "10", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {40.9, 68.1, 2040}, + {57.3, 34.4, 5225}, + }, + }, + [62096] = { + ["name"] = "Aira Starsworn", + ["lvl"] = "10", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {51.6, 64.6, 5225}, + }, + }, + [62148] = { + ["name"] = "Wisteria Gallagher", + ["lvl"] = "46", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {46.1, 60.6, 5581}, + }, + }, + [62307] = { + ["name"] = "Barbara Lee", + ["lvl"] = "31", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {33.6, 58.8, 5581}, + }, + }, + [62402] = { + ["name"] = "Kargun Mightfall", + ["lvl"] = "25", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {53.6, 57.3, 5602}, + }, + }, + [62403] = { + ["name"] = "Vohand Blundergate", + ["lvl"] = "33", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {52.1, 58.7, 5602}, + }, + }, + [62404] = { + ["name"] = "Kazand Blundergate", + ["lvl"] = "23", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {52.1, 58.9, 5602}, + }, + }, + [62409] = { + ["name"] = "Gazla Blackforge", + ["lvl"] = "28", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {54.8, 56.1, 5602}, + }, + }, + [62436] = { + ["name"] = "Hagrekk", + ["lvl"] = "28", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {59.3, 29.4, 5602}, + }, + }, + [62437] = { + ["name"] = "Maktha", + ["lvl"] = "34", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {60.2, 30.2, 5602}, + }, + }, + [62462] = { + ["name"] = "Elindra Swiftluck", + ["lvl"] = "35", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {70.9, 76.9, 5561}, + }, + }, + [62469] = { + ["name"] = "Navigator Weebletax", + ["lvl"] = "35", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {28.5, 10.5, 5561}, + }, + }, + [62628] = { + ["name"] = "Nyxma Niftpatch", + ["lvl"] = "43", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {25.3, 73.4, 440}, + }, + }, + [62739] = { + ["name"] = "Orthol the Tracker", + ["lvl"] = "51", + ["fac"] = "Unknown", + ["meta"] = { + ["repair"] = "", + ["vendor"] = "", + }, + ["coords"] = { + {66.6, 85.7, 618}, + }, + }, + [65002] = { + ["name"] = "Ethernos", + ["lvl"] = "57", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {63.8, 57.5, 440}, + {68.8, 55.7, 1941}, + }, + }, + [65003] = { + ["name"] = "Chradarmu", + ["lvl"] = "52", + ["fac"] = "Unknown", + ["meta"] = { + ["innkeeper"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {63.8, 57.7, 440}, + {68.4, 56.8, 1941}, + }, + }, + [80008] = { + ["name"] = "Aubrey Merton", + ["lvl"] = "57", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + }, + }, + [80102] = { + ["name"] = "Ohgi Cardya", + ["lvl"] = "3", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {98.6, 57.7, 14}, + {36.5, 61.6, 5536}, + }, + }, + [80214] = { + ["name"] = "Malanius Silvershine", + ["lvl"] = "3", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {7.2, 22.5, 139}, + {47.4, 84.1, 5225}, + }, + }, + [80267] = { + ["name"] = "Torial Dawnrise", + ["lvl"] = "3", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {7.3, 22.9, 139}, + {47.4, 84.6, 5225}, + }, + }, + [80808] = { + ["name"] = "Ranor Riptusk", + ["lvl"] = "3", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {25.2, 13.9, 406}, + }, + }, + [81035] = { + ["name"] = "Jake Booker", + ["lvl"] = "15", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {61.2, 52.7, 85}, + }, + }, + [91246] = { + ["name"] = "Valkar", + ["lvl"] = "13", + ["fac"] = "H", + ["meta"] = { + ["repair"] = "H", + ["vendor"] = "H", + }, + ["coords"] = { + {58.9, 26.3, 14}, + }, + }, + [91248] = { + ["name"] = "Wixx", + ["lvl"] = "10", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {58.4, 25.1, 14}, + }, + }, + [91403] = { + ["name"] = "Kintello", + ["lvl"] = "40-50", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {48.1, 26.4, 45}, + }, + }, + [91725] = { + ["name"] = "Darren Mistwall", + ["lvl"] = "15", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {22, 69.2, 85}, + }, + }, + [91868] = { + ["name"] = "Primalist Gojo", + ["lvl"] = "51", + ["fac"] = "AH", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {53.4, 49.4, 409}, + }, + }, + [91956] = { + ["name"] = "Denia Hale", + ["lvl"] = "47", + ["fac"] = "A", + ["meta"] = { + ["vendor"] = "A", + }, + ["coords"] = { + {60.1, 39.6, 409}, + }, + }, + [91977] = { + ["name"] = "Olfan Goldenbrow", + ["lvl"] = "15", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "AH", + ["vendor"] = "AH", + }, + ["coords"] = { + {13.9, 55.1, 85}, + }, + }, + [92169] = { + ["name"] = "Kryillos", + ["lvl"] = "50", + ["fac"] = "Unknown", + ["meta"] = { + ["vendor"] = "AH", + }, + ["coords"] = { + {50.3, 40.7, 408}, + }, + }, + [92177] = { + ["name"] = "Garto'ogg", + ["lvl"] = "52", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {69, 76.4, 408}, + }, + }, + [92200] = { + ["name"] = "Bhatra", + ["lvl"] = "50", + ["fac"] = "H", + ["meta"] = { + ["vendor"] = "H", + }, + ["coords"] = { + {53.4, 10.9, 16}, + }, + }, + [92217] = { + ["name"] = "Ticas Quirkfuzz", + ["lvl"] = "52", + ["fac"] = "A", + ["meta"] = { + ["repair"] = "A", + ["vendor"] = "A", + }, + ["coords"] = { + {45.1, 91.3, 16}, + }, + }, +} \ No newline at end of file diff --git a/data/ds-zones-fallback.lua b/data/ds-zones-fallback.lua new file mode 100644 index 0000000..1975c58 --- /dev/null +++ b/data/ds-zones-fallback.lua @@ -0,0 +1,75 @@ +-- Auto-generated fallback zone names for beast/vendor coordinate zone IDs +MTH_DS_ZoneNamesFallback = { + [1] = "Dun Morogh", + [3] = "Badlands", + [4] = "Blasted Lands", + [8] = "Swamp of Sorrows", + [10] = "Duskwood", + [11] = "Wetlands", + [12] = "Elwynn Forest", + [14] = "Durotar", + [15] = "Dustwallow Marsh", + [16] = "Azshara", + [17] = "The Barrens", + [28] = "Western Plaguelands", + [33] = "Stranglethorn Vale", + [36] = "Alterac Mountains", + [38] = "Loch Modan", + [40] = "Westfall", + [41] = "Deadwind Pass", + [44] = "Redridge Mountains", + [45] = "Arathi Highlands", + [46] = "Burning Steppes", + [47] = "The Hinterlands", + [51] = "Searing Gorge", + [85] = "Tirisfal Glades", + [130] = "Silverpine Forest", + [139] = "Eastern Plaguelands", + [141] = "Teldrassil", + [148] = "Darkshore", + [209] = "Shadowfang Keep", + [215] = "Mulgore", + [267] = "Hillsbrad Foothills", + [331] = "Ashenvale", + [357] = "Feralas", + [361] = "Felwood", + [400] = "Thousand Needles", + [405] = "Desolace", + [406] = "Stonetalon Mountains", + [408] = "Gillijim's Isle", + [409] = "Lapidis Isle", + [440] = "Tanaris", + [490] = "Un'Goro Crater", + [491] = "Razorfen Kraul", + [493] = "Moonglade", + [616] = "Hyjal", + [618] = "Winterspring", + [718] = "Wailing Caverns", + [719] = "Blackfathom Deeps", + [721] = "Gnomeregan", + [1176] = "Zul'Farrak", + [1337] = "Uldaman", + [1377] = "Silithus", + [1477] = "The Temple of Atal'Hakkar", + [1519] = "Stormwind City", + [1537] = "Ironforge", + [1583] = "Blackrock Spire", + [1941] = "Caverns of Time", + [1977] = "Zul'Gurub", + [2040] = "Alah'Thalas", + [2100] = "Maraudon", + [2597] = "Alterac Valley", + [3456] = "Naxxramas", + [3457] = "Tower of Karazhan", + [4012] = "Scarlet Enclave", + [5077] = "Crescent Grove", + [5121] = "Tel'Abim", + [5135] = "Scarlet Monastery Library", + [5179] = "Gilneas", + [5225] = "Thalassian Highlands", + [5536] = "Blackstone Island", + [5561] = "Balor", + [5581] = "Northwind", + [5601] = "Dragonmaw Retreat", + [5602] = "Grim Reaches", +} diff --git a/data/ds-zones.lua b/data/ds-zones.lua new file mode 100644 index 0000000..fa2ef50 --- /dev/null +++ b/data/ds-zones.lua @@ -0,0 +1,12054 @@ +-- MetaHunt Zones Database +-- Synced from pfQuest + pfQuest-turtle zone hierarchy and localized names +-- Total: 663 zones + +if not MTH_DS then MTH_DS = {} end + +MTH_DS_Zones = { + [9] = { + ["continent"] = 12, + ["parent"] = 12, + ["width"] = 17.47, + ["height"] = 27.69, + ["x1"] = 51.15, + ["y1"] = 42.29, + ["names"] = { + ["deDE"] = "Nordhaintal", + ["enUS"] = "Northshire Valley", + ["esES"] = "Valle de Villanorte", + ["frFR"] = "Vallée de Northshire", + ["koKR"] = "노스샤ì´ì–´ 계곡", + ["ptBR"] = "Vale Northshire", + ["ruRU"] = "Долина СевероземьÑ", + ["zhCN"] = "北郡山谷", + ["zhTW"] = "北郡山谷", + }, + }, + [18] = { + ["continent"] = 12, + ["parent"] = 12, + ["width"] = 15.47, + ["height"] = 14.22, + ["x1"] = 52.64, + ["y1"] = 64, + ["names"] = { + ["deDE"] = "Kristallsee", + ["enUS"] = "Crystal Lake", + ["esES"] = "Lago Cristal", + ["frFR"] = "Lac de Cristal", + ["koKR"] = "수정 호수", + ["ptBR"] = "Lago Cristalino", + ["ruRU"] = "Озеро ХруÑтальное", + ["zhCN"] = "æ°´æ™¶æ¹–", + ["zhTW"] = "æ°´æ™¶æ¹–", + }, + }, + [19] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 14.97, + ["height"] = 22.46, + ["x1"] = 59.88, + ["y1"] = 18.71, + ["names"] = { + ["deDE"] = "Zul\'Gurub", + ["enUS"] = "Zul\'Gurub", + ["esES"] = "Zul\'Gurub", + ["frFR"] = "Zul\'Gurub", + ["koKR"] = "줄구룹", + ["ptBR"] = "Zul\'Gurub", + ["ruRU"] = "Зул\'Гуруб", + ["zhCN"] = "祖尔格拉布", + ["zhTW"] = "祖爾格拉布", + }, + }, + [20] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 10.48, + ["height"] = 20.21, + ["x1"] = 44.16, + ["y1"] = 65.49, + ["names"] = { + ["deDE"] = "Mondbruch", + ["enUS"] = "Moonbrook", + ["esES"] = "Arroyo de la Luna", + ["frFR"] = "Ruisselune", + ["koKR"] = "문브룩", + ["ptBR"] = "Moonbrook", + ["ruRU"] = "Луноречье", + ["zhCN"] = "月溪镇", + ["zhTW"] = "月溪鎮", + }, + }, + [35] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 8.48, + ["height"] = 10.48, + ["x1"] = 26.2, + ["y1"] = 74.1, + ["names"] = { + ["deDE"] = "Beutebucht", + ["enUS"] = "Booty Bay", + ["esES"] = "Bahía del Botín", + ["frFR"] = "Baie-du-Butin", + ["koKR"] = "무법항", + ["ptBR"] = "Angra do Butim", + ["ruRU"] = "ПиратÑÐºÐ°Ñ Ð‘ÑƒÑ…Ñ‚Ð°", + ["zhCN"] = "è—宿µ·æ¹¾", + ["zhTW"] = "è—寶海ç£", + }, + }, + [37] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 5.99, + ["height"] = 7.49, + ["x1"] = 40.42, + ["y1"] = 18.71, + ["names"] = { + ["deDE"] = "Der Nazferitisee", + ["enUS"] = "Lake Nazferiti", + ["esES"] = "Lago Nazferiti", + ["frFR"] = "Lac Nazfériti", + ["koKR"] = "나즈페리티 호수", + ["ptBR"] = "Lago Nazferiti", + ["ruRU"] = "Озеро Ðазферити", + ["zhCN"] = "纳è²ç‘žææ¹–", + ["zhTW"] = "ç´è²ç‘žææ¹–", + }, + }, + [42] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 12.48, + ["height"] = 14.97, + ["x1"] = 77.1, + ["y1"] = 44.91, + ["names"] = { + ["deDE"] = "Dunkelhain", + ["enUS"] = "Darkshire", + ["esES"] = "Villa Oscura", + ["frFR"] = "Darkshire", + ["koKR"] = "다í¬ìƒ¤ì´ì–´", + ["ptBR"] = "Vila Sombria", + ["ruRU"] = "ТемнолеÑье", + ["zhCN"] = "夜色镇", + ["zhTW"] = "夜色鎮", + }, + }, + [43] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 4.99, + ["height"] = 7.49, + ["x1"] = 32.44, + ["y1"] = 79.34, + ["names"] = { + ["deDE"] = "Die Wilden Ufer", + ["enUS"] = "Wild Shore", + ["esES"] = "Costa Salvaje", + ["frFR"] = "Le Rivage cruel", + ["koKR"] = "거친 해안", + ["ptBR"] = "Costa Selvagem", + ["ruRU"] = "ПуÑтынный берег", + ["zhCN"] = "è›®è’æµ·å²¸", + ["zhTW"] = "è »è’æµ·å²¸", + }, + }, + [57] = { + ["continent"] = 12, + ["parent"] = 12, + ["width"] = 16.97, + ["height"] = 15.72, + ["x1"] = 38.42, + ["y1"] = 84.21, + ["names"] = { + ["deDE"] = "Tiefenschachtmine", + ["enUS"] = "Fargodeep Mine", + ["esES"] = "Mina Abisal", + ["frFR"] = "Mine de Fargodeep", + ["koKR"] = "개미굴 ê´‘ì‚°", + ["ptBR"] = "Mina Vistafunda", + ["ruRU"] = "Рудник Подземных Глубин", + ["zhCN"] = "法戈第矿洞", + ["zhTW"] = "法戈第礦å‘", + }, + }, + [60] = { + ["continent"] = 12, + ["parent"] = 12, + ["width"] = 9.98, + ["height"] = 29.94, + ["x1"] = 24.95, + ["y1"] = 81.59, + ["names"] = { + ["deDE"] = "Der Waldrand", + ["enUS"] = "Forest\'s Edge", + ["esES"] = "Linde del Bosque", + ["frFR"] = "La Lisière", + ["koKR"] = "숲 가장ìžë¦¬", + ["ptBR"] = "Margem da Floresta", + ["ruRU"] = "ЛеÑÐ½Ð°Ñ Ð¾Ð¿ÑƒÑˆÐºÐ°", + ["zhCN"] = "林边空地", + ["zhTW"] = "林邊空地", + }, + }, + [62] = { + ["continent"] = 12, + ["parent"] = 12, + ["width"] = 11.98, + ["height"] = 10.48, + ["x1"] = 67.37, + ["y1"] = 79.34, + ["names"] = { + ["deDE"] = "Brackbrunns Kürbisbeet", + ["enUS"] = "Brackwell Pumpkin Patch", + ["esES"] = "Campo de Calabazas Brackwell", + ["frFR"] = "Champ de potirons des Brackwell", + ["koKR"] = "브랙웰 호박밭", + ["ptBR"] = "Campo de Abóboras de Brackwell", + ["ruRU"] = "Тыквенное поле Бреквеллов", + ["zhCN"] = "布莱克å¨å°”å—瓜田", + ["zhTW"] = "布èŠå…‹å¨çˆ¾å—瓜田", + }, + }, + [68] = { + ["continent"] = 44, + ["parent"] = 44, + ["width"] = 34.93, + ["height"] = 20.96, + ["x1"] = 41.92, + ["y1"] = 59.13, + ["names"] = { + ["deDE"] = "Der Immerruhsee", + ["enUS"] = "Lake Everstill", + ["esES"] = "Lago Siemprequieto", + ["frFR"] = "Lac placide", + ["koKR"] = "ì˜ì›ì˜ 호수", + ["ptBR"] = "Lago Everstill", + ["ruRU"] = "Озеро БезмолвиÑ", + ["zhCN"] = "止水湖", + ["zhTW"] = "止水湖", + }, + }, + [69] = { + ["continent"] = 44, + ["parent"] = 44, + ["width"] = 17.96, + ["height"] = 11.98, + ["x1"] = 25.45, + ["y1"] = 43.41, + ["names"] = { + ["deDE"] = "Seenhain", + ["enUS"] = "Lakeshire", + ["esES"] = "Villa del Lago", + ["frFR"] = "Lakeshire", + ["koKR"] = "ë ˆì´í¬ìƒ¤ì´ì–´", + ["ptBR"] = "Lakeshire", + ["ruRU"] = "Приозерье", + ["zhCN"] = "湖畔镇", + ["zhTW"] = "湖畔鎮", + }, + }, + [70] = { + ["continent"] = 44, + ["parent"] = 44, + ["width"] = 13.47, + ["height"] = 20.21, + ["x1"] = 66.12, + ["y1"] = 55.01, + ["names"] = { + ["deDE"] = "Steinwacht", + ["enUS"] = "Stonewatch", + ["esES"] = "Vigilancia de Piedra", + ["frFR"] = "Guet-de-pierre", + ["koKR"] = "ëŒë§ë£¨ 요새", + ["ptBR"] = "Stonewatch", + ["ruRU"] = "КрепоÑть Каменной Стражи", + ["zhCN"] = "石堡", + ["zhTW"] = "石堡", + }, + }, + [71] = { + ["continent"] = 44, + ["parent"] = 44, + ["width"] = 18.46, + ["height"] = 12.72, + ["x1"] = 74.6, + ["y1"] = 66.99, + ["names"] = { + ["deDE"] = "Steinwachtfälle", + ["enUS"] = "Stonewatch Falls", + ["esES"] = "Cataratas Vigilancia de Piedra", + ["frFR"] = "Chutes de Guet-de-pierre", + ["koKR"] = "함ë½ëœ ëŒë§ë£¨ 요새", + ["ptBR"] = "Cataratas Stonewatch", + ["ruRU"] = "Водопад Каменной Стражи", + ["zhCN"] = "石堡瀑布", + ["zhTW"] = "石堡瀑布", + }, + }, + [72] = { + ["continent"] = 4, + ["parent"] = 4, + ["width"] = 12.97, + ["height"] = 15.72, + ["x1"] = 58.38, + ["y1"] = 56.51, + ["names"] = { + ["deDE"] = "Das Dunkle Portal", + ["enUS"] = "The Dark Portal", + ["esES"] = "El Portal Oscuro", + ["frFR"] = "La Porte des ténèbres", + ["koKR"] = "ì–´ë‘ ì˜ ë¬¸", + ["ptBR"] = "O Portal Negro", + ["ruRU"] = "Темный портал", + ["zhCN"] = "黑暗之门", + ["zhTW"] = "黑暗之門", + }, + }, + [73] = { + ["continent"] = 4, + ["parent"] = 4, + ["width"] = 22.95, + ["height"] = 36.68, + ["x1"] = 37.92, + ["y1"] = 62.5, + ["names"] = { + ["deDE"] = "Die Faulende Narbe", + ["enUS"] = "The Tainted Scar", + ["esES"] = "La Cicatriz Manchada", + ["frFR"] = "La Balafre impure", + ["koKR"] = "타ë½ì˜ í‰í„°", + ["ptBR"] = "A Cicatriz Corrompida", + ["ruRU"] = "Гниющий шрам", + ["zhCN"] = "è…烂之痕", + ["zhTW"] = "è…爛之痕", + }, + }, + [74] = { + ["continent"] = 8, + ["parent"] = 8, + ["width"] = 18.46, + ["height"] = 26.2, + ["x1"] = 71.11, + ["y1"] = 52.77, + ["names"] = { + ["deDE"] = "Tränenteich", + ["enUS"] = "Pool of Tears", + ["esES"] = "Charca de Lágrimas", + ["frFR"] = "Bassin des larmes", + ["koKR"] = "ëˆˆë¬¼ì˜ ì—°ëª»", + ["ptBR"] = "Poço das Lágrimas", + ["ruRU"] = "Озеро Слез", + ["zhCN"] = "泪水之池", + ["zhTW"] = "淚水之池", + }, + }, + [75] = { + ["continent"] = 8, + ["parent"] = 8, + ["width"] = 21.46, + ["height"] = 20.21, + ["x1"] = 47.65, + ["y1"] = 55.01, + ["names"] = { + ["deDE"] = "Steinard", + ["enUS"] = "Stonard", + ["esES"] = "Rocal", + ["frFR"] = "Stonard", + ["koKR"] = "스토나드", + ["ptBR"] = "Stonard", + ["ruRU"] = "Каменор", + ["zhCN"] = "斯通纳德", + ["zhTW"] = "斯通ç´å¾·", + }, + }, + [76] = { + ["continent"] = 8, + ["parent"] = 8, + ["width"] = 22.46, + ["height"] = 26.95, + ["x1"] = 65.12, + ["y1"] = 22.46, + ["names"] = { + ["deDE"] = "Zuflucht der Verirrten", + ["enUS"] = "Fallow Sanctuary", + ["esES"] = "Santuario Baldío", + ["frFR"] = "Sanctuaire des friches", + ["koKR"] = "드레노어 성역", + ["ptBR"] = "Santuário Esquecido", + ["ruRU"] = "Болотное приÑтанище", + ["zhCN"] = "农田é¿é𾿉€", + ["zhTW"] = "農田é¿é›£æ‰€", + }, + }, + [86] = { + ["continent"] = 12, + ["parent"] = 12, + ["width"] = 15.47, + ["height"] = 24.7, + ["x1"] = 75.1, + ["y1"] = 49.78, + ["names"] = { + ["deDE"] = "Der Steinhügelsee", + ["enUS"] = "Stone Cairn Lake", + ["esES"] = "Lago del Hito", + ["frFR"] = "Lac du Cairn", + ["koKR"] = "ëŒë¬´ë¤ 호수", + ["ptBR"] = "Lago Stone Cairn", + ["ruRU"] = "Озеро Каменных Столбов", + ["zhCN"] = "石碑湖", + ["zhTW"] = "石碑湖", + }, + }, + [87] = { + ["continent"] = 12, + ["parent"] = 12, + ["width"] = 8.48, + ["height"] = 9.73, + ["x1"] = 41.17, + ["y1"] = 64.75, + ["names"] = { + ["deDE"] = "Goldhain", + ["enUS"] = "Goldshire", + ["esES"] = "Villadorada", + ["frFR"] = "Goldshire", + ["koKR"] = "골드샤ì´ì–´", + ["ptBR"] = "Vila d\'Ouro", + ["ruRU"] = "Златоземье", + ["zhCN"] = "闪金镇", + ["zhTW"] = "閃金鎮", + }, + }, + [88] = { + ["continent"] = 12, + ["parent"] = 12, + ["width"] = 12.48, + ["height"] = 12.72, + ["x1"] = 83.58, + ["y1"] = 66.24, + ["names"] = { + ["deDE"] = "Holzfällerlager des Osttals", + ["enUS"] = "Eastvale Logging Camp", + ["esES"] = "Aserradero de la Vega del Este", + ["frFR"] = "Camp de bûcherons du Val d\'est", + ["koKR"] = "ë™ë¶€ 벌목지", + ["ptBR"] = "Campo Lenhador do Vale do Leste", + ["ruRU"] = "ЛеÑопилка ВоÑточной долины", + ["zhCN"] = "ä¸œè°·ä¼æœ¨åœº", + ["zhTW"] = "æ±è°·ä¼æœ¨å ´", + }, + }, + [91] = { + ["continent"] = 12, + ["parent"] = 12, + ["width"] = 13.47, + ["height"] = 13.47, + ["x1"] = 67.61, + ["y1"] = 66.62, + ["names"] = { + ["deDE"] = "Turm von Azora", + ["enUS"] = "Tower of Azora", + ["esES"] = "Torre de Azora", + ["frFR"] = "Tour d\'Azora", + ["koKR"] = "ì•„ì¡°ë¼ì˜ 탑", + ["ptBR"] = "A Torre de Azora", + ["ruRU"] = "Ð‘Ð°ÑˆÐ½Ñ Ðзоры", + ["zhCN"] = "阿祖拉之塔", + ["zhTW"] = "阿祖拉之塔", + }, + }, + [93] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 15.47, + ["height"] = 16.47, + ["x1"] = 36.18, + ["y1"] = 73.35, + ["names"] = { + ["deDE"] = "Ogerhort Vul\'Gol", + ["enUS"] = "Vul\'Gol Ogre Mound", + ["esES"] = "Túmulo de Ogro de Vul\'Gol", + ["frFR"] = "Tertre des ogres Vul\'Gol", + ["koKR"] = "벌골 오우거 소굴", + ["ptBR"] = "Montículo de Ogros Vul\'Gol", + ["ruRU"] = "Лощина Вул\'Гол", + ["zhCN"] = "沃å¤å°”食人魔山", + ["zhTW"] = "沃å¤çˆ¾å·¨é­”å±±", + }, + }, + [94] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 10.48, + ["height"] = 10.48, + ["x1"] = 20.71, + ["y1"] = 56.14, + ["names"] = { + ["deDE"] = "Rabenflucht", + ["enUS"] = "Raven Hill", + ["esES"] = "Colina del Cuervo", + ["frFR"] = "Colline-aux-Corbeaux", + ["koKR"] = "까마귀 ì–¸ë•", + ["ptBR"] = "Colina do Corvo", + ["ruRU"] = "Вороний Холм", + ["zhCN"] = "乌鸦岭", + ["zhTW"] = "çƒé´‰å¶º", + }, + }, + [95] = { + ["continent"] = 44, + ["parent"] = 44, + ["width"] = 22.46, + ["height"] = 18.71, + ["x1"] = 28.69, + ["y1"] = 28.07, + ["names"] = { + ["deDE"] = "Rotkammschlucht", + ["enUS"] = "Redridge Canyons", + ["esES"] = "Montañas Crestagrana", + ["frFR"] = "Canyons des Carmines", + ["koKR"] = "붉ì€ë§ˆë£¨ 협곡", + ["ptBR"] = "Cânions Redridge", + ["ruRU"] = "Каньоны КраÑногорьÑ", + ["zhCN"] = "赤脊峡谷", + ["zhTW"] = "赤脊峽谷", + }, + }, + [97] = { + ["continent"] = 44, + ["parent"] = 44, + ["width"] = 11.98, + ["height"] = 18.71, + ["x1"] = 53.39, + ["y1"] = 42.29, + ["names"] = { + ["deDE"] = "Althers Mühle", + ["enUS"] = "Alther\'s Mill", + ["esES"] = "Molino de Alther", + ["frFR"] = "Scierie d\'Alther", + ["koKR"] = "앨ë”ì˜ ì œìž¬ì†Œ", + ["ptBR"] = "Serraria de Alther", + ["ruRU"] = "ЛеÑопилка Ðльтера", + ["zhCN"] = "å¥¥ç‘Ÿå°”ä¼æœ¨åœº", + ["zhTW"] = "å¥§ç‘Ÿçˆ¾ä¼æœ¨å ´", + }, + }, + [99] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 7.98, + ["height"] = 5.24, + ["x1"] = 37.43, + ["y1"] = 5.61, + ["names"] = { + ["deDE"] = "Rebellenlager", + ["enUS"] = "Rebel Camp", + ["esES"] = "Campamento Rebelde", + ["frFR"] = "Camp rebelle", + ["koKR"] = "반란군 야ì˜ì§€", + ["ptBR"] = "Acampamento Rebelde", + ["ruRU"] = "Лагерь ПовÑтанцев", + ["zhCN"] = "åæŠ—军è¥åœ°", + ["zhTW"] = "åæŠ—è»ç‡Ÿåœ°", + }, + }, + [100] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 7.49, + ["height"] = 8.23, + ["x1"] = 34.18, + ["y1"] = 13.1, + ["names"] = { + ["deDE"] = "Nesingwarys Expedition", + ["enUS"] = "Nesingwary\'s Expedition", + ["esES"] = "Expedición de Nesingwary", + ["frFR"] = "Expédition de Nesingwary", + ["koKR"] = "네싱워리 ì›ì •대", + ["ptBR"] = "Expedição de Nesingwary", + ["ruRU"] = "ЭкÑÐ¿ÐµÐ´Ð¸Ñ†Ð¸Ñ Ð­Ñ€Ð½ÐµÑтуÑÑ", + ["zhCN"] = "奈辛瓦里远å¾é˜Ÿè¥åœ°", + ["zhTW"] = "奈辛瓦里é å¾éšŠç‡Ÿåœ°", + }, + }, + [101] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 7.98, + ["height"] = 8.23, + ["x1"] = 45.41, + ["y1"] = 9.36, + ["names"] = { + ["deDE"] = "Kurzens Truppenlager", + ["enUS"] = "Kurzen\'s Compound", + ["esES"] = "Compuesto de Kurzen", + ["frFR"] = "Base de Kurzen", + ["koKR"] = "쿠르젠 주둔지", + ["ptBR"] = "Complexo de Kurzen", + ["ruRU"] = "Лагерь Курцена", + ["zhCN"] = "库尔森的è¥åœ°", + ["zhTW"] = "庫爾森的營地", + }, + }, + [102] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 7.98, + ["height"] = 8.23, + ["x1"] = 25.95, + ["y1"] = 12.35, + ["names"] = { + ["deDE"] = "Ruinen von Zul\'Kunda", + ["enUS"] = "Ruins of Zul\'Kunda", + ["esES"] = "Ruinas de Zul\'Kunda", + ["frFR"] = "Ruines de Zul\'Kunda", + ["koKR"] = "ì¤„ì¿¤ë‹¤ì˜ íí—ˆ", + ["ptBR"] = "Ruínas de Zul\'Kunda", + ["ruRU"] = "Руины Зул\'Кунды", + ["zhCN"] = "祖昆达废墟", + ["zhTW"] = "祖昆é”廢墟", + }, + }, + [103] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 6.49, + ["height"] = 9.73, + ["x1"] = 46.66, + ["y1"] = 42.29, + ["names"] = { + ["deDE"] = "Ruinen von Zul\'Mamwe", + ["enUS"] = "Ruins of Zul\'Mamwe", + ["esES"] = "Ruinas de Zul\'Mamwe", + ["frFR"] = "Ruines de Zul\'Mamwe", + ["koKR"] = "ì¤„ë§˜ì›¨ì˜ íí—ˆ", + ["ptBR"] = "Ruínas de Zul\'Mamwe", + ["ruRU"] = "Руины Зул\'Мамве", + ["zhCN"] = "祖玛维废墟", + ["zhTW"] = "祖瑪維廢墟", + }, + }, + [104] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 12.48, + ["height"] = 14.97, + ["x1"] = 24.2, + ["y1"] = 27.69, + ["names"] = { + ["deDE"] = "Das finstere Riff", + ["enUS"] = "The Vile Reef", + ["esES"] = "El Arrecife Vil", + ["frFR"] = "Le Récif infâme", + ["koKR"] = "ì©ì€ë‚´ 산호초", + ["ptBR"] = "O Arrecife Vil", + ["ruRU"] = "Коварный риф", + ["zhCN"] = "æš—ç¤æµ·", + ["zhTW"] = "æš—ç¤æµ·", + }, + }, + [105] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 7.98, + ["height"] = 11.98, + ["x1"] = 49.4, + ["y1"] = 29.94, + ["names"] = { + ["deDE"] = "Ogerhügel der Mosh\'Ogg", + ["enUS"] = "Mosh\'Ogg Ogre Mound", + ["esES"] = "Túmulo de Ogro Mosh\'Ogg", + ["frFR"] = "Tertre des ogres Mosh\'Ogg", + ["koKR"] = "모쉬오그 오우거 소굴", + ["ptBR"] = "Monte dos Ogros Mosh\'Ogg", + ["ruRU"] = "Холм Мош\'Огг", + ["zhCN"] = "莫什奥格食人魔山", + ["zhTW"] = "莫什奧格巨魔山", + }, + }, + [107] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 9.98, + ["height"] = 11.23, + ["x1"] = 55.39, + ["y1"] = 32.56, + ["names"] = { + ["deDE"] = "Saldeans Hof", + ["enUS"] = "Saldean\'s Farm", + ["esES"] = "Granja de Saldean", + ["frFR"] = "Ferme des Saldean", + ["koKR"] = "살딘 ë†ìž¥", + ["ptBR"] = "Fazenda Saldean", + ["ruRU"] = "Ферма Сальдена", + ["zhCN"] = "è¨ä¸å†œåœº", + ["zhTW"] = "è–©ä¸è¾²å ´", + }, + }, + [108] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 8.98, + ["height"] = 15.72, + ["x1"] = 55.89, + ["y1"] = 49.78, + ["names"] = { + ["deDE"] = "Späherkuppe", + ["enUS"] = "Sentinel Hill", + ["esES"] = "Colina del Centinela", + ["frFR"] = "Colline des sentinelles", + ["koKR"] = "ê°ì‹œì˜ ì–¸ë•", + ["ptBR"] = "Colina Sentinel", + ["ruRU"] = "Сторожевой холм", + ["zhCN"] = "哨兵岭", + ["zhTW"] = "哨兵嶺", + }, + }, + [109] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 8.98, + ["height"] = 12.72, + ["x1"] = 50.9, + ["y1"] = 21.33, + ["names"] = { + ["deDE"] = "Brauenwirbels Kürbishof", + ["enUS"] = "Furlbrow\'s Pumpkin Farm", + ["esES"] = "Granja de Calabazas Furlbrow", + ["frFR"] = "Ferme de potirons de Furlbrow", + ["koKR"] = "펄브로우 호박밭", + ["ptBR"] = "Fazenda de Abóboras dos Monrabo", + ["ruRU"] = "Ð¢Ñ‹ÐºÐ²ÐµÐ½Ð½Ð°Ñ Ñ„ÐµÑ€Ð¼Ð° Хмуроброва", + ["zhCN"] = "法布隆å—瓜农场", + ["zhTW"] = "法布隆å—瓜農場", + }, + }, + [111] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 8.48, + ["height"] = 11.23, + ["x1"] = 44.66, + ["y1"] = 24.33, + ["names"] = { + ["deDE"] = "Der Jangoschacht", + ["enUS"] = "Jangolode Mine", + ["esES"] = "Mina Jangolode", + ["frFR"] = "Mine de Jangolode", + ["koKR"] = "장고로드 ê´‘ì‚°", + ["ptBR"] = "Mina Jangolode", + ["ruRU"] = "Рудник Янго", + ["zhCN"] = "詹戈洛德矿洞", + ["zhTW"] = "詹戈洛德礦å‘", + }, + }, + [113] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 11.98, + ["height"] = 14.22, + ["x1"] = 31.44, + ["y1"] = 41.54, + ["names"] = { + ["deDE"] = "Der Goldküstensteinbruch", + ["enUS"] = "Gold Coast Quarry", + ["esES"] = "Mina de la Costa del Oro", + ["frFR"] = "Carrière de la côte de l\'Or", + ["koKR"] = "황금해안 채ì„장", + ["ptBR"] = "Pedreira da Costa Dourada", + ["ruRU"] = "ПрииÑк на Золотом Берегу", + ["zhCN"] = "金海岸矿洞", + ["zhTW"] = "金海岸礦å‘", + }, + }, + [115] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 11.98, + ["height"] = 13.47, + ["x1"] = 31.94, + ["y1"] = 86.08, + ["names"] = { + ["deDE"] = "Der Leuchtturm von Westfall", + ["enUS"] = "Westfall Lighthouse", + ["esES"] = "Faro de Páramos de Poniente", + ["frFR"] = "Phare de l\'Ouest", + ["koKR"] = "서부 몰ë½ì§€ëŒ€ 등대", + ["ptBR"] = "Farol de Westfall", + ["ruRU"] = "МаÑк в Западном Крае", + ["zhCN"] = "西部è’野ç¯å¡”", + ["zhTW"] = "西部è’野燈塔", + }, + }, + [116] = { + ["continent"] = 8, + ["parent"] = 8, + ["width"] = 16.47, + ["height"] = 14.97, + ["x1"] = 13.22, + ["y1"] = 35.93, + ["names"] = { + ["deDE"] = "Das Neblige Tal", + ["enUS"] = "Misty Valley", + ["esES"] = "Valle Brumoso", + ["frFR"] = "Vallée des brumes", + ["koKR"] = "안개 골짜기", + ["ptBR"] = "Valle Nebuloso", + ["ruRU"] = "Ð¢ÑƒÐ¼Ð°Ð½Ð½Ð°Ñ Ð´Ð¾Ð»Ð¸Ð½Ð°", + ["zhCN"] = "迷雾谷", + ["zhTW"] = "迷霧谷", + }, + }, + [117] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 6.49, + ["height"] = 6.74, + ["x1"] = 31.69, + ["y1"] = 27.32, + ["names"] = { + ["deDE"] = "Basislager Grom\'gol", + ["enUS"] = "Grom\'gol Base Camp", + ["esES"] = "Campamento Base Grom\'gol", + ["frFR"] = "Campement Grom\'gol", + ["koKR"] = "그롬골 주둔지", + ["ptBR"] = "Acampamento Base de Grom\'gol", + ["ruRU"] = "Лагерь Гром\'гол", + ["zhCN"] = "格罗姆高è¥åœ°", + ["zhTW"] = "格羅姆高營地", + }, + }, + [118] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 9.98, + ["height"] = 9.73, + ["x1"] = 35.93, + ["y1"] = 49.03, + ["names"] = { + ["deDE"] = "Whelgars Ausgrabungsstätte", + ["enUS"] = "Whelgar\'s Excavation Site", + ["esES"] = "Sitio de Excavación de Whelgar", + ["frFR"] = "Excavations de Whelgar", + ["koKR"] = "ì›°ê°€ë¥´ì˜ ë°œêµ´í˜„ìž¥", + ["ptBR"] = "Sítio de Escavação de Whelgar", + ["ruRU"] = "РаÑкопки Вельгара", + ["zhCN"] = "维尔加挖掘场", + ["zhTW"] = "維爾加挖掘場", + }, + }, + [121] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 9.48, + ["height"] = 11.23, + ["x1"] = 80.09, + ["y1"] = 69.99, + ["names"] = { + ["enUS"] = "Tranquil Gardens Cemetery", + ["esES"] = "Cementerio de los Jardines Tranquilos", + ["frFR"] = "Cimetière des jardins paisibles", + ["koKR"] = "ê³ ìš”ì˜ ì •ì› ë¬˜ì§€", + ["ptBR"] = "O Cemitério dos Jardins Tranquilos", + ["ruRU"] = "БезмÑтежное кладбище", + ["zhCN"] = "é™è°§èŠ±å›­å¢“åœº", + ["zhTW"] = "éœè¬èŠ±åœ’å¢“å ´", + }, + }, + [122] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 4.49, + ["height"] = 5.99, + ["x1"] = 21.21, + ["y1"] = 14.97, + ["names"] = { + ["deDE"] = "Ruinen von Zuuldaia", + ["enUS"] = "Zuuldaia Ruins", + ["esES"] = "Ruinas de Zuuldaia", + ["frFR"] = "Ruines de Zuuldaia", + ["koKR"] = "줄다ì´ì•„ íí—ˆ", + ["ptBR"] = "Ruínas Zuuldaia", + ["ruRU"] = "Руины ЗуулдаÑ", + ["zhCN"] = "祖丹亚废墟", + ["zhTW"] = "祖丹亞廢墟", + }, + }, + [123] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 4.99, + ["height"] = 5.24, + ["x1"] = 29.44, + ["y1"] = 19.84, + ["names"] = { + ["deDE"] = "Ruinen von Bal\'lal", + ["enUS"] = "Bal\'lal Ruins", + ["esES"] = "Ruinas de Bal\'lal", + ["frFR"] = "Ruines de Bal\'lal", + ["koKR"] = "발랄 íí—ˆ", + ["ptBR"] = "Ruínas de Bal\'lal", + ["ruRU"] = "Руины Бал\'лал", + ["zhCN"] = "巴拉尔废墟", + ["zhTW"] = "巴拉爾廢墟", + }, + }, + [125] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 4.99, + ["height"] = 5.99, + ["x1"] = 34.43, + ["y1"] = 20.96, + ["names"] = { + ["deDE"] = "Ruinen von Kal\'ai", + ["enUS"] = "Kal\'ai Ruins", + ["esES"] = "Ruinas de Kal\'ai", + ["frFR"] = "Ruines de Kal\'ai", + ["koKR"] = "ì¹¼ì•„ì´ íí—ˆ", + ["ptBR"] = "Ruínas Kal\'ai", + ["ruRU"] = "Руины Кал\'аи", + ["zhCN"] = "å¡èŽ±åºŸå¢Ÿ", + ["zhTW"] = "å¡èŠå»¢å¢Ÿ", + }, + }, + [127] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 5.49, + ["height"] = 6.74, + ["x1"] = 41.67, + ["y1"] = 31.06, + ["names"] = { + ["deDE"] = "Ruinen von Balia\'mah", + ["enUS"] = "Balia\'mah Ruins", + ["esES"] = "Ruinas de Balia\'mah", + ["frFR"] = "Ruines de Balia\'mah", + ["koKR"] = "발리아마 íí—ˆ", + ["ptBR"] = "Ruínas de Balia\'mah", + ["ruRU"] = "Руины Балиа\'ма", + ["zhCN"] = "巴里亚曼废墟", + ["zhTW"] = "巴里亞曼廢墟", + }, + }, + [128] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 4.49, + ["height"] = 11.98, + ["x1"] = 40.67, + ["y1"] = 41.17, + ["names"] = { + ["deDE"] = "Ruinen von Ziata\'jai", + ["enUS"] = "Ziata\'jai Ruins", + ["esES"] = "Ruinas de Ziata\'jai", + ["frFR"] = "Ruines de Ziata\'jai", + ["koKR"] = "지아타ìžì´ íí—ˆ", + ["ptBR"] = "Ruínas Ziata\'jai", + ["ruRU"] = "Руины Зиата\'джаи", + ["zhCN"] = "赞塔加废墟", + ["zhTW"] = "贊塔加廢墟", + }, + }, + [129] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 3.99, + ["height"] = 5.24, + ["x1"] = 36.93, + ["y1"] = 29.57, + ["names"] = { + ["deDE"] = "Ruinen von Mizjah", + ["enUS"] = "Mizjah Ruins", + ["esES"] = "Ruinas de Mizjah", + ["frFR"] = "Ruines de Mizjah", + ["koKR"] = "ë¯¸ì¦ˆìž íí—ˆ", + ["ptBR"] = "Ruínas Mizjah", + ["ruRU"] = "Руины Мизжа", + ["zhCN"] = "米扎废墟", + ["zhTW"] = "米扎廢墟", + }, + }, + [131] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 10.98, + ["height"] = -18.71, + ["x1"] = 49.9, + ["y1"] = 67.74, + ["names"] = { + ["deDE"] = "Kharanos", + ["enUS"] = "Kharanos", + ["esES"] = "Kharanos", + ["frFR"] = "Kharanos", + ["koKR"] = "ì¹´ë¼ë…¸ìФ", + ["ptBR"] = "Kharanos", + ["ruRU"] = "КараноÑ", + ["zhCN"] = "塿‹‰è¯ºæ–¯", + ["zhTW"] = "塿‹‰è«¾æ–¯", + }, + }, + [132] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 14.97, + ["height"] = 17.22, + ["x1"] = 26.45, + ["y1"] = 75.22, + ["names"] = { + ["deDE"] = "Eisklammtal", + ["enUS"] = "Coldridge Valley", + ["esES"] = "Valle de Crestanevada", + ["frFR"] = "Vallée des Frigères", + ["koKR"] = "눈마루 골짜기", + ["ptBR"] = "Vale Crista Fria", + ["ruRU"] = "Ð¥Ð¾Ð»Ð¾Ð´Ð½Ð°Ñ Ð´Ð¾Ð»Ð¸Ð½Ð°", + ["zhCN"] = "寒脊山谷", + ["zhTW"] = "寒脊山谷", + }, + }, + [133] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 7.49, + ["height"] = 11.98, + ["x1"] = 24.2, + ["y1"] = 38.17, + ["names"] = { + ["deDE"] = "Gnomeregan", + ["enUS"] = "Gnomeregan", + ["esES"] = "Gnomeregan", + ["frFR"] = "Gnomeregan", + ["koKR"] = "놈리건", + ["ptBR"] = "Gnomeregan", + ["ruRU"] = "Гномреган", + ["zhCN"] = "诺莫瑞根", + ["zhTW"] = "諾姆瑞根", + }, + }, + [134] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 9.48, + ["height"] = 13.47, + ["x1"] = 68.61, + ["y1"] = 57.63, + ["names"] = { + ["deDE"] = "Gol\'bolar Steinbruch", + ["enUS"] = "Gol\'Bolar Quarry", + ["esES"] = "Cantera Gol\'Bolar", + ["frFR"] = "Carrière de Gol\'Bolar", + ["koKR"] = "ê³¨ë³¼ë¼ ì±„ì„장", + ["ptBR"] = "Pedreira Gol\'Bolar", + ["ruRU"] = "Карьер Гол\'Болар", + ["zhCN"] = "å¤åšæ‹‰é‡‡æŽ˜åœº", + ["zhTW"] = "å¤åšæ‹‰æŽ¡æŽ˜å ´", + }, + }, + [135] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 4.49, + ["height"] = 8.98, + ["x1"] = 28.19, + ["y1"] = 51.65, + ["names"] = { + ["deDE"] = "Höhle der Frostmähnen", + ["enUS"] = "Frostmane Hold", + ["esES"] = "Bastión Melena de Escarcha", + ["frFR"] = "Repaire des Frostmane", + ["koKR"] = "서리갈기 소굴", + ["ptBR"] = "Refúgio dos Gelomane", + ["ruRU"] = "Форт Мерзлогривов", + ["zhCN"] = "霜鬃巨魔è¦å¡ž", + ["zhTW"] = "霜鬃食人妖è¦å¡ž", + }, + }, + [136] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 5.99, + ["height"] = 11.23, + ["x1"] = 40.92, + ["y1"] = 58.01, + ["names"] = { + ["deDE"] = "Der Graufelsbau", + ["enUS"] = "The Grizzled Den", + ["esES"] = "El Cubil Pardo", + ["frFR"] = "L\'antre Gris", + ["koKR"] = "ì€ë¹› ë™êµ´", + ["ptBR"] = "A Caverna Rugosa", + ["ruRU"] = "Ð¡ÐµÑ€Ð°Ñ Ð±ÐµÑ€Ð»Ð¾Ð³Ð°", + ["zhCN"] = "ç°è‰²æ´žç©´", + ["zhTW"] = "ç°è‰²æ´žç©´", + }, + }, + [137] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 4.99, + ["height"] = 9.73, + ["x1"] = 30.44, + ["y1"] = 46.03, + ["names"] = { + ["deDE"] = "Bräuhall", + ["enUS"] = "Brewnall Village", + ["esES"] = "Aldea Brewnall", + ["frFR"] = "Brassetout", + ["koKR"] = "브루날 마ì„", + ["ptBR"] = "Aldeia de Brewnall", + ["ruRU"] = "ПоÑелок Пивоваров", + ["zhCN"] = "烈酒æ‘", + ["zhTW"] = "烈酒æ‘", + }, + }, + [138] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 5.99, + ["height"] = 11.98, + ["x1"] = 57.39, + ["y1"] = 42.66, + ["names"] = { + ["deDE"] = "Nebelfichtenzuflucht", + ["enUS"] = "Misty Pine Refuge", + ["esES"] = "Refugio de Pinos Brumosos", + ["frFR"] = "Refuge de Brumepins", + ["koKR"] = "안개소나무 ì€ê±°ì²˜", + ["ptBR"] = "Refugio Pino Nebuloso", + ["ruRU"] = "Сторожка у заÑнеженной ÑоÑны", + ["zhCN"] = "雾æ¾é¿é𾿉€", + ["zhTW"] = "霧æ¾é¿é›£æ‰€", + }, + }, + [142] = { + ["continent"] = 38, + ["parent"] = 38, + ["width"] = 13.97, + ["height"] = 17.22, + ["x1"] = 69.36, + ["y1"] = 65.49, + ["names"] = { + ["deDE"] = "Eisenbands Ausgrabungsstätte", + ["enUS"] = "Ironband\'s Excavation Site", + ["esES"] = "Sitio de Excavación de Ironband", + ["frFR"] = "Excavations d\'Ironband", + ["koKR"] = "ì•„ì´ì–¸ë°´ë“œì˜ 발굴현장", + ["ptBR"] = "Sítio de Escavação de Ironband", + ["ruRU"] = "РаÑкопки Сталекрута", + ["zhCN"] = "é“环挖掘场", + ["zhTW"] = "éµç’°æŒ–掘場", + }, + }, + [143] = { + ["continent"] = 38, + ["parent"] = 38, + ["width"] = 17.96, + ["height"] = 18.71, + ["x1"] = 70.86, + ["y1"] = 25.07, + ["names"] = { + ["deDE"] = "Festung Mo\'grosh", + ["enUS"] = "Mo\'grosh Stronghold", + ["esES"] = "Fortaleza Mo\'grosh", + ["frFR"] = "Bastion des Mo\'grosh", + ["koKR"] = "모그로쉬 소굴", + ["ptBR"] = "Bastión Mo\'grosh", + ["ruRU"] = "Оплот Мо\'грош", + ["zhCN"] = "莫格罗什è¦å¡ž", + ["zhTW"] = "莫格羅什è¦å¡ž", + }, + }, + [144] = { + ["continent"] = 38, + ["parent"] = 38, + ["width"] = 9.98, + ["height"] = 12.72, + ["x1"] = 34.93, + ["y1"] = 46.03, + ["names"] = { + ["deDE"] = "Thelsamar", + ["enUS"] = "Thelsamar", + ["esES"] = "Thelsamar", + ["frFR"] = "Thelsamar", + ["koKR"] = "텔사마", + ["ptBR"] = "Thelsamar", + ["ruRU"] = "Телcамар", + ["zhCN"] = "塞尔è¨çŽ›", + ["zhTW"] = "塞爾薩瑪", + }, + }, + [146] = { + ["continent"] = 38, + ["parent"] = 38, + ["width"] = 19.96, + ["height"] = 11.98, + ["x1"] = 47.9, + ["y1"] = 12.72, + ["names"] = { + ["deDE"] = "Der Steinwerkdamm", + ["enUS"] = "Stonewrought Dam", + ["esES"] = "Presa de Piedra Labrada", + ["frFR"] = "Barrage de Formepierre", + ["koKR"] = "ëŒë‹¤ì§€ ëŒ", + ["ptBR"] = "Represa Stonewrought", + ["ruRU"] = "ÐšÐ°Ð¼ÐµÐ½Ð½Ð°Ñ ÐŸÐ»Ð¾Ñ‚Ð¸Ð½Ð°", + ["zhCN"] = "巨石水å", + ["zhTW"] = "巨石水壩", + }, + }, + [147] = { + ["continent"] = 38, + ["parent"] = 38, + ["width"] = 8.48, + ["height"] = 11.98, + ["x1"] = 83.08, + ["y1"] = 62.87, + ["names"] = { + ["deDE"] = "Jagdhütte der Weltenwanderer", + ["enUS"] = "The Farstrider Lodge", + ["esES"] = "Logia del Caminante Lejano", + ["frFR"] = "Le pavillon des Pérégrins", + ["koKR"] = "순찰ìžì˜ 오ë‘막", + ["ptBR"] = "A Aldeia Farstrider", + ["ruRU"] = "Приют Странников", + ["zhCN"] = "远行者å°å±‹", + ["zhTW"] = "旅行者營地", + }, + }, + [149] = { + ["continent"] = 38, + ["parent"] = 38, + ["width"] = 10.98, + ["height"] = 16.47, + ["x1"] = 34.43, + ["y1"] = 21.71, + ["names"] = { + ["deDE"] = "Silberbachmine", + ["enUS"] = "Silver Stream Mine", + ["esES"] = "Mina de Fuenteplata", + ["frFR"] = "Mine du Ru d\'argent", + ["koKR"] = "ì€ì—¬ìš¸ ê´‘ì‚°", + ["ptBR"] = "Mina Silver Stream", + ["ruRU"] = "Рудник у СеребрÑного ручейка", + ["zhCN"] = "银泉矿洞", + ["zhTW"] = "銀泉礦å‘", + }, + }, + [150] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 6.49, + ["height"] = 10.48, + ["x1"] = 10.23, + ["y1"] = 56.89, + ["names"] = { + ["deDE"] = "Hafen von Menethil", + ["enUS"] = "Menethil Harbor", + ["esES"] = "Puerto Menethil", + ["frFR"] = "Port de Menethil", + ["koKR"] = "메네실 항구", + ["ptBR"] = "Puerto Menethil", + ["ruRU"] = "Гавань Менетилов", + ["zhCN"] = "米奈希尔港", + ["zhTW"] = "米奈希爾港", + }, + }, + [152] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 13.47, + ["height"] = 25.45, + ["x1"] = 83.58, + ["y1"] = 69.61, + ["names"] = { + ["deDE"] = "Das Bollwerk", + ["enUS"] = "The Bulwark", + ["esES"] = "El Baluarte", + ["frFR"] = "La Barricade", + ["koKR"] = "보루", + ["ptBR"] = "A Muralha", + ["ruRU"] = "БаÑтион", + ["zhCN"] = "亡çµå£åž’", + ["zhTW"] = "亡éˆå£å£˜", + }, + }, + [154] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 13.17, + ["height"] = 18.26, + ["x1"] = 34.43, + ["y1"] = 63.92, + ["names"] = { + ["deDE"] = "Todesend", + ["enUS"] = "Deathknell", + ["esES"] = "Toque de Muerte", + ["frFR"] = "Le Glas", + ["koKR"] = "ë°ìŠ¤ë„¬", + ["ptBR"] = "Necrotério", + ["ruRU"] = "Могильник", + ["zhCN"] = "丧钟镇", + ["zhTW"] = "å–ªé˜éŽ®", + }, + }, + [156] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 15.27, + ["height"] = 12.13, + ["x1"] = 35.98, + ["y1"] = 49.48, + ["names"] = { + ["deDE"] = "Sollidens Bauernhof", + ["enUS"] = "Solliden Farmstead", + ["esES"] = "Granja Solliden", + ["frFR"] = "Ferme des Solliden", + ["koKR"] = "ì†”ë¦¬ë´ ë†ìž¥", + ["ptBR"] = "Fazenda Solliden", + ["ruRU"] = "УÑадьба Соллиден", + ["zhCN"] = "索利丹农场", + ["zhTW"] = "索利丹農場", + }, + }, + [157] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 13.77, + ["height"] = 17.96, + ["x1"] = 45.21, + ["y1"] = 36.38, + ["names"] = { + ["deDE"] = "Agamands Mühlen", + ["enUS"] = "Agamand Mills", + ["esES"] = "Molinos Agamand", + ["frFR"] = "Moulins d\'Agamand", + ["koKR"] = "아가만드 ë°€ë†ìž¥", + ["ptBR"] = "Moinhos Agamand", + ["ruRU"] = "Мельницы Ðгамондов", + ["zhCN"] = "阿加曼德磨åŠ", + ["zhTW"] = "阿加曼德磨åŠ", + }, + }, + [159] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 7.49, + ["height"] = 14.67, + ["x1"] = 59.63, + ["y1"] = 55.24, + ["names"] = { + ["deDE"] = "Brill", + ["enUS"] = "Brill", + ["esES"] = "Brill", + ["frFR"] = "Brill", + ["koKR"] = "브릴", + ["ptBR"] = "Brill", + ["ruRU"] = "Брилл", + ["zhCN"] = "布瑞尔", + ["zhTW"] = "布瑞爾", + }, + }, + [160] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 16.67, + ["height"] = 22.6, + ["x1"] = 84.78, + ["y1"] = 32.86, + ["names"] = { + ["deDE"] = "Flüstergärten", + ["enUS"] = "Whispering Gardens", + ["esES"] = "Jardines Susurrantes", + ["frFR"] = "Jardins des murmures", + ["koKR"] = "ì†ì‚­ìž„ì˜ ì •ì›", + ["ptBR"] = "Jardins Sussurrantes", + ["ruRU"] = "Шепчущие Ñады", + ["zhCN"] = "耳语花园", + ["zhTW"] = "耳語花園", + }, + }, + [162] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 10.18, + ["height"] = 34.88, + ["x1"] = 68.26, + ["y1"] = 42.14, + ["names"] = { + ["deDE"] = "Blendwassersee", + ["enUS"] = "Brightwater Lake", + ["esES"] = "Lago Aguasclaras", + ["frFR"] = "Lac Etincelant", + ["koKR"] = "청명 호수", + ["ptBR"] = "Lago Ãguas Claras", + ["ruRU"] = "Озеро ЯÑноводное", + ["zhCN"] = "澈水湖", + ["zhTW"] = "澈水湖", + }, + }, + [164] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 9.58, + ["height"] = 21.86, + ["x1"] = 57.78, + ["y1"] = 38.17, + ["names"] = { + ["deDE"] = "Garrens Schlupfwinkel", + ["enUS"] = "Garren\'s Haunt", + ["esES"] = "El Refugio de Garren", + ["frFR"] = "Antre de Garren", + ["koKR"] = "ê°€ë Œì˜ í‰ê°€", + ["ptBR"] = "Refúgio de Garren", + ["ruRU"] = "Ферма Гаррена", + ["zhCN"] = "加伦鬼屋", + ["zhTW"] = "加倫鬼屋", + }, + }, + [165] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 13.07, + ["height"] = 23.05, + ["x1"] = 72.8, + ["y1"] = 61.68, + ["names"] = { + ["deDE"] = "Balnirs Bauernhof", + ["enUS"] = "Balnir Farmstead", + ["esES"] = "Granja Balnir", + ["frFR"] = "Ferme des Balnir", + ["koKR"] = "발니르 ë†ìž¥", + ["ptBR"] = "Fazenda de Balnir", + ["ruRU"] = "УÑадьба Балнира", + ["zhCN"] = "巴尼尔农场", + ["zhTW"] = "巴尼爾農場", + }, + }, + [166] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 7.29, + ["height"] = 10.63, + ["x1"] = 53.74, + ["y1"] = 59.96, + ["names"] = { + ["deDE"] = "Das Verlassene Anwesen", + ["enUS"] = "Cold Hearth Manor", + ["esES"] = "Mansión Fríogar", + ["frFR"] = "Manoir du Foyer froid", + ["koKR"] = "버려진 장ì›", + ["ptBR"] = "Mansão do Fogo Frio", + ["zhCN"] = "炉ç°åº„å›­", + ["zhTW"] = "çˆç°èŽŠåœ’", + }, + }, + [167] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 14.47, + ["height"] = 15.42, + ["x1"] = 77.89, + ["y1"] = 51.42, + ["names"] = { + ["deDE"] = "Außenposten der Kreuzzügler", + ["enUS"] = "Crusader Outpost", + ["esES"] = "Puesto de los Cruzados", + ["frFR"] = "Avant-poste des Croisés", + ["koKR"] = "붉ì€ì‹­ìžêµ° 전초기지", + ["ptBR"] = "Posto dos Cruzados", + ["ruRU"] = "ЗаÑтава Ðлого Ордена", + ["zhCN"] = "å字军å‰å“¨", + ["zhTW"] = "åå­—è»å‰å“¨", + }, + }, + [172] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 18.96, + ["height"] = 20.21, + ["x1"] = 71.86, + ["y1"] = 28.82, + ["names"] = { + ["deDE"] = "Insel Fenris", + ["enUS"] = "Fenris Isle", + ["esES"] = "Isla Fenris", + ["frFR"] = "Ile de Fenris", + ["koKR"] = "펜리스 섬", + ["ptBR"] = "Ilha Fenris", + ["ruRU"] = "ОÑтров ФенриÑа", + ["zhCN"] = "芬里斯岛", + ["zhTW"] = "芬里斯島", + }, + }, + [186] = { + ["continent"] = 141, + ["parent"] = 141, + ["width"] = 11.48, + ["height"] = 11.98, + ["x1"] = 55.14, + ["y1"] = 56.89, + ["names"] = { + ["deDE"] = "Dolanaar", + ["enUS"] = "Dolanaar", + ["esES"] = "Dolanaar", + ["frFR"] = "Dolanaar", + ["koKR"] = "ëŒë¼ë‚˜ë¥´", + ["ptBR"] = "Dolanaar", + ["ruRU"] = "Доланаар", + ["zhCN"] = "多兰纳尔", + ["zhTW"] = "多蘭ç´çˆ¾", + }, + }, + [188] = { + ["continent"] = 141, + ["parent"] = 141, + ["width"] = 12.48, + ["height"] = 18.71, + ["x1"] = 59.63, + ["y1"] = 40.04, + ["names"] = { + ["deDE"] = "Laubschattental", + ["enUS"] = "Shadowglen", + ["esES"] = "Cañada Sombría", + ["frFR"] = "Sombrevallon", + ["koKR"] = "그늘 협곡", + ["ptBR"] = "Shadowglen", + ["ruRU"] = "ТениÑÑ‚Ð°Ñ Ð´Ð¾Ð»Ð¸Ð½Ð°", + ["zhCN"] = "幽影谷", + ["zhTW"] = "幽影谷", + }, + }, + [190] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 21.46, + ["height"] = 19.46, + ["x1"] = 47.16, + ["y1"] = 17.96, + ["names"] = { + ["deDE"] = "Herdweiler", + ["enUS"] = "Hearthglen", + ["esES"] = "Vega del Amparo", + ["frFR"] = "Hearthglen", + ["koKR"] = "하스글렌", + ["ptBR"] = "Vila da Lareira", + ["ruRU"] = "Дольный Очаг", + ["zhCN"] = "å£ç‚‰è°·", + ["zhTW"] = "å£çˆè°·", + }, + }, + [192] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 9.98, + ["height"] = 14.22, + ["x1"] = 47.41, + ["y1"] = 36.3, + ["names"] = { + ["deDE"] = "Sägewerk des Nordkamms", + ["enUS"] = "Northridge Lumber Camp", + ["esES"] = "Campamento Maderero de Northridge", + ["frFR"] = "Camp de bûcherons de la Crête du nord", + ["koKR"] = "ë¶ë§ˆë£¨ 벌목지", + ["ptBR"] = "Acampamento de Madeira de Northridge", + ["ruRU"] = "ЛеÑопилка Северного КрÑжа", + ["zhCN"] = "åŒ—å±±ä¼æœ¨åœº", + ["zhTW"] = "åŒ—å±±ä¼æœ¨å ´", + }, + }, + [193] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 11.48, + ["height"] = 12.72, + ["x1"] = 43.66, + ["y1"] = 69.24, + ["names"] = { + ["deDE"] = "Die Ruinen von Andorhal", + ["enUS"] = "Ruins of Andorhal", + ["esES"] = "Ruinas de Andorhal", + ["frFR"] = "Ruines d\'Andorhal", + ["koKR"] = "안ëŒí•  íí—ˆ", + ["ptBR"] = "Ruínas de Andorhal", + ["ruRU"] = "Руины Ðндорала", + ["zhCN"] = "安多哈尔废墟", + ["zhTW"] = "安多哈爾廢墟", + }, + }, + [197] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 9.48, + ["height"] = 13.47, + ["x1"] = 51.65, + ["y1"] = 79.34, + ["names"] = { + ["deDE"] = "Trauerhügel", + ["enUS"] = "Sorrow Hill", + ["esES"] = "Colina de la Tristeza", + ["frFR"] = "Colline des chagrins", + ["koKR"] = "ìŠ¬í””ì˜ ì–¸ë•", + ["ptBR"] = "Colina Sorrow", + ["ruRU"] = "Холм Печали", + ["zhCN"] = "æ‚”æ¨å²­", + ["zhTW"] = "æ‚”æ¨å¶º", + }, + }, + [198] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 7.49, + ["height"] = 12.72, + ["x1"] = 65.12, + ["y1"] = 42.29, + ["names"] = { + ["deDE"] = "Die Weinende Höhle", + ["enUS"] = "The Weeping Cave", + ["esES"] = "La Cueva del LLanto", + ["frFR"] = "La grotte des Pleurs", + ["koKR"] = "ì§„í™íƒ• ë™êµ´", + ["ptBR"] = "A Caverna Llorosa", + ["ruRU"] = "Грот Слез", + ["zhCN"] = "哭泣洞穴", + ["zhTW"] = "哭泣之洞", + }, + }, + [199] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 7.98, + ["height"] = 9.73, + ["x1"] = 37.43, + ["y1"] = 55.76, + ["names"] = { + ["deDE"] = "Teufelssteinfeld", + ["enUS"] = "Felstone Field", + ["esES"] = "Campo de Piedra Vil", + ["frFR"] = "Champ de Felstone", + ["koKR"] = "펠스톤 ë†ìž¥", + ["ptBR"] = "Campo da Pedra Vil", + ["ruRU"] = "Поле ДжаниÑ", + ["zhCN"] = "费尔斯通农场", + ["zhTW"] = "費爾斯通農場", + }, + }, + [200] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 8.98, + ["height"] = 11.23, + ["x1"] = 46.41, + ["y1"] = 52.77, + ["names"] = { + ["deDE"] = "Dalsons Tränenfeld", + ["enUS"] = "Dalson\'s Tears", + ["esES"] = "Lágrimas de Dalson", + ["frFR"] = "Larmes de Dalson", + ["koKR"] = "ë‹¬ìŠ¨ì˜ ëˆˆë¬¼", + ["ptBR"] = "Lágrimas de Dalson", + ["ruRU"] = "Слезы ДалÑона", + ["zhCN"] = "达尔æ¾ä¹‹æ³ª", + ["zhTW"] = "é”爾æ¾ä¹‹æ·š", + }, + }, + [201] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 9.98, + ["height"] = 11.23, + ["x1"] = 62.87, + ["y1"] = 57.26, + ["names"] = { + ["deDE"] = "Gahrrons Trauerfeld", + ["enUS"] = "Gahrron\'s Withering", + ["esES"] = "Marchitamiento de Gahrron", + ["frFR"] = "La Flétrissure de Gahrron", + ["koKR"] = "ê°€ë¡ ì˜ í‰ê°€", + ["ptBR"] = "Praga de Gahrron", + ["ruRU"] = "ПуÑтошь Гаррона", + ["zhCN"] = "ç›–ç½—æ©å†œåœº", + ["zhTW"] = "è“‹ç¾…æ©è¾²å ´", + }, + }, + [202] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 6.99, + ["height"] = 9.73, + ["x1"] = 53.39, + ["y1"] = 65.49, + ["names"] = { + ["deDE"] = "Das Trostlose Feld", + ["enUS"] = "The Writhing Haunt", + ["esES"] = "El Refugio Retorcido", + ["frFR"] = "Le Repaire putride", + ["koKR"] = "ê³ í†µì˜ í‰ê°€", + ["ptBR"] = "O Embrujo Retorcido", + ["ruRU"] = "Удел Страданий", + ["zhCN"] = "嚎哭鬼屋", + ["zhTW"] = "嚎哭鬼屋", + }, + }, + [204] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 7.98, + ["height"] = 8.23, + ["x1"] = 44.91, + ["y1"] = 73.73, + ["names"] = { + ["deDE"] = "Lohenscheit", + ["enUS"] = "Pyrewood Village", + ["esES"] = "Aldea Pyrewood", + ["frFR"] = "Bois-du-Bûcher", + ["koKR"] = "장작나무 마ì„", + ["ptBR"] = "Aldeia de Pyrewood", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ ÐŸÐ¾Ð³Ñ€ÐµÐ±Ð°Ð»ÑŒÐ½Ñ‹Ñ… КоÑтров", + ["zhCN"] = "焚木æ‘", + ["zhTW"] = "焚木æ‘", + }, + }, + [205] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 9.98, + ["height"] = 9.73, + ["x1"] = 49.4, + ["y1"] = 16.84, + ["names"] = { + ["deDE"] = "Dun Modr", + ["enUS"] = "Dun Modr", + ["esES"] = "Dun Modr", + ["frFR"] = "Dun Modr", + ["koKR"] = "ë˜ ëª¨ë“œë¥´", + ["ptBR"] = "Dun Modr", + ["ruRU"] = "Дун Модр", + ["zhCN"] = "丹莫德", + ["zhTW"] = "丹莫德", + }, + }, + [211] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 6.99, + ["height"] = 18.71, + ["x1"] = 34.93, + ["y1"] = 38.55, + ["names"] = { + ["deDE"] = "Eiswellensee", + ["enUS"] = "Iceflow Lake", + ["esES"] = "Lago del Flujo de Hielo", + ["frFR"] = "Lac glacial", + ["koKR"] = "ì–¼ìŒ í˜¸ìˆ˜", + ["ptBR"] = "Lago Nevado", + ["ruRU"] = "Заледеневшее озеро", + ["zhCN"] = "涌冰湖", + ["zhTW"] = "湧冰湖", + }, + }, + [212] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 7.98, + ["height"] = 14.22, + ["x1"] = 76.85, + ["y1"] = 54.27, + ["names"] = { + ["deDE"] = "Helmsbettsee", + ["enUS"] = "Helm\'s Bed Lake", + ["esES"] = "Lago del Lecho de Helm", + ["frFR"] = "Lac du Lit d\'Helm", + ["koKR"] = "투구바닥 호수", + ["ptBR"] = "Lago da Cama do Elmo", + ["ruRU"] = "Озеро Хельмово Ложе", + ["zhCN"] = "盔枕湖", + ["zhTW"] = "盔枕湖", + }, + }, + [213] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 6.49, + ["height"] = 11.23, + ["x1"] = 56.14, + ["y1"] = 48.28, + ["names"] = { + ["deDE"] = "Tiefenfelsmine", + ["enUS"] = "Deep Elem Mine", + ["esES"] = "Mina de Elementos Profundos", + ["frFR"] = "Mine du gouffre d\'Elem", + ["koKR"] = "ì‹¬ì› ê´‘ì‚°", + ["ptBR"] = "Mina Profunda de Elem", + ["ruRU"] = "СеребрÑный рудник", + ["zhCN"] = "埃利姆矿洞", + ["zhTW"] = "埃利姆礦å‘", + }, + }, + [219] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 11.48, + ["height"] = 16.47, + ["x1"] = 38.17, + ["y1"] = 52.4, + ["names"] = { + ["deDE"] = "Alexstons Bauernhof", + ["enUS"] = "Alexston Farmstead", + ["esES"] = "Granja Alexston", + ["frFR"] = "Ferme des Alexston", + ["koKR"] = "알렉스턴 ë†ìž¥", + ["ptBR"] = "Fazenda de Alexston", + ["ruRU"] = "ПомеÑтье ÐлекÑтона", + ["zhCN"] = "阿历克斯顿农场", + ["zhTW"] = "艾力克斯頓農場", + }, + }, + [220] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 21.96, + ["height"] = 21.71, + ["x1"] = 44.91, + ["y1"] = 82.71, + ["names"] = { + ["deDE"] = "Hochwolkenebene", + ["enUS"] = "Red Cloud Mesa", + ["esES"] = "Meseta Nube Roja", + ["frFR"] = "Mesa de Nuage rouge", + ["koKR"] = "붉ì€êµ¬ë¦„ ê³ ì›", + ["ptBR"] = "Mesa Nuvem Vermelha", + ["ruRU"] = "Плато КраÑного Облака", + ["zhCN"] = "红云å°åœ°", + ["zhTW"] = "紅雲å°åœ°", + }, + }, + [222] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 11.98, + ["height"] = 14.97, + ["x1"] = 47.41, + ["y1"] = 59.13, + ["names"] = { + ["deDE"] = "Dorf der Bluthufe", + ["enUS"] = "Bloodhoof Village", + ["esES"] = "Aldea Pezuña de Sangre", + ["frFR"] = "Village Bloodhoof", + ["koKR"] = "블러드후프 마ì„", + ["ptBR"] = "Aldeia Casco Sangrento", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ ÐšÑ€Ð¾Ð²Ð°Ð²Ð¾Ð³Ð¾ Копыта", + ["zhCN"] = "血蹄æ‘", + ["zhTW"] = "血蹄æ‘", + }, + }, + [224] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 7.98, + ["height"] = 8.98, + ["x1"] = 54.39, + ["y1"] = 47.16, + ["names"] = { + ["deDE"] = "Überfallene Karawane", + ["enUS"] = "Ravaged Caravan", + ["esES"] = "Caravana Devastada", + ["frFR"] = "Caravane dévastée", + ["koKR"] = "습격당한 ì§ë§ˆì°¨ 행렬", + ["ptBR"] = "Caravana Destruída", + ["ruRU"] = "Разграбленный караван", + ["zhCN"] = "被破å的货车", + ["zhTW"] = "被破壞的貨車", + }, + }, + [225] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 11.48, + ["height"] = 17.96, + ["x1"] = 61.13, + ["y1"] = 20.21, + ["names"] = { + ["deDE"] = "Teufelsfelsen", + ["enUS"] = "Red Rocks", + ["esES"] = "Rocas Rojas", + ["frFR"] = "Rochers rouges", + ["koKR"] = "ë¶‰ì€ ë°”ìœ„ ì–¸ë•", + ["ptBR"] = "Rochas Vermelhas", + ["ruRU"] = "КраÑные Ñкалы", + ["zhCN"] = "赤色石", + ["zhTW"] = "赤色石", + }, + }, + [226] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 9.98, + ["height"] = 10.48, + ["x1"] = 37.43, + ["y1"] = 15.72, + ["names"] = { + ["deDE"] = "Das Huschdunkel", + ["enUS"] = "The Skittering Dark", + ["esES"] = "La Oscuridad Escurridiza", + ["frFR"] = "Le Grouillement", + ["koKR"] = "ì•”í‘ ë•…ê±°ë¯¸êµ´", + ["ptBR"] = "A Escuridão Escorregadia", + ["ruRU"] = "СтруÑщаÑÑÑ Ð¢ÑŒÐ¼Ð°", + ["zhCN"] = "ç²˜ä¸æ´ž", + ["zhTW"] = "粘絲洞", + }, + }, + [228] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 8.48, + ["height"] = 9.73, + ["x1"] = 44.66, + ["y1"] = 40.79, + ["names"] = { + ["deDE"] = "Das Grabmal", + ["enUS"] = "The Sepulcher", + ["esES"] = "El Sepulcro", + ["frFR"] = "Le Sépulcre", + ["koKR"] = "ê³µë™ë¬˜ì§€", + ["ptBR"] = "O Sepulcro", + ["ruRU"] = "Гробница", + ["zhCN"] = "瑟伯切尔", + ["zhTW"] = "瑟伯切爾", + }, + }, + [229] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 7.98, + ["height"] = 8.98, + ["x1"] = 46.91, + ["y1"] = 53.89, + ["names"] = { + ["deDE"] = "Olsens Acker", + ["enUS"] = "Olsen\'s Farthing", + ["esES"] = "La Moneda de Olsen", + ["frFR"] = "Ferme des Olsen", + ["koKR"] = "올슨 ë†ìž¥", + ["ptBR"] = "Fazenda de Olsen", + ["ruRU"] = "Удел ОльÑена", + ["zhCN"] = "奥森农场", + ["zhTW"] = "奧森農場", + }, + }, + [230] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 8.98, + ["height"] = 13.47, + ["x1"] = 46.41, + ["y1"] = 86.08, + ["names"] = { + ["deDE"] = "Der Graumähnenwall", + ["enUS"] = "The Greymane Wall", + ["esES"] = "La Muralla de Cringris", + ["frFR"] = "Le mur de Greymane", + ["koKR"] = "그레ì´ë©”ì¸ ì„±ë²½", + ["ptBR"] = "A Muralha Greymane", + ["ruRU"] = "Стена Седогрива", + ["zhCN"] = "格雷迈æ©ä¹‹å¢™", + ["zhTW"] = "æ ¼é›·é‚æ©ä¹‹ç‰†", + }, + }, + [231] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 7.98, + ["height"] = 9.73, + ["x1"] = 58.88, + ["y1"] = 72.23, + ["names"] = { + ["deDE"] = "Berens List", + ["enUS"] = "Beren\'s Peril", + ["esES"] = "El Peligro de Beren", + ["frFR"] = "Péril de Beren", + ["koKR"] = "ë² ë Œì˜ ë™êµ´", + ["ptBR"] = "Perigo de Beren", + ["ruRU"] = "Погибель Берена", + ["zhCN"] = "åšä¼¦çš„巢穴", + ["zhTW"] = "åšå€«çš„巢穴", + }, + }, + [233] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 10.98, + ["height"] = 14.97, + ["x1"] = 63.37, + ["y1"] = 62.87, + ["names"] = { + ["deDE"] = "Mühlenbern", + ["enUS"] = "Ambermill", + ["esES"] = "Molino de Ãmbar", + ["frFR"] = "Moulin-de-l\'Ambre", + ["koKR"] = "앰버밀", + ["ptBR"] = "Moinho Âmbar", + ["ruRU"] = "Ð¯Ð½Ñ‚Ð°Ñ€Ð½Ð°Ñ Ð¼ÐµÐ»ÑŒÐ½Ð¸Ñ†Ð°", + ["zhCN"] = "安伯米尔", + ["zhTW"] = "安伯米爾", + }, + }, + [236] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 10.48, + ["height"] = 9.73, + ["x1"] = 44.16, + ["y1"] = 66.24, + ["names"] = { + ["deDE"] = "Burg Schattenfang", + ["enUS"] = "Shadowfang Keep", + ["esES"] = "Fortaleza Colmillo Oscuro", + ["frFR"] = "Donjon d\'Ombrecroc", + ["koKR"] = "그림ìžì†¡ê³³ë‹ˆ 성채", + ["ptBR"] = "Fortaleza Shadowfang", + ["ruRU"] = "КрепоÑть Темного Клыка", + ["zhCN"] = "影牙城堡", + ["zhTW"] = "影牙城堡", + }, + }, + [237] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 8.98, + ["height"] = 11.23, + ["x1"] = 55.39, + ["y1"] = 36.3, + ["names"] = { + ["deDE"] = "Die Verfallene Fähre", + ["enUS"] = "The Decrepit Ferry", + ["esES"] = "El Viejo Embarcadero", + ["frFR"] = "Le bac délabré", + ["koKR"] = "ì˜¤ëž˜ëœ ë‚˜ë£¨í„°", + ["ptBR"] = "A Balsa Decrépita", + ["ruRU"] = "Ð¡Ñ‚Ð°Ñ€Ð°Ñ Ð¿ÐµÑ€ÐµÐ¿Ñ€Ð°Ð²Ð°", + ["zhCN"] = "破旧渡å£", + ["zhTW"] = "破舊渡å£", + }, + }, + [238] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 6.49, + ["height"] = 5.99, + ["x1"] = 56.64, + ["y1"] = 10.48, + ["names"] = { + ["deDE"] = "Maldens Obsthain", + ["enUS"] = "Malden\'s Orchard", + ["esES"] = "Huerto de Malden", + ["frFR"] = "Verger de Malden", + ["koKR"] = "ë§ë´ì˜ 과수ì›", + ["ptBR"] = "Huerta de Malden", + ["ruRU"] = "Сад Мальдена", + ["zhCN"] = "çŽ›å°”ä¸æžœå›­", + ["zhTW"] = "ç‘ªçˆ¾ä¸æžœåœ’", + }, + }, + [240] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 6.99, + ["height"] = 8.98, + ["x1"] = 46.41, + ["y1"] = 21.71, + ["names"] = { + ["deDE"] = "Das Todesfeld", + ["enUS"] = "The Dead Field", + ["esES"] = "El Campo Muerto", + ["frFR"] = "Le Champ des morts", + ["koKR"] = "죽ìŒì˜ ë†ìž¥", + ["ptBR"] = "O Campo Morto", + ["ruRU"] = "Мертвое поле", + ["zhCN"] = "亡者农场", + ["zhTW"] = "亡者農場", + }, + }, + [241] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 12.48, + ["height"] = 17.22, + ["x1"] = 64.12, + ["y1"] = 72.98, + ["names"] = { + ["deDE"] = "Der Verlassene Obstgarten", + ["enUS"] = "The Rotting Orchard", + ["esES"] = "El Huerto Podrido", + ["frFR"] = "Le verger pourrissant", + ["koKR"] = "ì˜¤ì—¼ëœ ê³¼ìˆ˜ì›", + ["ptBR"] = "O Pomar Podre", + ["ruRU"] = "Гниющий Ñад", + ["zhCN"] = "烂果园", + ["zhTW"] = "爛果園", + }, + }, + [242] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 11.48, + ["height"] = 32.19, + ["x1"] = 62.62, + ["y1"] = 40.79, + ["names"] = { + ["deDE"] = "Schattenhain", + ["enUS"] = "Brightwood Grove", + ["esES"] = "Bosque del Ocaso", + ["frFR"] = "Bosquet de Clairbois", + ["koKR"] = "ë°ì€ìˆ²", + ["ptBR"] = "Bosque de Lenha Brilhante", + ["ruRU"] = "Ð¡Ð²ÐµÑ‚Ð»Ð°Ñ Ñ€Ð¾Ñ‰Ð°", + ["zhCN"] = "阳光树林", + ["zhTW"] = "陽光樹林", + }, + }, + [245] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 9.48, + ["height"] = 14.97, + ["x1"] = 49.15, + ["y1"] = 73.35, + ["names"] = { + ["deDE"] = "Yorgens Bauernhof", + ["enUS"] = "The Yorgen Farmstead", + ["esES"] = "Granja Yorgen", + ["frFR"] = "La ferme des Yorgen", + ["koKR"] = "ìš”ë¥´ê² ë†ìž¥", + ["ptBR"] = "A Granja Yorgen", + ["ruRU"] = "УÑадьба Йоргена", + ["zhCN"] = "约根农场", + ["zhTW"] = "猶根農場", + }, + }, + [246] = { + ["continent"] = 51, + ["parent"] = 51, + ["width"] = 29.44, + ["height"] = 20.96, + ["x1"] = 46.16, + ["y1"] = 49.4, + ["names"] = { + ["deDE"] = "Der Kessel", + ["enUS"] = "The Cauldron", + ["esES"] = "El Caldero", + ["frFR"] = "Le Chaudron", + ["koKR"] = "용광로", + ["ptBR"] = "O Caldeirão", + ["ruRU"] = "Котлован", + ["zhCN"] = "大熔炉", + ["zhTW"] = "大熔çˆ", + }, + }, + [247] = { + ["continent"] = 51, + ["parent"] = 51, + ["width"] = 17.96, + ["height"] = 14.22, + ["x1"] = 64.87, + ["y1"] = 58.76, + ["names"] = { + ["deDE"] = "Rußschlacks Grabungsstätte", + ["enUS"] = "Grimesilt Dig Site", + ["esES"] = "Sitio de Excavación Grimesilt", + ["frFR"] = "Site de fouilles de Grimesilt", + ["koKR"] = "검댕가루 발굴현장", + ["ptBR"] = "Sítio de Escavação Fuliginosa", + ["ruRU"] = "Карьер ГрÑзнули", + ["zhCN"] = "煤渣挖掘场", + ["zhTW"] = "煤渣挖掘場", + }, + }, + [249] = { + ["continent"] = 46, + ["parent"] = 46, + ["width"] = 13.97, + ["height"] = 21.71, + ["x1"] = 81.34, + ["y1"] = 41.54, + ["names"] = { + ["deDE"] = "Schreckensfels", + ["enUS"] = "Dreadmaul Rock", + ["esES"] = "Roca Dreadmaul", + ["frFR"] = "Rocher des Cognepeurs", + ["koKR"] = "ìš°ë ˆë§ì¹˜ 바위굴", + ["ptBR"] = "Rochedo Muralha do Medo", + ["ruRU"] = "Скала Молота УжаÑа", + ["zhCN"] = "巨槌石", + ["zhTW"] = "巨槌石", + }, + }, + [250] = { + ["continent"] = 46, + ["parent"] = 46, + ["width"] = 20.46, + ["height"] = 25.45, + ["x1"] = 65.12, + ["y1"] = 34.43, + ["names"] = { + ["deDE"] = "Die Ruinen von Thaurissan", + ["enUS"] = "Ruins of Thaurissan", + ["esES"] = "Ruinas de Thaurissan", + ["frFR"] = "Ruines de Thaurissan", + ["koKR"] = "íƒ€ìš°ë¦¿ì‚°ì˜ íí—ˆ", + ["ptBR"] = "Ruínas de Thaurissan", + ["ruRU"] = "Руины ТауриÑÑана", + ["zhCN"] = "索瑞森废墟", + ["zhTW"] = "索瑞森廢墟", + }, + }, + [252] = { + ["continent"] = 46, + ["parent"] = 46, + ["width"] = 13.47, + ["height"] = 22.46, + ["x1"] = 43.66, + ["y1"] = 35.18, + ["names"] = { + ["deDE"] = "Schwarzfelsfestung", + ["enUS"] = "Blackrock Stronghold", + ["esES"] = "Cumbre de Roca Negra", + ["frFR"] = "Bastion des Blackrock", + ["koKR"] = "ê²€ì€ë°”위 요새", + ["ptBR"] = "Fortaleza Rocha Negra", + ["ruRU"] = "КрепоÑть Черной горы", + ["zhCN"] = "黑石è¦å¡ž", + ["zhTW"] = "黑石è¦å¡ž", + }, + }, + [253] = { + ["continent"] = 46, + ["parent"] = 46, + ["width"] = 17.47, + ["height"] = 15.72, + ["x1"] = 50.15, + ["y1"] = 56.51, + ["names"] = { + ["deDE"] = "Die Aschensäule", + ["enUS"] = "The Pillar of Ash", + ["esES"] = "El Pilar de Ceniza", + ["frFR"] = "Le Pilier de cendres", + ["koKR"] = "잿빛 기둥", + ["ptBR"] = "O Pilar de Cinzas", + ["ruRU"] = "Ð‘Ð°ÑˆÐ½Ñ ÐŸÐµÐ¿Ð»Ð°", + ["zhCN"] = "ç°çƒ¬ä¹‹æŸ±", + ["zhTW"] = "ç°ç‡¼ä¹‹æŸ±", + }, + }, + [254] = { + ["continent"] = 46, + ["parent"] = 46, + ["width"] = 15.97, + ["height"] = 20.21, + ["x1"] = 29.94, + ["y1"] = 31.81, + ["names"] = { + ["deDE"] = "Der Schwarzfels", + ["enUS"] = "Blackrock Mountain", + ["esES"] = "Montaña Roca Negra", + ["frFR"] = "Mont Blackrock", + ["koKR"] = "ê²€ì€ë°”위 ì‚°", + ["ptBR"] = "Montanha Rocha Negra", + ["ruRU"] = "Ð§ÐµÑ€Ð½Ð°Ñ Ð³Ð¾Ñ€Ð°", + ["zhCN"] = "黑石山", + ["zhTW"] = "黑石山", + }, + }, + [255] = { + ["continent"] = 46, + ["parent"] = 46, + ["width"] = 11.48, + ["height"] = 19.46, + ["x1"] = 15.72, + ["y1"] = 32.19, + ["names"] = { + ["deDE"] = "Altar der Stürme", + ["enUS"] = "Altar of Storms", + ["esES"] = "Altar de Tormentas", + ["frFR"] = "Autel des tempêtes", + ["koKR"] = "í­í’ì˜ ì œë‹¨", + ["ptBR"] = "Altar das Tempestades", + ["ruRU"] = "Ðлтарь Бурь", + ["zhCN"] = "风暴祭å›", + ["zhTW"] = "暴風祭壇", + }, + }, + [259] = { + ["continent"] = 141, + ["parent"] = 141, + ["width"] = 16.47, + ["height"] = 14.97, + ["x1"] = 57.63, + ["y1"] = 71.11, + ["names"] = { + ["deDE"] = "Al\'Amethsee", + ["enUS"] = "Lake Al\'Ameth", + ["esES"] = "Lago Al\'Ameth", + ["frFR"] = "Lac Al\'Ameth", + ["koKR"] = "알아메스 호수", + ["ptBR"] = "Lago Al\'Ameth", + ["ruRU"] = "Озеро Ðль\'Ðмет", + ["zhCN"] = "奥拉密斯湖", + ["zhTW"] = "奧拉密斯湖", + }, + }, + [260] = { + ["continent"] = 141, + ["parent"] = 141, + ["width"] = 10.48, + ["height"] = 11.98, + ["x1"] = 65.62, + ["y1"] = 57.63, + ["names"] = { + ["deDE"] = "Sternenhauch", + ["enUS"] = "Starbreeze Village", + ["esES"] = "Aldea Starbreeze", + ["frFR"] = "Brise-stellaire", + ["koKR"] = "별바람 마ì„", + ["ptBR"] = "Aldeia Starbreeze", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ Ð—Ð²ÐµÐ·Ð´Ð½Ð¾Ð³Ð¾ Ветра", + ["zhCN"] = "星风æ‘", + ["zhTW"] = "星風æ‘", + }, + }, + [261] = { + ["continent"] = 141, + ["parent"] = 141, + ["width"] = 7.98, + ["height"] = 10.48, + ["x1"] = 43.91, + ["y1"] = 74.85, + ["names"] = { + ["deDE"] = "Höhle der Knarzklauen", + ["enUS"] = "Gnarlpine Hold", + ["esES"] = "Fuerte Gnarlpine", + ["frFR"] = "Camp des Pins-tordus", + ["koKR"] = "ë‚˜ë¬´ì˜¹ì´ ìš”ìƒˆ", + ["ptBR"] = "Covil dos Narigudos", + ["ruRU"] = "Лагерь у Кривой СоÑны", + ["zhCN"] = "脊骨堡", + ["zhTW"] = "脊骨堡", + }, + }, + [264] = { + ["continent"] = 141, + ["parent"] = 141, + ["width"] = 5.99, + ["height"] = 21.71, + ["x1"] = 37.92, + ["y1"] = 36.3, + ["names"] = { + ["deDE"] = "Die Lichtung des Orakels", + ["enUS"] = "The Oracle Glade", + ["esES"] = "El Claro del Oráculo", + ["frFR"] = "La Clairière de l\'Oracle", + ["koKR"] = "ì‹ íƒì˜ 숲", + ["ptBR"] = "O Bosque do Oráculo", + ["ruRU"] = "ПолÑна Оракула", + ["zhCN"] = "神谕林地", + ["zhTW"] = "神諭林地", + }, + }, + [266] = { + ["continent"] = 141, + ["parent"] = 141, + ["width"] = 10.98, + ["height"] = 21.71, + ["x1"] = 46.91, + ["y1"] = 35.55, + ["names"] = { + ["deDE"] = "Quellsee", + ["enUS"] = "Wellspring Lake", + ["esES"] = "Lago Manantial", + ["frFR"] = "Lac d\'Aigue-vive", + ["koKR"] = "ìƒëª…ì˜ í˜¸ìˆ˜", + ["ptBR"] = "Lago das Fontes", + ["ruRU"] = "Родниковое озеро", + ["zhCN"] = "涌泉湖", + ["zhTW"] = "湧泉湖", + }, + }, + [271] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 10.48, + ["height"] = 17.96, + ["x1"] = 49.65, + ["y1"] = 55.39, + ["names"] = { + ["deDE"] = "Süderstade", + ["enUS"] = "Southshore", + ["esES"] = "Costa Sur", + ["frFR"] = "Southshore", + ["koKR"] = "사우스쇼어", + ["ptBR"] = "Southshore", + ["ruRU"] = "Южнобережье", + ["zhCN"] = "å—æµ·é•‡", + ["zhTW"] = "å—æµ·éŽ®", + }, + }, + [272] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 13.47, + ["height"] = 21.71, + ["x1"] = 61.13, + ["y1"] = 25.82, + ["names"] = { + ["deDE"] = "Tarrens Mühle", + ["enUS"] = "Tarren Mill", + ["esES"] = "Molino Tarren", + ["frFR"] = "Moulin-de-Tarren", + ["koKR"] = "타렌 ë°€ë†ìž¥", + ["ptBR"] = "Moinho Tarren", + ["ruRU"] = "Мельница Таррен", + ["zhCN"] = "塔伦米尔", + ["zhTW"] = "塔倫米爾", + }, + }, + [275] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 12.97, + ["height"] = 15.72, + ["x1"] = 76.85, + ["y1"] = 40.79, + ["names"] = { + ["deDE"] = "Burg Durnholde", + ["enUS"] = "Durnholde Keep", + ["esES"] = "Fortaleza Durnholde", + ["frFR"] = "Donjon de Durnholde", + ["koKR"] = "ë˜í™€ë“œ 요새", + ["ptBR"] = "Bastilha de Durnholde", + ["ruRU"] = "КрепоÑть Дарнхольд", + ["zhCN"] = "敦éœå°”德城堡", + ["zhTW"] = "敦éœçˆ¾å¾·åŸŽå ¡", + }, + }, + [278] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 8.98, + ["height"] = 12.72, + ["x1"] = 20.46, + ["y1"] = 84.96, + ["names"] = { + ["deDE"] = "Lordamere-Internierungslager", + ["enUS"] = "Lordamere Internment Camp", + ["esES"] = "Campo de Internamiento Lordamere", + ["frFR"] = "Camp d\'internement de Lordamere", + ["koKR"] = "로다미어 í¬ë¡œìˆ˜ìš©ì†Œ", + ["ptBR"] = "Campo de Internamiento de Lordamere", + ["ruRU"] = "ЛордамерÑкие выÑелки", + ["zhCN"] = "洛丹米尔收容所", + ["zhTW"] = "洛丹米爾收容所", + }, + }, + [279] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 15.47, + ["height"] = 23.95, + ["x1"] = 15.22, + ["y1"] = 65.87, + ["names"] = { + ["deDE"] = "Dalaran", + ["enUS"] = "Dalaran", + ["esES"] = "Dalaran", + ["frFR"] = "Dalaran", + ["koKR"] = "달ë¼ëž€", + ["ptBR"] = "Dalaran", + ["ruRU"] = "Даларан", + ["zhCN"] = "达拉然", + ["zhTW"] = "锿‹‰ç„¶", + }, + }, + [280] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 12.97, + ["height"] = 14.22, + ["x1"] = 63.37, + ["y1"] = 45.28, + ["names"] = { + ["deDE"] = "Strahnbrad", + ["enUS"] = "Strahnbrad", + ["esES"] = "Strahnbrad", + ["frFR"] = "Strahnbrad", + ["koKR"] = "스트란브래드", + ["ptBR"] = "Strahnbrad", + ["ruRU"] = "Странбрад", + ["zhCN"] = "æ–¯å¦æ©å¸ƒèޱ德", + ["zhTW"] = "æ–¯å¦æ©å¸ƒèŠå¾·", + }, + }, + [281] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 13.97, + ["height"] = 18.71, + ["x1"] = 39.92, + ["y1"] = 53.52, + ["names"] = { + ["deDE"] = "Die Ruinen von Alterac", + ["enUS"] = "Ruins of Alterac", + ["esES"] = "Ruinas de Alterac", + ["frFR"] = "Ruines d\'Alterac", + ["koKR"] = "알터랙 íí—ˆ", + ["ptBR"] = "Ruínas de Alterac", + ["ruRU"] = "Руины Ðльтерака", + ["zhCN"] = "奥特兰克废墟", + ["zhTW"] = "奧特蘭克廢墟", + }, + }, + [282] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 9.98, + ["height"] = 19.46, + ["x1"] = 48.4, + ["y1"] = 41.92, + ["names"] = { + ["deDE"] = "Trümmergrathöhle", + ["enUS"] = "Crushridge Hold", + ["esES"] = "Dominios de los Aplastacresta", + ["frFR"] = "Bastion Cassecrête", + ["koKR"] = "산사태 오우거 소굴", + ["ptBR"] = "Fortaleza da Rocha Esmagadora", + ["ruRU"] = "Логово Раздробленного Хребта", + ["zhCN"] = "破碎岭城堡", + ["zhTW"] = "破碎嶺城堡", + }, + }, + [284] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 12.97, + ["height"] = 18.71, + ["x1"] = 56.39, + ["y1"] = 27.32, + ["names"] = { + ["deDE"] = "Das Oberland", + ["enUS"] = "The Uplands", + ["esES"] = "Tierras Altas", + ["frFR"] = "Les Hauteurs", + ["koKR"] = "ê³ ì›", + ["ptBR"] = "As Terras Altas", + ["ruRU"] = "Ð’Ñ‹Ñокогорье", + ["zhCN"] = "高地", + ["zhTW"] = "高地", + }, + }, + [285] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 15.97, + ["height"] = 18.71, + ["x1"] = 15.47, + ["y1"] = 46.03, + ["names"] = { + ["deDE"] = "Südwacht", + ["enUS"] = "Southpoint Tower", + ["esES"] = "Torre Puntosur", + ["frFR"] = "Tour de la Pointe du Midi", + ["koKR"] = "남부 경비탑", + ["ptBR"] = "Torre Southpoint", + ["ruRU"] = "Ð®Ð¶Ð½Ð°Ñ Ð±Ð°ÑˆÐ½Ñ", + ["zhCN"] = "å—点哨塔", + ["zhTW"] = "å—點哨塔", + }, + }, + [286] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 19.96, + ["height"] = 29.94, + ["x1"] = 34.93, + ["y1"] = 44.91, + ["names"] = { + ["deDE"] = "Die Felder des Hügellands", + ["enUS"] = "Hillsbrad Fields", + ["esES"] = "Laderas de Trabalomas", + ["frFR"] = "Champs d\'Hillsbrad", + ["koKR"] = "ížìŠ¤ë¸Œëž˜ë“œ ë†ìž¥", + ["ptBR"] = "Campos de Eira dos Montes", + ["ruRU"] = "ХилÑбрадÑкие полÑ", + ["zhCN"] = "希尔斯布莱德农场", + ["zhTW"] = "希爾斯布èŠå¾·è¾²å ´", + }, + }, + [288] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 9.48, + ["height"] = 11.23, + ["x1"] = 26.2, + ["y1"] = 57.26, + ["names"] = { + ["deDE"] = "Der Azurschacht", + ["enUS"] = "Azurelode Mine", + ["esES"] = "Mina Azurelode", + ["frFR"] = "Mine d\'Azurelode", + ["koKR"] = "ì²­ê¸ˆì„ ê´‘ì‚°", + ["ptBR"] = "Mina Azurelode", + ["ruRU"] = "Лазуритовый рудник", + ["zhCN"] = "碧玉矿洞", + ["zhTW"] = "碧玉礦å‘", + }, + }, + [289] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 11.98, + ["height"] = 23.95, + ["x1"] = 63.87, + ["y1"] = 55.39, + ["names"] = { + ["deDE"] = "Nethandersiedlung", + ["enUS"] = "Nethander Stead", + ["esES"] = "Granja Nethander", + ["frFR"] = "Ferme des Nethander", + ["koKR"] = "ë„¤ì‚°ë” ë†ìž¥", + ["ptBR"] = "Fazenda de Nethander", + ["ruRU"] = "Владение Ðетандера", + ["zhCN"] = "奈æ‰å¾·å“¨å²—", + ["zhTW"] = "奈æ‰å¾·å´—哨", + }, + }, + [290] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 9.48, + ["height"] = 21.71, + ["x1"] = 72.11, + ["y1"] = 71.48, + ["names"] = { + ["deDE"] = "Dun Garok", + ["enUS"] = "Dun Garok", + ["esES"] = "Dun Garok", + ["frFR"] = "Dun Garok", + ["koKR"] = "ë˜ ê°€ë¡", + ["ptBR"] = "Dun Garok", + ["ruRU"] = "Дун Гарок", + ["zhCN"] = "丹加洛克", + ["zhTW"] = "丹加洛克", + }, + }, + [294] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 13.97, + ["height"] = 17.22, + ["x1"] = 61.88, + ["y1"] = 74.48, + ["names"] = { + ["deDE"] = "Oststrand", + ["enUS"] = "Eastern Strand", + ["esES"] = "Franja Este", + ["frFR"] = "Rivage oriental", + ["koKR"] = "ë™ë¶€ 해안", + ["ptBR"] = "Praia Oriental", + ["ruRU"] = "ВоÑточное побережье", + ["zhCN"] = "东部海滩", + ["zhTW"] = "æ±éƒ¨æµ·ç˜", + }, + }, + [295] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 23.45, + ["height"] = 11.98, + ["x1"] = 35.68, + ["y1"] = 68.11, + ["names"] = { + ["deDE"] = "Weststrand", + ["enUS"] = "Western Strand", + ["esES"] = "Franja Oeste", + ["frFR"] = "Rivage occidental", + ["koKR"] = "서부 해안", + ["ptBR"] = "Praia Ocidental", + ["ruRU"] = "Западное побережье", + ["zhCN"] = "西部海岸", + ["zhTW"] = "西部海岸", + }, + }, + [297] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 7.98, + ["height"] = 11.98, + ["x1"] = 38.92, + ["y1"] = 82.34, + ["names"] = { + ["deDE"] = "Die Insel Jaguero", + ["enUS"] = "Jaguero Isle", + ["esES"] = "Isla Jaguero", + ["frFR"] = "Ile aux jagueros", + ["koKR"] = "ìžêµ¬ì—로 섬", + ["ptBR"] = "Isla Jaguero", + ["ruRU"] = "ОÑтров Жагуаро", + ["zhCN"] = "哈圭罗岛", + ["zhTW"] = "哈圭羅島", + }, + }, + [300] = { + ["continent"] = 8, + ["parent"] = 8, + ["width"] = 13.97, + ["height"] = 83.08, + ["x1"] = 88.32, + ["y1"] = 52.77, + ["names"] = { + ["deDE"] = "Nebelschilfstrand", + ["enUS"] = "Misty Reed Strand", + ["esES"] = "Playa de Misty Reed", + ["frFR"] = "Grève de Brumejonc", + ["koKR"] = "안개갈대 해안", + ["ptBR"] = "Playa Cañas Nebulosas", + ["ruRU"] = "ТроÑтниковый берег", + ["zhCN"] = "芦苇海滩", + ["zhTW"] = "蘆葦海ç˜", + }, + }, + [307] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 7.98, + ["height"] = 32.93, + ["x1"] = 79.34, + ["y1"] = 68.86, + ["names"] = { + ["deDE"] = "Die Aussichtsklippen", + ["enUS"] = "The Overlook Cliffs", + ["esES"] = "Los Acantilados del Mirador", + ["frFR"] = "Les Hauts-Surplombs", + ["koKR"] = "ì „ë§ëŒ€ 절벽", + ["ptBR"] = "Os Penhascos do Mirante", + ["ruRU"] = "ОтвеÑные Ñкалы", + ["zhCN"] = "望海崖", + ["zhTW"] = "望海崖", + }, + }, + [309] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 12.48, + ["height"] = 17.22, + ["x1"] = 44.66, + ["y1"] = 31.06, + ["names"] = { + ["deDE"] = "Eisenbarts Grabmal", + ["enUS"] = "Ironbeard\'s Tomb", + ["esES"] = "Tumba de Ironbeard", + ["frFR"] = "Tombe d\'Ironbeard", + ["koKR"] = "ì•„ì´ì–¸ë¹„ì–´ë“œì˜ ê³ ë¶„", + ["ptBR"] = "Tumba de Ironbeard", + ["ruRU"] = "Гробница Железноборода", + ["zhCN"] = "é“须之墓", + ["zhTW"] = "éµé¬šä¹‹å¢“", + }, + }, + [310] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 5.99, + ["height"] = 8.23, + ["x1"] = 40.92, + ["y1"] = 50.52, + ["names"] = { + ["deDE"] = "Kristalladermine", + ["enUS"] = "Crystalvein Mine", + ["esES"] = "Mina Crystalvein", + ["frFR"] = "Mine aux cristaux", + ["koKR"] = "수정 ê´‘ì‚°", + ["ptBR"] = "Mina Veia Cristalina", + ["ruRU"] = "ХруÑÑ‚Ð°Ð»ÑŒÐ½Ð°Ñ ÑˆÐ°Ñ…Ñ‚Ð°", + ["zhCN"] = "水晶矿洞", + ["zhTW"] = "水晶礦å‘", + }, + }, + [311] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 4.49, + ["height"] = 5.99, + ["x1"] = 39.67, + ["y1"] = 59.13, + ["names"] = { + ["deDE"] = "Ruinen von Aboraz", + ["enUS"] = "Ruins of Aboraz", + ["esES"] = "Ruinas de Aboraz", + ["frFR"] = "Ruines d\'Aboraz", + ["koKR"] = "ì•„ë³´ë¼ì¦ˆì˜ íí—ˆ", + ["ptBR"] = "Ruínas de Aboraz", + ["ruRU"] = "Руины Ðбораза", + ["zhCN"] = "é˜¿åšæ‹‰å…¹åºŸå¢Ÿ", + ["zhTW"] = "é˜¿åšæ‹‰èŒ²å»¢å¢Ÿ", + }, + }, + [313] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 8.48, + ["height"] = 17.96, + ["x1"] = 31.69, + ["y1"] = 31.44, + ["names"] = { + ["deDE"] = "Nordhof", + ["enUS"] = "Northfold Manor", + ["esES"] = "Mansión Northfold", + ["frFR"] = "Manoir de Nordclos", + ["koKR"] = "ë¶ë¶€ìŠµê³¡ 장ì›", + ["ptBR"] = "Mansão Northfold", + ["ruRU"] = "Северное помеÑтье", + ["zhCN"] = "诺斯弗德农场", + ["zhTW"] = "諾斯弗德農場", + }, + }, + [314] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 7.98, + ["height"] = 11.23, + ["x1"] = 61.38, + ["y1"] = 56.51, + ["names"] = { + ["deDE"] = "Go\'Sheks Hof", + ["enUS"] = "Go\'Shek Farm", + ["esES"] = "Granja Go\'Shek", + ["frFR"] = "Ferme de Go\'Shek", + ["koKR"] = "ê³ ì…°í¬ ë†ìž¥", + ["ptBR"] = "Fazenda Go\'Shek", + ["ruRU"] = "Ферма Го\'Шека", + ["zhCN"] = "格沙克农场", + ["zhTW"] = "格沙克農場", + }, + }, + [315] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 9.48, + ["height"] = 10.48, + ["x1"] = 55.14, + ["y1"] = 39.67, + ["names"] = { + ["deDE"] = "Bauernhof der Dabyries", + ["enUS"] = "Dabyrie\'s Farmstead", + ["esES"] = "Granja Dabyrie", + ["frFR"] = "Ferme des Dabyrie", + ["koKR"] = "다비리 ë†ìž¥", + ["ptBR"] = "Fazenda Dabyrie", + ["ruRU"] = "УÑадьба Дабири", + ["zhCN"] = "达比雷农场", + ["zhTW"] = "锿¯”雷農場", + }, + }, + [316] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 13.47, + ["height"] = 20.21, + ["x1"] = 54.14, + ["y1"] = 73.73, + ["names"] = { + ["deDE"] = "Halle der Felsfäuste", + ["enUS"] = "Boulderfist Hall", + ["esES"] = "Sala Puño de Roca", + ["frFR"] = "Hall Rochepoing", + ["koKR"] = "ëŒì£¼ë¨¹ì¼ì¡± 소굴", + ["ptBR"] = "Salão dos Punhos de Pedra", + ["ruRU"] = "КрепоÑть ТÑжелого Кулака", + ["zhCN"] = "石拳大厅", + ["zhTW"] = "石拳大廳", + }, + }, + [317] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 14.47, + ["height"] = 11.23, + ["x1"] = 66.62, + ["y1"] = 66.99, + ["names"] = { + ["deDE"] = "Lager der Bleichborken", + ["enUS"] = "Witherbark Village", + ["esES"] = "Aldea Corteza Marchita", + ["frFR"] = "Village Witherbark", + ["koKR"] = "마른나무ê»ì§ˆ 마ì„", + ["ptBR"] = "Aldeia Witherbark", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ Ð¡ÑƒÑ…Ð¾ÐºÐ¾Ð¶Ð¸Ñ…", + ["zhCN"] = "枯木æ‘", + ["zhTW"] = "枯木æ‘", + }, + }, + [320] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 7.98, + ["height"] = 16.47, + ["x1"] = 45.91, + ["y1"] = 47.16, + ["names"] = { + ["deDE"] = "Die Zuflucht", + ["enUS"] = "Refuge Pointe", + ["esES"] = "Punta del Refugio", + ["frFR"] = "Refuge de l\'Ornière", + ["koKR"] = "임시 주둔지", + ["ptBR"] = "Ponto de Refúgio", + ["ruRU"] = "Опорный пункт", + ["zhCN"] = "é¿éš¾è°·åœ°", + ["zhTW"] = "é¿é›£è°·åœ°", + }, + }, + [321] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 12.48, + ["height"] = 16.47, + ["x1"] = 75.1, + ["y1"] = 36.68, + ["names"] = { + ["deDE"] = "Hammerfall", + ["enUS"] = "Hammerfall", + ["esES"] = "Caída de Martillo", + ["frFR"] = "Trépas-d\'Orgrim", + ["koKR"] = "해머í´", + ["ptBR"] = "Queda do Martelo", + ["ruRU"] = "Павший Молот", + ["zhCN"] = "è½é”¤é•‡", + ["zhTW"] = "è½éŒ˜éŽ®", + }, + }, + [324] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 17.47, + ["height"] = 22.46, + ["x1"] = 22.7, + ["y1"] = 62.13, + ["names"] = { + ["deDE"] = "Burg Stromgarde", + ["enUS"] = "Stromgarde Keep", + ["esES"] = "Fortaleza Stromgarde", + ["frFR"] = "Donjon de Stromgarde", + ["koKR"] = "스트롬가드 요새", + ["ptBR"] = "Fortaleza Stromgarde", + ["ruRU"] = "КрепоÑть Стромгард", + ["zhCN"] = "æ¿€æµå ¡", + ["zhTW"] = "æ¿€æµå ¡", + }, + }, + [327] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 18.96, + ["height"] = 17.22, + ["x1"] = 30.94, + ["y1"] = 79.72, + ["names"] = { + ["deDE"] = "Die Faldirbucht", + ["enUS"] = "Faldir\'s Cove", + ["esES"] = "La Ensenada de Faldir", + ["frFR"] = "La Crique de Faldir", + ["koKR"] = "íŒ”ë””ë¥´ì˜ ë§Œ", + ["ptBR"] = "Enseada de Faldir", + ["ruRU"] = "Бухта Фальдира", + ["zhCN"] = "法迪尔海湾", + ["zhTW"] = "法迪爾海ç£", + }, + }, + [333] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 8.48, + ["height"] = 15.72, + ["x1"] = 63.62, + ["y1"] = 34.06, + ["names"] = { + ["deDE"] = "Kreis der östlichen Bindung", + ["enUS"] = "Circle of East Binding", + ["esES"] = "Círculo del Vínculo Este", + ["frFR"] = "Cercle de lien oriental", + ["koKR"] = "ë™ìª½ ë´‰ì¸ì˜ 마법진", + ["ptBR"] = "Círculo da Ligação Oriental", + ["ruRU"] = "ВоÑточный круг ОбузданиÑ", + ["zhCN"] = "东部ç¦é”¢æ³•阵", + ["zhTW"] = "æ±éƒ¨ç¦éŒ®æ³•陣", + }, + }, + [334] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 10.48, + ["height"] = 17.96, + ["x1"] = 22.21, + ["y1"] = 25.45, + ["names"] = { + ["deDE"] = "Kreis der westlichen Bindung", + ["enUS"] = "Circle of West Binding", + ["esES"] = "Círculo del Vínculo Oeste", + ["frFR"] = "Cercle de lien occidental", + ["koKR"] = "서쪽 ë´‰ì¸ì˜ 마법진", + ["ptBR"] = "Círculo da Ligação Ocidental", + ["ruRU"] = "Западный Круг ОбузданиÑ", + ["zhCN"] = "西部ç¦é”¢æ³•阵", + ["zhTW"] = "西部ç¦éŒ®æ³•陣", + }, + }, + [335] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 10.48, + ["height"] = 14.22, + ["x1"] = 36.68, + ["y1"] = 61.75, + ["names"] = { + ["deDE"] = "Kreis der inneren Bindung", + ["enUS"] = "Circle of Inner Binding", + ["esES"] = "Círculo del Vínculo Interior", + ["frFR"] = "Cercle de lien intérieur", + ["koKR"] = "ë‚´ë¶€ ë´‰ì¸ì˜ 마법진", + ["ptBR"] = "Círculo da Ligação Interna", + ["ruRU"] = "Внутренний Круг ОбузданиÑ", + ["zhCN"] = "内ç¦é”¢æ³•阵", + ["zhTW"] = "å…§ç¦éŒ®æ³•陣", + }, + }, + [336] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 8.48, + ["height"] = 9.73, + ["x1"] = 52.64, + ["y1"] = 52.77, + ["names"] = { + ["deDE"] = "Kreis der äußeren Bindung", + ["enUS"] = "Circle of Outer Binding", + ["esES"] = "Círculo del Vínculo Exterior", + ["frFR"] = "Cercle de lien extérieur", + ["koKR"] = "외부 ë´‰ì¸ì˜ 마법진", + ["ptBR"] = "Círculo da Ligação Externa", + ["ruRU"] = "Внешний Круг ОбузданиÑ", + ["zhCN"] = "外ç¦é”¢æ³•阵", + ["zhTW"] = "外ç¦éŒ®æ³•陣", + }, + }, + [337] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 11.48, + ["height"] = 15.72, + ["x1"] = 15.72, + ["y1"] = 61.75, + ["names"] = { + ["deDE"] = "Apocryphans Ruheplatz", + ["enUS"] = "Apocryphan\'s Rest", + ["esES"] = "Descanso de Apocryphan", + ["frFR"] = "Repos d\'Apocryphan", + ["koKR"] = "ì•„í¬í¬ë¦¬íŒì˜ 안ì‹ì²˜", + ["ptBR"] = "Repouso do Apocryphan", + ["ruRU"] = "Скелет Ðпокрифана", + ["zhCN"] = "圣者之陵", + ["zhTW"] = "è–者之陵", + }, + }, + [338] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 13.97, + ["height"] = 15.72, + ["x1"] = 42.91, + ["y1"] = 33.31, + ["names"] = { + ["deDE"] = "Festung Angor", + ["enUS"] = "Angor Fortress", + ["esES"] = "Fortaleza Angor", + ["frFR"] = "Forteresse d\'Angor", + ["koKR"] = "ê³ í†µì˜ ìš”ìƒˆ", + ["ptBR"] = "Fortaleza de Angor", + ["ruRU"] = "КрепоÑть Ðнгор", + ["zhCN"] = "苦痛堡垒", + ["zhTW"] = "苦痛堡壘", + }, + }, + [339] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 19.96, + ["height"] = 34.43, + ["x1"] = 78.34, + ["y1"] = 50.9, + ["names"] = { + ["deDE"] = "Die Lethlorklamm", + ["enUS"] = "Lethlor Ravine", + ["esES"] = "Barranco Lethlor", + ["frFR"] = "Ravin de Lethlor", + ["koKR"] = "레슬로 협곡", + ["ptBR"] = "Desfiladeiro Lethlor", + ["ruRU"] = "Долина Летлор", + ["zhCN"] = "莱瑟罗峡谷", + ["zhTW"] = "èŠç‘Ÿç¾…峽谷", + }, + }, + [340] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 15.47, + ["height"] = 15.72, + ["x1"] = 8.73, + ["y1"] = 47.53, + ["names"] = { + ["deDE"] = "Kargath", + ["enUS"] = "Kargath", + ["esES"] = "Kargath", + ["frFR"] = "Kargath", + ["koKR"] = "카르가스", + ["ptBR"] = "Kargath", + ["ruRU"] = "Каргат", + ["zhCN"] = "å¡åŠ æ–¯", + ["zhTW"] = "å¡åŠ æ–¯", + }, + }, + [341] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 12.48, + ["height"] = 14.97, + ["x1"] = 66.12, + ["y1"] = 22.46, + ["names"] = { + ["deDE"] = "Camp Kosh", + ["enUS"] = "Camp Kosh", + ["esES"] = "Campamento Kosh", + ["frFR"] = "Camp Kosh", + ["koKR"] = "코쉬 야ì˜ì§€", + ["ptBR"] = "Acampamento Kosh", + ["ruRU"] = "Лагерь Кош", + ["zhCN"] = "柯什è¥åœ°", + ["zhTW"] = "柯什營地", + }, + }, + [342] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 13.97, + ["height"] = 25.45, + ["x1"] = 61.38, + ["y1"] = 72.6, + ["names"] = { + ["deDE"] = "Camp Boff", + ["enUS"] = "Camp Boff", + ["esES"] = "Campamento Boff", + ["frFR"] = "Camp Boff", + ["koKR"] = "보프 야ì˜ì§€", + ["ptBR"] = "Acampamento Boff", + ["ruRU"] = "Лагерь Бофф", + ["zhCN"] = "åšå¤«è¥åœ°", + ["zhTW"] = "åšå¤«ç‡Ÿåœ°", + }, + }, + [344] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 18.46, + ["height"] = 20.96, + ["x1"] = 13.22, + ["y1"] = 81.59, + ["names"] = { + ["deDE"] = "Camp Cagg", + ["enUS"] = "Camp Cagg", + ["esES"] = "Campamento Cagg", + ["frFR"] = "Camp Cagg", + ["koKR"] = "ì¹´ê·¸ 야ì˜ì§€", + ["ptBR"] = "Acampamento Cagg", + ["ruRU"] = "Лагерь КÑгг", + ["zhCN"] = "塿 ¼è¥åœ°", + ["zhTW"] = "塿 ¼ç‡Ÿåœ°", + }, + }, + [345] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 14.47, + ["height"] = 22.46, + ["x1"] = 47.16, + ["y1"] = 74.1, + ["names"] = { + ["deDE"] = "Agmondkuppe", + ["enUS"] = "Agmond\'s End", + ["esES"] = "Fin de Agmond", + ["frFR"] = "Fin d\'Agmond", + ["koKR"] = "ì•„ê·¸ëª¬ë“œì˜ ìµœí›„", + ["ptBR"] = "Fim de Agmond", + ["ruRU"] = "Удел Эгмонда", + ["zhCN"] = "阿戈莫德之末", + ["zhTW"] = "埃格蒙德的營地", + }, + }, + [346] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 13.47, + ["height"] = 15.72, + ["x1"] = 56.64, + ["y1"] = 36.3, + ["names"] = { + ["deDE"] = "Hammerzehs Grabungsstätte", + ["enUS"] = "Hammertoe\'s Digsite", + ["esES"] = "Sitio de Excavación de Hammertoe", + ["frFR"] = "Site de fouilles d\'Hammertoe", + ["koKR"] = "í•´ë¨¸í† ì˜ ë°œêµ´í˜„ìž¥", + ["ptBR"] = "Sítio de Escavação de Martelo", + ["ruRU"] = "Карьер ТÑжелоÑтупа", + ["zhCN"] = "é“趾挖掘场", + ["zhTW"] = "éµè¶¾æŒ–掘場", + }, + }, + [348] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 12.48, + ["height"] = 15.72, + ["x1"] = 14.72, + ["y1"] = 50.52, + ["names"] = { + ["deDE"] = "Nistgipfel", + ["enUS"] = "Aerie Peak", + ["esES"] = "Pico Aerie", + ["frFR"] = "Nid-de-l\'Aigle", + ["koKR"] = "ë§¹ê¸ˆì˜ ë´‰ìš°ë¦¬", + ["ptBR"] = "Ninho da Ãguia", + ["ruRU"] = "Заоблачный пик", + ["zhCN"] = "鹰巢山", + ["zhTW"] = "鷹巢山", + }, + }, + [350] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 11.98, + ["height"] = 18.71, + ["x1"] = 31.44, + ["y1"] = 43.04, + ["names"] = { + ["deDE"] = "Quel\'Danil Jagdhütte", + ["enUS"] = "Quel\'Danil Lodge", + ["esES"] = "Cabaña Quel\'Danil", + ["frFR"] = "Gîte de Quel\'Danil", + ["koKR"] = "ì¿ ì—˜ë‹¤ë‹ ì˜¤ë‘막", + ["ptBR"] = "Pousada de Quel\'Danil", + ["ruRU"] = "Сторожка Кель\'Данил", + ["zhCN"] = "奎尔丹尼å°å±‹", + ["zhTW"] = "奎爾丹尼å°å±‹", + }, + }, + [351] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 8.48, + ["height"] = 11.23, + ["x1"] = 59.13, + ["y1"] = 43.04, + ["names"] = { + ["deDE"] = "Lauerfels", + ["enUS"] = "Skulk Rock", + ["esES"] = "Roca Skulk", + ["frFR"] = "Rocher de l\'Affût", + ["koKR"] = "êµ¼ë²µì´ ë°”ìœ„êµ´", + ["ptBR"] = "Pedra Skulk", + ["ruRU"] = "ОÑÐºÐ»Ð¸Ð·Ð»Ð°Ñ Ñкала", + ["zhCN"] = "éšåŒ¿çŸ³", + ["zhTW"] = "隱匿石", + }, + }, + [353] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 10.48, + ["height"] = 14.22, + ["x1"] = 34.68, + ["y1"] = 71.48, + ["names"] = { + ["deDE"] = "Shadra\'Alor", + ["enUS"] = "Shadra\'Alor", + ["esES"] = "Shadra\'Alor", + ["frFR"] = "Shadra\'Alor", + ["koKR"] = "샤드ë¼ì•Œë¡œ", + ["ptBR"] = "Shadra\'Alor", + ["ruRU"] = "Шадра\'Ðлор", + ["zhCN"] = "沙德拉洛", + ["zhTW"] = "沙德拉洛", + }, + }, + [354] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 14.97, + ["height"] = 31.44, + ["x1"] = 62.38, + ["y1"] = 69.61, + ["names"] = { + ["deDE"] = "Jintha\'Alor", + ["enUS"] = "Jintha\'Alor", + ["esES"] = "Jintha\'Alor", + ["frFR"] = "Jintha\'Alor", + ["koKR"] = "진타알로", + ["ptBR"] = "Jintha\'Alor", + ["ruRU"] = "Джинта\'Ðлор", + ["zhCN"] = "è¾›è¨ç½—", + ["zhTW"] = "辛薩羅", + }, + }, + [355] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 9.48, + ["height"] = 11.23, + ["x1"] = 48.65, + ["y1"] = 66.24, + ["names"] = { + ["deDE"] = "Der Altar von Zul", + ["enUS"] = "The Altar of Zul", + ["esES"] = "El Altar de Zul", + ["frFR"] = "L\'Autel de Zul", + ["koKR"] = "ì¤„ì˜ ì œë‹¨", + ["ptBR"] = "O Altar de Zul", + ["ruRU"] = "Ðлтарь Зула", + ["zhCN"] = "祖尔祭å›", + ["zhTW"] = "祖爾祭壇", + }, + }, + [356] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 16.47, + ["height"] = 23.95, + ["x1"] = 64.12, + ["y1"] = 25.45, + ["names"] = { + ["deDE"] = "Seradane", + ["enUS"] = "Seradane", + ["esES"] = "Seradane", + ["frFR"] = "Seradane", + ["koKR"] = "세ë¼ë°ì¸", + ["ptBR"] = "Seradane", + ["ruRU"] = "Серадан", + ["zhCN"] = "瑟拉丹", + ["zhTW"] = "瑟拉丹", + }, + }, + [359] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 5.49, + ["height"] = 8.98, + ["x1"] = 47.65, + ["y1"] = 83.83, + ["names"] = { + ["deDE"] = "Bael Modan", + ["enUS"] = "Bael Modan", + ["esES"] = "Bael Modan", + ["frFR"] = "Bael Modan", + ["koKR"] = "바엘 모단", + ["ptBR"] = "Bael Modan", + ["ruRU"] = "Бейл Модан", + ["zhCN"] = "巴尔莫丹", + ["zhTW"] = "巴爾莫丹", + }, + }, + [360] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 6.49, + ["height"] = 16.47, + ["x1"] = 61.63, + ["y1"] = 50.15, + ["names"] = { + ["deDE"] = "Die Mine der Venture Co.", + ["enUS"] = "The Venture Co. Mine", + ["esES"] = "Mina de la Venture Co.", + ["frFR"] = "Mine de la KapitalRisk", + ["koKR"] = "투ìžê°œë°œíšŒì‚¬ ê´‘ì‚°", + ["ptBR"] = "A Mina da Companhia Venture", + ["ruRU"] = "Рудник Торговой Компании", + ["zhCN"] = "风险投资公å¸çŸ¿æ´ž", + ["zhTW"] = "風險投資公å¸ç¤¦å‘", + }, + }, + [362] = { + ["continent"] = 14, + ["parent"] = 14, + ["width"] = 9.48, + ["height"] = 11.98, + ["x1"] = 54.64, + ["y1"] = 42.66, + ["names"] = { + ["deDE"] = "Klingenhügel", + ["enUS"] = "Razor Hill", + ["esES"] = "Cerrotajo", + ["frFR"] = "Tranchecolline", + ["koKR"] = "칼바위 ì–¸ë•", + ["ptBR"] = "Colina Razor", + ["ruRU"] = "Колючий Холм", + ["zhCN"] = "剃刀岭", + ["zhTW"] = "剃刀嶺", + }, + }, + [363] = { + ["continent"] = 14, + ["parent"] = 14, + ["width"] = 9.98, + ["height"] = 15.72, + ["x1"] = 44.41, + ["y1"] = 66.99, + ["names"] = { + ["deDE"] = "Das Tal der Prüfungen", + ["enUS"] = "Valley of Trials", + ["esES"] = "Valle de las Pruebas", + ["frFR"] = "Vallée des épreuves", + ["koKR"] = "ì‹œí—˜ì˜ ê³¨ì§œê¸°", + ["ptBR"] = "Vale das Provas", + ["ruRU"] = "Долина ИÑпытаний", + ["zhCN"] = "试炼谷", + ["zhTW"] = "試煉谷", + }, + }, + [366] = { + ["continent"] = 14, + ["parent"] = 14, + ["width"] = 5.99, + ["height"] = 8.98, + ["x1"] = 48.9, + ["y1"] = 79.34, + ["names"] = { + ["deDE"] = "Kolkarklippe", + ["enUS"] = "Kolkar Crag", + ["esES"] = "Peñasco Kolkar", + ["frFR"] = "Combe des Kolkar", + ["koKR"] = "콜카르 바윗골", + ["ptBR"] = "Cerro Kolkar", + ["ruRU"] = "Ð£Ñ‚ÐµÑ ÐšÐ¾Ð»ÐºÐ°Ñ€", + ["zhCN"] = "ç§‘å¡å°”å³­å£", + ["zhTW"] = "ç§‘å¡çˆ¾å³­å£", + }, + }, + [367] = { + ["continent"] = 14, + ["parent"] = 14, + ["width"] = 7.49, + ["height"] = 11.23, + ["x1"] = 56.14, + ["y1"] = 72.98, + ["names"] = { + ["deDE"] = "Sen\'jin", + ["enUS"] = "Sen\'jin Village", + ["esES"] = "Aldea Sen\'jin", + ["frFR"] = "Village de Sen\'jin", + ["koKR"] = "센진 마ì„", + ["ptBR"] = "Aldeia Sen\'jin", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ Ð¡ÐµÐ½\'джин", + ["zhCN"] = "森金æ‘", + ["zhTW"] = "森金æ‘", + }, + }, + [368] = { + ["continent"] = 14, + ["parent"] = 14, + ["width"] = 10.98, + ["height"] = 28.44, + ["x1"] = 65.37, + ["y1"] = 84.58, + ["names"] = { + ["deDE"] = "Die Echoinseln", + ["enUS"] = "Echo Isles", + ["esES"] = "Islas del Eco", + ["frFR"] = "ÃŽles de l\'Écho", + ["koKR"] = "메아리 섬", + ["ptBR"] = "Ilhas do Eco", + ["ruRU"] = "ОÑтрова Эха", + ["zhCN"] = "回音群岛", + ["zhTW"] = "回音群島", + }, + }, + [369] = { + ["continent"] = 14, + ["parent"] = 14, + ["width"] = 10.48, + ["height"] = 17.22, + ["x1"] = 40.67, + ["y1"] = 24.33, + ["names"] = { + ["deDE"] = "Donnergrat", + ["enUS"] = "Thunder Ridge", + ["esES"] = "Cresta Trueno", + ["frFR"] = "Falaises du Tonnerre", + ["koKR"] = "천둥 골짜기", + ["ptBR"] = "A Colina do Trovão", + ["ruRU"] = "Громовой хребет", + ["zhCN"] = "雷霆山", + ["zhTW"] = "雷霆山", + }, + }, + [370] = { + ["continent"] = 14, + ["parent"] = 14, + ["width"] = 9.98, + ["height"] = 13.47, + ["x1"] = 52.4, + ["y1"] = 24.7, + ["names"] = { + ["deDE"] = "Staubwindklamm", + ["enUS"] = "Drygulch Ravine", + ["esES"] = "Barranco Seco", + ["frFR"] = "Ravin asséché", + ["koKR"] = "모래바람 협곡", + ["ptBR"] = "Ravina Rocha Seca", + ["ruRU"] = "Ð¡ÑƒÑ…Ð°Ñ Ð»Ð¾Ñ‰Ð¸Ð½Ð°", + ["zhCN"] = "枯水谷", + ["zhTW"] = "枯水谷", + }, + }, + [372] = { + ["continent"] = 14, + ["parent"] = 14, + ["width"] = 8.48, + ["height"] = 14.97, + ["x1"] = 57.63, + ["y1"] = 56.14, + ["names"] = { + ["deDE"] = "Burg Tiragarde", + ["enUS"] = "Tiragarde Keep", + ["esES"] = "Fortaleza Tiragarde", + ["frFR"] = "Donjon de Tiragarde", + ["koKR"] = "타ì´ë¼ê°€ë“œ 요새", + ["ptBR"] = "A Fortaleza de Tiragarde", + ["ruRU"] = "КрепоÑть Тирагард", + ["zhCN"] = "ææ‹‰åŠ å¾·åŸŽå ¡", + ["zhTW"] = "ææ‹‰åŠ å¾·åŸŽå ¡", + }, + }, + [378] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 6.49, + ["height"] = 8.23, + ["x1"] = 45.66, + ["y1"] = 61, + ["names"] = { + ["deDE"] = "Camp Taurajo", + ["enUS"] = "Camp Taurajo", + ["esES"] = "Campamento Taurajo", + ["frFR"] = "Camp Taurajo", + ["koKR"] = "타우ë¼ì¡° 야ì˜ì§€", + ["ptBR"] = "Acampamento Taurajo", + ["ruRU"] = "Лагерь Таурахо", + ["zhCN"] = "陶拉祖è¥åœ°", + ["zhTW"] = "陶拉祖營地", + }, + }, + [379] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 4.49, + ["height"] = 8.23, + ["x1"] = 61.13, + ["y1"] = 21.33, + ["names"] = { + ["deDE"] = "Fernwacht", + ["enUS"] = "Far Watch Post", + ["esES"] = "Puesto de Observación Lejana", + ["frFR"] = "Poste de garde extérieur", + ["koKR"] = "ì „ì´ˆ 기지", + ["ptBR"] = "Posto Vigia do Farol", + ["ruRU"] = "ДальнÑÑ Ð·Ð°Ñтава", + ["zhCN"] = "剿²¿å“¨æ‰€", + ["zhTW"] = "剿²¿å“¨æ‰€", + }, + }, + [380] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 6.49, + ["height"] = 7.49, + ["x1"] = 51.65, + ["y1"] = 28.44, + ["names"] = { + ["deDE"] = "Das Wegekreuz", + ["enUS"] = "The Crossroads", + ["esES"] = "Los Cruces", + ["frFR"] = "La Croisée", + ["koKR"] = "í¬ë¡œìŠ¤ë¡œë“œ", + ["ptBR"] = "Os Cruzamentos", + ["ruRU"] = "ПерекреÑток", + ["zhCN"] = "åå­—è·¯å£", + ["zhTW"] = "åå­—è·¯å£", + }, + }, + [381] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 4.99, + ["height"] = 10.48, + ["x1"] = 61.88, + ["y1"] = 6.74, + ["names"] = { + ["deDE"] = "Felsadermine", + ["enUS"] = "Boulder Lode Mine", + ["esES"] = "Mina de Vetalita", + ["frFR"] = "Mine des Pierriers", + ["koKR"] = "ëŒë¬´ë”기 ê´‘ì‚°", + ["ptBR"] = "Mina Pedregulho", + ["ruRU"] = "Каменный карьер", + ["zhCN"] = "石矿洞", + ["zhTW"] = "石礦å‘", + }, + }, + [382] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 9.98, + ["height"] = 8.98, + ["x1"] = 55.39, + ["y1"] = 7.49, + ["names"] = { + ["deDE"] = "Das Schlickmoor", + ["enUS"] = "The Sludge Fen", + ["esES"] = "Pantano de Lodo", + ["frFR"] = "La Videfange", + ["koKR"] = "ì§„í™ëŠª", + ["ptBR"] = "O Pântano de Lodo", + ["ruRU"] = "ÐефтÑное болото", + ["zhCN"] = "淤泥沼泽", + ["zhTW"] = "淤泥沼澤", + }, + }, + [383] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 9.48, + ["height"] = 11.23, + ["x1"] = 39.67, + ["y1"] = 14.6, + ["names"] = { + ["deDE"] = "Die Trockenen Hügel", + ["enUS"] = "The Dry Hills", + ["esES"] = "Las Colinas Secas", + ["frFR"] = "Les Collines arides", + ["koKR"] = "메마른 ì–¸ë•", + ["ptBR"] = "As Colinas Secas", + ["ruRU"] = "Сухие холмы", + ["zhCN"] = "æ— æ°´å²­", + ["zhTW"] = "無水嶺", + }, + }, + [384] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 6.99, + ["height"] = 8.23, + ["x1"] = 48.4, + ["y1"] = 18.34, + ["names"] = { + ["deDE"] = "Glutnebelgipfel", + ["enUS"] = "Dreadmist Peak", + ["esES"] = "Cima Niebla Mortal", + ["frFR"] = "Pic de Brume-funeste", + ["koKR"] = "ê³µí¬ì˜ 안개봉우리", + ["ptBR"] = "Pico Névoa do Medo", + ["ruRU"] = "Вершина Багрового Тумана", + ["zhCN"] = "鬼雾峰", + ["zhTW"] = "鬼霧峰", + }, + }, + [385] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 7.49, + ["height"] = 8.98, + ["x1"] = 61.13, + ["y1"] = 55.39, + ["names"] = { + ["deDE"] = "Die Feste Nordwacht", + ["enUS"] = "Northwatch Hold", + ["esES"] = "Bastión de la Vigía del Norte", + ["frFR"] = "Fort du Nord", + ["koKR"] = "ë¶ë¶€ 전초기지", + ["ptBR"] = "Fortaleza Northwatch", + ["ruRU"] = "КрепоÑть Северной Стражи", + ["zhCN"] = "北方城堡", + ["zhTW"] = "北方城堡", + }, + }, + [386] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 6.99, + ["height"] = 9.73, + ["x1"] = 45.41, + ["y1"] = 23.58, + ["names"] = { + ["deDE"] = "Die Vergessenen Teiche", + ["enUS"] = "The Forgotten Pools", + ["esES"] = "Las Piscinas Olvidadas", + ["frFR"] = "Les Bassins oubliés", + ["koKR"] = "잊혀진 ì›…ë©ì´", + ["ptBR"] = "As Piscinas Esquecidas", + ["ruRU"] = "Забытые пруды", + ["zhCN"] = "é—忘之池", + ["zhTW"] = "éºå¿˜ä¹‹æ± ", + }, + }, + [387] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 6.49, + ["height"] = 10.48, + ["x1"] = 46.66, + ["y1"] = 38.92, + ["names"] = { + ["deDE"] = "Die Blühende Oase", + ["enUS"] = "Lushwater Oasis", + ["esES"] = "Oasis Agua Viva", + ["frFR"] = "Oasis luxuriante", + ["koKR"] = "푸른 오아시스", + ["ptBR"] = "Oasis Agua Lujosa", + ["ruRU"] = "Цветущий оазиÑ", + ["zhCN"] = "甜水绿洲", + ["zhTW"] = "甜水綠洲", + }, + }, + [388] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 5.49, + ["height"] = 8.98, + ["x1"] = 55.64, + ["y1"] = 42.66, + ["names"] = { + ["deDE"] = "Die Brackige Oase", + ["enUS"] = "The Stagnant Oasis", + ["esES"] = "El Oasis Estancado", + ["frFR"] = "L\'oasis stagnante", + ["koKR"] = "ì£½ì€ ì˜¤ì•„ì‹œìŠ¤", + ["ptBR"] = "O Oasis Estagnado", + ["ruRU"] = "ЗаÑтывший оазиÑ", + ["zhCN"] = "死水绿洲", + ["zhTW"] = "死水綠洲", + }, + }, + [390] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 7.49, + ["height"] = 8.23, + ["x1"] = 46.16, + ["y1"] = 70.73, + ["names"] = { + ["deDE"] = "Feld der Riesen", + ["enUS"] = "Field of Giants", + ["esES"] = "Campo de los Gigantes", + ["frFR"] = "Champ des Géants", + ["koKR"] = "ê±°ì¸ì˜ 들íŒ", + ["ptBR"] = "Campo dos Gigantes", + ["ruRU"] = "ÐŸÐ¾Ð»Ñ Ð’ÐµÐ»Ð¸ÐºÐ°Ð½Ð¾Ð²", + ["zhCN"] = "巨人旷野", + ["zhTW"] = "巨人曠野", + }, + }, + [391] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 4.49, + ["height"] = 9.73, + ["x1"] = 63.62, + ["y1"] = 45.28, + ["names"] = { + ["deDE"] = "Die Händlerküste", + ["enUS"] = "The Merchant Coast", + ["esES"] = "La Costa del Mercader", + ["frFR"] = "La Côte des marchands", + ["koKR"] = "무역 해안", + ["ptBR"] = "A Costa dos Comerciantes", + ["ruRU"] = "Торговое побережье", + ["zhCN"] = "商旅海岸", + ["zhTW"] = "商旅海岸", + }, + }, + [392] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 5.49, + ["height"] = 7.49, + ["x1"] = 62.62, + ["y1"] = 37.43, + ["names"] = { + ["deDE"] = "Ratschet", + ["enUS"] = "Ratchet", + ["esES"] = "Ratchett", + ["frFR"] = "Ratchet", + ["koKR"] = "톱니항", + ["ptBR"] = "Carroça", + ["ruRU"] = "КабеÑтан", + ["zhCN"] = "棘齿城", + ["zhTW"] = "棘齒城", + }, + }, + [396] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 7.49, + ["height"] = 8.98, + ["x1"] = 54.14, + ["y1"] = 65.87, + ["names"] = { + ["deDE"] = "Wasserbrunnen der Winterhufe", + ["enUS"] = "Winterhoof Water Well", + ["esES"] = "Pozo de Agua Winterhoof", + ["frFR"] = "Puits Winterhoof", + ["koKR"] = "윈터후프 우물", + ["ptBR"] = "Poço de Ãgua Winterhoof", + ["ruRU"] = "Колодец Заиндевевшего Копыта", + ["zhCN"] = "冰蹄水井", + ["zhTW"] = "冰蹄水井", + }, + }, + [397] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 6.49, + ["height"] = 10.48, + ["x1"] = 44.66, + ["y1"] = 45.66, + ["names"] = { + ["deDE"] = "Wasserbrunnen der Donnerhörner", + ["enUS"] = "Thunderhorn Water Well", + ["esES"] = "Pozo de Agua Thunderhorn", + ["frFR"] = "Puits Thunderhorn", + ["koKR"] = "ì¬ë”혼 우물", + ["ptBR"] = "O Poço de Ãgua Thunderhorn", + ["ruRU"] = "Колодец Громового Рога", + ["zhCN"] = "雷角水井", + ["zhTW"] = "雷角水井", + }, + }, + [398] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 12.97, + ["height"] = 9.73, + ["x1"] = 39.42, + ["y1"] = 12.35, + ["names"] = { + ["deDE"] = "Wasserbrunnen der Wildmähnen", + ["enUS"] = "Wildmane Water Well", + ["esES"] = "Pozo de Agua Wildmane", + ["frFR"] = "Puits Wildmane", + ["koKR"] = "와ì¼ë“œë©”ì¸ ìš°ë¬¼", + ["ptBR"] = "Poço de Ãgua Wildmane", + ["ruRU"] = "Колодец Буйногривых", + ["zhCN"] = "蛮鬃水井", + ["zhTW"] = "蠻鬃水井", + }, + }, + [404] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 9.48, + ["height"] = 14.97, + ["x1"] = 33.68, + ["y1"] = 46.41, + ["names"] = { + ["deDE"] = "Grabungsstätte von Bael\'dun", + ["enUS"] = "Bael\'dun Digsite", + ["esES"] = "Sitio de Excavación de Bael\'dun", + ["frFR"] = "Site de fouilles de Bael\'Dun", + ["koKR"] = "ë°”ì—˜ë˜ ë°œêµ´í˜„ìž¥", + ["ptBR"] = "Sítio de Escavação Bael\'dun", + ["ruRU"] = "РаÑкопки Бейл\'дана", + ["zhCN"] = "巴尔莫丹挖掘场", + ["zhTW"] = "巴爾丹挖掘場", + }, + }, + [413] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 8.48, + ["height"] = 24.7, + ["x1"] = 29.19, + ["y1"] = 25.82, + ["names"] = { + ["deDE"] = "Maestras Posten", + ["enUS"] = "Maestra\'s Post", + ["esES"] = "Puesto de Maestra", + ["frFR"] = "Poste de Maestra", + ["koKR"] = "마ì—ìŠ¤íŠ¸ë¼ ì£¼ë‘”ì§€", + ["ptBR"] = "Posto de Maestra", + ["ruRU"] = "ЗаÑтава МейÑтры", + ["zhCN"] = "迈斯特拉岗哨", + ["zhTW"] = "邿–¯ç‰¹æ‹‰å´—哨", + }, + }, + [414] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 16.97, + ["height"] = 26.2, + ["x1"] = 12.97, + ["y1"] = 22.08, + ["names"] = { + ["deDE"] = "Der Zoramstrand", + ["enUS"] = "The Zoram Strand", + ["esES"] = "La Playa Zoram", + ["frFR"] = "La grève de Zoram", + ["koKR"] = "조람 해안", + ["ptBR"] = "A Praia Zoram", + ["ruRU"] = "ЗорамÑкое взморье", + ["zhCN"] = "使‹‰å§†æµ·å²¸", + ["zhTW"] = "使‹‰å§†æµ·å²¸", + }, + }, + [415] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 12.48, + ["height"] = 14.97, + ["x1"] = 37.18, + ["y1"] = 51.65, + ["names"] = { + ["deDE"] = "Astranaar", + ["enUS"] = "Astranaar", + ["esES"] = "Astranaar", + ["frFR"] = "Astranaar", + ["koKR"] = "아스트ë¼ë‚˜ë¥´", + ["ptBR"] = "Astranaar", + ["ruRU"] = "ÐÑтранаар", + ["zhCN"] = "阿斯特兰纳", + ["zhTW"] = "阿斯特蘭ç´", + }, + }, + [416] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 9.48, + ["height"] = 14.22, + ["x1"] = 22.21, + ["y1"] = 52.77, + ["names"] = { + ["deDE"] = "Der Schrein von Aessina", + ["enUS"] = "The Shrine of Aessina", + ["esES"] = "El Santuario de Aessina", + ["frFR"] = "Le sanctuaire d\'Aessina", + ["koKR"] = "ì•„ì—ì‹œë‚˜ì˜ ì œë‹¨", + ["ptBR"] = "O Santuário de Aessina", + ["ruRU"] = "СвÑтилище ЭÑÑины", + ["zhCN"] = "艾森娜神殿", + ["zhTW"] = "艾森娜神殿", + }, + }, + [417] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 6.49, + ["height"] = 10.48, + ["x1"] = 26.7, + ["y1"] = 63.62, + ["names"] = { + ["deDE"] = "Schrein des sengenden Feuers", + ["enUS"] = "Fire Scar Shrine", + ["esES"] = "Santuario Cicatriz de Fuego", + ["frFR"] = "Sanctuaire de Scarfeu", + ["koKR"] = "불타버린 ì‹ ì „", + ["ptBR"] = "Santuário Cicatriz de Fogo", + ["ruRU"] = "СвÑтилище Огненной РаÑщелины", + ["zhCN"] = "ç«ç—•神殿", + ["zhTW"] = "ç«ç—•神殿", + }, + }, + [418] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 7.98, + ["height"] = 13.47, + ["x1"] = 33.43, + ["y1"] = 67.37, + ["names"] = { + ["deDE"] = "Die Sternenstaubruinen", + ["enUS"] = "The Ruins of Stardust", + ["esES"] = "Ruinas de Stardust", + ["frFR"] = "Les ruines de Chimétoile", + ["koKR"] = "ë³„ê°€ë£¨ì˜ íí—ˆ", + ["ptBR"] = "As Ruínas de Stardust", + ["ruRU"] = "Руины Звездной Пыли", + ["zhCN"] = "星尘废墟", + ["zhTW"] = "星塵廢墟", + }, + }, + [419] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 12.48, + ["height"] = 14.22, + ["x1"] = 56.14, + ["y1"] = 37.05, + ["names"] = { + ["deDE"] = "Das Heulende Tal", + ["enUS"] = "The Howling Vale", + ["esES"] = "El Valle Aullante", + ["frFR"] = "Le Val hurlant", + ["koKR"] = "울부짖는 골짜기", + ["ptBR"] = "O Vale Uivante", + ["ruRU"] = "Ð’Ð¾ÑŽÑ‰Ð°Ñ Ð´Ð¾Ð»Ð¸Ð½Ð°", + ["zhCN"] = "狼嚎谷", + ["zhTW"] = "狼嚎谷", + }, + }, + [421] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 14.97, + ["height"] = 17.96, + ["x1"] = 49.4, + ["y1"] = 73.35, + ["names"] = { + ["deDE"] = "Mystralsee", + ["enUS"] = "Mystral Lake", + ["esES"] = "Lago Mystral", + ["frFR"] = "Lac Mystral", + ["koKR"] = "미스트랄 호수", + ["ptBR"] = "Lago Mystral", + ["ruRU"] = "Озеро МиÑтраль", + ["zhCN"] = "密斯特拉湖", + ["zhTW"] = "密斯特拉湖", + }, + }, + [422] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 11.48, + ["height"] = 12.72, + ["x1"] = 69.11, + ["y1"] = 81.96, + ["names"] = { + ["deDE"] = "Himmelssturzsee", + ["enUS"] = "Fallen Sky Lake", + ["esES"] = "Lago del Cielo Caído", + ["frFR"] = "Lac Tombeciel", + ["koKR"] = "유성 호수", + ["ptBR"] = "Lago Céu Caído", + ["ruRU"] = "Зеркало ÐебеÑ", + ["zhCN"] = "å æ˜Ÿæ¹–", + ["zhTW"] = "墜星湖", + }, + }, + [424] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 6.99, + ["height"] = 11.23, + ["x1"] = 46.41, + ["y1"] = 46.78, + ["names"] = { + ["deDE"] = "Irissee", + ["enUS"] = "Iris Lake", + ["esES"] = "Lago Iris", + ["frFR"] = "Lac Iris", + ["koKR"] = "ì•„ì´ë¦¬ìФ 호수", + ["ptBR"] = "Lago Iris", + ["ruRU"] = "Озеро ИриÑ", + ["zhCN"] = "伊瑞斯湖", + ["zhTW"] = "伊瑞斯湖", + }, + }, + [426] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 9.98, + ["height"] = 11.98, + ["x1"] = 60.88, + ["y1"] = 51.65, + ["names"] = { + ["deDE"] = "Rajenbaum", + ["enUS"] = "Raynewood Retreat", + ["esES"] = "Retiro Bosque Rayne", + ["frFR"] = "Retraite de Raynewood", + ["koKR"] = "ë¼ì´ë„¤ë‚˜ë¬´ ì€ì‹ ì²˜", + ["ptBR"] = "Refúgio Raynewood", + ["ruRU"] = "Приют в Ðочных ЛеÑах", + ["zhCN"] = "林中树居", + ["zhTW"] = "林中樹居", + }, + }, + [428] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 11.48, + ["height"] = 16.47, + ["x1"] = 71.61, + ["y1"] = 59.13, + ["names"] = { + ["deDE"] = "Nachtflucht", + ["enUS"] = "Night Run", + ["esES"] = "Carrera Nocturna", + ["frFR"] = "Défilé de la nuit", + ["koKR"] = "ì–´ë‘ ì˜ í„°", + ["ptBR"] = "Corrida Noturna", + ["ruRU"] = "ÐÐ¾Ñ‡Ð½Ð°Ñ Ð¿Ð¾Ð»Ñна", + ["zhCN"] = "夜é“è°·", + ["zhTW"] = "夜é“è°·", + }, + }, + [430] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 13.97, + ["height"] = 9.73, + ["x1"] = 81.84, + ["y1"] = 48.28, + ["names"] = { + ["deDE"] = "Satyrnaar", + ["enUS"] = "Satyrnaar", + ["esES"] = "Satyrnaar", + ["frFR"] = "Satyrnaar", + ["koKR"] = "사티르나르", + ["ptBR"] = "Satyrnaar", + ["ruRU"] = "Сатирнаар", + ["zhCN"] = "è¨æçº³å°”", + ["zhTW"] = "è–©æç´çˆ¾", + }, + }, + [434] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 16.47, + ["height"] = 15.72, + ["x1"] = 83.08, + ["y1"] = 77.47, + ["names"] = { + ["deDE"] = "Dämonenhügel", + ["enUS"] = "Felfire Hill", + ["esES"] = "Colina Fuego Vil", + ["frFR"] = "Colline Gangrefeu", + ["koKR"] = "지옥불 ì–¸ë•", + ["ptBR"] = "Colina Fogo Vil", + ["ruRU"] = "Холм ДемоничеÑкого ОгнÑ", + ["zhCN"] = "冥ç«å²­", + ["zhTW"] = "冥ç«å¶º", + }, + }, + [437] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 11.48, + ["height"] = 11.98, + ["x1"] = 91.07, + ["y1"] = 59.13, + ["names"] = { + ["deDE"] = "Holzfällerlager des Kriegshymnenklans", + ["enUS"] = "Warsong Lumber Camp", + ["esES"] = "Campo de Leña Canción de Guerra", + ["frFR"] = "Camp de bûcherons Warsong", + ["koKR"] = "ì „ìŸë…¸ëž˜ë¶€ì¡± 벌목기지", + ["ptBR"] = "Acampamento de Madeira Grito de Guerra", + ["ruRU"] = "ЛеÑозаготовки ПеÑни Войны", + ["zhCN"] = "æˆ˜æ­Œä¼æœ¨è¥åœ°", + ["zhTW"] = "æˆ°æ­Œä¼æœ¨å ´", + }, + }, + [438] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 8.98, + ["height"] = 12.72, + ["x1"] = 93.81, + ["y1"] = 36.3, + ["names"] = { + ["deDE"] = "Schattengrün", + ["enUS"] = "Bough Shadow", + ["esES"] = "Sombra de Rama", + ["frFR"] = "L\'Ombrage", + ["koKR"] = "ì–´ë‘ ì˜ ë‚˜ë¬´", + ["ptBR"] = "Sombra dos Ramos", + ["ruRU"] = "ТениÑÑ‚Ð°Ñ ÐšÑ€Ð¾Ð½Ð°", + ["zhCN"] = "大树è«", + ["zhTW"] = "大樹蔭", + }, + }, + [439] = { + ["continent"] = 400, + ["parent"] = 400, + ["width"] = 24.95, + ["height"] = 48.65, + ["x1"] = 78.34, + ["y1"] = 72.98, + ["names"] = { + ["deDE"] = "Die Schimmernde Ebene", + ["enUS"] = "The Shimmering Flats", + ["esES"] = "Las Llanuras Brillantes", + ["frFR"] = "Les Salines", + ["koKR"] = "소금 í‰ì›", + ["ptBR"] = "As Planícies Brilhantes", + ["ruRU"] = "ÐœÐµÑ€Ñ†Ð°ÑŽÑ‰Ð°Ñ Ñ€Ð°Ð²Ð½Ð¸Ð½Ð°", + ["zhCN"] = "闪光平原", + ["zhTW"] = "閃光平原", + }, + }, + [441] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 5.99, + ["height"] = 11.98, + ["x1"] = 20.46, + ["y1"] = 40.42, + ["names"] = { + ["deDE"] = "Falathimsee", + ["enUS"] = "Lake Falathim", + ["esES"] = "Lago Falathim", + ["frFR"] = "Lac Falathim", + ["koKR"] = "팔ë¼ë”¤ 호수", + ["ptBR"] = "Lago Falathim", + ["ruRU"] = "Озеро Фалатим", + ["zhCN"] = "法拉希姆湖", + ["zhTW"] = "法拉希姆湖", + }, + }, + [442] = { + ["continent"] = 148, + ["parent"] = 148, + ["width"] = 7.49, + ["height"] = 11.23, + ["x1"] = 39.17, + ["y1"] = 43.79, + ["names"] = { + ["deDE"] = "Auberdine", + ["enUS"] = "Auberdine", + ["esES"] = "Auberdine", + ["frFR"] = "Auberdine", + ["koKR"] = "아우버다ì¸", + ["ptBR"] = "Auberdine", + ["ruRU"] = "Ðубердин", + ["zhCN"] = "奥伯ä¸", + ["zhTW"] = "奧伯ä¸", + }, + }, + [443] = { + ["continent"] = 148, + ["parent"] = 148, + ["width"] = 6.49, + ["height"] = 14.97, + ["x1"] = 58.63, + ["y1"] = 16.47, + ["names"] = { + ["deDE"] = "Die Ruinen von Mathystra", + ["enUS"] = "Ruins of Mathystra", + ["esES"] = "Ruinas de Mathystra", + ["frFR"] = "Ruines de Mathystra", + ["koKR"] = "마시스트ë¼ì˜ íí—ˆ", + ["ptBR"] = "Ruínas de Mathystra", + ["ruRU"] = "Руины МатиÑтры", + ["zhCN"] = "玛塞斯特拉废墟", + ["zhTW"] = "瑪塞斯特拉廢墟", + }, + }, + [444] = { + ["continent"] = 148, + ["parent"] = 148, + ["width"] = 8.98, + ["height"] = 8.98, + ["x1"] = 56.39, + ["y1"] = 26.2, + ["names"] = { + ["deDE"] = "Turm von Althalaxx", + ["enUS"] = "Tower of Althalaxx", + ["esES"] = "Torre de Althalaxx", + ["frFR"] = "Tour d\'Althalaxx", + ["koKR"] = "알살ë½ìŠ¤ì˜ íƒ‘", + ["ptBR"] = "A Torre de Althalaxx", + ["ruRU"] = "Ð‘Ð°ÑˆÐ½Ñ ÐлталакÑа", + ["zhCN"] = "å¥¥è¨æ‹‰å…‹æ–¯ä¹‹å¡”", + ["zhTW"] = "奧薩拉克斯之塔", + }, + }, + [446] = { + ["continent"] = 148, + ["parent"] = 148, + ["width"] = 6.99, + ["height"] = 8.23, + ["x1"] = 44.91, + ["y1"] = 36.3, + ["names"] = { + ["deDE"] = "Bashal\'Aran", + ["enUS"] = "Bashal\'Aran", + ["esES"] = "Bashal\'Aran", + ["frFR"] = "Bashal\'Aran", + ["koKR"] = "바샬아란", + ["ptBR"] = "Bashal\'Aran", + ["ruRU"] = "Башал\'Ðран", + ["zhCN"] = "巴莎兰", + ["zhTW"] = "巴莎蘭", + }, + }, + [447] = { + ["continent"] = 148, + ["parent"] = 148, + ["width"] = 6.49, + ["height"] = 10.48, + ["x1"] = 42.66, + ["y1"] = 57.63, + ["names"] = { + ["deDE"] = "Ameth\'Aran", + ["enUS"] = "Ameth\'Aran", + ["esES"] = "Ameth\'Aran", + ["frFR"] = "Ameth\'Aran", + ["koKR"] = "아메스아란", + ["ptBR"] = "Ameth\'Aran", + ["ruRU"] = "Ðмет\'Ðран", + ["zhCN"] = "亚米è¨å…°", + ["zhTW"] = "亞米薩蘭", + }, + }, + [448] = { + ["continent"] = 148, + ["parent"] = 148, + ["width"] = 8.98, + ["height"] = 11.98, + ["x1"] = 42.91, + ["y1"] = 77.1, + ["names"] = { + ["deDE"] = "Der Hain der Uralten", + ["enUS"] = "Grove of the Ancients", + ["esES"] = "Arboleda de los Antiguos", + ["frFR"] = "Bosquet des Anciens", + ["koKR"] = "ê³ ëŒ€ì •ë ¹ì˜ ìˆ²", + ["ptBR"] = "Bosque dos Ancestrais", + ["ruRU"] = "Роща Древних", + ["zhCN"] = "夿 ‘之林", + ["zhTW"] = "夿¨¹ä¹‹æž—", + }, + }, + [449] = { + ["continent"] = 148, + ["parent"] = 148, + ["width"] = 8.98, + ["height"] = 11.23, + ["x1"] = 41.42, + ["y1"] = 88.7, + ["names"] = { + ["deDE"] = "Die Meistergleve", + ["enUS"] = "The Master\'s Glaive", + ["esES"] = "La Hoja del Maestro", + ["frFR"] = "Le Glaive du Maître", + ["koKR"] = "지배ìžì˜ 무ë¤", + ["ptBR"] = "A Lâmina do Mestre", + ["ruRU"] = "Меч ВлаÑтителÑ", + ["zhCN"] = "主宰之剑", + ["zhTW"] = "主宰之åŠ", + }, + }, + [450] = { + ["continent"] = 148, + ["parent"] = 148, + ["width"] = 5.99, + ["height"] = 8.98, + ["x1"] = 34.93, + ["y1"] = 85.33, + ["names"] = { + ["deDE"] = "Wirrbarts Ausgrabung", + ["enUS"] = "Remtravel\'s Excavation", + ["esES"] = "Excavación de Remtravel", + ["frFR"] = "Excavations de Remtravel", + ["koKR"] = "ë ˜íŠ¸ë ˆë¸”ì˜ ë°œêµ´í˜„ìž¥", + ["ptBR"] = "Escavação Remtravel", + ["ruRU"] = "РаÑкопки раÑÑеÑнного геолога", + ["zhCN"] = "雷姆塔维尔挖掘场", + ["zhTW"] = "雷姆塔維爾挖掘場", + }, + }, + [456] = { + ["continent"] = 148, + ["parent"] = 148, + ["width"] = 10.48, + ["height"] = 9.73, + ["x1"] = 53.64, + ["y1"] = 33.31, + ["names"] = { + ["deDE"] = "Klippenquell", + ["enUS"] = "Cliffspring River", + ["esES"] = "Río Fonroca", + ["frFR"] = "La Bondissante", + ["koKR"] = "í­í¬ìˆ˜ ê°•", + ["ptBR"] = "Rio da Fonte da Falésia", + ["ruRU"] = "Река СкалиÑтаÑ", + ["zhCN"] = "壿³‰æ²³", + ["zhTW"] = "壿³‰æ²³", + }, + }, + [459] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 7.98, + ["height"] = 31.59, + ["x1"] = 77.84, + ["y1"] = 32.86, + ["names"] = { + ["deDE"] = "Scharlachroter Wachposten", + ["enUS"] = "Scarlet Watch Post", + ["esES"] = "Puesto de Vigilancia Escarlata", + ["frFR"] = "Poste de garde de la Croisade", + ["koKR"] = "붉ì€ì‹­ìžêµ° 경비초소", + ["ptBR"] = "Posto de Vigia Scarlet", + ["ruRU"] = "Сторожевой поÑÑ‚ Ðлого ордена", + ["zhCN"] = "血色å字军哨岗", + ["zhTW"] = "血色åå­—è»å´—哨", + }, + }, + [460] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 7.49, + ["height"] = 7.49, + ["x1"] = 49.55, + ["y1"] = 61.98, + ["names"] = { + ["deDE"] = "Sonnenfels", + ["enUS"] = "Sun Rock Retreat", + ["esES"] = "Refugio Roca del Sol", + ["frFR"] = "Retraite de Roche-Soleil", + ["koKR"] = "해바위 야ì˜ì§€", + ["ptBR"] = "Retiro Sun Rock", + ["ruRU"] = "Приют у Солнечного КамнÑ", + ["zhCN"] = "烈日石居", + ["zhTW"] = "烈日石居", + }, + }, + [461] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 14.47, + ["height"] = 18.71, + ["x1"] = 66.32, + ["y1"] = 51.57, + ["names"] = { + ["deDE"] = "Die Scherwindklippe", + ["enUS"] = "Windshear Crag", + ["esES"] = "Peñasco Viento Cortante", + ["frFR"] = "Combe des Cisailles", + ["koKR"] = "칼바람 바위산", + ["ptBR"] = "Cume Windshear", + ["ruRU"] = "Ð£Ñ‚ÐµÑ Ð’ÐµÑ‚Ñ€Ð¾Ñ€ÐµÐ·Ð¾Ð²", + ["zhCN"] = "狂风峭å£", + ["zhTW"] = "狂風峭å£", + }, + }, + [464] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 8.88, + ["height"] = 10.33, + ["x1"] = 51.05, + ["y1"] = 45.88, + ["names"] = { + ["deDE"] = "Mirkfallonsee", + ["enUS"] = "Mirkfallon Lake", + ["esES"] = "Lago Mirkfallon", + ["frFR"] = "Lac Mirkfallon", + ["koKR"] = "땅거미 호수", + ["ptBR"] = "Lago Mirkfallon", + ["ruRU"] = "Мутное озеро", + ["zhCN"] = "暗色湖", + ["zhTW"] = "暗色湖", + }, + }, + [465] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 10.28, + ["height"] = 11.83, + ["x1"] = 38.27, + ["y1"] = 68.64, + ["names"] = { + ["deDE"] = "Das Verbrannte Tal", + ["enUS"] = "The Charred Vale", + ["esES"] = "El Valle Carbonizado", + ["frFR"] = "Le Val calciné", + ["koKR"] = "ìž¿ë”미 계곡", + ["ptBR"] = "O Vale Queimado", + ["ruRU"] = "ÐžÐ±ÑƒÐ³Ð»ÐµÐ½Ð½Ð°Ñ Ð´Ð¾Ð»Ð¸Ð½Ð°", + ["zhCN"] = "焦炭谷", + ["zhTW"] = "焦炭谷", + }, + }, + [467] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 12.67, + ["height"] = 13.47, + ["x1"] = 41.67, + ["y1"] = 22.16, + ["names"] = { + ["deDE"] = "Der Steinkrallengipfel", + ["enUS"] = "Stonetalon Peak", + ["esES"] = "Cima Colina de Piedra", + ["frFR"] = "Pic des Serres-Rocheuses", + ["koKR"] = "ëŒë°œí†± 봉우리", + ["ptBR"] = "Pico Stonetalon", + ["ruRU"] = "Пик Каменного КогтÑ", + ["zhCN"] = "石爪峰", + ["zhTW"] = "石爪峰", + }, + }, + [477] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 4.49, + ["height"] = 5.99, + ["x1"] = 35.68, + ["y1"] = 51.65, + ["names"] = { + ["deDE"] = "Ruinen von Jubuwal", + ["enUS"] = "Ruins of Jubuwal", + ["esES"] = "Ruinas de Jubuwal", + ["frFR"] = "Ruines de Jubuwal", + ["koKR"] = "ì£¼ë¶€ì™ˆì˜ íí—ˆ", + ["ptBR"] = "Ruínas de Jubuwal", + ["ruRU"] = "Руины ЖубувалÑ", + ["zhCN"] = "朱布瓦尔废墟", + ["zhTW"] = "朱布瓦爾廢墟", + }, + }, + [478] = { + ["continent"] = 141, + ["parent"] = 141, + ["width"] = 5.99, + ["height"] = 13.47, + ["x1"] = 40.42, + ["y1"] = 63.62, + ["names"] = { + ["deDE"] = "Teiche von Arlithrien", + ["enUS"] = "Pools of Arlithrien", + ["esES"] = "Pozos de Arlithrien", + ["frFR"] = "Bassins d\'Arlithrien", + ["koKR"] = "아리스리엔 연못", + ["ptBR"] = "Poços de Arlithrien", + ["ruRU"] = "Пруды ÐрлитриÑна", + ["zhCN"] = "阿里斯瑞æ©ä¹‹æ± ", + ["zhTW"] = "亞里斯瑞æ©ä¹‹æ± ", + }, + }, + [480] = { + ["continent"] = 400, + ["parent"] = 400, + ["width"] = 19.46, + ["height"] = 23.2, + ["x1"] = 16.22, + ["y1"] = 17.59, + ["names"] = { + ["deDE"] = "Camp E\'thok", + ["enUS"] = "Camp E\'thok", + ["esES"] = "Campamento E\'thok", + ["frFR"] = "Camp E\'thok", + ["koKR"] = "ì—톡 야ì˜ì§€", + ["ptBR"] = "Acampamento E\'thok", + ["ruRU"] = "Лагерь Э\'Ток", + ["zhCN"] = "伊索克è¥åœ°", + ["zhTW"] = "伊索克營地", + }, + }, + [481] = { + ["continent"] = 400, + ["parent"] = 400, + ["width"] = 15.97, + ["height"] = 14.97, + ["x1"] = 47.9, + ["y1"] = 41.17, + ["names"] = { + ["deDE"] = "Spalthufklippe", + ["enUS"] = "Splithoof Crag", + ["esES"] = "Peñasco Pezuña Rota", + ["frFR"] = "Combe du Sabot fendu", + ["koKR"] = "갈래발굽 바윗골", + ["ptBR"] = "Cratera Splithoof", + ["ruRU"] = "Ð£Ñ‚ÐµÑ Ð¢Ñ€ÐµÑнувшего Копыта", + ["zhCN"] = "裂蹄峭å£", + ["zhTW"] = "裂蹄峭å£", + }, + }, + [482] = { + ["continent"] = 400, + ["parent"] = 400, + ["width"] = 12.97, + ["height"] = 23.2, + ["x1"] = 13.47, + ["y1"] = 34.06, + ["names"] = { + ["deDE"] = "Der Steilhang", + ["enUS"] = "Highperch", + ["esES"] = "Altura Alta", + ["frFR"] = "Haut-perchoir", + ["koKR"] = "마루둥지", + ["ptBR"] = "Alto do Penacho", + ["ruRU"] = "Скальное гнездовье", + ["zhCN"] = "风巢", + ["zhTW"] = "風巢", + }, + }, + [483] = { + ["continent"] = 400, + ["parent"] = 400, + ["width"] = 16.47, + ["height"] = 24.7, + ["x1"] = 30.69, + ["y1"] = 47.53, + ["names"] = { + ["deDE"] = "Der kreischende Canyon", + ["enUS"] = "The Screeching Canyon", + ["esES"] = "Cañón de los Gritos", + ["frFR"] = "Canyon des hurlements", + ["koKR"] = "회오리 협곡", + ["ptBR"] = "O Desfiladeiro Uivante", + ["ruRU"] = "Каньон Визга", + ["zhCN"] = "尖啸峡谷", + ["zhTW"] = "尖嘯峽谷", + }, + }, + [484] = { + ["continent"] = 400, + ["parent"] = 400, + ["width"] = 12.48, + ["height"] = 15.72, + ["x1"] = 46.16, + ["y1"] = 52.77, + ["names"] = { + ["deDE"] = "Freiwindposten", + ["enUS"] = "Freewind Post", + ["esES"] = "Puesto Viento Libre", + ["frFR"] = "Poste de Librevent", + ["koKR"] = "높새바람 봉우리", + ["ptBR"] = "Posto Ventos Livres", + ["ruRU"] = "ЗаÑтава Вольного Ветра", + ["zhCN"] = "乱风岗", + ["zhTW"] = "亂風崗", + }, + }, + [485] = { + ["continent"] = 400, + ["parent"] = 400, + ["width"] = 9.48, + ["height"] = 10.48, + ["x1"] = 32.19, + ["y1"] = 22.46, + ["names"] = { + ["deDE"] = "Der große Aufzug", + ["enUS"] = "The Great Lift", + ["esES"] = "El Gran Ascensor", + ["frFR"] = "La Grande élévation", + ["koKR"] = "구름 승강장", + ["ptBR"] = "O Grande Elevador", + ["ruRU"] = "Великий Подъемник", + ["zhCN"] = "å‡é™æ¢¯", + ["zhTW"] = "å‡é™æ¢¯", + }, + }, + [492] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 11.48, + ["height"] = 20.96, + ["x1"] = 20.21, + ["y1"] = 41.92, + ["names"] = { + ["deDE"] = "Friedhof von Rabenflucht", + ["enUS"] = "Raven Hill Cemetery", + ["esES"] = "Cementerio de Colina del Cuervo", + ["frFR"] = "Cimetière de Colline-aux-Corbeaux", + ["koKR"] = "까마귀 ì–¸ë• ë¬˜ì§€", + ["ptBR"] = "Cemitério da Colina do Corvo", + ["ruRU"] = "Кладбище Вороньего Холма", + ["zhCN"] = "乌鸦岭墓地", + ["zhTW"] = "çƒé´‰å¶ºå¢“園", + }, + }, + [496] = { + ["continent"] = 15, + ["parent"] = 15, + ["width"] = 12.97, + ["height"] = 24.7, + ["x1"] = 36.93, + ["y1"] = 22.83, + ["names"] = { + ["deDE"] = "Brackenwall", + ["enUS"] = "Brackenwall Village", + ["esES"] = "Aldea Muro de Helechos", + ["frFR"] = "Mur-de-Fougères", + ["koKR"] = "ë‹´ìŸì´ 마ì„", + ["ptBR"] = "Aldeia de Parede de Samambaia", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ Ð“Ð¸Ð±Ð»Ð¾Ñ‚Ð¾Ð¿ÑŒ", + ["zhCN"] = "蕨墙æ‘", + ["zhTW"] = "蕨牆æ‘", + }, + }, + [502] = { + ["continent"] = 15, + ["parent"] = 15, + ["width"] = 18.96, + ["height"] = 27.69, + ["x1"] = 53.89, + ["y1"] = 22.83, + ["names"] = { + ["deDE"] = "Hexenhügel", + ["enUS"] = "Witch Hill", + ["esES"] = "Colina de la Bruja", + ["frFR"] = "Colline des sorcières", + ["koKR"] = "마녀 ì–¸ë•", + ["ptBR"] = "Colina da Bruxa", + ["ruRU"] = "Ведьмин холм", + ["zhCN"] = "女巫岭", + ["zhTW"] = "女巫嶺", + }, + }, + [509] = { + ["continent"] = 15, + ["parent"] = 15, + ["width"] = 10.98, + ["height"] = 19.46, + ["x1"] = 36.93, + ["y1"] = 61.38, + ["names"] = { + ["deDE"] = "Der Flammenbau", + ["enUS"] = "The Den of Flame", + ["esES"] = "El Refugio de la Llama", + ["frFR"] = "L\'Antre des flammes", + ["koKR"] = "í™”ì—¼ì˜ ë‘¥ì§€", + ["ptBR"] = "A Cova das Chamas", + ["ruRU"] = "Огненное логово", + ["zhCN"] = "ç«ç„°æ´žç©´", + ["zhTW"] = "ç«ç„°æ´žç©´", + }, + }, + [511] = { + ["continent"] = 15, + ["parent"] = 15, + ["width"] = 17.47, + ["height"] = 20.21, + ["x1"] = 52.64, + ["y1"] = 75.22, + ["names"] = { + ["deDE"] = "Der Drachensumpf", + ["enUS"] = "Wyrmbog", + ["esES"] = "Foso del Dragón", + ["frFR"] = "Tourbière du Ver", + ["koKR"] = "ìš©ì˜ ë‘¥ì§€", + ["ptBR"] = "Pântano Wyrmbog", + ["ruRU"] = "Драконьи топи", + ["zhCN"] = "巨龙沼泽", + ["zhTW"] = "巍龿²¼æ¾¤", + }, + }, + [513] = { + ["continent"] = 15, + ["parent"] = 15, + ["width"] = 13.47, + ["height"] = 18.71, + ["x1"] = 68.11, + ["y1"] = 51.27, + ["names"] = { + ["deDE"] = "Die Insel Theramore", + ["enUS"] = "Theramore Isle", + ["esES"] = "Isla Theramore", + ["frFR"] = "Ile de Theramore", + ["koKR"] = "í…Œë¼ëª¨ì–´ 섬", + ["ptBR"] = "A Ilha Theramore", + ["ruRU"] = "ОÑтров Терамор", + ["zhCN"] = "塞拉摩岛", + ["zhTW"] = "塞拉摩島", + }, + }, + [536] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 11.48, + ["height"] = 14.22, + ["x1"] = 21.71, + ["y1"] = 69.24, + ["names"] = { + ["deDE"] = "Addles Siedlung", + ["enUS"] = "Addle\'s Stead", + ["esES"] = "Granja de Addle", + ["frFR"] = "Ferme des Addle", + ["koKR"] = "ì—ì´ë“¤ì˜ ì€ì‹ ì²˜", + ["ptBR"] = "Fazenda de Addle", + ["ruRU"] = "УчаÑток Эддла", + ["zhCN"] = "è…è‰å†œåœº", + ["zhTW"] = "è…è‰è¾²å ´", + }, + }, + [537] = { + ["continent"] = 490, + ["parent"] = 490, + ["width"] = 17.96, + ["height"] = 24.7, + ["x1"] = 50.4, + ["y1"] = 49.78, + ["names"] = { + ["deDE"] = "Feuersäulengrat", + ["enUS"] = "Fire Plume Ridge", + ["esES"] = "Cresta de la Pluma de Fuego", + ["frFR"] = "Crête de la Fournaise", + ["koKR"] = "불기둥 마루", + ["ptBR"] = "Crista do Penacho de Fogo", + ["ruRU"] = "Вулкан Огненного Венца", + ["zhCN"] = "ç«ç¾½å±±", + ["zhTW"] = "ç«ç¾½å±±", + }, + }, + [538] = { + ["continent"] = 490, + ["parent"] = 490, + ["width"] = 30.44, + ["height"] = 33.68, + ["x1"] = 50.65, + ["y1"] = 20.58, + ["names"] = { + ["deDE"] = "Teergruben von Lakkari", + ["enUS"] = "Lakkari Tar Pits", + ["esES"] = "Pozos de Alquimia Lakkari", + ["frFR"] = "Fosses de goudron de Lakkari", + ["koKR"] = "ë½ì¹´ë¦¬ 잿구ë©ì´", + ["ptBR"] = "Fosos de Alquitrán Lakkari", + ["ruRU"] = "СмолÑные Ñмы Лаккари", + ["zhCN"] = "拉å¡åˆ©æ²¹æ²¼", + ["zhTW"] = "拉å¡åˆ©æ²¹æ²¼", + }, + }, + [539] = { + ["continent"] = 490, + ["parent"] = 490, + ["width"] = 14.47, + ["height"] = 23.95, + ["x1"] = 37.18, + ["y1"] = 74.1, + ["names"] = { + ["deDE"] = "Terrorflucht", + ["enUS"] = "Terror Run", + ["esES"] = "Sendero del Terror", + ["frFR"] = "Coteaux de la terreur", + ["koKR"] = "ê³µí¬ì˜ í„°", + ["ptBR"] = "Caminho do Terror", + ["ruRU"] = "Путь УжаÑа", + ["zhCN"] = "ææƒ§å°é“", + ["zhTW"] = "ææ‡¼å°é“", + }, + }, + [540] = { + ["continent"] = 490, + ["parent"] = 490, + ["width"] = 15.47, + ["height"] = 19.46, + ["x1"] = 51.65, + ["y1"] = 80.09, + ["names"] = { + ["deDE"] = "Die Wuchernde Narbe", + ["enUS"] = "The Slithering Scar", + ["esES"] = "La Cicatriz Serpenteante", + ["frFR"] = "La Balafre sinueuse", + ["koKR"] = "ê°ˆëž˜êµ½ì´ êµ¬ë ", + ["ptBR"] = "A Cicatriz Serpenteante", + ["ruRU"] = "Скользкий овраг", + ["zhCN"] = "巨痕谷", + ["zhTW"] = "巨痕谷", + }, + }, + [543] = { + ["continent"] = 490, + ["parent"] = 490, + ["width"] = 17.47, + ["height"] = 29.94, + ["x1"] = 30.19, + ["y1"] = 50.15, + ["names"] = { + ["deDE"] = "Die heißen Quellen von Golakka", + ["enUS"] = "Golakka Hot Springs", + ["esES"] = "Aguas Termales Golakka", + ["frFR"] = "Sources de Golakka", + ["koKR"] = "골ë½ì¹´ ê°„í—천", + ["ptBR"] = "Fontes Termais Golakka", + ["ruRU"] = "ГорÑчие иÑточники Голакка", + ["zhCN"] = "è‘›æ‹‰å¡æ¸©æ³‰", + ["zhTW"] = "è‘›æ‹‰å¡æº«æ³‰", + }, + }, + [556] = { + ["continent"] = 38, + ["parent"] = 38, + ["width"] = 19.46, + ["height"] = 50.15, + ["x1"] = 50.15, + ["y1"] = 42.29, + ["names"] = { + ["deDE"] = "Der Loch", + ["enUS"] = "The Loch", + ["esES"] = "El Loch", + ["frFR"] = "Le Loch", + ["koKR"] = "대호수", + ["ptBR"] = "O Lago", + ["ruRU"] = "Озеро Лок", + ["zhCN"] = "洛克湖", + ["zhTW"] = "洛克湖", + }, + }, + [596] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 14.47, + ["height"] = 18.71, + ["x1"] = 50.65, + ["y1"] = 58.01, + ["names"] = { + ["deDE"] = "Der Kodofriedhof", + ["enUS"] = "Kodo Graveyard", + ["esES"] = "Cementerio de Kodo", + ["frFR"] = "Cimetière des kodos", + ["koKR"] = "ì½”ë„ ë¬´ë¤", + ["ptBR"] = "Cementerio Kodo", + ["ruRU"] = "Кладбище кодо", + ["zhCN"] = "科多兽åŸåœº", + ["zhTW"] = "科多ç¸å¢³å ´", + }, + }, + [599] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 10.98, + ["height"] = 11.23, + ["x1"] = 54.39, + ["y1"] = 28.07, + ["names"] = { + ["deDE"] = "Festung Donneraxt", + ["enUS"] = "Thunder Axe Fortress", + ["esES"] = "Fortaleza Hacha Trueno", + ["frFR"] = "Forteresse de Hache-Tonnerre", + ["koKR"] = "천둥ë„ë¼ ìš”ìƒˆ", + ["ptBR"] = "A Fortaleza do Machado do Trovão", + ["ruRU"] = "КрепоÑть Громового Топора", + ["zhCN"] = "é›·æ–§å ¡åž’", + ["zhTW"] = "雷斧堡壘", + }, + }, + [602] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 15.97, + ["height"] = 17.96, + ["x1"] = 53.39, + ["y1"] = 79.34, + ["names"] = { + ["deDE"] = "Zirkel der Mannoroc", + ["enUS"] = "Mannoroc Coven", + ["esES"] = "Coven Mannoroc", + ["frFR"] = "Convent de Mannoroc", + ["koKR"] = "ë§Œë…¸ë¡œí¬ ì†Œêµ´", + ["ptBR"] = "Coven Mannoroc", + ["ruRU"] = "Поле Маннорок", + ["zhCN"] = "玛诺洛克集会所", + ["zhTW"] = "瑪諾洛克集會所", + }, + }, + [603] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 12.97, + ["height"] = 17.22, + ["x1"] = 75.85, + ["y1"] = 22.83, + ["names"] = { + ["deDE"] = "Sargeron", + ["enUS"] = "Sargeron", + ["esES"] = "Sargeron", + ["frFR"] = "Sargeron", + ["koKR"] = "살게론", + ["ptBR"] = "Sargeron", + ["ruRU"] = "Саргерон", + ["zhCN"] = "è¨æ ¼éš†", + ["zhTW"] = "薩格隆", + }, + }, + [604] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 12.48, + ["height"] = 18.71, + ["x1"] = 71.11, + ["y1"] = 72.98, + ["names"] = { + ["deDE"] = "Magram", + ["enUS"] = "Magram Village", + ["esES"] = "Aldea Magram", + ["frFR"] = "Village des Magram", + ["koKR"] = "마그람 마ì„", + ["ptBR"] = "Aldeia Magram", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ ÐœÐ°Ð³Ñ€Ð°Ð¼", + ["zhCN"] = "玛格拉姆æ‘", + ["zhTW"] = "瑪格拉姆æ‘", + }, + }, + [606] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 12.97, + ["height"] = 16.47, + ["x1"] = 38.42, + ["y1"] = 89.07, + ["names"] = { + ["deDE"] = "Gelkis", + ["enUS"] = "Gelkis Village", + ["esES"] = "Aldea Gelkis", + ["frFR"] = "Village des Gelkis", + ["koKR"] = "겔키스 마ì„", + ["ptBR"] = "Aldeia Gelkis", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ Ð“ÐµÐ»ÐºÐ¸Ñ", + ["zhCN"] = "å‰å°”剿–¯æ‘", + ["zhTW"] = "å‰çˆ¾å‰æ–¯æ‘", + }, + }, + [607] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 16.47, + ["height"] = 27.69, + ["x1"] = 33.18, + ["y1"] = 52.02, + ["names"] = { + ["deDE"] = "Das Tal der Speere", + ["enUS"] = "Valley of Spears", + ["esES"] = "Valle de las Lanzas", + ["frFR"] = "Vallée des Lances", + ["koKR"] = "뾰족바위 골짜기", + ["ptBR"] = "Vale das Lanças", + ["ruRU"] = "Долина Копий", + ["zhCN"] = "长矛谷", + ["zhTW"] = "長矛谷", + }, + }, + [608] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 9.98, + ["height"] = 11.23, + ["x1"] = 64.87, + ["y1"] = 9.36, + ["names"] = { + ["deDE"] = "Die Nijelspitze", + ["enUS"] = "Nijel\'s Point", + ["esES"] = "Punta de Nijel", + ["frFR"] = "Combe de Nijel", + ["koKR"] = "나ì´ì ¤ì˜ 야ì˜ì§€", + ["ptBR"] = "Ponto de Nijel", + ["ruRU"] = "Ð’Ñ‹Ñота Ðайджела", + ["zhCN"] = "尼耶尔å‰å“¨ç«™", + ["zhTW"] = "尼耶爾å‰å“¨ç«™", + }, + }, + [609] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 9.98, + ["height"] = 14.97, + ["x1"] = 74.35, + ["y1"] = 48.65, + ["names"] = { + ["deDE"] = "Kolkar", + ["enUS"] = "Kolkar Village", + ["esES"] = "Aldea Kolkar", + ["frFR"] = "Village des Kolkar", + ["koKR"] = "콜카르 마ì„", + ["ptBR"] = "Aldeia Kolkar", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ ÐšÐ¾Ð»ÐºÐ°Ñ€", + ["zhCN"] = "ç§‘å°”å¡æ‘", + ["zhTW"] = "ç§‘å¡çˆ¾æ‘", + }, + }, + [656] = { + ["continent"] = 493, + ["parent"] = 493, + ["width"] = 28.44, + ["height"] = 38.92, + ["x1"] = 50.65, + ["y1"] = 54.64, + ["names"] = { + ["deDE"] = "See von Elune\'ara", + ["enUS"] = "Lake Elune\'ara", + ["esES"] = "Lago Elune\'ara", + ["frFR"] = "Lac Elune\'ara", + ["koKR"] = "ì—˜ë£¬ì•„ë¼ í˜¸ìˆ˜", + ["ptBR"] = "Lago Elune\'ara", + ["ruRU"] = "Озеро Элуне\'ара", + ["zhCN"] = "月神湖", + ["zhTW"] = "月神湖", + }, + }, + [657] = { + ["continent"] = 8, + ["parent"] = 8, + ["width"] = 12.48, + ["height"] = 14.97, + ["x1"] = 27.69, + ["y1"] = 35.93, + ["names"] = { + ["deDE"] = "Der Sichere Hafen", + ["enUS"] = "The Harborage", + ["esES"] = "El Refugio", + ["frFR"] = "Le Havre boueux", + ["koKR"] = "피난처", + ["ptBR"] = "O Refúgio", + ["ruRU"] = "Убежище", + ["zhCN"] = "é¿éš¾è¥", + ["zhTW"] = "é¿é›£ç‡Ÿ", + }, + }, + [702] = { + ["continent"] = 141, + ["parent"] = 141, + ["width"] = 1.5, + ["height"] = 2.25, + ["x1"] = 55.64, + ["y1"] = 90.94, + ["names"] = { + ["deDE"] = "Rut\'theran", + ["enUS"] = "Rut\'theran Village", + ["esES"] = "Aldea Rut\'theran", + ["frFR"] = "Rut\'theran", + ["koKR"] = "루테란 마ì„", + ["ptBR"] = "Aldeia Rut\'theran", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ Ð ÑƒÑ‚\'теран", + ["zhCN"] = "é²ç‘Ÿå…°æ‘", + ["zhTW"] = "魯瑟蘭æ‘", + }, + }, + [736] = { + ["continent"] = 141, + ["parent"] = 141, + ["width"] = 8.98, + ["height"] = 23.2, + ["x1"] = 46.41, + ["y1"] = 57.26, + ["names"] = { + ["deDE"] = "Lichtung von Ban\'ethil", + ["enUS"] = "Ban\'ethil Hollow", + ["esES"] = "Hoya Ban\'ethil", + ["frFR"] = "Creux de Ban\'ethil", + ["koKR"] = "바네실 ë™êµ´", + ["ptBR"] = "Clareira de Ban\'ethil", + ["ruRU"] = "Лощина Бен\'Ñтиль", + ["zhCN"] = "ç­å°¼å¸Œå°”山谷", + ["zhTW"] = "ç­å°¼å¸Œçˆ¾å±±è°·", + }, + }, + [797] = { + ["continent"] = 12, + ["parent"] = 12, + ["width"] = 15.47, + ["height"] = 17.96, + ["x1"] = 53.64, + ["y1"] = 83.08, + ["names"] = { + ["deDE"] = "Jerods Anlegestelle", + ["enUS"] = "Jerod\'s Landing", + ["esES"] = "Desembarco de Jerod", + ["frFR"] = "Le Débarcadère de Jerod", + ["koKR"] = "제로드 선착장", + ["ptBR"] = "Atracadero de Jerod", + ["ruRU"] = "Лагерь Джерода", + ["zhCN"] = "æ°ç½—å¾·ç å¤´", + ["zhTW"] = "傑羅德碼頭", + }, + }, + [798] = { + ["continent"] = 12, + ["parent"] = 12, + ["width"] = 12.48, + ["height"] = 9.73, + ["x1"] = 85.08, + ["y1"] = 80.46, + ["names"] = { + ["deDE"] = "Kammwacht", + ["enUS"] = "Ridgepoint Tower", + ["esES"] = "Torre de la Peña", + ["frFR"] = "Tour de la Crête", + ["koKR"] = "마루목 ê°ì‹œíƒ‘", + ["ptBR"] = "Torre Ridgepoint", + ["ruRU"] = "Ð”Ð¾Ð·Ð¾Ñ€Ð½Ð°Ñ Ð±Ð°ÑˆÐ½Ñ", + ["zhCN"] = "山巅之塔", + ["zhTW"] = "山巔之塔", + }, + }, + [799] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 84.33, + ["height"] = 17.22, + ["x1"] = 54.64, + ["y1"] = 21.33, + ["names"] = { + ["deDE"] = "Das Dunkle Ufer", + ["enUS"] = "The Darkened Bank", + ["esES"] = "El Banco Oscurecido", + ["frFR"] = "La Rive sombre", + ["koKR"] = "어스름 ê°•ë‘‘", + ["ptBR"] = "O Banco Escurecido", + ["ruRU"] = "Темный берег", + ["zhCN"] = "暗色河滩", + ["zhTW"] = "暗色河ç˜", + }, + }, + [800] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 5.99, + ["height"] = 10.48, + ["x1"] = 34.93, + ["y1"] = 68.11, + ["names"] = { + ["deDE"] = "Eisklamm", + ["enUS"] = "Coldridge Pass", + ["esES"] = "Paso Fríovelo", + ["frFR"] = "Passe des Frigères", + ["koKR"] = "눈마루 ê³ ê°œ", + ["ptBR"] = "Passagem Crista Fria", + ["ruRU"] = "Туннель Холодной долины", + ["zhCN"] = "寒脊山å°å¾„", + ["zhTW"] = "寒脊山å°å¾‘", + }, + }, + [801] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 7.98, + ["height"] = 10.48, + ["x1"] = 35.93, + ["y1"] = 53.14, + ["names"] = { + ["deDE"] = "Frosthauchtal", + ["enUS"] = "Chill Breeze Valley", + ["esES"] = "Valle Brisa Gélida", + ["frFR"] = "Vallée de la bise", + ["koKR"] = "찬바람 골짜기", + ["ptBR"] = "Vale Brisa Gelada", + ["ruRU"] = "Долина Промозглого Ветра", + ["zhCN"] = "寒风峡谷", + ["zhTW"] = "寒風峽谷", + }, + }, + [802] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 5.99, + ["height"] = 14.22, + ["x1"] = 40.92, + ["y1"] = 40.04, + ["names"] = { + ["deDE"] = "Schimmergrat", + ["enUS"] = "Shimmer Ridge", + ["esES"] = "Cresta Brillante", + ["frFR"] = "Crête scintillante", + ["koKR"] = "쉼머 마루", + ["ptBR"] = "Pico Shimmer", + ["ruRU"] = "Мерцающий хребет", + ["zhCN"] = "闪光岭", + ["zhTW"] = "閃光嶺", + }, + }, + [803] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 5.99, + ["height"] = 6.74, + ["x1"] = 63.37, + ["y1"] = 50.52, + ["names"] = { + ["deDE"] = "Gehöft Bernruh", + ["enUS"] = "Amberstill Ranch", + ["esES"] = "Rancho Amberstill", + ["frFR"] = "Ferme des Amberstill", + ["koKR"] = "앰버스틸 목장", + ["ptBR"] = "Rancho Âmbar Silente", + ["ruRU"] = "Ферма Янтарленов", + ["zhCN"] = "冻石农场", + ["zhTW"] = "å‡çŸ³è¾²å ´", + }, + }, + [804] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 8.98, + ["height"] = 8.23, + ["x1"] = 59.88, + ["y1"] = 57.26, + ["names"] = { + ["deDE"] = "Die Tundridhügel", + ["enUS"] = "The Tundrid Hills", + ["esES"] = "Las Colinas Tundridas", + ["frFR"] = "Les Collines de Tundrid", + ["koKR"] = "툰드리드 ì–¸ë•", + ["ptBR"] = "As Colinas Tundridas", + ["ruRU"] = "Холмы Тандрид", + ["zhCN"] = "冻土岭", + ["zhTW"] = "å‡åœŸå¶º", + }, + }, + [806] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 5.99, + ["height"] = 10.48, + ["x1"] = 85.33, + ["y1"] = 50.9, + ["names"] = { + ["deDE"] = "Südtoraußenposten", + ["enUS"] = "South Gate Outpost", + ["esES"] = "Puesto de la Puerta Sur", + ["frFR"] = "Avant-poste de la porte Sud", + ["koKR"] = "남부 관문 전초기지", + ["ptBR"] = "Acampamento Avançado do Portão Sul", + ["ruRU"] = "ЗаÑтава у Южных врат", + ["zhCN"] = "å—门哨岗", + ["zhTW"] = "å—門崗哨", + }, + }, + [808] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 5.99, + ["height"] = 14.22, + ["x1"] = 82.83, + ["y1"] = 37.8, + ["names"] = { + ["deDE"] = "Nordtoraußenposten", + ["enUS"] = "North Gate Outpost", + ["esES"] = "Puesto de la Puerta Norte", + ["frFR"] = "Avant-poste de la porte Nord", + ["koKR"] = "ë¶ë¶€ 관문 전초기지", + ["ptBR"] = "Posto Avançado da Porta Norte", + ["ruRU"] = "ЗаÑтава у Северных врат", + ["zhCN"] = "北门哨岗", + ["zhTW"] = "北門崗哨", + }, + }, + [809] = { + ["continent"] = 1, + ["parent"] = 1, + ["width"] = 10.98, + ["height"] = 13.47, + ["x1"] = 49.4, + ["y1"] = 37.43, + ["names"] = { + ["deDE"] = "Tore von Eisenschmiede", + ["enUS"] = "Gates of Ironforge", + ["esES"] = "Puertas de Forjaz", + ["frFR"] = "Portes d\'Ironforge", + ["koKR"] = "ì•„ì´ì–¸í¬ì§€ 성문", + ["ptBR"] = "Portões de Altaforja", + ["ruRU"] = "Врата Стальгорна", + ["zhCN"] = "é“炉堡大门", + ["zhTW"] = "éµçˆå ¡å¤§é–€", + }, + }, + [810] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 10.28, + ["height"] = 10.63, + ["x1"] = 47.95, + ["y1"] = 51.27, + ["names"] = { + ["deDE"] = "Stillwassertümpel", + ["enUS"] = "Stillwater Pond", + ["esES"] = "Estanque Aguaserena", + ["frFR"] = "Etang immobile", + ["koKR"] = "ê³ ìš”ì˜ ì—°ëª»", + ["ptBR"] = "Lago Stillwater", + ["ruRU"] = "Тихий Омут", + ["zhCN"] = "陿°´æ± ", + ["zhTW"] = "éœæ°´æ± ", + }, + }, + [811] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 14.57, + ["height"] = 14.07, + ["x1"] = 47.31, + ["y1"] = 66.32, + ["names"] = { + ["deDE"] = "Alptraumtal", + ["enUS"] = "Nightmare Vale", + ["esES"] = "Valle de la Pesadilla", + ["frFR"] = "Vallée des cauchemars", + ["koKR"] = "ì•…ëª½ì˜ ê³¨ì§œê¸°", + ["ptBR"] = "Vale do Pesadelo", + ["ruRU"] = "Долина Кошмаров", + ["zhCN"] = "噩梦谷", + ["zhTW"] = "噩夢谷", + }, + }, + [812] = { + ["continent"] = 85, + ["parent"] = 85, + ["width"] = 19.26, + ["height"] = 25.15, + ["x1"] = 87.18, + ["y1"] = 48.5, + ["names"] = { + ["deDE"] = "Giftwebertal", + ["enUS"] = "Venomweb Vale", + ["esES"] = "Valle de la Telaraña Venenosa", + ["frFR"] = "Vallée de Tissevenin", + ["koKR"] = "ë…그물 골짜기", + ["ptBR"] = "Vale Venomweb", + ["ruRU"] = "Долина Ñдовитых пауков", + ["zhCN"] = "毒蛛峡谷", + ["zhTW"] = "毒蛛峽谷", + }, + }, + [813] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 11.98, + ["height"] = 11.23, + ["x1"] = 26.45, + ["y1"] = 58.01, + ["names"] = { + ["deDE"] = "Das Bollwerk", + ["enUS"] = "The Bulwark", + ["esES"] = "El Baluarte", + ["frFR"] = "La Barricade", + ["koKR"] = "보루", + ["ptBR"] = "A Muralha", + ["ruRU"] = "БаÑтион", + ["zhCN"] = "亡çµå£åž’", + ["zhTW"] = "亡éˆå£å£˜", + }, + }, + [816] = { + ["continent"] = 14, + ["parent"] = 14, + ["width"] = 12.48, + ["height"] = 17.96, + ["x1"] = 41.17, + ["y1"] = 43.41, + ["names"] = { + ["deDE"] = "Revier der Klingenmähnen", + ["enUS"] = "Razormane Grounds", + ["esES"] = "Llanuras de los Sable de Roca", + ["frFR"] = "Terres des Tranchecrins", + ["koKR"] = "서슬갈기ì¼ì¡± ì˜í† ", + ["ptBR"] = "Terrenos Razormane", + ["ruRU"] = "Земли Иглогривых", + ["zhCN"] = "钢鬃è¥åœ°", + ["zhTW"] = "鋼鬃營地", + }, + }, + [817] = { + ["continent"] = 14, + ["parent"] = 14, + ["width"] = 7.49, + ["height"] = 7.49, + ["x1"] = 53.64, + ["y1"] = 11.98, + ["names"] = { + ["deDE"] = "Die Knochenhöhle", + ["enUS"] = "Skull Rock", + ["esES"] = "Roca Calavera", + ["frFR"] = "Rocher du crâne", + ["koKR"] = "해골 바위굴", + ["ptBR"] = "Pedra Skull", + ["ruRU"] = "Скала Черепа", + ["zhCN"] = "骷髅石", + ["zhTW"] = "骷é«çŸ³", + }, + }, + [818] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 6.99, + ["height"] = 17.96, + ["x1"] = 36.43, + ["y1"] = 62.13, + ["names"] = { + ["deDE"] = "Bleichmähnenfels", + ["enUS"] = "Palemane Rock", + ["esES"] = "Roca de los Pelomalo", + ["frFR"] = "Rocher des crins-pâles", + ["koKR"] = "회색갈기ì¼ì¡± 바위굴", + ["ptBR"] = "Pedra Palemane", + ["ruRU"] = "Ð£Ñ‚ÐµÑ Ð‘Ð»ÐµÐ´Ð½Ð¾Ð³Ñ€Ð¸Ð²Ð¾Ð²", + ["zhCN"] = "白鬃石", + ["zhTW"] = "白鬃石", + }, + }, + [819] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 10.48, + ["height"] = 10.48, + ["x1"] = 50.15, + ["y1"] = 11.98, + ["names"] = { + ["deDE"] = "Windfurienkamm", + ["enUS"] = "Windfury Ridge", + ["esES"] = "Cima del Viento Furioso", + ["frFR"] = "Crête des Furies-des-vents", + ["koKR"] = "성난바람 마루", + ["ptBR"] = "Colina Windfury", + ["ruRU"] = "ГрÑда ÐеиÑтовÑтва Ветра", + ["zhCN"] = "狂风山", + ["zhTW"] = "狂風山", + }, + }, + [820] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 8.98, + ["height"] = 23.95, + ["x1"] = 52.89, + ["y1"] = 29.94, + ["names"] = { + ["deDE"] = "Die Goldenen Ebenen", + ["enUS"] = "The Golden Plains", + ["esES"] = "Llanuras Doradas", + ["frFR"] = "Les plaines dorées", + ["koKR"] = "황금 ì´ˆì›", + ["ptBR"] = "As Planícies Douradas", + ["ruRU"] = "Золотые равнины", + ["zhCN"] = "金色平原", + ["zhTW"] = "金色平原", + }, + }, + [821] = { + ["continent"] = 215, + ["parent"] = 215, + ["width"] = 10.98, + ["height"] = 12.72, + ["x1"] = 62.87, + ["y1"] = 67.74, + ["names"] = { + ["deDE"] = "Die Wogenden Ebenen", + ["enUS"] = "The Rolling Plains", + ["esES"] = "Llanuras Onduladas", + ["frFR"] = "Les plaines vallonnées", + ["koKR"] = "구릉 ì´ˆì›", + ["ptBR"] = "As Planícies Onduladas", + ["ruRU"] = "ХолмиÑтые равнины", + ["zhCN"] = "è‰æµ·å¹³åŽŸ", + ["zhTW"] = "è‰æµ·å¹³åŽŸ", + }, + }, + [838] = { + ["continent"] = 38, + ["parent"] = 38, + ["width"] = 12.97, + ["height"] = 17.22, + ["x1"] = 22.46, + ["y1"] = 15.34, + ["names"] = { + ["deDE"] = "Nordtorpass", + ["enUS"] = "North Gate Pass", + ["esES"] = "Paso de la Puerta Norte", + ["frFR"] = "Passage de la porte Nord", + ["koKR"] = "ë¶ë¶€ 관문 통행로", + ["ptBR"] = "Passagem da Porta Norte", + ["ruRU"] = "Северные врата", + ["zhCN"] = "北门å°å¾„", + ["zhTW"] = "北門å°å¾‘", + }, + }, + [856] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 21.46, + ["height"] = 37.43, + ["x1"] = 47.65, + ["y1"] = 40.42, + ["names"] = { + ["deDE"] = "Zwielichtshain", + ["enUS"] = "Twilight Grove", + ["esES"] = "Arboleda Crepuscular", + ["frFR"] = "Bosquet du crépuscule", + ["koKR"] = "í™©í˜¼ì˜ ìˆ²", + ["ptBR"] = "Bosque Crepúsculo", + ["ruRU"] = "МглиÑÑ‚Ð°Ñ Ñ€Ð¾Ñ‰Ð°", + ["zhCN"] = "黎明森林", + ["zhTW"] = "黎明森林", + }, + }, + [880] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 11.98, + ["height"] = 20.96, + ["x1"] = 45.91, + ["y1"] = 83.08, + ["names"] = { + ["deDE"] = "Thandolübergang", + ["enUS"] = "Thandol Span", + ["esES"] = "Tramo Thandol", + ["frFR"] = "Viaduc de Thandol", + ["koKR"] = "íƒ„ëŒ êµê°", + ["ptBR"] = "Ponte Thandol", + ["ruRU"] = "МоÑÑ‚ Тандола", + ["zhCN"] = "è¨å¤šå°”大桥", + ["zhTW"] = "薩多爾大橋", + }, + }, + [896] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 12.48, + ["height"] = 15.72, + ["x1"] = 17.22, + ["y1"] = 79.72, + ["names"] = { + ["deDE"] = "Fegefeuerinsel", + ["enUS"] = "Purgation Isle", + ["esES"] = "Isla de la Purga", + ["frFR"] = "Ile de la purification", + ["koKR"] = "ì†ì£„ì˜ ì„¬", + ["ptBR"] = "Ilha da Purgatória", + ["ruRU"] = "ОÑтров ОчищениÑ", + ["zhCN"] = "赎罪岛", + ["zhTW"] = "贖罪島", + }, + }, + [916] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 9.48, + ["height"] = 12.72, + ["x1"] = 56.64, + ["y1"] = 17.59, + ["names"] = { + ["deDE"] = "Jansens Hof", + ["enUS"] = "The Jansen Stead", + ["esES"] = "La Granja Jansen", + ["frFR"] = "La ferme des Jansen", + ["koKR"] = "얀센 ë†ìž¥", + ["ptBR"] = "A Fazenda Jansen", + ["ruRU"] = "Владение ЯнÑена", + ["zhCN"] = "贾森农场", + ["zhTW"] = "傑生農場", + }, + }, + [917] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 9.98, + ["height"] = 11.98, + ["x1"] = 62.38, + ["y1"] = 61.38, + ["names"] = { + ["deDE"] = "Der Todesacker", + ["enUS"] = "The Dead Acre", + ["esES"] = "El Terreno Muerto", + ["frFR"] = "L\'acre mort", + ["koKR"] = "메마른 논밭", + ["ptBR"] = "O Campo Morto", + ["ruRU"] = "ÐœÐµÑ€Ñ‚Ð²Ð°Ñ Ð¿Ð°ÑˆÐ½Ñ", + ["zhCN"] = "死亡农地", + ["zhTW"] = "死亡農地", + }, + }, + [918] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 10.98, + ["height"] = 12.72, + ["x1"] = 44.91, + ["y1"] = 36.3, + ["names"] = { + ["deDE"] = "Molsens Hof", + ["enUS"] = "The Molsen Farm", + ["esES"] = "La Granja Molsen", + ["frFR"] = "La ferme des Molsen", + ["koKR"] = "몰센 ë†ìž¥", + ["ptBR"] = "A Fazenda Molsen", + ["ruRU"] = "Ферма МольÑена", + ["zhCN"] = "摩尔森农场", + ["zhTW"] = "摩爾森農場", + }, + }, + [920] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 17.96, + ["height"] = 11.23, + ["x1"] = 46.91, + ["y1"] = 78.97, + ["names"] = { + ["deDE"] = "Die Dolchhügel", + ["enUS"] = "The Dagger Hills", + ["esES"] = "Las Colinas de la Daga", + ["frFR"] = "Les collines de la Dague", + ["koKR"] = "비수 ì–¸ë•", + ["ptBR"] = "As Colinas Dagger", + ["ruRU"] = "Холмы Разбойничьих Кинжалов", + ["zhCN"] = "匕首岭", + ["zhTW"] = "匕首嶺", + }, + }, + [921] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 10.48, + ["height"] = 13.47, + ["x1"] = 31.19, + ["y1"] = 69.61, + ["names"] = { + ["deDE"] = "Demonts Heim", + ["enUS"] = "Demont\'s Place", + ["esES"] = "El Lugar de Demont", + ["frFR"] = "Maison de Demont", + ["koKR"] = "ë°ëª¬íŠ¸ì˜ ì§‘í„°", + ["ptBR"] = "Lugar de Demont", + ["ruRU"] = "УчаÑток Демонта", + ["zhCN"] = "迪蒙特è’野", + ["zhTW"] = "迪蒙特è’野", + }, + }, + [922] = { + ["continent"] = 40, + ["parent"] = 40, + ["width"] = 14.97, + ["height"] = 10.48, + ["x1"] = 63.37, + ["y1"] = 72.6, + ["names"] = { + ["deDE"] = "Die Staubebenen", + ["enUS"] = "The Dust Plains", + ["esES"] = "Las Llanuras Polvorientas", + ["frFR"] = "Les Plaines de poussière", + ["koKR"] = "먼지 ì–¸ë•", + ["ptBR"] = "As Planícies de Poeira", + ["ruRU"] = "Пыльные равнины", + ["zhCN"] = "尘埃平原", + ["zhTW"] = "塵埃平原", + }, + }, + [923] = { + ["continent"] = 38, + ["parent"] = 38, + ["width"] = 14.97, + ["height"] = 26.95, + ["x1"] = 34.93, + ["y1"] = 77.84, + ["names"] = { + ["deDE"] = "Splittersteintal", + ["enUS"] = "Stonesplinter Valley", + ["esES"] = "Valle Fragmentado", + ["frFR"] = "Vallée des Brisepierre", + ["koKR"] = "가루바위 골짜기", + ["ptBR"] = "Vale Stonesplinter", + ["ruRU"] = "Долина Камнедробов", + ["zhCN"] = "碎石怪之谷", + ["zhTW"] = "碎石怪之谷", + }, + }, + [924] = { + ["continent"] = 38, + ["parent"] = 38, + ["width"] = 10.98, + ["height"] = 27.69, + ["x1"] = 20.46, + ["y1"] = 73.73, + ["names"] = { + ["deDE"] = "Tal der Könige", + ["enUS"] = "Valley of Kings", + ["esES"] = "Valle de los Reyes", + ["frFR"] = "Vallée des rois", + ["koKR"] = "ì™•ì˜ ê³„ê³¡", + ["ptBR"] = "Vale dos Reis", + ["ruRU"] = "Долина Королей", + ["zhCN"] = "国王谷", + ["zhTW"] = "國王谷", + }, + }, + [927] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 6.49, + ["height"] = 11.23, + ["x1"] = 54.14, + ["y1"] = 22.83, + ["names"] = { + ["deDE"] = "Der Leuchtende Strand", + ["enUS"] = "The Shining Strand", + ["esES"] = "La Playa Resplandeciente", + ["frFR"] = "Le Rivage rayonnant", + ["koKR"] = "ë°˜ì§ì´ëŠ” 호숫가", + ["ptBR"] = "A Praia Brilhante", + ["ruRU"] = "СиÑющий берег", + ["zhCN"] = "闪光湖岸", + ["zhTW"] = "閃光湖岸", + }, + }, + [928] = { + ["continent"] = 130, + ["parent"] = 130, + ["width"] = 7.98, + ["height"] = 8.98, + ["x1"] = 40.42, + ["y1"] = 28.44, + ["names"] = { + ["deDE"] = "Nördliche Gezeitenlichtung", + ["enUS"] = "North Tide\'s Hollow", + ["esES"] = "El Refugio de la Marea del Norte", + ["frFR"] = "Creux de la côte nord", + ["koKR"] = "ë¶ë¶€ 해안 골짜기", + ["ptBR"] = "Caverna da Maré do Norte", + ["ruRU"] = "Ð¡ÐµÐ²ÐµÑ€Ð½Ð°Ñ Ð¿Ñ€Ð¸Ð»Ð¸Ð²Ð½Ð°Ñ Ð½Ð¸Ð·Ð¸Ð½Ð°", + ["zhCN"] = "北æµè°·", + ["zhTW"] = "北æµè°·", + }, + }, + [936] = { + ["continent"] = 38, + ["parent"] = 38, + ["width"] = 11.48, + ["height"] = 19.46, + ["x1"] = 45.16, + ["y1"] = 68.86, + ["names"] = { + ["deDE"] = "Grautatzengrat", + ["enUS"] = "Grizzlepaw Ridge", + ["esES"] = "Cima Garra de Osos", + ["frFR"] = "Falaise de Vieillegriffe", + ["koKR"] = "곰발바닥 마루", + ["ptBR"] = "Crista Garrabruta", + ["ruRU"] = "Хребет Седых Лап", + ["zhCN"] = "ç°çˆªå±±", + ["zhTW"] = "ç°çˆªå±±", + }, + }, + [976] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 10.48, + ["height"] = 12.72, + ["x1"] = 51.15, + ["y1"] = 27.32, + ["names"] = { + ["deDE"] = "Gadgetzan", + ["enUS"] = "Gadgetzan", + ["esES"] = "Gadgetzan", + ["frFR"] = "Gadgetzan", + ["koKR"] = "가젯잔", + ["ptBR"] = "Geringontzan", + ["ruRU"] = "ПрибамбаÑÑк", + ["zhCN"] = "加基森", + ["zhTW"] = "加基森", + }, + }, + [977] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 7.98, + ["height"] = 9.73, + ["x1"] = 66.87, + ["y1"] = 21.33, + ["names"] = { + ["deDE"] = "Dampfdruckpier", + ["enUS"] = "Steamwheedle Port", + ["esES"] = "Puerto Steamwheedle", + ["frFR"] = "Port Gentepression", + ["koKR"] = "스팀휘들 항구", + ["ptBR"] = "Porto Steamwheedle", + ["ruRU"] = "Порт КартелÑ", + ["zhCN"] = "热砂港", + ["zhTW"] = "熱砂港", + }, + }, + [978] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 14.97, + ["height"] = 17.96, + ["x1"] = 37.43, + ["y1"] = 12.72, + ["names"] = { + ["deDE"] = "Zul\'Farrak", + ["enUS"] = "Zul\'Farrak", + ["esES"] = "Zul\'Farrak", + ["frFR"] = "Zul\'Farrak", + ["koKR"] = "줄파ë½", + ["ptBR"] = "Zul\'Farrak", + ["ruRU"] = "Зул\'Фаррак", + ["zhCN"] = "祖尔法拉克", + ["zhTW"] = "祖爾法拉克", + }, + }, + [979] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 6.99, + ["height"] = 10.48, + ["x1"] = 39.92, + ["y1"] = 28.44, + ["names"] = { + ["deDE"] = "Sandmarterwache", + ["enUS"] = "Sandsorrow Watch", + ["esES"] = "Mirador de la Pena Arenosa", + ["frFR"] = "Guet de Tristesable", + ["koKR"] = "슬픈모래 ê°ì‹œíƒ‘", + ["ptBR"] = "Vigia Sandsorrow", + ["ruRU"] = "ЗаÑтава Скорбных ПеÑков", + ["zhCN"] = "æµæ²™å²—哨", + ["zhTW"] = "æµæ²™å´—哨", + }, + }, + [980] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 12.48, + ["height"] = 17.22, + ["x1"] = 30.69, + ["y1"] = 62.5, + ["names"] = { + ["deDE"] = "Disteltal", + ["enUS"] = "Thistleshrub Valley", + ["esES"] = "Valle Cardoespina", + ["frFR"] = "Vallée des Chardonniers", + ["koKR"] = "ë¤ë¶ˆë‚˜ë¬´ 골짜기", + ["ptBR"] = "O Vale Thistleshrub", + ["ruRU"] = "Долина КактуÑов", + ["zhCN"] = "çŒæœ¨è°·", + ["zhTW"] = "çŒæœ¨è°·", + }, + }, + [981] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 10.98, + ["height"] = 17.22, + ["x1"] = 54.39, + ["y1"] = 70.73, + ["names"] = { + ["deDE"] = "Die Klaffende Schlucht", + ["enUS"] = "The Gaping Chasm", + ["esES"] = "El Abismo Boquiabierto", + ["frFR"] = "Le Gouffre béant", + ["koKR"] = "모래지옥 협곡", + ["ptBR"] = "O Abismo Gargante", + ["ruRU"] = "ЗиÑÑŽÑ‰Ð°Ñ Ð±ÐµÐ·Ð´Ð½Ð°", + ["zhCN"] = "大裂å£", + ["zhTW"] = "大裂å£", + }, + }, + [982] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 7.49, + ["height"] = 14.97, + ["x1"] = 34.68, + ["y1"] = 45.66, + ["names"] = { + ["deDE"] = "Der Giftige Unterschlupf", + ["enUS"] = "The Noxious Lair", + ["esES"] = "El Refugio Nocivo", + ["frFR"] = "Le Repaire nuisible", + ["koKR"] = "ë§¹ë…ì˜ ë‘¥ì§€", + ["ptBR"] = "A Caverna Nociva", + ["ruRU"] = "Ядовитый улей", + ["zhCN"] = "è…化之巢", + ["zhTW"] = "è…化之巢", + }, + }, + [983] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 7.98, + ["height"] = 11.98, + ["x1"] = 41.92, + ["y1"] = 56.14, + ["names"] = { + ["deDE"] = "Truppenlager der Dünenbrecher", + ["enUS"] = "Dunemaul Compound", + ["esES"] = "Complejo Dunemaul", + ["frFR"] = "Base des Cognedunes", + ["koKR"] = "모래ë§ì¹˜ 주둔지", + ["ptBR"] = "Complexo Lançatroz", + ["ruRU"] = "ПоÑеление ПеÑчаного Молота", + ["zhCN"] = "ç ‚æ§Œè¥åœ°", + ["zhTW"] = "砂槌營地", + }, + }, + [984] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 6.49, + ["height"] = 7.49, + ["x1"] = 46.66, + ["y1"] = 65.12, + ["names"] = { + ["deDE"] = "Ostmondruinen", + ["enUS"] = "Eastmoon Ruins", + ["esES"] = "Ruinas de la Luna Este", + ["frFR"] = "Ruines d\'Estelune", + ["koKR"] = "ë™ìª½ ë‹¬ì˜ íí—ˆ", + ["ptBR"] = "Ruínas de Luar do Leste", + ["ruRU"] = "Руины ВоÑточной Луны", + ["zhCN"] = "东月废墟", + ["zhTW"] = "æ±æœˆå»¢å¢Ÿ", + }, + }, + [985] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 9.48, + ["height"] = 12.72, + ["x1"] = 59.63, + ["y1"] = 37.8, + ["names"] = { + ["deDE"] = "Wasserfeld", + ["enUS"] = "Waterspring Field", + ["esES"] = "Campo Manantial", + ["frFR"] = "Champ des puisatiers", + ["koKR"] = "샘솟는 벌íŒ", + ["ptBR"] = "Campo das Fontes", + ["ruRU"] = "Родниковое поле", + ["zhCN"] = "清泉平原", + ["zhTW"] = "清泉平原", + }, + }, + [986] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 5.99, + ["height"] = 9.73, + ["x1"] = 66.87, + ["y1"] = 33.31, + ["names"] = { + ["deDE"] = "Zalashjis Bau", + ["enUS"] = "Zalashji\'s Den", + ["esES"] = "El Refugio de Zalashji", + ["frFR"] = "La tanière de Zalashji", + ["koKR"] = "잘ë¼ì‰¬ì§€ì˜ êµ´", + ["ptBR"] = "Toca de Zalashji", + ["ruRU"] = "Логово Залашьи", + ["zhCN"] = "è¨æ‹‰è¾›ä¹‹ç©´", + ["zhTW"] = "薩拉辛之穴", + }, + }, + [987] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 13.97, + ["height"] = 14.22, + ["x1"] = 54.89, + ["y1"] = 91.69, + ["names"] = { + ["deDE"] = "Landendestrand", + ["enUS"] = "Land\'s End Beach", + ["esES"] = "Playa Fin de la Tierra", + ["frFR"] = "Plage du Bout-du-Monde", + ["koKR"] = "ëìžë½ 해안", + ["ptBR"] = "Playa del Fin del Mundo", + ["ruRU"] = "ПлÑж на Краю Света", + ["zhCN"] = "天涯海滩", + ["zhTW"] = "天涯海ç˜", + }, + }, + [990] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 7.98, + ["height"] = 14.22, + ["x1"] = 36.93, + ["y1"] = 80.46, + ["names"] = { + ["deDE"] = "Tal der Behüter", + ["enUS"] = "Valley of the Watchers", + ["esES"] = "Valle de los Vigilantes", + ["frFR"] = "Vallée des guetteurs", + ["koKR"] = "ê°ì‹œìžì˜ 골짜기", + ["ptBR"] = "Vale dos Observadores", + ["ruRU"] = "Долина Стражей", + ["zhCN"] = "守å«ä¹‹è°·", + ["zhTW"] = "守衛之谷", + }, + }, + [992] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 7.49, + ["height"] = 9.73, + ["x1"] = 40.17, + ["y1"] = 71.48, + ["names"] = { + ["deDE"] = "Südmondruinen", + ["enUS"] = "Southmoon Ruins", + ["esES"] = "Ruinas de la Luna Sur", + ["frFR"] = "Ruines de Sudelune", + ["koKR"] = "서쪽 ë‹¬ì˜ íí—ˆ", + ["ptBR"] = "Ruínas Southmoon", + ["ruRU"] = "Руины Южной Луны", + ["zhCN"] = "å—æœˆåºŸå¢Ÿ", + ["zhTW"] = "å—æœˆå»¢å¢Ÿ", + }, + }, + [996] = { + ["continent"] = 44, + ["parent"] = 44, + ["width"] = 19.46, + ["height"] = 26.2, + ["x1"] = 40.17, + ["y1"] = 16.84, + ["names"] = { + ["deDE"] = "Renders Lager", + ["enUS"] = "Render\'s Camp", + ["esES"] = "Campamento de Render", + ["frFR"] = "Camp des Etripeurs", + ["koKR"] = "ì•½íƒˆì˜ ì•¼ì˜ì§€", + ["ptBR"] = "Acampamento Render", + ["ruRU"] = "Лагерь Ренда", + ["zhCN"] = "撕裂者è¥åœ°", + ["zhTW"] = "撕裂者營地", + }, + }, + [997] = { + ["continent"] = 44, + ["parent"] = 44, + ["width"] = 39.42, + ["height"] = 14.97, + ["x1"] = 73.1, + ["y1"] = 77.84, + ["names"] = { + ["deDE"] = "Renders Tal", + ["enUS"] = "Render\'s Valley", + ["esES"] = "Valle de Render", + ["frFR"] = "Vallée des Etripeurs", + ["koKR"] = "ì•½íƒˆì˜ ê³„ê³¡", + ["ptBR"] = "Vale de Render", + ["ruRU"] = "Долина Ренда", + ["zhCN"] = "撕裂者山谷", + ["zhTW"] = "撕裂者山谷", + }, + }, + [1000] = { + ["continent"] = 44, + ["parent"] = 44, + ["width"] = 16.47, + ["height"] = 26.95, + ["x1"] = 78.09, + ["y1"] = 42.66, + ["names"] = { + ["deDE"] = "Galardelltal", + ["enUS"] = "Galardell Valley", + ["esES"] = "Valle de Galardell", + ["frFR"] = "Vallée de Galardell", + ["koKR"] = "ê°ˆë¼ë¸ 골짜기", + ["ptBR"] = "Vale Galardell", + ["ruRU"] = "Долина Галарделл", + ["zhCN"] = "加拉德尔山谷", + ["zhTW"] = "加拉德爾山谷", + }, + }, + [1001] = { + ["continent"] = 44, + ["parent"] = 44, + ["width"] = 32.44, + ["height"] = 13.47, + ["x1"] = 38.17, + ["y1"] = 73.35, + ["names"] = { + ["deDE"] = "Uferpfad", + ["enUS"] = "Lakeridge Highway", + ["esES"] = "Autopista de la Cima del Lago", + ["frFR"] = "Grand-route de la Crête du lac", + ["koKR"] = "호수마루 오솔길", + ["ptBR"] = "Carretera Lakeridge", + ["ruRU"] = "Озерный тракт", + ["zhCN"] = "湖边大é“", + ["zhTW"] = "湖邊大é“", + }, + }, + [1002] = { + ["continent"] = 44, + ["parent"] = 44, + ["width"] = 17.96, + ["height"] = 26.2, + ["x1"] = 18.46, + ["y1"] = 69.24, + ["names"] = { + ["deDE"] = "Drei Ecken", + ["enUS"] = "Three Corners", + ["esES"] = "Tres Esquinas", + ["frFR"] = "Trois chemins", + ["koKR"] = "붉ì€ë§ˆë£¨ 삼거리", + ["ptBR"] = "Os Três Cantos", + ["ruRU"] = "Три Угла", + ["zhCN"] = "三角路å£", + ["zhTW"] = "三角路å£", + }, + }, + [1016] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 11.48, + ["height"] = 20.21, + ["x1"] = 60.63, + ["y1"] = 32.56, + ["names"] = { + ["deDE"] = "Hügel der Düsterschmiede", + ["enUS"] = "Direforge Hill", + ["esES"] = "Colina de la Forja Oscura", + ["frFR"] = "Colline de Morneforge", + ["koKR"] = "불길한 ì–¸ë•", + ["ptBR"] = "Monte Umbraforja", + ["ruRU"] = "Зловещий холм", + ["zhCN"] = "æ¶é“å²­", + ["zhTW"] = "惡éµå¶º", + }, + }, + [1017] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 11.48, + ["height"] = 13.47, + ["x1"] = 72.11, + ["y1"] = 38.17, + ["names"] = { + ["deDE"] = "Raptorgrat", + ["enUS"] = "Raptor Ridge", + ["esES"] = "Cima de los Raptadores", + ["frFR"] = "Crête des raptors", + ["koKR"] = "랩터 마루", + ["ptBR"] = "Pico do Raptor", + ["ruRU"] = "ГрÑда Ящеров", + ["zhCN"] = "æé¾™å²­", + ["zhTW"] = "æé¾å¶º", + }, + }, + [1018] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 14.97, + ["height"] = 12.72, + ["x1"] = 19.96, + ["y1"] = 49.03, + ["names"] = { + ["deDE"] = "Schwarzkanalmarschen", + ["enUS"] = "Black Channel Marsh", + ["esES"] = "Marisma del Canal Negro", + ["frFR"] = "Marais des eaux-noires", + ["koKR"] = "ê²€ì€ë°”ë‹¥ 습지대", + ["ptBR"] = "Pântano do Canal Negro", + ["ruRU"] = "Ð§ÐµÑ€Ð½Ð°Ñ Ñ‚Ñ€ÑÑина", + ["zhCN"] = "黑水沼泽", + ["zhTW"] = "黑水沼澤", + }, + }, + [1020] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 10.98, + ["height"] = 17.96, + ["x1"] = 61.88, + ["y1"] = 54.64, + ["names"] = { + ["deDE"] = "Moosfellmoor", + ["enUS"] = "Mosshide Fen", + ["esES"] = "Ciénaga Pielmusgo", + ["frFR"] = "Marais des Poils-moussus", + ["koKR"] = "ì´ë¼ê°€ì£½ 수ë ", + ["ptBR"] = "Pântano Pele de Musgo", + ["ruRU"] = "Болото Мохошкуров", + ["zhCN"] = "藓皮沼泽", + ["zhTW"] = "蘚皮沼澤", + }, + }, + [1021] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 11.98, + ["height"] = 16.47, + ["x1"] = 56.39, + ["y1"] = 68.86, + ["names"] = { + ["deDE"] = "Thelgenfelsen", + ["enUS"] = "Thelgen Rock", + ["esES"] = "Roca Thelgen", + ["frFR"] = "Rocher de Thelgen", + ["koKR"] = "í…”ê² ë°”ìœ„êµ´", + ["ptBR"] = "A Rocha Thelgen", + ["ruRU"] = "Пещера Тельгена", + ["zhCN"] = "瑟根石", + ["zhTW"] = "瑟根石", + }, + }, + [1022] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 14.47, + ["height"] = 11.23, + ["x1"] = 19.21, + ["y1"] = 36.3, + ["names"] = { + ["deDE"] = "Blaukiemenmarschen", + ["enUS"] = "Bluegill Marsh", + ["esES"] = "Marisma Branquiazules", + ["frFR"] = "Marais des Branchies-bleues", + ["koKR"] = "푸른아가미 습지대", + ["ptBR"] = "Pântano dos Guelrazul", + ["ruRU"] = "Болота Синежабрых", + ["zhCN"] = "è“腮沼泽", + ["zhTW"] = "è—腮沼澤", + }, + }, + [1023] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 13.47, + ["height"] = 20.21, + ["x1"] = 34.18, + ["y1"] = 24.33, + ["names"] = { + ["deDE"] = "Salzgischtschlucht", + ["enUS"] = "Saltspray Glen", + ["esES"] = "Valle Rocío Salado", + ["frFR"] = "Vallon des embruns", + ["koKR"] = "소금물 습지대", + ["ptBR"] = "Bosque Saltspray", + ["ruRU"] = "Долина Солевого Тумана", + ["zhCN"] = "ç›æ²«æ²¼æ³½", + ["zhTW"] = "鹽沫沼澤", + }, + }, + [1024] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 18.46, + ["height"] = 14.97, + ["x1"] = 23.2, + ["y1"] = 28.44, + ["names"] = { + ["deDE"] = "Sonnenwendmarschen", + ["enUS"] = "Sundown Marsh", + ["esES"] = "Marisma del Ocaso", + ["frFR"] = "Marais du couchant", + ["koKR"] = "í•´ëª°ì´ ëŠª", + ["ptBR"] = "Pantano Sundown", + ["ruRU"] = "Закатное болото", + ["zhCN"] = "æ—¥è½æ²¼æ³½", + ["zhTW"] = "æ—¥è½æ²¼æ¾¤", + }, + }, + [1025] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 10.48, + ["height"] = 22.46, + ["x1"] = 53.64, + ["y1"] = 36.68, + ["names"] = { + ["deDE"] = "Der Grüne Gürtel", + ["enUS"] = "The Green Belt", + ["esES"] = "El Cinturón Verde", + ["frFR"] = "La Ceinture verte", + ["koKR"] = "녹지대", + ["ptBR"] = "O Cinturão Verde", + ["ruRU"] = "Зеленый ПоÑÑ", + ["zhCN"] = "绿带è‰åœ°", + ["zhTW"] = "綠帶è‰åœ°", + }, + }, + [1036] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 14.97, + ["height"] = 15.72, + ["x1"] = 47.41, + ["y1"] = 48.28, + ["names"] = { + ["deDE"] = "Lager der Grollhauer", + ["enUS"] = "Angerfang Encampment", + ["esES"] = "Campamento Colmillo Furioso", + ["frFR"] = "Campement de Hargnecroc", + ["koKR"] = "성난송곳니 야ì˜ì§€", + ["ptBR"] = "Acampamento Presa-de-Fúria", + ["ruRU"] = "Лагерь Злого Клыка", + ["zhCN"] = "怒牙è¥åœ°", + ["zhTW"] = "怒牙營地", + }, + }, + [1038] = { + ["continent"] = 11, + ["parent"] = 11, + ["width"] = 21.96, + ["height"] = 43.41, + ["x1"] = 81.84, + ["y1"] = 63.62, + ["names"] = { + ["deDE"] = "Tore des Drachenmals", + ["enUS"] = "Dragonmaw Gates", + ["esES"] = "Puertas de Garganta de Dragón", + ["frFR"] = "Portes des Dragonmaw", + ["koKR"] = "용아귀부족 관문", + ["ptBR"] = "Portões Presa do Dragão", + ["ruRU"] = "Врата Драконьей ПаÑти", + ["zhCN"] = "龙喉大门", + ["zhTW"] = "é¾å–‰å¤§é–€", + }, + }, + [1056] = { + ["continent"] = 267, + ["parent"] = 267, + ["width"] = 14.97, + ["height"] = 10.48, + ["x1"] = 50.9, + ["y1"] = 33.68, + ["names"] = { + ["deDE"] = "Darrohügel", + ["enUS"] = "Darrow Hill", + ["esES"] = "Colina Darrow", + ["frFR"] = "Colline de Darrow", + ["koKR"] = "다로우 ì–¸ë•", + ["ptBR"] = "Colina de Darrow", + ["ruRU"] = "Холм Дарроу", + ["zhCN"] = "达隆山", + ["zhTW"] = "é”隆山", + }, + }, + [1076] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 15.17, + ["height"] = 30.54, + ["x1"] = 60.88, + ["y1"] = 70.36, + ["names"] = { + ["deDE"] = "Weberpass", + ["enUS"] = "Webwinder Path", + ["esES"] = "Sendero Tejeweb", + ["frFR"] = "Sentier des Tisseuses", + ["koKR"] = "그물누비 ê³ ê°œ", + ["ptBR"] = "Caminho Webwinder", + ["ruRU"] = "Паучий тракт", + ["zhCN"] = "蛛网å°å¾„", + ["zhTW"] = "蛛網å°å¾‘", + }, + }, + [1097] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 8.88, + ["height"] = 31.74, + ["x1"] = 10.03, + ["y1"] = 44.46, + ["names"] = { + ["deDE"] = "Das Stille Ufer", + ["enUS"] = "The Hushed Bank", + ["esES"] = "El Banco Silente", + ["frFR"] = "La Rive silencieuse", + ["koKR"] = "ê³ ìš”ì˜ ê°•ë‘‘", + ["ptBR"] = "A Margem Silenciosa", + ["ruRU"] = "Затихший берег", + ["zhCN"] = "坂陿²³å²¸", + ["zhTW"] = "å¯‚éœæ²³å²¸", + }, + }, + [1098] = { + ["continent"] = 10, + ["parent"] = 10, + ["width"] = 9.98, + ["height"] = 8.98, + ["x1"] = 74.35, + ["y1"] = 33.68, + ["names"] = { + ["deDE"] = "Anwesen der Dunstmantels", + ["enUS"] = "Manor Mistmantle", + ["esES"] = "Mansión Mantovaho", + ["frFR"] = "Manoir Mistmantle", + ["koKR"] = "미스트맨틀 장ì›", + ["ptBR"] = "Mansión Mistmantle", + ["ruRU"] = "ПомеÑтье МиÑтмантла", + ["zhCN"] = "密斯特曼托庄园", + ["zhTW"] = "密斯特曼托莊園", + }, + }, + [1099] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 7.98, + ["height"] = 9.73, + ["x1"] = 75.35, + ["y1"] = 42.29, + ["names"] = { + ["deDE"] = "Camp Mojache", + ["enUS"] = "Camp Mojache", + ["esES"] = "Campamento Mojache", + ["frFR"] = "Camp Mojache", + ["koKR"] = "모ìžì¼€ 야ì˜ì§€", + ["ptBR"] = "Acampamento Mojache", + ["ruRU"] = "Лагерь Мохаче", + ["zhCN"] = "莫沙彻è¥åœ°", + ["zhTW"] = "莫沙徹營地", + }, + }, + [1100] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 5.99, + ["height"] = 14.97, + ["x1"] = 67.86, + ["y1"] = 40.42, + ["names"] = { + ["deDE"] = "Truppenlager der Grimmtotem", + ["enUS"] = "Grimtotem Compound", + ["esES"] = "Compuesto Grimtotem", + ["frFR"] = "Base des Totem sinistre", + ["koKR"] = "그림토템 주둔지", + ["ptBR"] = "Complexo Totem Vil", + ["ruRU"] = "Лагерь Зловещего Тотема", + ["zhCN"] = "ææ€–图腾è¥åœ°", + ["zhTW"] = "ææ€–圖騰營地", + }, + }, + [1101] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 16.97, + ["height"] = 20.21, + ["x1"] = 72.85, + ["y1"] = 61, + ["names"] = { + ["deDE"] = "Die Verwundene Tiefe", + ["enUS"] = "The Writhing Deep", + ["esES"] = "Las Profundidades Retorcidas", + ["frFR"] = "Gouffre grouillant", + ["koKR"] = "ê³ í†µì˜ êµ¬ë©ì´", + ["ptBR"] = "O Profundo Retorcido", + ["ruRU"] = "ГудÑÑ‰Ð°Ñ Ð‘ÐµÐ·Ð´Ð½Ð°", + ["zhCN"] = "痛苦深渊", + ["zhTW"] = "痛苦深淵", + }, + }, + [1103] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 9.98, + ["height"] = 9.73, + ["x1"] = 77.35, + ["y1"] = 31.06, + ["names"] = { + ["deDE"] = "Außenposten der Gordunni", + ["enUS"] = "Gordunni Outpost", + ["esES"] = "Avanzada Gordunni", + ["frFR"] = "Avant-poste des Gordunni", + ["koKR"] = "골ë‘니 전초기지", + ["ptBR"] = "Posto Gordunni", + ["ruRU"] = "ПоÑеление Гордунни", + ["zhCN"] = "戈æœå°¼å‰å“¨ç«™", + ["zhTW"] = "戈æœå°¼å‰å“¨ç«™", + }, + }, + [1105] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 6.99, + ["height"] = 8.98, + ["x1"] = 54.39, + ["y1"] = 56.89, + ["names"] = { + ["deDE"] = "Wildschrammtal", + ["enUS"] = "Feral Scar Vale", + ["esES"] = "Valle Cicatriz Salvaje", + ["frFR"] = "Val des Griffes farouches", + ["koKR"] = "거친í‰í„° 골짜기", + ["ptBR"] = "Vale Cicatriz Feral", + ["ruRU"] = "Долина Свирепого УтеÑа", + ["zhCN"] = "深痕谷", + ["zhTW"] = "深痕谷", + }, + }, + [1106] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 6.49, + ["height"] = 17.96, + ["x1"] = 53.64, + ["y1"] = 71.11, + ["names"] = { + ["deDE"] = "Fransenfederhochland", + ["enUS"] = "Frayfeather Highlands", + ["esES"] = "Tierras Altas Plumas de Pelea", + ["frFR"] = "Hautes-terres des Aigreplumes", + ["koKR"] = "공작날개 ê³ ì›", + ["ptBR"] = "Terras Altas Penaprateada", + ["ruRU"] = "Ð’Ñ‹Ñокогорье Блеклых Перьев", + ["zhCN"] = "乱羽高地", + ["zhTW"] = "亂羽高地", + }, + }, + [1108] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 6.99, + ["height"] = 41.92, + ["x1"] = 46.41, + ["y1"] = 62.13, + ["names"] = { + ["deDE"] = "Die Vergessene Küste", + ["enUS"] = "The Forgotten Coast", + ["esES"] = "La Costa Olvidada", + ["frFR"] = "La Côte oubliée", + ["koKR"] = "잊혀진 해안", + ["ptBR"] = "A Costa Esquecida", + ["ruRU"] = "Забытый берег", + ["zhCN"] = "被é—忘的海岸", + ["zhTW"] = "被éºå¿˜çš„æµ·å²¸", + }, + }, + [1111] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 4.99, + ["height"] = 8.23, + ["x1"] = 51.4, + ["y1"] = 10.1, + ["names"] = { + ["deDE"] = "Traumgeäst", + ["enUS"] = "Dream Bough", + ["esES"] = "Rama del Sueño", + ["frFR"] = "Bosquet du rêve", + ["koKR"] = "꿈나무", + ["ptBR"] = "Ramo dos Sonhos", + ["ruRU"] = "Сонные Ветви", + ["zhCN"] = "梦境之树", + ["zhTW"] = "夢境之樹", + }, + }, + [1113] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 5.49, + ["height"] = 5.24, + ["x1"] = 54.64, + ["y1"] = 16.84, + ["names"] = { + ["deDE"] = "Oneiros", + ["enUS"] = "Oneiros", + ["esES"] = "Oneiros", + ["frFR"] = "Oneiros", + ["koKR"] = "오네ì´ë¡œìФ", + ["ptBR"] = "Oneiros", + ["ruRU"] = "ОнейроÑ", + ["zhCN"] = "奥奈罗斯", + ["zhTW"] = "奧奈羅斯", + }, + }, + [1114] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 7.49, + ["height"] = 8.23, + ["x1"] = 39.67, + ["y1"] = 10.85, + ["names"] = { + ["deDE"] = "Ruinen von Rabenwind", + ["enUS"] = "Ruins of Ravenwind", + ["esES"] = "Ruinas de Viento de Cuervo", + ["frFR"] = "Ruines de Vent-du-Corbeau", + ["koKR"] = "까마귀바람 íí—ˆ", + ["ptBR"] = "Ruínas de Ravenwind", + ["ruRU"] = "Руины ЯроÑтного Ветра", + ["zhCN"] = "鸦风废墟", + ["zhTW"] = "鴉風廢墟", + }, + }, + [1119] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 15.47, + ["height"] = 20.96, + ["x1"] = 47.16, + ["y1"] = 25.45, + ["names"] = { + ["deDE"] = "Die Zwillingskolosse", + ["enUS"] = "The Twin Colossals", + ["esES"] = "Los Colosos Gemelos", + ["frFR"] = "Les Colosses jumeaux", + ["koKR"] = "ìŒë‘¥ì´ 바위산", + ["ptBR"] = "Os Colossos Gêmeos", + ["ruRU"] = "Два КолоÑÑа", + ["zhCN"] = "åŒå¡”å±±", + ["zhTW"] = "雙塔山", + }, + }, + [1120] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 13.47, + ["height"] = 16.47, + ["x1"] = 30.19, + ["y1"] = 50.15, + ["names"] = { + ["deDE"] = "Insel Sardor", + ["enUS"] = "Sardor Isle", + ["esES"] = "Isla Sardor", + ["frFR"] = "Ile de Sardor", + ["koKR"] = "ì‚´ë„르 섬", + ["ptBR"] = "Ilha Sardor", + ["ruRU"] = "ОÑтров Сардор", + ["zhCN"] = "è¨å°”多岛", + ["zhTW"] = "薩爾多島", + }, + }, + [1121] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 14.47, + ["height"] = 35.18, + ["x1"] = 29.69, + ["y1"] = 78.97, + ["names"] = { + ["deDE"] = "Die Insel des Schreckens", + ["enUS"] = "Isle of Dread", + ["esES"] = "Isla del Terror", + ["frFR"] = "lle de l\'effroi", + ["koKR"] = "ê³µí¬ì˜ í„°", + ["ptBR"] = "Isla del Terror", + ["ruRU"] = "ОÑтров УжаÑа", + ["zhCN"] = "ææ€–之岛", + ["zhTW"] = "ææ€–之島", + }, + }, + [1137] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 12.48, + ["height"] = 14.22, + ["x1"] = 86.08, + ["y1"] = 43.04, + ["names"] = { + ["deDE"] = "Die Untere Wildnis", + ["enUS"] = "Lower Wilds", + ["esES"] = "Tierras Salvajes Inferiores", + ["frFR"] = "Etendues sauvages", + ["koKR"] = "ë‚®ì€ë²Œ", + ["ptBR"] = "Territorios Inferiores", + ["ruRU"] = "Ðизинные чащобы", + ["zhCN"] = "低地è’野", + ["zhTW"] = "低地è’野", + }, + }, + [1216] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 10.48, + ["height"] = 15.72, + ["x1"] = 37.18, + ["y1"] = 33.31, + ["names"] = { + ["deDE"] = "Holzschlundfeste", + ["enUS"] = "Timbermaw Hold", + ["esES"] = "Bastión de los Timbermaw", + ["frFR"] = "Repaire des Grumegueules", + ["koKR"] = "ë‚˜ë¬´êµ¬ë  ìš”ìƒˆ", + ["ptBR"] = "O Refugio Timbermaw", + ["ruRU"] = "КрепоÑть Древобрюхов", + ["zhCN"] = "木喉è¦å¡ž", + ["zhTW"] = "木喉è¦å¡ž", + }, + }, + [1219] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 16.97, + ["height"] = 8.98, + ["x1"] = 57.88, + ["y1"] = 17.96, + ["names"] = { + ["deDE"] = "Lager der Legashi", + ["enUS"] = "Legash Encampment", + ["esES"] = "Campamento Legash", + ["frFR"] = "Campement Legashi", + ["koKR"] = "레가쉬 야ì˜ì§€", + ["ptBR"] = "Acampamento Legash", + ["ruRU"] = "Лагерь Легаш", + ["zhCN"] = "雷加什è¥åœ°", + ["zhTW"] = "雷加什營地", + }, + }, + [1220] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 14.97, + ["height"] = 9.73, + ["x1"] = 60.88, + ["y1"] = 28.82, + ["names"] = { + ["deDE"] = "Thalassischer Stützpunkt", + ["enUS"] = "Thalassian Base Camp", + ["esES"] = "Campamento Base Thalassiano", + ["frFR"] = "Camp de base thalassien", + ["koKR"] = "탈ë¼ì‹œì•ˆ 주둔지", + ["ptBR"] = "Acampamento Base Thalassian", + ["ruRU"] = "ТалаÑÑийÑкое поÑеление", + ["zhCN"] = "è¨æ‹‰æ–¯è¥åœ°", + ["zhTW"] = "薩拉斯營地", + }, + }, + [1221] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 12.48, + ["height"] = 15.72, + ["x1"] = 36.68, + ["y1"] = 54.27, + ["names"] = { + ["deDE"] = "Die Ruinen von Eldarath", + ["enUS"] = "Ruins of Eldarath ", + ["esES"] = "Ruinas de Eldarath", + ["frFR"] = "Ruines d\'Eldarath", + ["koKR"] = "엘다ë¼ìФ íí—ˆ", + ["ptBR"] = "Ruínas de Eldarath", + ["ruRU"] = "Руины Эльдарата", + ["zhCN"] = "埃达拉斯废墟", + ["zhTW"] = "åŸƒé”æ‹‰æ–¯å»¢å¢Ÿ", + }, + }, + [1225] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 7.98, + ["height"] = 11.98, + ["x1"] = 46.91, + ["y1"] = 28.44, + ["names"] = { + ["deDE"] = "Ursolan", + ["enUS"] = "Ursolan", + ["esES"] = "Ursolan", + ["frFR"] = "Ursolan", + ["koKR"] = "우르솔란", + ["ptBR"] = "Ursolan", + ["ruRU"] = "УрÑолан", + ["zhCN"] = "乌索兰", + ["zhTW"] = "çƒç´¢è˜­", + }, + }, + [1226] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 10.48, + ["height"] = 14.97, + ["x1"] = 76.6, + ["y1"] = 41.17, + ["names"] = { + ["deDE"] = "Der Tempel von Arkkoran", + ["enUS"] = "Temple of Arkkoran", + ["esES"] = "Templo de Arkkoran", + ["frFR"] = "Temple d\'Arkkoran", + ["koKR"] = "ì•„í¬ì½”ëž€ì˜ ì‹ ì „", + ["ptBR"] = "Templo de Arkkoran", + ["ruRU"] = "Храм Ðрккоран", + ["zhCN"] = "亚考兰神殿", + ["zhTW"] = "亞考蘭神殿", + }, + }, + [1227] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 16.97, + ["height"] = 32.93, + ["x1"] = 58.88, + ["y1"] = 54.64, + ["names"] = { + ["deDE"] = "Die Bucht der Stürme", + ["enUS"] = "Bay of Storms", + ["esES"] = "Bahía de las Tormentas", + ["frFR"] = "Baie des tempêtes", + ["koKR"] = "í­í’ì˜ ë§Œ", + ["ptBR"] = "Baía das Tempestades", + ["ruRU"] = "Залив Бурь", + ["zhCN"] = "风暴海湾", + ["zhTW"] = "風暴海ç£", + }, + }, + [1228] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 5.49, + ["height"] = 18.71, + ["x1"] = 45.66, + ["y1"] = 46.03, + ["names"] = { + ["deDE"] = "Strand der Trümmer", + ["enUS"] = "The Shattered Strand", + ["esES"] = "La Franja Rota", + ["frFR"] = "La Grève fracassée", + ["koKR"] = "ì¡°ê°ë‚œ 해안", + ["ptBR"] = "A Praia Fragmentada", + ["ruRU"] = "Разоренное побережье", + ["zhCN"] = "破碎海岸", + ["zhTW"] = "破碎海岸", + }, + }, + [1229] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 6.99, + ["height"] = 12.72, + ["x1"] = 88.32, + ["y1"] = 30.31, + ["names"] = { + ["deDE"] = "Turm von Eldara", + ["enUS"] = "Tower of Eldara", + ["esES"] = "Torre de Eldara", + ["frFR"] = "Tour d\'Eldara", + ["koKR"] = "엘다ë¼ì˜ 탑", + ["ptBR"] = "A Torre de Eldara", + ["ruRU"] = "Ð‘Ð°ÑˆÐ½Ñ Ð­Ð»ÑŒÐ´Ð°Ñ€Ñ‹", + ["zhCN"] = "埃达拉之塔", + ["zhTW"] = "è‰¾é”æ‹‰ä¹‹å¡”", + }, + }, + [1230] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 47.9, + ["height"] = 11.98, + ["x1"] = 63.87, + ["y1"] = 11.23, + ["names"] = { + ["deDE"] = "Gezacktes Riff", + ["enUS"] = "Jagged Reef", + ["esES"] = "Arrecife Afilado", + ["frFR"] = "Récif déchiqueté", + ["koKR"] = "톱니 산호초", + ["ptBR"] = "Arrecife Serrado", + ["ruRU"] = "ОÑтрые рифы", + ["zhCN"] = "锯齿暗ç¤", + ["zhTW"] = "鋸齒暗ç¤", + }, + }, + [1231] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 28.94, + ["height"] = 17.96, + ["x1"] = 58.38, + ["y1"] = 71.86, + ["names"] = { + ["deDE"] = "Südklippenufer", + ["enUS"] = "Southridge Beach", + ["esES"] = "Playa de la Cima Sur", + ["frFR"] = "Plage des Crêtes du sud", + ["koKR"] = "남녘마루 해안", + ["ptBR"] = "Praia Southridge", + ["ruRU"] = "Южный плÑж", + ["zhCN"] = "å—山海滩", + ["zhTW"] = "å—山海ç˜", + }, + }, + [1232] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 9.48, + ["height"] = 9.73, + ["x1"] = 70.61, + ["y1"] = 84.96, + ["names"] = { + ["deDE"] = "Rabenkrones Monument", + ["enUS"] = "Ravencrest Monument", + ["esES"] = "Monumento Cuervo del Crestón", + ["frFR"] = "Colosse de Ravencrest", + ["koKR"] = "ë ˆì´ë¸í¬ë ˆìŠ¤íŠ¸ 기ë…비", + ["ptBR"] = "Monumento Ravencrest", + ["ruRU"] = "ПамÑтник Гребню Ворона", + ["zhCN"] = "拉文凯斯雕åƒ", + ["zhTW"] = "黑羽紀念碑", + }, + }, + [1233] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 14.97, + ["height"] = 15.72, + ["x1"] = 29.44, + ["y1"] = 71.48, + ["names"] = { + ["deDE"] = "Der Einsame Grat", + ["enUS"] = "Forlorn Ridge", + ["esES"] = "Cima Desolada", + ["frFR"] = "La crête lugubre", + ["koKR"] = "쓸쓸한 마루", + ["ptBR"] = "Crista dos Esquecidos", + ["ruRU"] = "ÐžÐ´Ð¸Ð½Ð¾ÐºÐ°Ñ Ð²ÐµÑ€ÑˆÐ¸Ð½Ð°", + ["zhCN"] = "凄凉山", + ["zhTW"] = "淒涼山", + }, + }, + [1234] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 11.48, + ["height"] = 13.47, + ["x1"] = 43.16, + ["y1"] = 80.09, + ["names"] = { + ["deDE"] = "Mennarsee", + ["enUS"] = "Lake Mennar", + ["esES"] = "Lago Mennar", + ["frFR"] = "Lac Mennar", + ["koKR"] = "멘나르 호수", + ["ptBR"] = "Lago Mennar", + ["ruRU"] = "Озеро Меннар", + ["zhCN"] = "门纳尔湖", + ["zhTW"] = "é–€ç´çˆ¾æ¹–", + }, + }, + [1235] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 9.98, + ["height"] = 13.47, + ["x1"] = 17.47, + ["y1"] = 74.85, + ["names"] = { + ["deDE"] = "Schattensangschrein", + ["enUS"] = "Shadowsong Shrine", + ["esES"] = "Santuario Canción de las Sombras", + ["frFR"] = "Sanctuaire de Shadowsong", + ["koKR"] = "ì„€ë„송 제단", + ["ptBR"] = "Santuário Shadowsong", + ["ruRU"] = "СвÑтилище ПеÑни Теней", + ["zhCN"] = "影歌神殿", + ["zhTW"] = "影歌神殿", + }, + }, + [1236] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 8.48, + ["height"] = 9.73, + ["x1"] = 20.71, + ["y1"] = 60.25, + ["names"] = { + ["deDE"] = "Lager der Haldarr", + ["enUS"] = "Haldarr Encampment", + ["esES"] = "Campamento Haldarr", + ["frFR"] = "Campement des Haldarr", + ["koKR"] = "할다르 야ì˜ì§€", + ["ptBR"] = "Acampamento Haldarr", + ["ruRU"] = "Лагерь Халдарр", + ["zhCN"] = "哈达尔è¥åœ°", + ["zhTW"] = "哈é”爾營地", + }, + }, + [1237] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 9.98, + ["height"] = 8.23, + ["x1"] = 21.46, + ["y1"] = 50.52, + ["names"] = { + ["deDE"] = "Valormok", + ["enUS"] = "Valormok", + ["esES"] = "Valormok", + ["frFR"] = "Valormok", + ["koKR"] = "발로르모í¬", + ["ptBR"] = "Valormok", + ["ruRU"] = "ХраброÑтан", + ["zhCN"] = "瓦罗莫克", + ["zhTW"] = "瓦羅莫克", + }, + }, + [1256] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 28.44, + ["height"] = 9.73, + ["x1"] = 58.13, + ["y1"] = 91.69, + ["names"] = { + ["deDE"] = "Die Verfallenen Gegenden", + ["enUS"] = "The Ruined Reaches", + ["esES"] = "Las Ensenadas Arruinadas", + ["frFR"] = "Les Confins dévastés", + ["koKR"] = "버려진 착륙장", + ["ptBR"] = "Os Alcances Arruinados", + ["ruRU"] = "ОпуÑтошенный берег", + ["zhCN"] = "废墟海岸", + ["zhTW"] = "廢墟海岸", + }, + }, + [1316] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 8.98, + ["height"] = 9.73, + ["x1"] = 48.9, + ["y1"] = 91.69, + ["names"] = { + ["deDE"] = "Hügel der Klingenhauer", + ["enUS"] = "Razorfen Downs", + ["esES"] = "Zahúrda Rajacieno", + ["frFR"] = "Souilles de Tranchebauge", + ["koKR"] = "가시ë©êµ´ 구릉", + ["ptBR"] = "Urzal dos Mortos", + ["ruRU"] = "Курганы Иглошкурых", + ["zhCN"] = "剃刀高地", + ["zhTW"] = "剃刀高地", + }, + }, + [1336] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 8.48, + ["height"] = 17.96, + ["x1"] = 71.61, + ["y1"] = 47.16, + ["names"] = { + ["deDE"] = "Mast- und Schotbucht", + ["enUS"] = "Lost Rigger Cove", + ["esES"] = "Cueva del Rigger Perdido", + ["frFR"] = "Crique des Gréements", + ["koKR"] = "í•´ì ë‹¨ 소굴", + ["ptBR"] = "Cova Lost Rigger", + ["ruRU"] = "Бухта Сорванных ПаруÑов", + ["zhCN"] = "è½å¸†æµ·æ¹¾", + ["zhTW"] = "è½å¸†æµ·ç£", + }, + }, + [1357] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 8.48, + ["height"] = 11.23, + ["x1"] = 50.15, + ["y1"] = 58.76, + ["names"] = { + ["deDE"] = "Galgeneck", + ["enUS"] = "Gallows\' Corner", + ["esES"] = "Esquina de la Horca", + ["frFR"] = "Fourche du gibet", + ["koKR"] = "갤로우 삼거리", + ["ptBR"] = "Esquina da Forca", + ["ruRU"] = "ПерекреÑток ВиÑельников", + ["zhCN"] = "绞刑场", + ["zhTW"] = "絞刑場", + }, + }, + [1437] = { + ["continent"] = 4, + ["parent"] = 4, + ["width"] = 9.48, + ["height"] = 8.98, + ["x1"] = 43.16, + ["y1"] = 14.22, + ["names"] = { + ["deDE"] = "Feste Schreckensfels", + ["enUS"] = "Dreadmaul Hold", + ["esES"] = "Bastión Martillo Tenebroso", + ["frFR"] = "Bastion Cognepeur", + ["koKR"] = "ìš°ë ˆë§ì¹˜ 요새", + ["ptBR"] = "Fortaleza Muralha do Medo", + ["ruRU"] = "Форт Молота УжаÑа", + ["zhCN"] = "巨槌è¦å¡ž", + ["zhTW"] = "巨槌è¦å¡ž", + }, + }, + [1438] = { + ["continent"] = 4, + ["parent"] = 4, + ["width"] = 9.98, + ["height"] = 11.23, + ["x1"] = 64.37, + ["y1"] = 18.34, + ["names"] = { + ["deDE"] = "Burg Nethergarde", + ["enUS"] = "Nethergarde Keep", + ["esES"] = "Fortaleza Nethergarde", + ["frFR"] = "Rempart-du-Néant", + ["koKR"] = "네ë”가드 요새", + ["ptBR"] = "Fortaleza de Nethergarde", + ["ruRU"] = "КрепоÑть Стражей ПуÑтоты", + ["zhCN"] = "守望堡", + ["zhTW"] = "守望堡", + }, + }, + [1439] = { + ["continent"] = 4, + ["parent"] = 4, + ["width"] = 10.98, + ["height"] = 11.98, + ["x1"] = 50.4, + ["y1"] = 44.16, + ["names"] = { + ["deDE"] = "Schreckensfelsposten", + ["enUS"] = "Dreadmaul Post", + ["esES"] = "Puesto Martillo Tenebroso", + ["frFR"] = "Poste Cognepeur", + ["koKR"] = "ìš°ë ˆë§ì¹˜ 주둔지", + ["ptBR"] = "Posto Muralha do Medo", + ["ruRU"] = "СтоÑнка Молота УжаÑа", + ["zhCN"] = "巨槌岗哨", + ["zhTW"] = "巨槌崗哨", + }, + }, + [1440] = { + ["continent"] = 4, + ["parent"] = 4, + ["width"] = 10.98, + ["height"] = 9.73, + ["x1"] = 62.87, + ["y1"] = 33.31, + ["names"] = { + ["deDE"] = "Schlangenschlinge", + ["enUS"] = "Serpent\'s Coil", + ["esES"] = "Enredo de Serpiente", + ["frFR"] = "Anneaux du serpent", + ["koKR"] = "ë±€ì˜ ë³´ê¸ˆìžë¦¬", + ["ptBR"] = "Caracol do Serpente", + ["ruRU"] = "Змеиные Кольца", + ["zhCN"] = "盘蛇谷", + ["zhTW"] = "盤蛇谷", + }, + }, + [1441] = { + ["continent"] = 4, + ["parent"] = 4, + ["width"] = 6.49, + ["height"] = 9.73, + ["x1"] = 39.67, + ["y1"] = 31.81, + ["names"] = { + ["deDE"] = "Altar der Stürme", + ["enUS"] = "Altar of Storms", + ["esES"] = "Altar de Tormentas", + ["frFR"] = "Autel des tempêtes", + ["koKR"] = "í­í’ì˜ ì œë‹¨", + ["ptBR"] = "Altar das Tempestades", + ["ruRU"] = "Ðлтарь Бурь", + ["zhCN"] = "风暴祭å›", + ["zhTW"] = "暴風祭壇", + }, + }, + [1442] = { + ["continent"] = 51, + ["parent"] = 51, + ["width"] = 23.95, + ["height"] = 28.44, + ["x1"] = 27.94, + ["y1"] = 33.68, + ["names"] = { + ["deDE"] = "Feuerwachtgrat", + ["enUS"] = "Firewatch Ridge", + ["esES"] = "Cima Vigía de Fuego", + ["frFR"] = "Crête de Guet-du-feu", + ["koKR"] = "불ë§ë£¨ 마루", + ["ptBR"] = "Crista Vigia do Fogo", + ["ruRU"] = "ГрÑда Огненной Ñтражи", + ["zhCN"] = "è§‚ç«å²­", + ["zhTW"] = "è§€ç«å¶º", + }, + }, + [1444] = { + ["continent"] = 51, + ["parent"] = 51, + ["width"] = 23.95, + ["height"] = 30.69, + ["x1"] = 44.91, + ["y1"] = 84.21, + ["names"] = { + ["deDE"] = "Das Meer der Asche", + ["enUS"] = "The Sea of Cinders", + ["esES"] = "El Mar de Cenizas", + ["frFR"] = "La Mer de braises", + ["koKR"] = "ìž¿ë”미 바다", + ["ptBR"] = "O Mar de Cinzas", + ["ruRU"] = "Пепельное Море", + ["zhCN"] = "ç°çƒ¬ä¹‹æµ·", + ["zhTW"] = "ç°ç‡¼ä¹‹æµ·", + }, + }, + [1457] = { + ["continent"] = 4, + ["parent"] = 4, + ["width"] = 7.98, + ["height"] = 8.23, + ["x1"] = 55.89, + ["y1"] = 13.85, + ["names"] = { + ["deDE"] = "Garnisonswaffenkammer", + ["enUS"] = "Garrison Armory", + ["esES"] = "Armería de la Guarnición", + ["frFR"] = "Armurerie de la garnison", + ["koKR"] = "요새 무기고", + ["ptBR"] = "Arsenal da Guarnição", + ["ruRU"] = "МаÑтерÑкие Гарнизона", + ["zhCN"] = "è¦å¡žå†›æ¢°åº“", + ["zhTW"] = "è¦å¡žè»æ¢°åº«", + }, + }, + [1497] = { + ["continent"] = 0, + ["parent"] = 0, + ["width"] = 14.57, + ["height"] = 21.71, + ["x1"] = 61.88, + ["y1"] = 71.63, + ["names"] = { + ["deDE"] = "Unterstadt", + ["enUS"] = "Undercity", + ["esES"] = "Entrañas", + ["frFR"] = "Undercity", + ["koKR"] = "Undercity", + ["ptBR"] = "Cidade Subterrânea", + ["ruRU"] = "Подгород", + ["zhCN"] = "幽暗城", + ["zhTW"] = "幽暗城", + }, + }, + [1519] = { + ["continent"] = 0, + ["parent"] = 0, + ["width"] = 25.45, + ["height"] = 35.18, + ["x1"] = 24.2, + ["y1"] = 35.55, + ["names"] = { + ["deDE"] = "Sturmwind", + ["enUS"] = "Stormwind City", + ["esES"] = "Ciudad de Ventormenta", + ["frFR"] = "Cité de Stormwind", + ["koKR"] = "Stormwind City", + ["ptBR"] = "Cidade Stormwind", + ["ruRU"] = "Штормград", + ["zhCN"] = "暴风城", + ["zhTW"] = "暴風城", + }, + }, + [1637] = { + ["continent"] = 0, + ["parent"] = 0, + ["width"] = 22.95, + ["height"] = 7.49, + ["x1"] = 46.91, + ["y1"] = 5.24, + ["names"] = { + ["deDE"] = "Orgrimmar", + ["enUS"] = "Orgrimmar", + ["esES"] = "Orgrimmar", + ["frFR"] = "Orgrimmar", + ["koKR"] = "Orgrimmar", + ["ptBR"] = "Orgrimmar", + ["ruRU"] = "Оргриммар", + ["zhCN"] = "奥格瑞玛", + ["zhTW"] = "奧格瑪", + }, + }, + [1638] = { + ["continent"] = 0, + ["parent"] = 0, + ["width"] = 18.46, + ["height"] = 23.95, + ["x1"] = 38.67, + ["y1"] = 27.69, + ["names"] = { + ["deDE"] = "Donnerfels", + ["enUS"] = "Thunder Bluff", + ["esES"] = "Cima del Trueno", + ["frFR"] = "Thunder Bluff", + ["koKR"] = "Thunder Bluff", + ["ptBR"] = "Thunder Bluff", + ["ruRU"] = "Громовой УтеÑ", + ["zhCN"] = "雷霆崖", + ["zhTW"] = "雷霆崖", + }, + }, + [1657] = { + ["continent"] = 0, + ["parent"] = 0, + ["width"] = 14.97, + ["height"] = 28.44, + ["x1"] = 27.45, + ["y1"] = 57.63, + ["names"] = { + ["deDE"] = "Darnassus", + ["enUS"] = "Darnassus", + ["esES"] = "Darnassus", + ["frFR"] = "Darnassus", + ["koKR"] = "Darnassus", + ["ptBR"] = "Darnassus", + ["ruRU"] = "ДарнаÑ", + ["zhCN"] = "è¾¾çº³è‹æ–¯", + ["zhTW"] = "é”ç´è˜‡æ–¯", + }, + }, + [1677] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 7.49, + ["height"] = 14.22, + ["x1"] = 30.69, + ["y1"] = 85.7, + ["names"] = { + ["deDE"] = "Gavins Landspitze", + ["enUS"] = "Gavin\'s Naze", + ["esES"] = "Promontorio de Gavin", + ["frFR"] = "Promontoire de Gavin", + ["koKR"] = "가빈 절벽", + ["ptBR"] = "Nazé de Gavin", + ["ruRU"] = "ВозвышенноÑть ГÑвина", + ["zhCN"] = "加文高地", + ["zhTW"] = "加文高地", + }, + }, + [1678] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 8.98, + ["height"] = 13.47, + ["x1"] = 58.38, + ["y1"] = 68.86, + ["names"] = { + ["deDE"] = "Soferas Landspitze", + ["enUS"] = "Sofera\'s Naze", + ["esES"] = "Promontorio de Sofera", + ["frFR"] = "Promontoire de Sofera", + ["koKR"] = "ì†ŒíŽ˜ë¼ ì ˆë²½", + ["ptBR"] = "Naze de Sofera", + ["ruRU"] = "ВозвышенноÑть Соферы", + ["zhCN"] = "ç´¢è²äºšé«˜åœ°", + ["zhTW"] = "ç´¢è²äºžé«˜åœ°", + }, + }, + [1679] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 9.48, + ["height"] = 19.46, + ["x1"] = 48.15, + ["y1"] = 83.83, + ["names"] = { + ["deDE"] = "Corrahns Dolch", + ["enUS"] = "Corrahn\'s Dagger", + ["esES"] = "Daga de Corrahn", + ["frFR"] = "Dague de Corrahn", + ["koKR"] = "ì½”ëž€ì˜ ë¹„ìˆ˜", + ["ptBR"] = "Adaga de Corrahn", + ["ruRU"] = "УÑтуп Коррана", + ["zhCN"] = "考兰之匕", + ["zhTW"] = "考蘭之匕", + }, + }, + [1680] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 8.98, + ["height"] = 17.22, + ["x1"] = 38.92, + ["y1"] = 90.19, + ["names"] = { + ["deDE"] = "Die Landzunge", + ["enUS"] = "The Headland", + ["esES"] = "El Promontorio", + ["frFR"] = "L\'Avancée", + ["koKR"] = "마루터기", + ["ptBR"] = "A Cabeceira", + ["ruRU"] = "Ð’Ñ‹Ñокий отрог", + ["zhCN"] = "山头è¥åœ°", + ["zhTW"] = "山頭營地", + }, + }, + [1681] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 12.48, + ["height"] = 18.71, + ["x1"] = 28.19, + ["y1"] = 37.8, + ["names"] = { + ["deDE"] = "Nebelufer", + ["enUS"] = "Misty Shore", + ["esES"] = "Costa Brumosa", + ["frFR"] = "Rivage brumeux", + ["koKR"] = "물안개 호숫가", + ["ptBR"] = "Costa Nebulosa", + ["ruRU"] = "МглиÑтый берег", + ["zhCN"] = "雾气湖岸", + ["zhTW"] = "霧氣湖岸", + }, + }, + [1682] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 6.99, + ["height"] = 13.47, + ["x1"] = 40.42, + ["y1"] = 17.22, + ["names"] = { + ["deDE"] = "Dandreds Senke", + ["enUS"] = "Dandred\'s Fold", + ["esES"] = "Pliegue de Dandred", + ["frFR"] = "Clos de Dandred", + ["koKR"] = "단드레드 장ì›", + ["ptBR"] = "Vinhedo de Dandred", + ["ruRU"] = "ÐžÐ²Ñ‡Ð°Ñ€Ð½Ñ Ð”Ð°Ð½Ð´Ñ€ÐµÐ´Ð°", + ["zhCN"] = "达伦德农场", + ["zhTW"] = "é”倫德農場", + }, + }, + [1683] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 10.48, + ["height"] = 12.72, + ["x1"] = 39.67, + ["y1"] = 69.24, + ["names"] = { + ["deDE"] = "Eisfellhöhle", + ["enUS"] = "Growless Cave", + ["esES"] = "Cueva de Crestaferia", + ["frFR"] = "Caverne stérile", + ["koKR"] = "ì¹¨ë¬µì˜ ë™êµ´", + ["ptBR"] = "Caverna Sem Crescimento", + ["ruRU"] = "ÐŸÑ€Ð¾Ð¼ÐµÑ€Ð·ÑˆÐ°Ñ Ð¿ÐµÑ‰ÐµÑ€Ð°", + ["zhCN"] = "æ— è‰æ´ž", + ["zhTW"] = "ç„¡è‰æ´ž", + }, + }, + [1684] = { + ["continent"] = 36, + ["parent"] = 36, + ["width"] = 18.46, + ["height"] = 34.43, + ["x1"] = 82.09, + ["y1"] = 66.62, + ["names"] = { + ["deDE"] = "Zugwindspitze", + ["enUS"] = "Chillwind Point", + ["esES"] = "Punta Viento Gélido", + ["frFR"] = "Pointe du Noroît", + ["koKR"] = "서리바람 ê±°ì ", + ["ptBR"] = "Ponto Ventogelado", + ["ruRU"] = "Берег Промозглого Ветра", + ["zhCN"] = "冰风岗", + ["zhTW"] = "冰風崗哨", + }, + }, + [1697] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 5.99, + ["height"] = 6.74, + ["x1"] = 55.89, + ["y1"] = 51.27, + ["names"] = { + ["deDE"] = "Raptorgründe", + ["enUS"] = "Raptor Grounds", + ["esES"] = "Terreno de los Raptadores", + ["frFR"] = "Terres des Raptors", + ["koKR"] = "랩터 서ì‹ì§€", + ["ptBR"] = "Terrenos de Raptor", + ["ruRU"] = "Земли Ящеров", + ["zhCN"] = "迅猛龙巢穴", + ["zhTW"] = "迅猛é¾å·¢ç©´", + }, + }, + [1698] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 4.49, + ["height"] = 10.48, + ["x1"] = 50.65, + ["y1"] = 58.38, + ["names"] = { + ["deDE"] = "Dornennarbe", + ["enUS"] = "Bramblescar", + ["esES"] = "Rasguño de Zarza", + ["frFR"] = "Ronceplaie", + ["koKR"] = "가시ë©êµ´ 벌íŒ", + ["ptBR"] = "Cicatriz dos Espinhos", + ["ruRU"] = "Ежевичный овраг", + ["zhCN"] = "迅猛龙平原", + ["zhTW"] = "迅猛é¾å¹³åŽŸ", + }, + }, + [1699] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 5.99, + ["height"] = 7.49, + ["x1"] = 56.89, + ["y1"] = 28.44, + ["names"] = { + ["deDE"] = "Dornenhügel", + ["enUS"] = "Thorn Hill", + ["esES"] = "Colina Espinosa", + ["frFR"] = "Colline des épines", + ["koKR"] = "가시ë©êµ´ ì–¸ë•", + ["ptBR"] = "A Colina das Espinhas", + ["ruRU"] = "ТерниÑтый холм", + ["zhCN"] = "è†æ£˜å²­", + ["zhTW"] = "èŠæ£˜å¶º", + }, + }, + [1700] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 8.48, + ["height"] = 14.22, + ["x1"] = 43.16, + ["y1"] = 48.28, + ["names"] = { + ["deDE"] = "Agama\'gor", + ["enUS"] = "Agama\'gor", + ["esES"] = "Agama\'gor", + ["frFR"] = "Agama\'gor", + ["koKR"] = "아가마고르", + ["ptBR"] = "Agama\'gor", + ["ruRU"] = "Ðгама\'гор", + ["zhCN"] = "阿迦玛戈", + ["zhTW"] = "阿迦瑪戈", + }, + }, + [1701] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 6.49, + ["height"] = 11.23, + ["x1"] = 41.67, + ["y1"] = 78.97, + ["names"] = { + ["deDE"] = "Schwarzdorngrat", + ["enUS"] = "Blackthorn Ridge", + ["esES"] = "Cima Espina Negra", + ["frFR"] = "Crête de Noirépine", + ["koKR"] = "ê²€ì€ê°€ì‹œ 마루", + ["ptBR"] = "Cordilheira Espinho Negro", + ["ruRU"] = "Хребет Черных Шипов", + ["zhCN"] = "黑棘山", + ["zhTW"] = "黑棘山", + }, + }, + [1702] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 7.98, + ["height"] = 10.48, + ["x1"] = 37.43, + ["y1"] = 28.44, + ["names"] = { + ["deDE"] = "Ehrenmal", + ["enUS"] = "Honor\'s Stand", + ["esES"] = "Bastión del Honor", + ["frFR"] = "Le lieu de l\'Honneur", + ["koKR"] = "ëª…ì˜ˆì˜ ê°ì‹œíƒ‘", + ["ptBR"] = "Posto do Valor", + ["ruRU"] = "ЗаÑтава ЧеÑти", + ["zhCN"] = "è£è€€å²—哨", + ["zhTW"] = "榮耀崗哨", + }, + }, + [1703] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 7.98, + ["height"] = 8.98, + ["x1"] = 47.9, + ["y1"] = 6.74, + ["names"] = { + ["deDE"] = "Schutzwall von Mor\'shan", + ["enUS"] = "The Mor\'shan Rampart", + ["esES"] = "Murallas de Mor\'shan", + ["frFR"] = "Le Rempart de Mor\'shan", + ["koKR"] = "ëª°ìƒ¨ì˜ ë§ë£¨", + ["ptBR"] = "A Muralha de Mor\'shan", + ["ruRU"] = "ЗаÑтава Мор\'шан", + ["zhCN"] = "摩尔沙农场", + ["zhTW"] = "摩爾沙農場", + }, + }, + [1704] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 5.49, + ["height"] = 6.74, + ["x1"] = 55.64, + ["y1"] = 19.09, + ["names"] = { + ["deDE"] = "Grol\'doms Hof", + ["enUS"] = "Grol\'dom Farm", + ["esES"] = "Granja Grol\'dom", + ["frFR"] = "Ferme de Grol\'dom", + ["koKR"] = "ê·¸ë¡¤ë” ë†ìž¥", + ["ptBR"] = "Fazenda Grol\'dom", + ["ruRU"] = "Ферма Гроль\'дома", + ["zhCN"] = "格罗多姆农场", + ["zhTW"] = "格羅多姆農場", + }, + }, + [1717] = { + ["continent"] = 17, + ["parent"] = 17, + ["width"] = 7.98, + ["height"] = 10.48, + ["x1"] = 40.92, + ["y1"] = 88.32, + ["names"] = { + ["deDE"] = "Kral der Klingenhauer", + ["enUS"] = "Razorfen Kraul", + ["esES"] = "Madriguera de Rajacieno", + ["frFR"] = "Kraal de Tranchebauge", + ["koKR"] = "가시ë©êµ´ 우리", + ["ptBR"] = "Razorfen Kraul", + ["ruRU"] = "Лабиринты Иглошкурых", + ["zhCN"] = "剃刀沼泽", + ["zhTW"] = "剃刀沼澤", + }, + }, + [1737] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 3.49, + ["height"] = 10.48, + ["x1"] = 33.68, + ["y1"] = 64.37, + ["names"] = { + ["deDE"] = "Nebeltal", + ["enUS"] = "Mistvale Valley", + ["esES"] = "Valle de la Bruma", + ["frFR"] = "Valbrume", + ["koKR"] = "안개계곡 골짜기", + ["ptBR"] = "Valle Mistvale", + ["ruRU"] = "МглиÑÑ‚Ð°Ñ Ð´Ð¾Ð»Ð¸Ð½Ð°", + ["zhCN"] = "薄雾谷", + ["zhTW"] = "薄霧山谷", + }, + }, + [1738] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 5.49, + ["height"] = 9.73, + ["x1"] = 25.7, + ["y1"] = 62.5, + ["names"] = { + ["deDE"] = "Nek\'maniquellbrunnen", + ["enUS"] = "Nek\'mani Wellspring", + ["esES"] = "Manantial Nek\'mani", + ["frFR"] = "Fontaine des Nek\'mani", + ["koKR"] = "네í¬ë§ˆë‹ˆ 수ì›ì§€", + ["ptBR"] = "Fonte de Nek\'mani", + ["ruRU"] = "Родник Ðек\'Мани", + ["zhCN"] = "纳克迈尼圣泉", + ["zhTW"] = "ç´å…‹é‚å°¼è–æ³‰", + }, + }, + [1739] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 9.98, + ["height"] = 9.73, + ["x1"] = 26.95, + ["y1"] = 53.52, + ["names"] = { + ["deDE"] = "Truppenlager der Blutsegelbukaniere", + ["enUS"] = "Bloodsail Compound", + ["esES"] = "Complejo Velasangre", + ["frFR"] = "Base de la Voile sanglante", + ["koKR"] = "붉ì€í•´ì ë‹¨ 주둔지", + ["ptBR"] = "Complexo Velasangre", + ["ruRU"] = "Лагерь Кровавого ПаруÑа", + ["zhCN"] = "血帆è¥åœ°", + ["zhTW"] = "血帆營地", + }, + }, + [1740] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 4.49, + ["height"] = 9.73, + ["x1"] = 44.16, + ["y1"] = 19.84, + ["names"] = { + ["deDE"] = "Stützpunkt der Venture Co.", + ["enUS"] = "Venture Co. Base Camp", + ["esES"] = "Campamento Base de Venture Co.", + ["frFR"] = "Campement de la KapitalRisk", + ["koKR"] = "투ìžê°œë°œíšŒì‚¬ íƒì‚¬ê¸°ì§€", + ["ptBR"] = "Acampamento Base da Companhia Venture", + ["ruRU"] = "Главный лагерь Торговой Компании", + ["zhCN"] = "风险投资公å¸è¥åœ°", + ["zhTW"] = "風險投資公å¸ç‡Ÿåœ°", + }, + }, + [1741] = { + ["continent"] = 33, + ["parent"] = 33, + ["width"] = 8.98, + ["height"] = 11.98, + ["x1"] = 29.44, + ["y1"] = 44.16, + ["names"] = { + ["deDE"] = "Arena der Gurubashi", + ["enUS"] = "Gurubashi Arena", + ["esES"] = "Arena Gurubashi", + ["frFR"] = "Arène des Gurubashi", + ["koKR"] = "구루바시 투기장", + ["ptBR"] = "Arena Gurubashi", + ["ruRU"] = "Ðрена Гурубаши", + ["zhCN"] = "夿‹‰å·´ä»€ç«žæŠ€åœº", + ["zhTW"] = "夿‹‰å·´ä»€ç«¶æŠ€å ´", + }, + }, + [1761] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 8.48, + ["height"] = 8.98, + ["x1"] = 49.15, + ["y1"] = 91.32, + ["names"] = { + ["deDE"] = "Lager der Totenwaldfelle", + ["enUS"] = "Deadwood Village", + ["esES"] = "Aldea Madera Muerta", + ["frFR"] = "Village des Mort-bois", + ["koKR"] = "마른가지 마ì„", + ["ptBR"] = "Aldeia Lenha Morta", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ ÐœÐµÑ€Ñ‚Ð²Ð¾Ð³Ð¾ ЛеÑа", + ["zhCN"] = "死木æ‘", + ["zhTW"] = "死木æ‘", + }, + }, + [1762] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 9.98, + ["height"] = 11.23, + ["x1"] = 63.37, + ["y1"] = 10.85, + ["names"] = { + ["deDE"] = "Revier der Teufelspfoten", + ["enUS"] = "Felpaw Village", + ["esES"] = "Aldea Garra Vil", + ["frFR"] = "Village de Gangrepatte", + ["koKR"] = "악령발 마ì„", + ["ptBR"] = "Aldeia Patavil", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ Ð¡ÐºÐ²ÐµÑ€Ð½Ð¾Ð»Ð°Ð¿Ð¾Ð²", + ["zhCN"] = "魔爪æ‘", + ["zhTW"] = "魔爪æ‘", + }, + }, + [1763] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 12.97, + ["height"] = 7.49, + ["x1"] = 36.43, + ["y1"] = 59.88, + ["names"] = { + ["deDE"] = "Jaedenar", + ["enUS"] = "Jaedenar", + ["esES"] = "Jaedenar", + ["frFR"] = "Jaedenar", + ["koKR"] = "ìžì—ë°ë‚˜ë¥´", + ["ptBR"] = "Jaedenar", + ["ruRU"] = "Джеденар", + ["zhCN"] = "加德纳尔", + ["zhTW"] = "加德ç´çˆ¾", + }, + }, + [1765] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 12.97, + ["height"] = 8.98, + ["x1"] = 42.42, + ["y1"] = 48.65, + ["names"] = { + ["deDE"] = "Die Blutgiftfälle", + ["enUS"] = "Bloodvenom Falls", + ["esES"] = "Cascadas Veneno de Sangre", + ["frFR"] = "Chutes de la Vénéneuse", + ["koKR"] = "피ë©ìš¸ í­í¬", + ["ptBR"] = "Quedas do Sangue Venenoso", + ["ruRU"] = "Водопад Отравленной Крови", + ["zhCN"] = "血毒瀑布", + ["zhTW"] = "血毒瀑布", + }, + }, + [1766] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 10.48, + ["height"] = 17.96, + ["x1"] = 42.17, + ["y1"] = 35.18, + ["names"] = { + ["deDE"] = "Narbengrund", + ["enUS"] = "Shatter Scar Vale", + ["esES"] = "Valle Cicatriz Rota", + ["frFR"] = "Val Grêlé", + ["koKR"] = "ë¶ˆë²¼ë½ ê³¨ì§œê¸°", + ["ptBR"] = "Vale Shatter Scar", + ["ruRU"] = "Долина Рваных Ран", + ["zhCN"] = "碎痕谷", + ["zhTW"] = "碎痕谷", + }, + }, + [1767] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 9.48, + ["height"] = 11.23, + ["x1"] = 50.15, + ["y1"] = 23.58, + ["names"] = { + ["deDE"] = "Der Eisenwald", + ["enUS"] = "Irontree Woods", + ["esES"] = "Bosques Ãrbol de Hierro", + ["frFR"] = "Bois d\'Arbrefer", + ["koKR"] = "강철나무 숲", + ["ptBR"] = "Bosques Irontree", + ["ruRU"] = "ЖелезнолеÑье", + ["zhCN"] = "铿œ¨æ£®æž—", + ["zhTW"] = "鵿œ¨æ£®æž—", + }, + }, + [1777] = { + ["continent"] = 8, + ["parent"] = 8, + ["width"] = 9.98, + ["height"] = 20.96, + ["x1"] = 14.97, + ["y1"] = 62.87, + ["names"] = { + ["deDE"] = "Itharius\' Höhle", + ["enUS"] = "Itharius\'s Cave", + ["esES"] = "Cueva de Itharius", + ["frFR"] = "Caverne d\'Itharius", + ["koKR"] = "ì´íƒ€ë¦¬ìš°ìŠ¤ì˜ ë™êµ´", + ["ptBR"] = "Cueva de Itharius", + ["ruRU"] = "Пещера Итара", + ["zhCN"] = "伊è¨é‡Œå¥¥æ–¯æ´žç©´", + ["zhTW"] = "伊薩里奧斯的洞穴", + }, + }, + [1778] = { + ["continent"] = 8, + ["parent"] = 8, + ["width"] = 12.97, + ["height"] = 40.42, + ["x1"] = 83.83, + ["y1"] = 46.41, + ["names"] = { + ["deDE"] = "Sorgendunkel", + ["enUS"] = "Sorrowmurk", + ["esES"] = "Pantojo de la Pena", + ["frFR"] = "Noirchagrin", + ["koKR"] = "ìŠ¬í””ì˜ ê·¸ëŠ˜", + ["ptBR"] = "Sorrowmurk", + ["ruRU"] = "Горемрак", + ["zhCN"] = "忧伤湿地", + ["zhTW"] = "憂傷濕地", + }, + }, + [1780] = { + ["continent"] = 8, + ["parent"] = 8, + ["width"] = 18.96, + ["height"] = 17.96, + ["x1"] = 24.45, + ["y1"] = 52.4, + ["names"] = { + ["deDE"] = "Splitterspeerkreuzung", + ["enUS"] = "Splinterspear Junction", + ["esES"] = "Encrucijada de Lanza Astillada", + ["frFR"] = "Croisement de Lance-brisée", + ["koKR"] = "부러진창 êµì°¨ë¡œ", + ["ptBR"] = "Encruzilhada Splinterspear", + ["ruRU"] = "Развилка РаÑщепленного КопьÑ", + ["zhCN"] = "断矛路å£", + ["zhTW"] = "斷矛路å£", + }, + }, + [1797] = { + ["continent"] = 8, + ["parent"] = 8, + ["width"] = 17.96, + ["height"] = 21.71, + ["x1"] = 70.36, + ["y1"] = 78.97, + ["names"] = { + ["deDE"] = "Stagalsumpf", + ["enUS"] = "Stagalbog", + ["esES"] = "Pantano de los Ciervos", + ["frFR"] = "Stagalbog", + ["koKR"] = "ì§„í™ëŠª", + ["ptBR"] = "Pântano Stagalbog", + ["ruRU"] = "Ð’ÑÐ·ÐºÐ°Ñ Ñ‚Ð¾Ð¿ÑŒ", + ["zhCN"] = "雄鹿沼泽", + ["zhTW"] = "雄鹿沼澤", + }, + }, + [1798] = { + ["continent"] = 8, + ["parent"] = 8, + ["width"] = 23.95, + ["height"] = 21.71, + ["x1"] = 43.91, + ["y1"] = 34.81, + ["names"] = { + ["deDE"] = "Der Wabersumpf", + ["enUS"] = "The Shifting Mire", + ["esES"] = "El Pantano Cambiante", + ["frFR"] = "Le Bourbier changeant", + ["koKR"] = "ë³€í™”ì˜ ëŠª", + ["ptBR"] = "O Pântano Mutante", + ["ruRU"] = "ÐŸÐ¾Ð»Ð·ÑƒÑ‡Ð°Ñ Ñ‚Ñ€ÑÑина", + ["zhCN"] = "æµæ²™æ³¥æ½­", + ["zhTW"] = "æµæ²™æ³¥æ½­", + }, + }, + [1857] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 10.48, + ["height"] = 21.71, + ["x1"] = 17.71, + ["y1"] = 37.8, + ["names"] = { + ["deDE"] = "Thoradinswall", + ["enUS"] = "Thoradin\'s Wall", + ["esES"] = "Murallas de Thoradin", + ["frFR"] = "Mur de Thoradin", + ["koKR"] = "소ë¼ë”˜ì˜ 성벽", + ["ptBR"] = "A Muralha Thoradin", + ["ruRU"] = "Стена Торадина", + ["zhCN"] = "索拉ä¸ä¹‹å¢™", + ["zhTW"] = "索拉ä¸ä¹‹ç‰†", + }, + }, + [1858] = { + ["continent"] = 45, + ["parent"] = 45, + ["width"] = 5.49, + ["height"] = 18.71, + ["x1"] = 37.67, + ["y1"] = 41.54, + ["names"] = { + ["deDE"] = "Fels\'gor", + ["enUS"] = "Boulder\'gor", + ["esES"] = "Roca\'gor", + ["frFR"] = "Boulder\'gor", + ["koKR"] = "ëŒì£¼ë¨¹ ì–¸ë•", + ["ptBR"] = "Boulder\'gor", + ["ruRU"] = "Камен\'гор", + ["zhCN"] = "åšå¾·æˆˆå°”", + ["zhTW"] = "åšå¤šæˆˆçˆ¾", + }, + }, + [1877] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 12.97, + ["height"] = 17.96, + ["x1"] = 46.41, + ["y1"] = 56.89, + ["names"] = { + ["deDE"] = "Fangzahntal", + ["enUS"] = "Valley of Fangs", + ["esES"] = "Valle de los Colmillos", + ["frFR"] = "Vallée des crocs", + ["koKR"] = "송곳니 골짜기", + ["ptBR"] = "Vale das Presas", + ["ruRU"] = "Долина Клыков", + ["zhCN"] = "巨牙谷", + ["zhTW"] = "巨牙谷", + }, + }, + [1878] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 16.97, + ["height"] = 20.96, + ["x1"] = 30.44, + ["y1"] = 51.65, + ["names"] = { + ["deDE"] = "Das Staubbecken", + ["enUS"] = "The Dustbowl", + ["esES"] = "El Torbellino de Polvo", + ["frFR"] = "Vallée de la poussière", + ["koKR"] = "먼지받ì´", + ["ptBR"] = "O Buraco de Poeira", + ["ruRU"] = "Знойные ПеÑки", + ["zhCN"] = "漫尘盆地", + ["zhTW"] = "漫塵盆地", + }, + }, + [1879] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 18.46, + ["height"] = 24.7, + ["x1"] = 30.69, + ["y1"] = 75.22, + ["names"] = { + ["deDE"] = "Illusionenebene", + ["enUS"] = "Mirage Flats", + ["esES"] = "Llanuras del Espejismo", + ["frFR"] = "Vallée des mirages", + ["koKR"] = "신기루 벌íŒ", + ["ptBR"] = "Llanuras Mirage", + ["ruRU"] = "Маревые равнины", + ["zhCN"] = "雾气平原", + ["zhTW"] = "霧氣平原", + }, + }, + [1882] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 7.49, + ["height"] = 22.46, + ["x1"] = 22.7, + ["y1"] = 38.17, + ["names"] = { + ["deDE"] = "Seuchennebelklamm", + ["enUS"] = "Plaguemist Ravine", + ["esES"] = "Barranco Niebla Pestilente", + ["frFR"] = "Ravin de Pestebrume", + ["koKR"] = "역병안개 협곡", + ["ptBR"] = "Ravina da Névoa Pestilenta", + ["ruRU"] = "Ð§ÑƒÐ¼Ð½Ð°Ñ Ð»Ð¾Ñ‰Ð¸Ð½Ð°", + ["zhCN"] = "毒雾峡谷", + ["zhTW"] = "毒霧峽谷", + }, + }, + [1883] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 7.49, + ["height"] = 10.48, + ["x1"] = 40.17, + ["y1"] = 58.38, + ["names"] = { + ["deDE"] = "Ehrenwindsee", + ["enUS"] = "Valorwind Lake", + ["esES"] = "Lago Viento de Valor", + ["frFR"] = "Lac Vent-vaillant", + ["koKR"] = "ëŒê°œë°”람 호수", + ["ptBR"] = "Lago Valorwind", + ["ruRU"] = "Озеро ДоблеÑти", + ["zhCN"] = "瓦罗温湖", + ["zhTW"] = "瓦羅溫湖", + }, + }, + [1884] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 11.48, + ["height"] = 12.72, + ["x1"] = 49.65, + ["y1"] = 37.05, + ["names"] = { + ["deDE"] = "Agol\'watha", + ["enUS"] = "Agol\'watha", + ["esES"] = "Agol\'watha", + ["frFR"] = "Agol\'watha", + ["koKR"] = "아골와타", + ["ptBR"] = "Agol\'watha", + ["ruRU"] = "Ðгол\'вата", + ["zhCN"] = "亚戈瓦è¨", + ["zhTW"] = "亞戈瓦薩", + }, + }, + [1885] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 10.48, + ["height"] = 14.22, + ["x1"] = 28.19, + ["y1"] = 61.75, + ["names"] = { + ["deDE"] = "Hiri\'watha", + ["enUS"] = "Hiri\'watha", + ["esES"] = "Hiri\'watha", + ["frFR"] = "Hiri\'watha", + ["koKR"] = "히리와타", + ["ptBR"] = "Hiri\'watha", + ["ruRU"] = "Хири\'вата", + ["zhCN"] = "西利瓦è¨", + ["zhTW"] = "西利瓦薩", + }, + }, + [1886] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 9.48, + ["height"] = 12.72, + ["x1"] = 50.15, + ["y1"] = 53.52, + ["names"] = { + ["deDE"] = "Die Gruselruinen", + ["enUS"] = "The Creeping Ruin", + ["esES"] = "La Ruina Acechante", + ["frFR"] = "La Ruine aux rampants", + ["koKR"] = "êµ¼ë²µì´ íí—ˆ", + ["ptBR"] = "A Ruína Crescente", + ["ruRU"] = "Ползучие руины", + ["zhCN"] = "爬虫废墟", + ["zhTW"] = "爬蟲廢墟", + }, + }, + [1897] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 18.46, + ["height"] = 17.96, + ["x1"] = 49.15, + ["y1"] = 16.47, + ["names"] = { + ["deDE"] = "Terrasse des Schöpfers", + ["enUS"] = "The Maker\'s Terrace", + ["esES"] = "Terraza del Creador", + ["frFR"] = "La Terrasse des faiseurs", + ["koKR"] = "ì°½ì¡°ì£¼ì˜ ì •ì›", + ["ptBR"] = "A Terraço do Criador", + ["ruRU"] = "ТерраÑа Творца", + ["zhCN"] = "造物者é—迹", + ["zhTW"] = "造物者éºè·¡", + }, + }, + [1898] = { + ["continent"] = 3, + ["parent"] = 3, + ["width"] = 12.48, + ["height"] = 18.71, + ["x1"] = 62.62, + ["y1"] = 46.03, + ["names"] = { + ["deDE"] = "Staubwindschlucht", + ["enUS"] = "Dustwind Gulch", + ["esES"] = "Barranco Viento de Polvo", + ["frFR"] = "Goulet de la Bourrasque", + ["koKR"] = "먼지바람 협곡", + ["ptBR"] = "Ravina Vento de Poeira", + ["ruRU"] = "Лощина СуховеÑ", + ["zhCN"] = "尘风峡谷", + ["zhTW"] = "塵風峽谷", + }, + }, + [1917] = { + ["continent"] = 47, + ["parent"] = 47, + ["width"] = 13.47, + ["height"] = 15.72, + ["x1"] = 70.11, + ["y1"] = 50.52, + ["names"] = { + ["deDE"] = "Shaol\'watha", + ["enUS"] = "Shaol\'watha", + ["esES"] = "Shaol\'watha", + ["frFR"] = "Shaol\'watha", + ["koKR"] = "샤올와타", + ["ptBR"] = "Shaol\'watha", + ["ruRU"] = "Шаол\'вата", + ["zhCN"] = "沙尔瓦è¨", + ["zhTW"] = "沙爾瓦薩", + }, + }, + [1937] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 5.99, + ["height"] = 7.49, + ["x1"] = 59.88, + ["y1"] = 24.7, + ["names"] = { + ["deDE"] = "Tagschattenruinen", + ["enUS"] = "Noonshade Ruins", + ["esES"] = "Ruinas de Sombra del Mediodía", + ["frFR"] = "Ruines d\'Ombre-du-Zénith", + ["koKR"] = "해그늘 íí—ˆ", + ["ptBR"] = "Ruínas de Meio-dia", + ["ruRU"] = "Руины Полуденной Тени", + ["zhCN"] = "热影废墟", + ["zhTW"] = "熱影廢墟", + }, + }, + [1938] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 6.49, + ["height"] = 8.98, + ["x1"] = 53.14, + ["y1"] = 45.66, + ["names"] = { + ["deDE"] = "Zerbrochene Säule", + ["enUS"] = "Broken Pillar", + ["esES"] = "Pilar Roto", + ["frFR"] = "Pilier brisé", + ["koKR"] = "무너진 기둥", + ["ptBR"] = "Pilar Quebrado", + ["ruRU"] = "Ð Ð°Ð·Ð±Ð¸Ñ‚Ð°Ñ ÐšÐ¾Ð»Ð¾Ð½Ð½Ð°", + ["zhCN"] = "破碎石柱", + ["zhTW"] = "破碎石柱", + }, + }, + [1939] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 9.48, + ["height"] = 13.47, + ["x1"] = 45.66, + ["y1"] = 42.66, + ["names"] = { + ["deDE"] = "Die Ewigen Sande", + ["enUS"] = "Abyssal Sands", + ["esES"] = "Arenas Abisales", + ["frFR"] = "Désert Abysséen", + ["koKR"] = "ë없는 사막", + ["ptBR"] = "Areias Abissais", + ["ruRU"] = "Безбрежные пеÑки", + ["zhCN"] = "深沙平原", + ["zhTW"] = "深沙平原", + }, + }, + [1940] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 10.98, + ["height"] = 9.73, + ["x1"] = 62.38, + ["y1"] = 59.51, + ["names"] = { + ["deDE"] = "Südbrandung", + ["enUS"] = "Southbreak Shore", + ["esES"] = "Costa Rompe Sur", + ["frFR"] = "Rivage de Brisesud", + ["koKR"] = "ê±°ì¹œíŒŒë„ í•´ì•ˆ", + ["ptBR"] = "Costa Southbreak", + ["ruRU"] = "Берег за Южной грÑдой", + ["zhCN"] = "å—堤海岸", + ["zhTW"] = "å¡”ç´åˆ©æ–¯å—æµ·", + }, + }, + [1942] = { + ["continent"] = 490, + ["parent"] = 490, + ["width"] = 16.47, + ["height"] = 35.18, + ["x1"] = 69.61, + ["y1"] = 63.25, + ["names"] = { + ["deDE"] = "Die Marschen", + ["enUS"] = "The Marshlands", + ["esES"] = "Las Marismas", + ["frFR"] = "La Fondrière", + ["koKR"] = "늪지대", + ["ptBR"] = "As Terras Pantanosas", + ["ruRU"] = "Топи", + ["zhCN"] = "沼泽地", + ["zhTW"] = "沼澤地", + }, + }, + [1943] = { + ["continent"] = 490, + ["parent"] = 490, + ["width"] = 19.96, + ["height"] = 26.2, + ["x1"] = 72.36, + ["y1"] = 34.81, + ["names"] = { + ["deDE"] = "Eisensteinplateau", + ["enUS"] = "Ironstone Plateau", + ["esES"] = "Meseta Piedranegra", + ["frFR"] = "Plateau de Rochefer", + ["koKR"] = "강철바위 ê³ ì›", + ["ptBR"] = "Planalto Ironstone", + ["ruRU"] = "Плато Железного КамнÑ", + ["zhCN"] = "é“石高原", + ["zhTW"] = "éµçŸ³é«˜åŽŸ", + }, + }, + [1957] = { + ["continent"] = 51, + ["parent"] = 51, + ["width"] = 14.97, + ["height"] = 22.46, + ["x1"] = 22.95, + ["y1"] = 73.35, + ["names"] = { + ["deDE"] = "Schwarzbrandhöhle", + ["enUS"] = "Blackchar Cave", + ["esES"] = "Cueva Carbón Negro", + ["frFR"] = "Caverne de Noircharbon", + ["koKR"] = "ê²€ì€ìž¬ ë™êµ´", + ["ptBR"] = "Caverna Brasão Negro", + ["ruRU"] = "ÐžÐ±ÑƒÐ³Ð»ÐµÐ½Ð½Ð°Ñ Ð¿ÐµÑ‰ÐµÑ€Ð°", + ["zhCN"] = "黑炭谷", + ["zhTW"] = "黑炭谷", + }, + }, + [1958] = { + ["continent"] = 51, + ["parent"] = 51, + ["width"] = 17.96, + ["height"] = 20.96, + ["x1"] = 69.36, + ["y1"] = 76.35, + ["names"] = { + ["deDE"] = "Gerbers Lager", + ["enUS"] = "Tanner Camp", + ["esES"] = "Campamento de Curtidores", + ["frFR"] = "Camp de Tanner", + ["koKR"] = "무ë‘ìž¥ì´ ì•¼ì˜ì§€", + ["ptBR"] = "Acampamento Tanner", + ["ruRU"] = "Палатка Дубильщицы", + ["zhCN"] = "制皮匠è¥åœ°", + ["zhTW"] = "製皮匠營地", + }, + }, + [1959] = { + ["continent"] = 51, + ["parent"] = 51, + ["width"] = 28.94, + ["height"] = 40.42, + ["x1"] = 67.37, + ["y1"] = 29.19, + ["names"] = { + ["deDE"] = "Staubfeuertal", + ["enUS"] = "Dustfire Valley", + ["esES"] = "Valle Fuego Polvoriento", + ["frFR"] = "Vallée des Escarbilles", + ["koKR"] = "먼지불 골짜기", + ["ptBR"] = "Vale de Poeira Ardente", + ["ruRU"] = "Ð’Ñ‹Ð¶Ð¶ÐµÐ½Ð½Ð°Ñ Ð´Ð¾Ð»Ð¸Ð½Ð°", + ["zhCN"] = "å°˜ç«è°·", + ["zhTW"] = "塵ç«è°·", + }, + }, + [1998] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 7.49, + ["height"] = 9.73, + ["x1"] = 62.13, + ["y1"] = 24.33, + ["names"] = { + ["deDE"] = "Nachtlaublichtung", + ["enUS"] = "Talonbranch Glade ", + ["esES"] = "Claro Rama Diente", + ["frFR"] = "Clairière de Griffebranche", + ["koKR"] = "갈퀴가지 숲", + ["ptBR"] = "Clareira Talonbranch", + ["ruRU"] = "ПолÑна КогтиÑтых Ветвей", + ["zhCN"] = "åˆºæžæž—地", + ["zhTW"] = "åˆºæžæž—地", + }, + }, + [2040] = { + ["continent"] = 0, + ["parent"] = 0, + ["width"] = 0, + ["height"] = 0, + ["x1"] = 0, + ["y1"] = 0, + ["names"] = { + ["deDE"] = "Alah\'Thalas", + ["enUS"] = "Alah\'Thalas", + ["esES"] = "Alah\'Thalas", + ["ptBR"] = "Alah\'Thalas", + ["zhCN"] = "é˜¿å°”è¨æ‹‰æ–¯", + }, + }, + [2041] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 5.99, + ["height"] = 9.28, + ["x1"] = 24.05, + ["y1"] = 12.13, + ["names"] = { + ["deDE"] = "Amani\'Alor", + ["enUS"] = "Amani\'Alor", + ["esES"] = "Amani\'Alor", + ["ptBR"] = "Amani\'Alor", + ["zhCN"] = "阿曼尼岗哨", + }, + }, + [2079] = { + ["continent"] = 15, + ["parent"] = 15, + ["width"] = 12.48, + ["height"] = 18.71, + ["x1"] = 75.6, + ["y1"] = 17.59, + ["names"] = { + ["deDE"] = "Insel Alcaz", + ["enUS"] = "Alcaz Island", + ["esES"] = "Isla Alcaz", + ["frFR"] = "Ile d\'Alcaz", + ["koKR"] = "알카즈 섬", + ["ptBR"] = "Ilha de Alcaz", + ["ruRU"] = "ОÑтров Ðлькац", + ["zhCN"] = "奥å¡å…¹å²›", + ["zhTW"] = "奧å¡èŒ²å³¶", + }, + }, + [2097] = { + ["continent"] = 400, + ["parent"] = 400, + ["width"] = 8.98, + ["height"] = 15.72, + ["x1"] = 34.93, + ["y1"] = 36.3, + ["names"] = { + ["deDE"] = "Düsterwolkengipfel", + ["enUS"] = "Darkcloud Pinnacle", + ["esES"] = "Cima Nube Oscura", + ["frFR"] = "Cime de Noir-nuage", + ["koKR"] = "먹구름 봉우리", + ["ptBR"] = "Pináculo da Nuvem Negra", + ["ruRU"] = "Пик Темного Облака", + ["zhCN"] = "黑云峰", + ["zhTW"] = "黑雲峰", + }, + }, + [2198] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 8.98, + ["height"] = 13.47, + ["x1"] = 80.34, + ["y1"] = 79.34, + ["names"] = { + ["deDE"] = "Schattenklamm", + ["enUS"] = "Shadowbreak Ravine", + ["esES"] = "Barranco Ruptura de Sombra", + ["frFR"] = "Ravin de Brèche-de-l\'Ombre", + ["koKR"] = "ë™íŠ¸ëŠ” 협곡", + ["ptBR"] = "Desfiladeiro Shadowbreak", + ["ruRU"] = "Лощина Тьмы", + ["zhCN"] = "破影峡谷", + ["zhTW"] = "破影峽谷", + }, + }, + [2241] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 10.98, + ["height"] = 8.98, + ["x1"] = 49.4, + ["y1"] = 12.72, + ["names"] = { + ["deDE"] = "Frostsäblerfelsen", + ["enUS"] = "Frostsaber Rock", + ["esES"] = "Roca Sable de Hielo", + ["frFR"] = "Roc des Sabres-de-Givre", + ["koKR"] = "ëˆˆí˜¸ëž‘ì´ ë°”ìœ„", + ["ptBR"] = "Rochedo Sabre-de-Gelo", + ["ruRU"] = "УÑтуп Ледопардов", + ["zhCN"] = "霜刀石", + ["zhTW"] = "霜刀石", + }, + }, + [2242] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 9.48, + ["height"] = 10.48, + ["x1"] = 64.62, + ["y1"] = 17.22, + ["names"] = { + ["deDE"] = "Der Versteckte Hain", + ["enUS"] = "The Hidden Grove", + ["esES"] = "El Bosque Oculto", + ["frFR"] = "Le Bosquet caché", + ["koKR"] = "숨겨진 숲", + ["ptBR"] = "O Bosque Oculto", + ["ruRU"] = "Ð¡Ð¾ÐºÑ€Ñ‹Ñ‚Ð°Ñ Ñ€Ð¾Ñ‰Ð°", + ["zhCN"] = "éšç§˜å°æž—", + ["zhTW"] = "éš±ç§˜å°æž—", + }, + }, + [2243] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 7.49, + ["height"] = 5.99, + ["x1"] = 40.17, + ["y1"] = 43.41, + ["names"] = { + ["deDE"] = "Holzschlundposten", + ["enUS"] = "Timbermaw Post", + ["esES"] = "Puesto de los Manos de Madera", + ["frFR"] = "Poste des Grumegueules", + ["koKR"] = "ë‚˜ë¬´êµ¬ë  ì•¼ì˜ì§€", + ["ptBR"] = "O Posto Timbermaw", + ["ruRU"] = "ЗаÑтава Древобрюхов", + ["zhCN"] = "木喉岗哨", + ["zhTW"] = "木喉崗哨", + }, + }, + [2244] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 6.99, + ["height"] = 7.49, + ["x1"] = 68.36, + ["y1"] = 36.68, + ["names"] = { + ["deDE"] = "Lager der Winterfelle", + ["enUS"] = "Winterfall Village", + ["esES"] = "Villa Caída Invernal", + ["frFR"] = "Village Tombe-hiver", + ["koKR"] = "눈사태ì¼ì¡± 마ì„", + ["ptBR"] = "Aldeia Winterfall", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ Ð—Ð¸Ð¼Ð½ÐµÐ¹ СпÑчки", + ["zhCN"] = "寒水æ‘", + ["zhTW"] = "寒水æ‘", + }, + }, + [2245] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 5.99, + ["height"] = 8.23, + ["x1"] = 58.38, + ["y1"] = 58.01, + ["names"] = { + ["deDE"] = "Mazthoril", + ["enUS"] = "Mazthoril", + ["esES"] = "Mazthoril", + ["frFR"] = "Mazthoril", + ["koKR"] = "마즈소릴", + ["ptBR"] = "Mazthoril", + ["ruRU"] = "Мазторил", + ["zhCN"] = "麦索瑞尔", + ["zhTW"] = "麥索瑞爾", + }, + }, + [2246] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 11.98, + ["height"] = 7.49, + ["x1"] = 33.43, + ["y1"] = 36.68, + ["names"] = { + ["deDE"] = "Die Frostfeuerquellen", + ["enUS"] = "Frostfire Hot Springs", + ["esES"] = "Aguas Termales Fuegohelado", + ["frFR"] = "Sources de Givrefeu", + ["koKR"] = "ì–¼ìŒë¶ˆê½ƒ 온천", + ["ptBR"] = "Fontes Termais Fogofrio", + ["ruRU"] = "ИÑточники ЛедÑного огнÑ", + ["zhCN"] = "å†°ç«æ¸©æ³‰", + ["zhTW"] = "å†°ç«æº«æ³‰", + }, + }, + [2247] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 6.49, + ["height"] = 11.98, + ["x1"] = 68.11, + ["y1"] = 45.66, + ["names"] = { + ["deDE"] = "Eisdistelberge", + ["enUS"] = "Ice Thistle Hills", + ["esES"] = "Colinas Cardo de Hielo", + ["frFR"] = "Collines des Chardons de glace", + ["koKR"] = "ì–¼ìŒì—‰ê²…퀴 ì–¸ë•", + ["ptBR"] = "Colinas Cardo de Gelo", + ["ruRU"] = "Холмы Ледополоха", + ["zhCN"] = "冰蓟岭", + ["zhTW"] = "冰薊嶺", + }, + }, + [2249] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 8.98, + ["height"] = 11.23, + ["x1"] = 61.88, + ["y1"] = 68.49, + ["names"] = { + ["deDE"] = "Frosthauchschlucht", + ["enUS"] = "Frostwhisper Gorge", + ["esES"] = "Garganta Susurro de Hielo", + ["frFR"] = "Gorge du Blanc murmure", + ["koKR"] = "서리ì†ì‚­ìž„ 골짜기", + ["ptBR"] = "Garganta Sussurro Gelado", + ["ruRU"] = "ТеÑнина ЛедÑного Шепота", + ["zhCN"] = "霜语峡谷", + ["zhTW"] = "霜語峽谷", + }, + }, + [2250] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 6.99, + ["height"] = 9.73, + ["x1"] = 65.37, + ["y1"] = 61.75, + ["names"] = { + ["deDE"] = "Eulenflügeldickicht", + ["enUS"] = "Owl Wing Thicket", + ["esES"] = "Bosque Alasalvaje", + ["frFR"] = "Fourré de l\'Aile de la chouette", + ["koKR"] = "올빼미날개 숲", + ["ptBR"] = "Matagal Asa de Coruja", + ["ruRU"] = "Ð¡Ð¾Ð²Ð¸Ð½Ð°Ñ Ñ‡Ð°Ñ‰Ð°", + ["zhCN"] = "枭翼树丛", + ["zhTW"] = "梟翼樹å¢", + }, + }, + [2251] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 9.48, + ["height"] = 11.98, + ["x1"] = 53.14, + ["y1"] = 43.41, + ["names"] = { + ["deDE"] = "Kel\'Therilsee", + ["enUS"] = "Lake Kel\'Theril", + ["esES"] = "Lago Kel\'Theril", + ["frFR"] = "Lac Kel\'Theril", + ["koKR"] = "켈테릴 호수", + ["ptBR"] = "Lago Kel\'Theril", + ["ruRU"] = "Озеро Кел\'Терил", + ["zhCN"] = "凯斯利尔湖", + ["zhTW"] = "凱斯利爾湖", + }, + }, + [2253] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 10.48, + ["height"] = 8.98, + ["x1"] = 49.65, + ["y1"] = 28.44, + ["names"] = { + ["deDE"] = "Sternfall", + ["enUS"] = "Starfall Village", + ["esES"] = "Villa Caída de Estrellas", + ["frFR"] = "Pluie-d\'Etoiles", + ["koKR"] = "별똥별 마ì„", + ["ptBR"] = "Aldeia Starfall", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ Ð—Ð²ÐµÐ·Ð´Ð¾Ð¿Ð°Ð´Ð°", + ["zhCN"] = "å æ˜Ÿæ‘", + ["zhTW"] = "墜星æ‘", + }, + }, + [2255] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 8.98, + ["height"] = 9.73, + ["x1"] = 60.88, + ["y1"] = 37.8, + ["names"] = { + ["deDE"] = "Ewige Warte", + ["enUS"] = "Everlook", + ["esES"] = "Mirada Eterna", + ["frFR"] = "Long-guet", + ["koKR"] = "눈ë§ë£¨ 마ì„", + ["ptBR"] = "Visteterna", + ["ruRU"] = "Круговзор", + ["zhCN"] = "永望镇", + ["zhTW"] = "永望鎮", + }, + }, + [2256] = { + ["continent"] = 618, + ["parent"] = 618, + ["width"] = 17.96, + ["height"] = 16.47, + ["x1"] = 57.88, + ["y1"] = 83.08, + ["names"] = { + ["deDE"] = "Die Flüsternde Schlucht", + ["enUS"] = "Darkwhisper Gorge", + ["esES"] = "Garganta Susurro Oscuro", + ["frFR"] = "Gorge du Sombre murmure", + ["koKR"] = "ê²€ì€ì†ì‚­ìž„ 협곡", + ["ptBR"] = "Garganta do Sussurro Sombrio", + ["ruRU"] = "ТеÑнина Зловещего Шепота", + ["zhCN"] = "暗语峡谷", + ["zhTW"] = "暗語峽谷", + }, + }, + [2258] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 9.98, + ["height"] = 14.22, + ["x1"] = 40.42, + ["y1"] = 52.02, + ["names"] = { + ["deDE"] = "Das Fungustal", + ["enUS"] = "The Fungal Vale", + ["esES"] = "Valle Fungoso", + ["frFR"] = "La Vallée des fongus", + ["koKR"] = "ê³°íŒ¡ì´ ê³„ê³¡", + ["ptBR"] = "O Vale Fúngico", + ["ruRU"] = "Ð“Ñ€Ð¸Ð±Ð½Ð°Ñ Ð´Ð¾Ð»Ð¸Ð½Ð°", + ["zhCN"] = "蘑è‡è°·", + ["zhTW"] = "蘑è‡è°·", + }, + }, + [2260] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 10.48, + ["height"] = 10.48, + ["x1"] = 26.7, + ["y1"] = 73.35, + ["names"] = { + ["deDE"] = "Marris\' Siedlung", + ["enUS"] = "The Marris Stead", + ["esES"] = "La Granja de Marris", + ["frFR"] = "La ferme des Marris", + ["koKR"] = "마리스 ë†ìž¥", + ["ptBR"] = "A Fazenda Marris", + ["ruRU"] = "ПомеÑтье МарриÑов", + ["zhCN"] = "玛瑞斯农场", + ["zhTW"] = "瑪瑞斯農場", + }, + }, + [2261] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 7.98, + ["height"] = 13.47, + ["x1"] = 28.44, + ["y1"] = 84.58, + ["names"] = { + ["deDE"] = "Das Tiefgewölbe", + ["enUS"] = "The Undercroft", + ["esES"] = "La Cripta", + ["frFR"] = "Le caveau de Zaeldarr", + ["koKR"] = "지하 납골당", + ["ptBR"] = "O Subterrâneo", + ["ruRU"] = "Крипта", + ["zhCN"] = "墓室", + ["zhTW"] = "墓室", + }, + }, + [2262] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 11.98, + ["height"] = 11.98, + ["x1"] = 39.92, + ["y1"] = 89.82, + ["names"] = { + ["deDE"] = "Darroheim", + ["enUS"] = "Darrowshire", + ["esES"] = "Darrowshire", + ["frFR"] = "Darrowshire", + ["koKR"] = "다로우샤ì´ì–´", + ["ptBR"] = "Aldeia de Darrowshire", + ["ruRU"] = "Дарроушир", + ["zhCN"] = "达隆郡", + ["zhTW"] = "é”隆郡", + }, + }, + [2263] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 8.48, + ["height"] = 11.98, + ["x1"] = 38.67, + ["y1"] = 75.6, + ["names"] = { + ["deDE"] = "Turm der Kronenwache", + ["enUS"] = "Crown Guard Tower", + ["esES"] = "Torre de la Guardia de la Corona", + ["frFR"] = "Tour de garde de la couronne", + ["koKR"] = "산마루 경비탑", + ["ptBR"] = "Torre da Guarda Real", + ["ruRU"] = "Ð‘Ð°ÑˆÐ½Ñ ÐºÐ¾Ñ€Ð¾Ð»ÐµÐ²Ñкой Ñтражи", + ["zhCN"] = "皇冠哨塔", + ["zhTW"] = "皇冠哨塔", + }, + }, + [2264] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 9.48, + ["height"] = 11.98, + ["x1"] = 59.63, + ["y1"] = 70.36, + ["names"] = { + ["deDE"] = "Corins Kreuzung", + ["enUS"] = "Corin\'s Crossing", + ["esES"] = "Cruce de Corin", + ["frFR"] = "La Croisée de Corin", + ["koKR"] = "코린 삼거리", + ["ptBR"] = "Travessia de Corin", + ["ruRU"] = "ПерекреÑток Корина", + ["zhCN"] = "考林路å£", + ["zhTW"] = "考林路å£", + }, + }, + [2266] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 16.97, + ["height"] = 14.97, + ["x1"] = 84.83, + ["y1"] = 83.08, + ["names"] = { + ["deDE"] = "Tyrs Hand", + ["enUS"] = "Tyr\'s Hand", + ["esES"] = "Mano de Tyr", + ["frFR"] = "Main de Tyr", + ["koKR"] = "í‹°ë¥´ì˜ ì† ìˆ˜ë„ì›", + ["ptBR"] = "A Mão de Tyr", + ["ruRU"] = "Длань Тира", + ["zhCN"] = "æå°”之手", + ["zhTW"] = "æçˆ¾ä¹‹æ‰‹", + }, + }, + [2268] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 7.49, + ["height"] = 11.98, + ["x1"] = 82.09, + ["y1"] = 58.38, + ["names"] = { + ["deDE"] = "Kapelle des Hoffnungsvollen Lichts", + ["enUS"] = "Light\'s Hope Chapel", + ["esES"] = "Capilla de la Esperanza de la Luz", + ["frFR"] = "Chapelle de l\'Espoir de Lumière", + ["koKR"] = "í¬ë§ì˜ ë¹› 예배당", + ["ptBR"] = "Capilla Esperanza de la Luz", + ["ruRU"] = "ЧаÑÐ¾Ð²Ð½Ñ ÐŸÐ¾Ñледней Ðадежды", + ["zhCN"] = "圣光之愿礼拜堂", + ["zhTW"] = "è–光之願禮拜堂", + }, + }, + [2270] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 11.98, + ["height"] = 15.72, + ["x1"] = 84.33, + ["y1"] = 41.54, + ["names"] = { + ["deDE"] = "Das Giftige Tal", + ["enUS"] = "The Noxious Glade", + ["esES"] = "Claro Noxioso", + ["frFR"] = "La Clairière nocive", + ["koKR"] = "ë§¹ë…ì˜ ìˆ²", + ["ptBR"] = "O Bosque Nocivo", + ["ruRU"] = "Ð¯Ð´Ð¾Ð²Ð¸Ñ‚Ð°Ñ Ð¿Ð¾Ð»Ñна", + ["zhCN"] = "剧毒林地", + ["zhTW"] = "劇毒林地", + }, + }, + [2271] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 6.99, + ["height"] = 12.72, + ["x1"] = 67.86, + ["y1"] = 49.78, + ["names"] = { + ["deDE"] = "Ostwallturm", + ["enUS"] = "Eastwall Tower", + ["esES"] = "Torre de la Muralla Este", + ["frFR"] = "Tour du Mur d\'est", + ["koKR"] = "ë™ë¶€ë°©ë²½ 경비탑", + ["ptBR"] = "Torre do Muro Leste", + ["ruRU"] = "ВоÑÑ‚Ð¾Ñ‡Ð½Ð°Ñ Ð±Ð°ÑˆÐ½Ñ", + ["zhCN"] = "东墙哨塔", + ["zhTW"] = "æ±ç‰†ä¹‹å¡”", + }, + }, + [2272] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 8.48, + ["height"] = 12.72, + ["x1"] = 70.61, + ["y1"] = 31.81, + ["names"] = { + ["deDE"] = "Nordtal", + ["enUS"] = "Northdale", + ["esES"] = "Nortevalle", + ["frFR"] = "Valnord", + ["koKR"] = "노스ë°ì¼", + ["ptBR"] = "Northdale", + ["ruRU"] = "Северный Дол", + ["zhCN"] = "北谷", + ["zhTW"] = "北谷", + }, + }, + [2273] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 12.48, + ["height"] = 11.23, + ["x1"] = 73.1, + ["y1"] = 14.6, + ["names"] = { + ["deDE"] = "Zul\'Mashar", + ["enUS"] = "Zul\'Mashar", + ["esES"] = "Zul\'Mashar", + ["frFR"] = "Zul\'Mashar", + ["koKR"] = "줄마샤르", + ["ptBR"] = "Zul\'Mashar", + ["ruRU"] = "Зул\'Машар", + ["zhCN"] = "祖玛沙尔", + ["zhTW"] = "祖瑪沙爾", + }, + }, + [2275] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 12.97, + ["height"] = 13.47, + ["x1"] = 57.88, + ["y1"] = 28.44, + ["names"] = { + ["deDE"] = "Nordpassturm", + ["enUS"] = "Northpass Tower", + ["esES"] = "Torre del Paso Norte", + ["frFR"] = "Tour du Col du nord", + ["koKR"] = "ë¶ë¶€ê´€ë¬¸ 경비탑", + ["ptBR"] = "Torre do Passo Norte", + ["ruRU"] = "Ð‘Ð°ÑˆÐ½Ñ Ð¡ÐµÐ²ÐµÑ€Ð½Ð¾Ð³Ð¾ перевала", + ["zhCN"] = "北地哨塔", + ["zhTW"] = "北地哨塔", + }, + }, + [2276] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 8.98, + ["height"] = 8.98, + ["x1"] = 52.89, + ["y1"] = 16.47, + ["names"] = { + ["deDE"] = "Jagdhütte von Quel\'Lithien", + ["enUS"] = "Quel\'Lithien Lodge", + ["esES"] = "Refugio Quel\'Lithien", + ["frFR"] = "Gîte de Quel\'Lithien", + ["koKR"] = "쿠엘리시엔 오ë‘막", + ["ptBR"] = "Pousada de Quel\'Lithien", + ["ruRU"] = "Сторожка Кель\'Литиен", + ["zhCN"] = "奎尔林斯å°å±‹", + ["zhTW"] = "奎爾林斯å°å±‹", + }, + }, + [2277] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 25.95, + ["height"] = 19.46, + ["x1"] = 36.43, + ["y1"] = 33.68, + ["names"] = { + ["deDE"] = "Der Pestwald", + ["enUS"] = "Plaguewood", + ["esES"] = "Bosque de la Plaga", + ["frFR"] = "Pestebois", + ["koKR"] = "ì—­ë³‘ì˜ ìˆ²", + ["ptBR"] = "Floresta Plaguewood", + ["ruRU"] = "ПроклÑтый леÑ", + ["zhCN"] = "病木林", + ["zhTW"] = "病木林", + }, + }, + [2279] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 13.47, + ["height"] = 20.21, + ["x1"] = 31.19, + ["y1"] = 15.34, + ["names"] = { + ["deDE"] = "Stratholme", + ["enUS"] = "Stratholme", + ["esES"] = "Stratholme", + ["frFR"] = "Stratholme", + ["koKR"] = "스트ë¼ì†”름", + ["ptBR"] = "Stratholme", + ["ruRU"] = "Стратхольм", + ["zhCN"] = "æ–¯å¦ç´¢å§†", + ["zhTW"] = "æ–¯å¦ç´¢å§†", + }, + }, + [2297] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 25.95, + ["height"] = 24.7, + ["x1"] = 68.36, + ["y1"] = 73.73, + ["names"] = { + ["deDE"] = "Darromersee", + ["enUS"] = "Darrowmere Lake", + ["esES"] = "Lago Darrowmere", + ["frFR"] = "Lac Darrowmere", + ["koKR"] = "다로미어 호수", + ["ptBR"] = "Lago Darrowmere", + ["ruRU"] = "Озеро Дарроумир", + ["zhCN"] = "达隆米尔湖", + ["zhTW"] = "é”隆米爾湖", + }, + }, + [2298] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 9.98, + ["height"] = 14.22, + ["x1"] = 68.86, + ["y1"] = 75.22, + ["names"] = { + ["deDE"] = "Darrowehr", + ["enUS"] = "Caer Darrow", + ["esES"] = "Caer Darrow", + ["frFR"] = "Caer Darrow", + ["koKR"] = "ì¹´ì—˜ 다로우", + ["ptBR"] = "Caer Darrow", + ["ruRU"] = "КаÑÑ€ Дарроу", + ["zhCN"] = "凯尔达隆", + ["zhTW"] = "凱爾é”隆", + }, + }, + [2300] = { + ["continent"] = 440, + ["parent"] = 440, + ["width"] = 9.48, + ["height"] = 10.48, + ["x1"] = 64.12, + ["y1"] = 49.4, + ["names"] = { + ["deDE"] = "Höhlen der Zeit", + ["enUS"] = "Caverns of Time", + ["esES"] = "Cavernas del Tiempo", + ["frFR"] = "Grottes du temps", + ["koKR"] = "ì‹œê°„ì˜ ë™êµ´", + ["ptBR"] = "Cavernas do Tempo", + ["ruRU"] = "Пещеры Времени", + ["zhCN"] = "时光之穴", + ["zhTW"] = "時光洞穴", + }, + }, + [2301] = { + ["continent"] = 331, + ["parent"] = 331, + ["width"] = 6.99, + ["height"] = 8.98, + ["x1"] = 36.93, + ["y1"] = 36.68, + ["names"] = { + ["deDE"] = "Lager der Distelfelle", + ["enUS"] = "Thistlefur Village", + ["esES"] = "Villa Pelofrondoso", + ["frFR"] = "Village des Crins-de-Chardon", + ["koKR"] = "엉겅퀴 마ì„", + ["ptBR"] = "A Aldeia Thistlefur", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ ÐšÐ¾Ð»ÑŽÑ‡ÐµÐ³Ð¾ Меха", + ["zhCN"] = "蓟皮æ‘", + ["zhTW"] = "è–Šçš®æ‘", + }, + }, + [2302] = { + ["continent"] = 15, + ["parent"] = 15, + ["width"] = 24.95, + ["height"] = 25.45, + ["x1"] = 44.41, + ["y1"] = 48.65, + ["names"] = { + ["deDE"] = "Der Morast", + ["enUS"] = "The Quagmire", + ["esES"] = "El Pantano", + ["frFR"] = "Le Bourbier", + ["koKR"] = "먼지 수ë ", + ["ptBR"] = "O Pântano", + ["ruRU"] = "ТрÑÑина", + ["zhCN"] = "泥潭沼泽", + ["zhTW"] = "泥潭沼澤", + }, + }, + [2303] = { + ["continent"] = 400, + ["parent"] = 400, + ["width"] = 14.97, + ["height"] = 24.7, + ["x1"] = 62.38, + ["y1"] = 55.01, + ["names"] = { + ["deDE"] = "Schlucht der heulenden Winde", + ["enUS"] = "Windbreak Canyon", + ["esES"] = "Cañón Rompeviento", + ["frFR"] = "Canyon de Brisevent", + ["koKR"] = "갈기바람 협곡", + ["ptBR"] = "Canyon Windbreak", + ["ruRU"] = "Безветренный каньон", + ["zhCN"] = "风裂峡谷", + ["zhTW"] = "風裂峽谷", + }, + }, + [2404] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 9.98, + ["height"] = 9.73, + ["x1"] = 54.39, + ["y1"] = 12.35, + ["names"] = { + ["deDE"] = "Tethris Aran", + ["enUS"] = "Tethris Aran", + ["esES"] = "Tethris Aran", + ["frFR"] = "Tethris Aran", + ["koKR"] = "테드리스 아란", + ["ptBR"] = "Tethris Aran", + ["ruRU"] = "ТефриÑ-Ðран", + ["zhCN"] = "塔迪è¨å…°", + ["zhTW"] = "塔迪薩蘭", + }, + }, + [2405] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 12.97, + ["height"] = 20.96, + ["x1"] = 40.92, + ["y1"] = 30.69, + ["names"] = { + ["deDE"] = "Ethel Rethor", + ["enUS"] = "Ethel Rethor", + ["esES"] = "Ethel Rethor", + ["frFR"] = "Ethel Rethor", + ["koKR"] = "ì—í…” 레소르", + ["ptBR"] = "Ethel Rethor", + ["ruRU"] = "Этель-Ретор", + ["zhCN"] = "艾瑟雷索", + ["zhTW"] = "艾瑟雷索", + }, + }, + [2406] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 6.99, + ["height"] = 8.98, + ["x1"] = 28.94, + ["y1"] = 8.23, + ["names"] = { + ["deDE"] = "Die Insel Ranazjar", + ["enUS"] = "Ranazjar Isle", + ["esES"] = "Isla Ranazjar", + ["frFR"] = "Ile de Ranazjar", + ["koKR"] = "ë¼ë‚˜ì¦ˆìžë¥´ 섬", + ["ptBR"] = "Ilha Ranazjar", + ["ruRU"] = "ОÑтров РаназьÑÑ€", + ["zhCN"] = "拉纳加尔岛", + ["zhTW"] = "拉ç´åŠ çˆ¾å³¶", + }, + }, + [2407] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 10.98, + ["height"] = 11.98, + ["x1"] = 64.37, + ["y1"] = 40.42, + ["names"] = { + ["deDE"] = "Kormeks Hütte", + ["enUS"] = "Kormek\'s Hut", + ["esES"] = "Cabaña de Kormek", + ["frFR"] = "Hutte de Kormek", + ["koKR"] = "코르메í¬ì˜ 오ë‘막", + ["ptBR"] = "Cabaña de Kormek", + ["ruRU"] = "Хижина Кормека", + ["zhCN"] = "考米克å°å±‹", + ["zhTW"] = "考米克å°å±‹", + }, + }, + [2408] = { + ["continent"] = 405, + ["parent"] = 405, + ["width"] = 11.98, + ["height"] = 14.97, + ["x1"] = 25.45, + ["y1"] = 70.36, + ["names"] = { + ["deDE"] = "Schattenflucht", + ["enUS"] = "Shadowprey Village", + ["esES"] = "Poblado Presacaz", + ["frFR"] = "Proie-de-l\'Ombre", + ["koKR"] = "그늘수렵 마ì„", + ["ptBR"] = "Aldeia Shadowprey", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ Ðочных Охотников", + ["zhCN"] = "葬影æ‘", + ["zhTW"] = "葬影æ‘", + }, + }, + [2417] = { + ["continent"] = 46, + ["parent"] = 46, + ["width"] = 14.47, + ["height"] = 21.71, + ["x1"] = 75.1, + ["y1"] = 68.49, + ["names"] = { + ["deDE"] = "Schwarzfelspass", + ["enUS"] = "Blackrock Pass", + ["esES"] = "Paso de Roca Negra", + ["frFR"] = "Défilé des Blackrock", + ["koKR"] = "ê²€ì€ë°”위 ê³ ê°œ", + ["ptBR"] = "Passagem Rocha Negra", + ["ruRU"] = "Перевал Черной горы", + ["zhCN"] = "黑石å°å¾„", + ["zhTW"] = "黑石å°å¾‘", + }, + }, + [2418] = { + ["continent"] = 46, + ["parent"] = 46, + ["width"] = 16.47, + ["height"] = 24.7, + ["x1"] = 88.57, + ["y1"] = 64.75, + ["names"] = { + ["deDE"] = "Morgans Wacht", + ["enUS"] = "Morgan\'s Vigil", + ["esES"] = "Vigilancia de Morgan", + ["frFR"] = "Veille de Morgan", + ["koKR"] = "ëª¨ê±´ì˜ ë§ë£¨", + ["ptBR"] = "Vigília de Morgan", + ["ruRU"] = "Дозор Моргана", + ["zhCN"] = "摩根的岗哨", + ["zhTW"] = "摩根的崗哨", + }, + }, + [2420] = { + ["continent"] = 46, + ["parent"] = 46, + ["width"] = 21.46, + ["height"] = 37.43, + ["x1"] = 88.57, + ["y1"] = 35.93, + ["names"] = { + ["deDE"] = "Schreckenspfad", + ["enUS"] = "Terror Wing Path", + ["esES"] = "Sendero Alas de Terror", + ["frFR"] = "Chemin de l\'Aile de la terreur", + ["koKR"] = "ê³µí¬ì˜ ë‚ ê°œ 골짜기", + ["ptBR"] = "Caminho das Asas do Terror", + ["ruRU"] = "Тропа Крылатого УжаÑа", + ["zhCN"] = "龙翼å°å¾„", + ["zhTW"] = "é¾ç¿¼å°å¾‘", + }, + }, + [2421] = { + ["continent"] = 46, + ["parent"] = 46, + ["width"] = 30.94, + ["height"] = 32.19, + ["x1"] = 27.94, + ["y1"] = 65.49, + ["names"] = { + ["deDE"] = "Draco\'dar", + ["enUS"] = "Draco\'dar", + ["esES"] = "Draco\'dar", + ["frFR"] = "Draco\'dar", + ["koKR"] = "드ë¼ì½”다르", + ["ptBR"] = "Draco\'dar", + ["ruRU"] = "Драко\'дар", + ["zhCN"] = "德拉考达尔", + ["zhTW"] = "德拉考é”爾", + }, + }, + [2478] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 7.49, + ["height"] = 8.23, + ["x1"] = 57.63, + ["y1"] = 87.95, + ["names"] = { + ["deDE"] = "Morlos\'Aran", + ["enUS"] = "Morlos\'Aran", + ["esES"] = "Morlos\'Aran", + ["frFR"] = "Morlos\'Aran", + ["koKR"] = "모를로스아란", + ["ptBR"] = "Morlos\'Aran", + ["ruRU"] = "МорлоÑ\'Ðран", + ["zhCN"] = "æ‘©ç½—è¨å…°", + ["zhTW"] = "摩羅薩蘭", + }, + }, + [2479] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 9.48, + ["height"] = 8.23, + ["x1"] = 51.65, + ["y1"] = 79.72, + ["names"] = { + ["deDE"] = "Das Smaragdrefugium", + ["enUS"] = "Emerald Sanctuary", + ["esES"] = "Santuario Esmeralda", + ["frFR"] = "Sanctuaire d\'émeraude", + ["koKR"] = "ì—메랄드 성소", + ["ptBR"] = "Santuário Esmeralda", + ["ruRU"] = "Изумрудное ÑвÑтилище", + ["zhCN"] = "翡翠圣地", + ["zhTW"] = "ç¿¡ç¿ è–地", + }, + }, + [2480] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 8.48, + ["height"] = 12.72, + ["x1"] = 41.17, + ["y1"] = 82.71, + ["names"] = { + ["deDE"] = "Jadefeuertal", + ["enUS"] = "Jadefire Glen", + ["esES"] = "Glen Jadefire", + ["frFR"] = "Vallon des Jadefeu", + ["koKR"] = "비취불꽃 숲", + ["ptBR"] = "Bosque Jadefuego", + ["ruRU"] = "Долина Ðефритового Пламени", + ["zhCN"] = "碧ç«è°·", + ["zhTW"] = "碧ç«è°·", + }, + }, + [2481] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 8.98, + ["height"] = 11.23, + ["x1"] = 37.92, + ["y1"] = 69.24, + ["names"] = { + ["deDE"] = "Ruinen von Constellas", + ["enUS"] = "Ruins of Constellas", + ["esES"] = "Ruinas de Constellas", + ["frFR"] = "Ruines de Constellas", + ["koKR"] = "콘스텔ë¼ìФ íí—ˆ", + ["ptBR"] = "Ruínas de Constellas", + ["ruRU"] = "Руины КонÑтеллаÑа", + ["zhCN"] = "克斯特拉斯废墟", + ["zhTW"] = "克斯特拉斯廢墟", + }, + }, + [2497] = { + ["continent"] = 16, + ["parent"] = 16, + ["width"] = 16.47, + ["height"] = 14.97, + ["x1"] = 76.6, + ["y1"] = 19.46, + ["names"] = { + ["deDE"] = "Bittere Landzunge", + ["enUS"] = "Bitter Reaches", + ["esES"] = "Alcázares Amargos", + ["frFR"] = "Confins amers", + ["koKR"] = "ì‹œë ¨ì˜ ì‚°ë§ˆë£¨", + ["ptBR"] = "Línguas Amargas", + ["ruRU"] = "Горькие плеÑÑ‹", + ["zhCN"] = "痛苦海岸", + ["zhTW"] = "痛苦海岸", + }, + }, + [2517] = { + ["continent"] = 4, + ["parent"] = 4, + ["width"] = 8.48, + ["height"] = 11.98, + ["x1"] = 48.65, + ["y1"] = 29.94, + ["names"] = { + ["deDE"] = "Anhöhe des Entweihers", + ["enUS"] = "Rise of the Defiler", + ["esES"] = "La Subida del Corruptor", + ["frFR"] = "Cime du Souilleur", + ["koKR"] = "파멸ìžì˜ 봉우리", + ["ptBR"] = "Ascensão do Defiler", + ["ruRU"] = "Ð£Ñ‚ÐµÑ ÐžÑквернителÑ", + ["zhCN"] = "污染者高地", + ["zhTW"] = "污染者高地", + }, + }, + [2522] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 9.98, + ["height"] = 21.71, + ["x1"] = 61.38, + ["y1"] = 66.99, + ["names"] = { + ["deDE"] = "Ruinen von Isildien", + ["enUS"] = "Ruins of Isildien", + ["esES"] = "Ruinas de Isildien", + ["frFR"] = "Ruines d\'Isildien", + ["koKR"] = "ì´ì‹¤ë””ì—” íí—ˆ", + ["ptBR"] = "Ruínas de Isildien", + ["ruRU"] = "Руины ИÑильдиÑна", + ["zhCN"] = "伊斯迪尔废墟", + ["zhTW"] = "伊斯迪爾廢墟", + }, + }, + [2537] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 9.28, + ["height"] = 5.84, + ["x1"] = 75.3, + ["y1"] = 83.16, + ["names"] = { + ["deDE"] = "Posten der Grimmtotem", + ["enUS"] = "Grimtotem Post", + ["esES"] = "Puesto Totem Siniestro", + ["frFR"] = "Poste Totem sinistre", + ["koKR"] = "그림토템 초소", + ["ptBR"] = "Posto Totem Vil", + ["ruRU"] = "ЗаÑтава Зловещего Тотема", + ["zhCN"] = "ææ€–图腾岗哨", + ["zhTW"] = "ææ€–圖騰崗哨", + }, + }, + [2538] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 4.69, + ["height"] = 5.99, + ["x1"] = 75.1, + ["y1"] = 88.47, + ["names"] = { + ["deDE"] = "Camp Aparaje", + ["enUS"] = "Camp Aparaje", + ["esES"] = "Campamento Aparaje", + ["frFR"] = "Camp Aparaje", + ["koKR"] = "아파ë¼ì œ 야ì˜ì§€", + ["ptBR"] = "Acampamento Aparaje", + ["ruRU"] = "Лагерь Ðпарахе", + ["zhCN"] = "阿帕拉耶è¥åœ°", + ["zhTW"] = "阿帕拉耶營地", + }, + }, + [2539] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 6.29, + ["height"] = 5.99, + ["x1"] = 71.21, + ["y1"] = 90.87, + ["names"] = { + ["deDE"] = "Malaka\'jin", + ["enUS"] = "Malaka\'jin", + ["esES"] = "Malaka\'jin", + ["frFR"] = "Malaka\'jin", + ["koKR"] = "ë§ë¼ì¹´ì§„", + ["ptBR"] = "Malaka\'jin", + ["ruRU"] = "Малака\'джин", + ["zhCN"] = "玛拉å¡é‡‘", + ["zhTW"] = "瑪拉å¡é‡‘", + }, + }, + [2540] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 6.19, + ["height"] = 6.89, + ["x1"] = 63.47, + ["y1"] = 88.62, + ["names"] = { + ["deDE"] = "Steinschlagklamm", + ["enUS"] = "Boulderslide Ravine", + ["esES"] = "Barranco de Boulderslide", + ["frFR"] = "Ravin des Eboulis", + ["koKR"] = "구릉바위 협곡", + ["ptBR"] = "Ravina Deslizapedra", + ["ruRU"] = "Ущелье Камнепадов", + ["zhCN"] = "滚岩峡谷", + ["zhTW"] = "滾岩峽谷", + }, + }, + [2541] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 5.49, + ["height"] = 7.63, + ["x1"] = 56.04, + ["y1"] = 72.83, + ["names"] = { + ["deDE"] = "Sishircanyon", + ["enUS"] = "Sishir Canyon", + ["esES"] = "Cañón Sishir", + ["frFR"] = "Canyon de Sishir", + ["koKR"] = "시쉬르 협곡", + ["ptBR"] = "Desfiladeiro Sishir", + ["ruRU"] = "СиширÑкий каньон", + ["zhCN"] = "希塞尔山谷", + ["zhTW"] = "希塞爾山谷", + }, + }, + [2561] = { + ["continent"] = 41, + ["parent"] = 41, + ["width"] = 15.47, + ["height"] = 32.19, + ["x1"] = 58.63, + ["y1"] = 64.75, + ["names"] = { + ["deDE"] = "Das Joch", + ["enUS"] = "The Vice", + ["esES"] = "El Torniquete", + ["frFR"] = "L\'Etau", + ["koKR"] = "ì•…ì˜ ì†Œêµ´", + ["ptBR"] = "O Vício", + ["ruRU"] = "Погань", + ["zhCN"] = "罪æ¶è°·", + ["zhTW"] = "罪惡谷", + }, + }, + [2562] = { + ["continent"] = 41, + ["parent"] = 41, + ["width"] = 18.46, + ["height"] = 23.95, + ["x1"] = 42.66, + ["y1"] = 68.86, + ["names"] = { + ["deDE"] = "Karazhan", + ["enUS"] = "Karazhan", + ["esES"] = "Karazhan", + ["frFR"] = "Karazhan", + ["koKR"] = "ì¹´ë¼ìž”", + ["ptBR"] = "Karazhan", + ["ruRU"] = "Каражан", + ["zhCN"] = "塿‹‰èµž", + ["zhTW"] = "塿‹‰è´Š", + }, + }, + [2577] = { + ["continent"] = 357, + ["parent"] = 357, + ["width"] = 12.97, + ["height"] = 13.47, + ["x1"] = 58.88, + ["y1"] = 41.92, + ["names"] = { + ["deDE"] = "Düsterbruch", + ["enUS"] = "Dire Maul", + ["esES"] = "La Masacre", + ["frFR"] = "Hache-tripes", + ["koKR"] = "í˜ˆíˆ¬ì˜ ì „ìž¥", + ["ptBR"] = "Gládio Cruel", + ["ruRU"] = "Забытый Город", + ["zhCN"] = "厄è¿ä¹‹æ§Œ", + ["zhTW"] = "厄é‹ä¹‹æ§Œ", + }, + }, + [2618] = { + ["continent"] = 361, + ["parent"] = 361, + ["width"] = 8.48, + ["height"] = 10.48, + ["x1"] = 41.17, + ["y1"] = 17.96, + ["names"] = { + ["deDE"] = "Jadefeuerbach", + ["enUS"] = "Jadefire Run", + ["esES"] = "Sendero Fuego de Jade", + ["frFR"] = "Défilé des Jadefeu", + ["koKR"] = "비취불꽃 비탈", + ["ptBR"] = "Sendero Jadefuego", + ["ruRU"] = "Холм Ðефритового Пламени", + ["zhCN"] = "碧ç«å°å¾„", + ["zhTW"] = "碧ç«å°å¾‘", + }, + }, + [2619] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 10.48, + ["height"] = 44.16, + ["x1"] = 7.73, + ["y1"] = 60.25, + ["names"] = { + ["deDE"] = "Thondroril", + ["enUS"] = "Thondroril River", + ["esES"] = "Río Thondroril", + ["frFR"] = "La Thondroril", + ["koKR"] = "톤드로릴 ê°•", + ["ptBR"] = "O Rio Thondroril", + ["ruRU"] = "Река Тондрорил", + ["zhCN"] = "索多里尔河", + ["zhTW"] = "索多里爾河", + }, + }, + [2620] = { + ["continent"] = 28, + ["parent"] = 28, + ["width"] = 10.48, + ["height"] = 41.17, + ["x1"] = 67.61, + ["y1"] = 38.55, + ["names"] = { + ["deDE"] = "Thondroril", + ["enUS"] = "Thondroril River", + ["esES"] = "Río Thondroril", + ["frFR"] = "La Thondroril", + ["koKR"] = "톤드로릴 ê°•", + ["ptBR"] = "O Rio Thondroril", + ["ruRU"] = "Река Тондрорил", + ["zhCN"] = "索多里尔河", + ["zhTW"] = "索多里爾河", + }, + }, + [2621] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 12.48, + ["height"] = 13.47, + ["x1"] = 62.62, + ["y1"] = 80.09, + ["names"] = { + ["deDE"] = "Mereldarsee", + ["enUS"] = "Lake Mereldar", + ["esES"] = "Lago Mereldar", + ["frFR"] = "Lac Mereldar", + ["koKR"] = "ë©”ë ë‹¤ë¥´ 호수", + ["ptBR"] = "Lago Mereldar", + ["ruRU"] = "Озеро Мерельдар", + ["zhCN"] = "米雷达尔湖", + ["zhTW"] = "米雷é”爾湖", + }, + }, + [2622] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 9.48, + ["height"] = 18.71, + ["x1"] = 74.1, + ["y1"] = 62.5, + ["names"] = { + ["deDE"] = "Pestilenznarbe", + ["enUS"] = "Pestilent Scar", + ["esES"] = "Cicatriz Pestilente", + ["frFR"] = "Balafre pestilentielle", + ["koKR"] = "ì „ì—¼ì˜ í‰í„°", + ["ptBR"] = "Cicatriz Pestilenta", + ["ruRU"] = "Моровой овраг", + ["zhCN"] = "瘟疫之痕", + ["zhTW"] = "瘟疫之痕", + }, + }, + [2623] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 10.48, + ["height"] = 11.98, + ["x1"] = 52.64, + ["y1"] = 66.62, + ["names"] = { + ["deDE"] = "Die Infektnarbe", + ["enUS"] = "The Infectis Scar", + ["esES"] = "La Cicatriz Infectis", + ["frFR"] = "La Balafre infecte", + ["koKR"] = "ì˜¤ì—¼ì˜ í‰í„°", + ["ptBR"] = "A Cicatriz Infecta", + ["ruRU"] = "Чумной овраг", + ["zhCN"] = "魔刃之痕", + ["zhTW"] = "魔刃之痕", + }, + }, + [2624] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 8.98, + ["height"] = 12.72, + ["x1"] = 51.9, + ["y1"] = 49.78, + ["names"] = { + ["deDE"] = "Faulholzsee", + ["enUS"] = "Blackwood Lake", + ["esES"] = "Lago Bosque Negro", + ["frFR"] = "Lac de Noirbois", + ["koKR"] = "ê²€ì€ë‚˜ë¬´ 호수", + ["ptBR"] = "Lago Bosquenero", + ["ruRU"] = "Озеро ЧернолеÑÑŒÑ", + ["zhCN"] = "黑木湖", + ["zhTW"] = "黑木湖", + }, + }, + [2627] = { + ["continent"] = 139, + ["parent"] = 139, + ["width"] = 12.48, + ["height"] = 10.48, + ["x1"] = 17.71, + ["y1"] = 32.19, + ["names"] = { + ["deDE"] = "Schreckenstal", + ["enUS"] = "Terrordale", + ["esES"] = "Valle del Terror", + ["frFR"] = "Val-Terreur", + ["koKR"] = "테러ë°ì¼", + ["ptBR"] = "Vale Terrordale", + ["ruRU"] = "Долина УжаÑа", + ["zhCN"] = "ææƒ§è°·", + ["zhTW"] = "ææ‡¼è°·", + }, + }, + [2697] = { + ["continent"] = 41, + ["parent"] = 41, + ["width"] = 27.45, + ["height"] = 34.43, + ["x1"] = 45.16, + ["y1"] = 35.93, + ["names"] = { + ["deDE"] = "Totmannsfurt", + ["enUS"] = "Deadman\'s Crossing", + ["esES"] = "Cruce del Hombre Muerto", + ["frFR"] = "Croisée de l\'homme mort", + ["koKR"] = "사ìžì˜ 길", + ["ptBR"] = "Travessia do Morto", + ["ruRU"] = "ПерекреÑток Мертвеца", + ["zhCN"] = "死者åå­—", + ["zhTW"] = "死者åå­—", + }, + }, + [2737] = { + ["continent"] = 1377, + ["parent"] = 1377, + ["width"] = 13.97, + ["height"] = 5.99, + ["x1"] = 28.94, + ["y1"] = 95.06, + ["names"] = { + ["deDE"] = "Der Skarabäuswall", + ["enUS"] = "The Scarab Wall", + ["esES"] = "Murallas del Escarabajo", + ["frFR"] = "Le Mur du scarabée", + ["koKR"] = "스카ë¼ë²  성벽", + ["ptBR"] = "A Muralha do Escaravelho", + ["ruRU"] = "Стена СкарабеÑ", + ["zhCN"] = "甲虫之墙", + ["zhTW"] = "甲蟲之牆", + }, + }, + [2738] = { + ["continent"] = 1377, + ["parent"] = 1377, + ["width"] = 7.98, + ["height"] = 11.98, + ["x1"] = 62.38, + ["y1"] = 53.14, + ["names"] = { + ["deDE"] = "Südwindposten", + ["enUS"] = "Southwind Village", + ["esES"] = "Villa Viento del Sur", + ["frFR"] = "Village de Sudevent", + ["koKR"] = "마파람 마ì„", + ["ptBR"] = "Aldeia Southwind", + ["ruRU"] = "Ð”ÐµÑ€ÐµÐ²Ð½Ñ Ð®Ð¶Ð½Ð¾Ð³Ð¾ Ветра", + ["zhCN"] = "å—风æ‘", + ["zhTW"] = "å—風æ‘", + }, + }, + [2740] = { + ["continent"] = 1377, + ["parent"] = 1377, + ["width"] = 16.47, + ["height"] = 20.21, + ["x1"] = 23.2, + ["y1"] = 17.59, + ["names"] = { + ["deDE"] = "Das Kristalltal", + ["enUS"] = "The Crystal Vale", + ["esES"] = "El Valle de Cristal", + ["frFR"] = "La Vallée des cristaux", + ["koKR"] = "수정 골짜기", + ["ptBR"] = "O Vale Cristalino", + ["ruRU"] = "Долина КриÑталлов", + ["zhCN"] = "æ°´æ™¶è°·", + ["zhTW"] = "æ°´æ™¶è°·", + }, + }, + [2742] = { + ["continent"] = 1377, + ["parent"] = 1377, + ["width"] = 12.97, + ["height"] = 18.71, + ["x1"] = 44.91, + ["y1"] = 24.33, + ["names"] = { + ["deDE"] = "Bau des Ashischwarms", + ["enUS"] = "Hive\'Ashi", + ["esES"] = "Colmena\'Ashi", + ["frFR"] = "Ruche\'Ashi", + ["koKR"] = "하ì´ë¸Œì•„쉬", + ["ptBR"] = "Colme\'Ashi", + ["ruRU"] = "Улей Ðши", + ["zhCN"] = "亚什虫巢", + ["zhTW"] = "亞什蟲巢", + }, + }, + [2743] = { + ["continent"] = 1377, + ["parent"] = 1377, + ["width"] = 16.47, + ["height"] = 23.95, + ["x1"] = 24.7, + ["y1"] = 58.38, + ["names"] = { + ["deDE"] = "Bau des Zoraschwarms", + ["enUS"] = "Hive\'Zora", + ["esES"] = "Colmena\'Zora", + ["frFR"] = "Ruche\'Zora", + ["koKR"] = "하ì´ë¸Œì¡°ë¼", + ["ptBR"] = "Colme\'Zora", + ["ruRU"] = "Улей Зора", + ["zhCN"] = "使‹‰è™«å·¢", + ["zhTW"] = "使‹‰èŸ²å·¢", + }, + }, + [2744] = { + ["continent"] = 1377, + ["parent"] = 1377, + ["width"] = 16.47, + ["height"] = 33.68, + ["x1"] = 57.14, + ["y1"] = 80.46, + ["names"] = { + ["deDE"] = "Bau des Regalschwarms", + ["enUS"] = "Hive\'Regal", + ["esES"] = "Colmena\'Regal", + ["frFR"] = "Ruche\'Regal", + ["koKR"] = "하ì´ë¸Œë ˆê°ˆ", + ["ptBR"] = "Colme\'Regal", + ["ruRU"] = "Улей Регал", + ["zhCN"] = "雷戈虫巢", + ["zhTW"] = "雷戈蟲巢", + }, + }, + [2959] = { + ["continent"] = 2597, + ["parent"] = 2597, + ["width"] = 28.94, + ["height"] = 26.2, + ["x1"] = 41.92, + ["y1"] = 20.58, + ["names"] = { + ["deDE"] = "Dun Baldar", + ["enUS"] = "Dun Baldar", + ["esES"] = "Dun Baldar", + ["frFR"] = "Dun Baldar", + ["koKR"] = "ë˜ ë°œë‹¤ë¥´", + ["ptBR"] = "Dun Baldar", + ["ruRU"] = "Дун Болдар", + ["zhCN"] = "丹巴达尔", + ["zhTW"] = "丹巴é”爾", + }, + }, + [2977] = { + ["continent"] = 2597, + ["parent"] = 2597, + ["width"] = 21.96, + ["height"] = 29.94, + ["x1"] = 48.9, + ["y1"] = 49.4, + ["names"] = { + ["deDE"] = "Eisblutgarnison", + ["enUS"] = "Iceblood Garrison", + ["esES"] = "Guarnición Sangre de Hielo", + ["frFR"] = "Garnison de Glace-sang", + ["koKR"] = "ì–¼ìŒí”¼ 주둔지", + ["ptBR"] = "Guarnição Sanguefrio", + ["ruRU"] = "Гарнизон Стылой Крови", + ["zhCN"] = "冰血è¦å¡ž", + ["zhTW"] = "冰血è¦å¡ž", + }, + }, + [2978] = { + ["continent"] = 2597, + ["parent"] = 2597, + ["width"] = 16.97, + ["height"] = 29.19, + ["x1"] = 51.9, + ["y1"] = 78.22, + ["names"] = { + ["deDE"] = "Burg Frostwolf", + ["enUS"] = "Frostwolf Keep", + ["esES"] = "Bastión Lobo Gélido", + ["frFR"] = "Donjon Frostwolf", + ["koKR"] = "서리늑대 요새", + ["ptBR"] = "Fortaleza Lobo Gélido", + ["ruRU"] = "КрепоÑть Северного Волка", + ["zhCN"] = "霜狼è¦å¡ž", + ["zhTW"] = "霜狼è¦å¡ž", + }, + }, + [3425] = { + ["continent"] = 1377, + ["parent"] = 1377, + ["width"] = 6.49, + ["height"] = 10.48, + ["x1"] = 50.65, + ["y1"] = 37.43, + ["names"] = { + ["deDE"] = "Burg Cenarius", + ["enUS"] = "Cenarion Hold", + ["esES"] = "Bastión Cenarion", + ["frFR"] = "Fort cénarien", + ["koKR"] = "세나리온 요새", + ["ptBR"] = "Forte Cenariano", + ["ruRU"] = "КрепоÑть КенариÑ", + ["zhCN"] = "塞纳里奥è¦å¡ž", + ["zhTW"] = "塞ç´é‡Œå¥§åŸŽå ¡", + }, + }, + [3881] = { + ["continent"] = 5225, + ["parent"] = 5225, + ["width"] = 3.19, + ["height"] = 3.14, + ["x1"] = 41.42, + ["y1"] = 74.33, + ["names"] = { + ["deDE"] = "Silbersonnenmine", + ["enUS"] = "Silver Sun Mine", + ["esES"] = "Mina Solplata", + ["ptBR"] = "Mina Sol Prateado", + ["zhCN"] = "银日矿井", + }, + }, + [4011] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 7.19, + ["height"] = 7.49, + ["x1"] = 41.12, + ["y1"] = 40.27, + ["names"] = { + ["deDE"] = "Lager der Venture Co.", + ["enUS"] = "Venture Company Camp", + ["esES"] = "Campamento de la Compañía de Exploración", + ["ptBR"] = "Acampamento da Companhia Venture", + ["zhCN"] = "风险投资公å¸è¥åœ°", + }, + }, + [5043] = { + ["continent"] = 409, + ["parent"] = 409, + ["width"] = 10.58, + ["height"] = 9.58, + ["x1"] = 57.19, + ["y1"] = 22.31, + ["names"] = { + ["deDE"] = "Shank\'s Riff", + ["enUS"] = "Shank\'s Reef", + ["esES"] = "Arrecife de Shank", + ["ptBR"] = "Recife de Shank", + ["zhCN"] = "çŠç‘šæš—ç¤", + }, + }, + [5044] = { + ["continent"] = 409, + ["parent"] = 409, + ["width"] = 9.98, + ["height"] = 9.13, + ["x1"] = 57.58, + ["y1"] = 12.35, + ["names"] = { + ["deDE"] = "Kroneninsel", + ["enUS"] = "Crown Island", + ["esES"] = "Isla Corona", + ["ptBR"] = "Ilha da Coroa", + ["zhCN"] = "皇冠岛", + }, + }, + [5045] = { + ["continent"] = 409, + ["parent"] = 409, + ["width"] = 5.09, + ["height"] = 8.23, + ["x1"] = 72.21, + ["y1"] = 28.22, + ["names"] = { + ["deDE"] = "Der Fels", + ["enUS"] = "The Rock", + ["esES"] = "La Roca", + ["ptBR"] = "A Rocha", + ["zhCN"] = "巨石", + }, + }, + [5046] = { + ["continent"] = 409, + ["parent"] = 409, + ["width"] = 12.28, + ["height"] = 14.82, + ["x1"] = 33.68, + ["y1"] = 58.31, + ["names"] = { + ["deDE"] = "Schimmernde Küste", + ["enUS"] = "Bright Coast", + ["esES"] = "Costa Brillante", + ["ptBR"] = "Costa Brilhante", + ["zhCN"] = "明亮海岸", + }, + }, + [5047] = { + ["continent"] = 409, + ["parent"] = 409, + ["width"] = 14.17, + ["height"] = 14.37, + ["x1"] = 47.5, + ["y1"] = 75.45, + ["names"] = { + ["deDE"] = "Zul\'Hazu", + ["enUS"] = "Zul\'Hazu", + ["esES"] = "Zul\'Hazu", + ["ptBR"] = "Zul\'Hazu", + ["zhCN"] = "祖尔哈祖", + }, + }, + [5048] = { + ["continent"] = 409, + ["parent"] = 409, + ["width"] = 6.29, + ["height"] = 14.22, + ["x1"] = 69.81, + ["y1"] = 61.6, + ["names"] = { + ["deDE"] = "Die Wälzende Küste", + ["enUS"] = "The Wallowing Coast", + ["esES"] = "La Costa Enlodada", + ["ptBR"] = "A Costa Pantanosa", + ["zhCN"] = "沉沦海岸", + }, + }, + [5049] = { + ["continent"] = 409, + ["parent"] = 409, + ["width"] = 6.09, + ["height"] = 7.63, + ["x1"] = 52.94, + ["y1"] = 52.02, + ["names"] = { + ["deDE"] = "Hazzuri Lichtung", + ["enUS"] = "Hazzuri Glade", + ["esES"] = "Claro Hazzuri", + ["ptBR"] = "Clareira de Hazzuri", + ["zhCN"] = "海祖瑞林地", + }, + }, + [5050] = { + ["continent"] = 409, + ["parent"] = 409, + ["width"] = 11.68, + ["height"] = 23.8, + ["x1"] = 37.87, + ["y1"] = 33.91, + ["names"] = { + ["deDE"] = "Gor\'dosh Anhöhen", + ["enUS"] = "Gor\'dosh Heights", + ["esES"] = "Alturas Gor\'dosh", + ["ptBR"] = "Planícies de Gor\'dosh", + ["zhCN"] = "戈尔多什高地", + }, + }, + [5051] = { + ["continent"] = 409, + ["parent"] = 409, + ["width"] = 7.58, + ["height"] = 9.13, + ["x1"] = 59.98, + ["y1"] = 43.94, + ["names"] = { + ["deDE"] = "Caelan\'s Rast", + ["enUS"] = "Caelan\'s Rest", + ["esES"] = "El Descanso de Caelan", + ["ptBR"] = "Descanso de Caelan", + ["zhCN"] = "å¡å…°ä¹‹å¢“", + }, + }, + [5052] = { + ["continent"] = 5024, + ["parent"] = 5024, + ["width"] = 9.28, + ["height"] = 8.83, + ["x1"] = 55.34, + ["y1"] = 64.45, + ["names"] = { + ["deDE"] = "Knaeq\'nuun", + ["enUS"] = "Kaneq\'nuun", + ["esES"] = "Kaneq\'nuun", + ["ptBR"] = "Kaneq\'nuun", + ["zhCN"] = "å¡çº³å…‹åŠªæ©", + }, + }, + [5055] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 16.27, + ["height"] = 10.48, + ["x1"] = 29.69, + ["y1"] = 87.28, + ["names"] = { + ["deDE"] = "Tiefebben Sanktum", + ["enUS"] = "Deeptide Sanctum", + ["esES"] = "Sanctum de Profundidades", + ["ptBR"] = "Santuário das Marés Profundas", + ["zhCN"] = "深潮圣殿", + }, + }, + [5056] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 13.27, + ["height"] = 7.04, + ["x1"] = 45.16, + ["y1"] = 80.16, + ["names"] = { + ["deDE"] = "Die Silberküste", + ["enUS"] = "The Silver Coast", + ["esES"] = "La Costa Plateada", + ["ptBR"] = "A Costa Prateada", + ["zhCN"] = "银色海岸", + }, + }, + [5057] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 3.39, + ["height"] = 15.42, + ["x1"] = 27.84, + ["y1"] = 69.39, + ["names"] = { + ["deDE"] = "Die Silberne Sandbank", + ["enUS"] = "The Silver Sandbar", + ["esES"] = "La Barra de Arena Plateada", + ["ptBR"] = "A Barra de Areia Prateada", + ["zhCN"] = "银色沙洲", + }, + }, + [5058] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 6.39, + ["height"] = 12.13, + ["x1"] = 45.61, + ["y1"] = 57.11, + ["names"] = { + ["deDE"] = "Zul\'Razar", + ["enUS"] = "Zul\'Razar", + ["esES"] = "Zul\'Razar", + ["ptBR"] = "Zul\'Razar", + ["zhCN"] = "祖尔拉泽", + }, + }, + [5059] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 7.98, + ["height"] = 12.87, + ["x1"] = 64.27, + ["y1"] = 57.19, + ["names"] = { + ["deDE"] = "Der Verknotete Wald", + ["enUS"] = "The Tangled Wood", + ["esES"] = "El Bosque Enredado", + ["ptBR"] = "A Floresta Enredada", + ["zhCN"] = "混乱森林", + }, + }, + [5060] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 4.19, + ["height"] = 5.09, + ["x1"] = 58.48, + ["y1"] = 48.2, + ["names"] = { + ["deDE"] = "Die Jademine", + ["enUS"] = "The Jade Mine", + ["esES"] = "La Mina de Jade", + ["ptBR"] = "A Mina de Jade", + ["zhCN"] = "翡翠矿井", + }, + }, + [5061] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 7.78, + ["height"] = 6.14, + ["x1"] = 45.51, + ["y1"] = 69.99, + ["names"] = { + ["deDE"] = "Ruinen von Zul\'Razar", + ["enUS"] = "Ruins of Zul\'Razar", + ["esES"] = "Ruinas de Zul\'Razar", + ["ptBR"] = "Ruínas de Zul\'Razar", + ["zhCN"] = "祖尔拉泽废墟", + }, + }, + [5062] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 6.89, + ["height"] = 21.11, + ["x1"] = 70.81, + ["y1"] = 56.36, + ["names"] = { + ["deDE"] = "Gillijim Küste", + ["enUS"] = "Gillijim Strand", + ["esES"] = "Playa Gillijim", + ["ptBR"] = "Praia de Gillijim", + ["zhCN"] = "å‰åˆ©å‰å§†æ²™æ»©", + }, + }, + [5065] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 9.58, + ["height"] = 14.07, + ["x1"] = 69.46, + ["y1"] = 78.74, + ["names"] = { + ["deDE"] = "Zuflucht von Maul\'Ogg", + ["enUS"] = "Maul\'ogg Refuge", + ["esES"] = "Refugio Maul\'ogg", + ["ptBR"] = "Refugio Maul\'ogg", + ["zhCN"] = "莫尔奥格é¿é𾿉€", + }, + }, + [5066] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 3.79, + ["height"] = 5.09, + ["x1"] = 59.08, + ["y1"] = 82.04, + ["names"] = { + ["deDE"] = "Maul\'ogg Posten", + ["enUS"] = "Maul\'ogg Post", + ["esES"] = "Puesto Maul\'ogg", + ["ptBR"] = "Posto Maul\'ogg", + ["zhCN"] = "莫尔奥格哨所", + }, + }, + [5067] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 4.29, + ["height"] = 6.44, + ["x1"] = 49.75, + ["y1"] = 38.1, + ["names"] = { + ["deDE"] = "Kalkorspitze", + ["enUS"] = "Kalkor Point", + ["esES"] = "Punto Kalkor", + ["ptBR"] = "Ponto Kalkor", + ["zhCN"] = "å¡å°”科尔角", + }, + }, + [5068] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 17.27, + ["height"] = 14.22, + ["x1"] = 52.45, + ["y1"] = 15.49, + ["names"] = { + ["deDE"] = "Insel Kazon", + ["enUS"] = "Kazon Island", + ["esES"] = "Isla Kazon", + ["ptBR"] = "Ilha Kazon", + ["zhCN"] = "塿¾å²›", + }, + }, + [5069] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 10.78, + ["height"] = 22.16, + ["x1"] = 24.35, + ["y1"] = 22.01, + ["names"] = { + ["deDE"] = "Die Südmeer Sandbank", + ["enUS"] = "The Southsea Sandbar", + ["esES"] = "La Barra de Arena del Mar del Sur", + ["ptBR"] = "A Barra de Areia Marinha", + ["zhCN"] = "å—æµ·æ²™æ´²", + }, + }, + [5070] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 7.29, + ["height"] = 7.34, + ["x1"] = 27.69, + ["y1"] = 37.5, + ["names"] = { + ["deDE"] = "Insel der Brauerei", + ["enUS"] = "Distillery Island", + ["esES"] = "Isla de la Destilería", + ["ptBR"] = "Ilha da Destilaria", + ["zhCN"] = "é…¿é…’å²›", + }, + }, + [5071] = { + ["continent"] = 408, + ["parent"] = 408, + ["width"] = 6.99, + ["height"] = 10.18, + ["x1"] = 37.62, + ["y1"] = 52.1, + ["names"] = { + ["deDE"] = "Faelon\'s Torheit", + ["enUS"] = "Faelon\'s Folly", + ["esES"] = "La Locura de Faelon", + ["ptBR"] = "Tolo de Faelon", + ["zhCN"] = "费隆之愚", + }, + }, + [5072] = { + ["continent"] = 409, + ["parent"] = 409, + ["width"] = 5.29, + ["height"] = 7.34, + ["x1"] = 60.03, + ["y1"] = 31.81, + ["names"] = { + ["deDE"] = "Der Turm von Lapidis", + ["enUS"] = "The Tower of Lapidis", + ["esES"] = "La Torre de Lapidis", + ["ptBR"] = "A Torre de Lapidis", + ["zhCN"] = "拉匹迪斯之塔", + }, + }, + [5117] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 13.27, + ["height"] = 20.51, + ["x1"] = 28.19, + ["y1"] = 32.41, + ["names"] = { + ["deDE"] = "Schwarzsand Ölfelder", + ["enUS"] = "Blacksand Oil Fields", + ["esES"] = "Campos de Petróleo Arena Negra", + ["ptBR"] = "Campos de Petróleo Areia Negra", + ["zhCN"] = "黑沙油田", + }, + }, + [5118] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 6.49, + ["height"] = 6.14, + ["x1"] = 36.98, + ["y1"] = 50.07, + ["names"] = { + ["deDE"] = "Pulverstadt", + ["enUS"] = "Powder Town", + ["esES"] = "Ciudad Polvorienta", + ["ptBR"] = "Cidade do Pó", + ["zhCN"] = "ç«è¯é•‡", + }, + }, + [5119] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 13.27, + ["height"] = 13.62, + ["x1"] = 75, + ["y1"] = 72.38, + ["names"] = { + ["deDE"] = "Stacheldornpass", + ["enUS"] = "Bramblethorn Pass", + ["esES"] = "Paso Espinas de Zarza", + ["ptBR"] = "Passagem Espinhalhaço", + ["zhCN"] = "è†æ£˜å°å¾„", + }, + }, + [5120] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 6.19, + ["height"] = 7.49, + ["x1"] = 77.84, + ["y1"] = 61.83, + ["names"] = { + ["deDE"] = "Bael Hardul", + ["enUS"] = "Bael Hardul", + ["esES"] = "Bael Hardul", + ["ptBR"] = "Bael Hardul", + ["zhCN"] = "è´å°”哈æœå°”", + }, + }, + [5123] = { + ["continent"] = 5121, + ["parent"] = 5121, + ["width"] = 7.58, + ["height"] = 6.59, + ["x1"] = 42.12, + ["y1"] = 79.04, + ["names"] = { + ["deDE"] = "Tel Co. Basislager", + ["enUS"] = "Tel Co. Basecamp", + ["esES"] = "Campamento Base Tel Co.", + ["ptBR"] = "Base Tel Co.", + ["zhCN"] = "风险投资公å¸è¥åœ°", + }, + }, + [5124] = { + ["continent"] = 5121, + ["parent"] = 5121, + ["width"] = 5.49, + ["height"] = 4.94, + ["x1"] = 51.35, + ["y1"] = 33.76, + ["names"] = { + ["deDE"] = "Bixxle\'s Lagerhaus", + ["enUS"] = "Bixxle\'s Storehouse", + ["esES"] = "Almacén de Bixxle", + ["ptBR"] = "Depósito de Bixxle", + ["zhCN"] = "比克瑟仓库", + }, + }, + [5125] = { + ["continent"] = 5121, + ["parent"] = 5121, + ["width"] = 9.58, + ["height"] = 17.81, + ["x1"] = 55.19, + ["y1"] = 50.37, + ["names"] = { + ["deDE"] = "Hochtalhöhe", + ["enUS"] = "Highvale Rise", + ["esES"] = "Ascenso Alto Valle", + ["ptBR"] = "Elevado Vistalva", + ["zhCN"] = "海瓦尔高地", + }, + }, + [5126] = { + ["continent"] = 5121, + ["parent"] = 5121, + ["width"] = 3.69, + ["height"] = 4.94, + ["x1"] = 41.57, + ["y1"] = 52.77, + ["names"] = { + ["deDE"] = "Das Verlassene Lager", + ["enUS"] = "The Derelict Camp", + ["esES"] = "El Campamento Abandonado", + ["ptBR"] = "O Campo Abandonado", + ["zhCN"] = "被é—弃的è¥åœ°", + }, + }, + [5127] = { + ["continent"] = 5121, + ["parent"] = 5121, + ["width"] = 4.59, + ["height"] = 6.59, + ["x1"] = 53.09, + ["y1"] = 81.44, + ["names"] = { + ["deDE"] = "Tazzo\'s Hütte", + ["enUS"] = "Tazzo\'s Shack", + ["esES"] = "Choza de Tazzo", + ["ptBR"] = "Casebre do Tazzo", + ["zhCN"] = "å¡”ä½å°å±‹", + }, + }, + [5128] = { + ["continent"] = 5121, + ["parent"] = 5121, + ["width"] = 19.26, + ["height"] = 23.2, + ["x1"] = 53.84, + ["y1"] = 19.54, + ["names"] = { + ["deDE"] = "Die Zackeninseln", + ["enUS"] = "The Jagged Isles", + ["esES"] = "Las Islas Dentadas", + ["ptBR"] = "As Ilhas Cerradas", + ["zhCN"] = "锯齿群岛", + }, + }, + [5180] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 11.78, + ["height"] = 23.95, + ["x1"] = 23.15, + ["y1"] = 36.23, + ["names"] = { + ["deDE"] = "Stadt von Gilneas", + ["enUS"] = "Gilneas City", + ["esES"] = "Ciudad Gilneas", + ["ptBR"] = "Cidade de Gilneas", + ["zhCN"] = "å‰å°”尼斯城", + }, + }, + [5181] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 6.69, + ["height"] = 7.34, + ["x1"] = 58.63, + ["y1"] = 72.23, + ["names"] = { + ["deDE"] = "Rabenruh", + ["enUS"] = "Ravenshire", + ["esES"] = "Ravenshire", + ["ptBR"] = "Ravenshire", + ["zhCN"] = "拉文郡", + }, + }, + [5182] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 7.19, + ["height"] = 12.43, + ["x1"] = 63.67, + ["y1"] = 83.61, + ["names"] = { + ["deDE"] = "Rabenholz Festung", + ["enUS"] = "Ravenwood Keep", + ["esES"] = "Fortaleza Bosque Cuervo", + ["ptBR"] = "Forte Mata do Corvo", + ["zhCN"] = "拉文ä¼å¾·åŸŽå ¡", + }, + }, + [5183] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 7.29, + ["height"] = 7.04, + ["x1"] = 54.84, + ["y1"] = 63.55, + ["names"] = { + ["deDE"] = "Brol\'oks Hügel", + ["enUS"] = "Brol\'ok Mound", + ["esES"] = "Montículo Brol\'ok", + ["ptBR"] = "Monte Brol\'ok", + ["zhCN"] = "布罗克山丘", + }, + }, + [5184] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 5.79, + ["height"] = 5.09, + ["x1"] = 44.61, + ["y1"] = 65.57, + ["names"] = { + ["deDE"] = "Südmorast Obstgarten", + ["enUS"] = "Southmire Orchard", + ["esES"] = "Huerta de Mirasur", + ["ptBR"] = "Pomar de Southmire", + ["zhCN"] = "å—æ³¥æžœå›­", + }, + }, + [5185] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 11.78, + ["height"] = 7.63, + ["x1"] = 42.91, + ["y1"] = 75.37, + ["names"] = { + ["deDE"] = "Düsternetz Wald", + ["enUS"] = "Hollow Web Woods", + ["esES"] = "Bosques Tejidos Oscuros", + ["ptBR"] = "Floresta Teia Oca", + ["zhCN"] = "空网树林", + }, + }, + [5186] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 3.09, + ["height"] = 3.44, + ["x1"] = 33.38, + ["y1"] = 76.27, + ["names"] = { + ["deDE"] = "Düsternetz Friedhof", + ["enUS"] = "Hollow Web Cemetery", + ["esES"] = "Cementerio Tejidos Oscuros", + ["ptBR"] = "Cemitério Teia Oca", + ["zhCN"] = "空网墓地", + }, + }, + [5187] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 2, + ["height"] = 2.54, + ["x1"] = 33.53, + ["y1"] = 53.67, + ["names"] = { + ["enUS"] = "Shademore Tavern", + ["esES"] = "Taberna Sombraloma", + ["ptBR"] = "Estalagem Sombral", + ["zhCN"] = "沙德摩尔旅店", + }, + }, + [5188] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 7.49, + ["height"] = 6.14, + ["x1"] = 33.68, + ["y1"] = 47.68, + ["names"] = { + ["deDE"] = "Ruinen von Graufurt", + ["enUS"] = "Ruins of Greyshire", + ["esES"] = "Ruinas de Greyshire", + ["ptBR"] = "Ruínas de Greyshire", + ["zhCN"] = "格雷郡é—å€", + }, + }, + [5189] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 2.5, + ["height"] = 2.99, + ["x1"] = 47.95, + ["y1"] = 35.63, + ["names"] = { + ["deDE"] = "Nordtor Turm", + ["enUS"] = "Northgate Tower", + ["esES"] = "Torre Puerta Norte", + ["ptBR"] = "Torre do Portão Norte", + ["zhCN"] = "北门之塔", + }, + }, + [5191] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 2.4, + ["height"] = 3.74, + ["x1"] = 43.81, + ["y1"] = 27.77, + ["names"] = { + ["deDE"] = "Rosendocht Plantage", + ["enUS"] = "Rosewick Plantation", + ["esES"] = "Plantación Rosewick", + ["ptBR"] = "Plantação de Rosewick", + ["zhCN"] = "ç½—æ–¯å¨å…‹ç§æ¤å›­", + }, + }, + [5192] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 2.99, + ["height"] = 3.74, + ["x1"] = 53.39, + ["y1"] = 29.27, + ["names"] = { + ["deDE"] = "Glanzmoor Hof", + ["enUS"] = "Glaymore Stead", + ["esES"] = "Granja Glaymore", + ["ptBR"] = "Assentamento de Glaymore", + ["zhCN"] = "格莱摩尔农场", + }, + }, + [5195] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 3.09, + ["height"] = 4.34, + ["x1"] = 48.05, + ["y1"] = 56.66, + ["names"] = { + ["deDE"] = "Der Wuchernde Acker", + ["enUS"] = "The Overgrown Acre", + ["esES"] = "El Acre Crecido", + ["ptBR"] = "O Acre Coberto de Vegetação", + ["zhCN"] = "茂è‰ä¹‹é‡Ž", + }, + }, + [5196] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 4.19, + ["height"] = 10.48, + ["x1"] = 20.26, + ["y1"] = 64.67, + ["names"] = { + ["deDE"] = "Die Trockenfels Grube", + ["enUS"] = "The Dryrock Pit", + ["esES"] = "La Fosa de Roca Secas", + ["ptBR"] = "Poço Rocha Seca", + ["zhCN"] = "干岩矿å‘", + }, + }, + [5197] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 0, + ["height"] = 0, + ["x1"] = 0, + ["y1"] = 0, + ["names"] = { + ["deDE"] = "Die Trockenfels Mine", + ["enUS"] = "The Dryrock Mine", + ["esES"] = "Mina Roca Secas", + ["ptBR"] = "Mina Rocha Seca", + ["zhCN"] = "干岩矿洞", + }, + }, + [5198] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 2.79, + ["height"] = 4.04, + ["x1"] = 66.17, + ["y1"] = 65.19, + ["names"] = { + ["deDE"] = "Wachfeste Freiblick", + ["enUS"] = "Freyshear Keep", + ["esES"] = "Fortaleza Freyshear", + ["ptBR"] = "Forte Freyshear", + ["zhCN"] = "弗雷希尔è¦å¡ž", + }, + }, + [5199] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 2.59, + ["height"] = 4.34, + ["x1"] = 64.97, + ["y1"] = 57.86, + ["names"] = { + ["deDE"] = "Graumähnes Wacht", + ["enUS"] = "Greymane\'s Watch", + ["esES"] = "Vigía de Greymane", + ["ptBR"] = "Vigiíia dos Greymane", + ["zhCN"] = "格雷迈æ©å“¨å²—", + }, + }, + [5200] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 4.79, + ["height"] = 16.32, + ["x1"] = 39.62, + ["y1"] = 15.94, + ["names"] = { + ["deDE"] = "Altfelsenpass", + ["enUS"] = "Oldrock Pass", + ["esES"] = "Paso Viejoroca", + ["ptBR"] = "Passagem Rochavelha", + ["zhCN"] = "旧石峡谷", + }, + }, + [5201] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 4.89, + ["height"] = 5.84, + ["x1"] = 56.94, + ["y1"] = 40.04, + ["names"] = { + ["deDE"] = "Kirche von Stillwacht", + ["enUS"] = "Stillward Church", + ["esES"] = "Iglesia Stillward", + ["ptBR"] = "Igreja Stillward", + ["zhCN"] = "寂é™å®ˆå«æ•™å ‚", + }, + }, + [5203] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 5.09, + ["height"] = 6.59, + ["x1"] = 46.76, + ["y1"] = 17.07, + ["names"] = { + ["deDE"] = "Der Graumähnenwall", + ["enUS"] = "The Greymane Wall", + ["esES"] = "El Muro de Greymane", + ["ptBR"] = "Muralha Greymane", + ["zhCN"] = "格雷迈æ©ä¹‹å¢™", + }, + }, + [5206] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 3.29, + ["height"] = 4.64, + ["x1"] = 14.32, + ["y1"] = 33.76, + ["names"] = { + ["deDE"] = "Schwarzdorn\'s Lager", + ["enUS"] = "Blackthorn\'s Camp", + ["esES"] = "Campamento Espino Negro", + ["ptBR"] = "Acampamento de Blackthorn", + ["zhCN"] = "é»‘è†æ£˜è¥åœ°", + }, + }, + [5207] = { + ["continent"] = 5179, + ["parent"] = 5179, + ["width"] = 0, + ["height"] = 0, + ["x1"] = 0, + ["y1"] = 0, + ["names"] = { + ["deDE"] = "Dämmerstein Mine", + ["enUS"] = "Dawnstone Mine", + ["esES"] = "Mina Piedra del Alba", + ["ptBR"] = "Mina da Pedra Amanhecida", + ["zhCN"] = "黎明石矿", + }, + }, + [5212] = { + ["continent"] = 616, + ["parent"] = 616, + ["width"] = 22.16, + ["height"] = 30.39, + ["x1"] = 55.49, + ["y1"] = 32.11, + ["names"] = { + ["deDE"] = "Trauenhöhlenkrater", + ["enUS"] = "Bleakhollow Crater", + ["esES"] = "Cráter Hondonada Sombría", + ["ptBR"] = "Cratera Oca e Desolada", + ["zhCN"] = "凄凉è’å‘", + }, + }, + [5213] = { + ["continent"] = 616, + ["parent"] = 616, + ["width"] = 14.77, + ["height"] = 33.53, + ["x1"] = 73.95, + ["y1"] = 31.89, + ["names"] = { + ["deDE"] = "Lichtung von Nordrassil", + ["enUS"] = "Nordrassil Glade", + ["esES"] = "Claro Nordrassil", + ["ptBR"] = "Clareira Nordrassil", + ["zhCN"] = "诺达希尔森林", + }, + }, + [5214] = { + ["continent"] = 616, + ["parent"] = 616, + ["width"] = 5.49, + ["height"] = 7.19, + ["x1"] = 84.08, + ["y1"] = 28.89, + ["names"] = { + ["deDE"] = "Nordanaar", + ["enUS"] = "Nordanaar", + ["esES"] = "Nordanaar", + ["ptBR"] = "Nordanaar", + ["zhCN"] = "诺达纳尔", + }, + }, + [5215] = { + ["continent"] = 616, + ["parent"] = 616, + ["width"] = 13.37, + ["height"] = 12.72, + ["x1"] = 50.7, + ["y1"] = 55.46, + ["names"] = { + ["deDE"] = "Borkenhaut Plateau", + ["enUS"] = "Barkskin Plateau", + ["esES"] = "Meseta Corteza de Piel", + ["ptBR"] = "Planalto Pele de Ãrvore", + ["zhCN"] = "树皮高原", + }, + }, + [5216] = { + ["continent"] = 616, + ["parent"] = 616, + ["width"] = 13.27, + ["height"] = 7.78, + ["x1"] = 63.52, + ["y1"] = 67.07, + ["names"] = { + ["deDE"] = "Dorf der Borkenhäute", + ["enUS"] = "Barkskin Village", + ["esES"] = "Pueblo Corteza de Piel", + ["ptBR"] = "Vilarejo Pele de Ãrvore", + ["zhCN"] = "æ ‘çš®æ‘", + }, + }, + [5217] = { + ["continent"] = 616, + ["parent"] = 616, + ["width"] = 8.28, + ["height"] = 9.13, + ["x1"] = 33.68, + ["y1"] = 40.94, + ["names"] = { + ["deDE"] = "Ring der Macht", + ["enUS"] = "Circle of Power", + ["esES"] = "Círculo de Poder", + ["ptBR"] = "Círculo de Poder", + ["zhCN"] = "åŠ›é‡æ³•阵", + }, + }, + [5218] = { + ["continent"] = 616, + ["parent"] = 616, + ["width"] = 11.18, + ["height"] = 18.11, + ["x1"] = 21.76, + ["y1"] = 49.33, + ["names"] = { + ["deDE"] = "Die Ruinen von Telennas", + ["enUS"] = "The Ruins of Telennas", + ["esES"] = "Las Ruinas de Telennas", + ["ptBR"] = "As Ruinas de Telennas", + ["zhCN"] = "特兰纳废墟", + }, + }, + [5219] = { + ["continent"] = 616, + ["parent"] = 616, + ["width"] = 10.08, + ["height"] = 12.13, + ["x1"] = 9.93, + ["y1"] = 44.84, + ["names"] = { + ["deDE"] = "Zul\'Hatha", + ["enUS"] = "Zul\'Hatha", + ["esES"] = "Zul\'Hatha", + ["ptBR"] = "Zul\'Hatha", + ["zhCN"] = "祖尔哈è¨", + }, + }, + [5220] = { + ["continent"] = 616, + ["parent"] = 616, + ["width"] = 9.08, + ["height"] = 11.98, + ["x1"] = 43.26, + ["y1"] = 76.5, + ["names"] = { + ["deDE"] = "Dunkelhöhlenpass", + ["enUS"] = "Darkhollow Pass", + ["esES"] = "Paso Oscurallano", + ["ptBR"] = "Estreito Covatreva", + ["zhCN"] = "è¾¾å…‹éœæ´›å±±å£", + }, + }, + [5221] = { + ["continent"] = 616, + ["parent"] = 616, + ["width"] = 5.59, + ["height"] = 6.59, + ["x1"] = 20.56, + ["y1"] = 60.48, + ["names"] = { + ["deDE"] = "Das Smaragdtor", + ["enUS"] = "The Emerald Gateway", + ["esES"] = "El Portal Esmeralda", + ["ptBR"] = "O Portal Esmeralda", + ["zhCN"] = "翡翠之门", + }, + }, + [5528] = { + ["continent"] = 5225, + ["parent"] = 5225, + ["width"] = 13.37, + ["height"] = 20.21, + ["x1"] = 27.74, + ["y1"] = 78.82, + ["names"] = { + ["deDE"] = "Zuflucht der Teufelsschreiter", + ["enUS"] = "Felstrider Retreat", + ["esES"] = "Refugio Felstrider", + ["ptBR"] = "Refúgio Felstrider", + ["zhCN"] = "邪行者居所", + }, + }, + [5529] = { + ["continent"] = 5225, + ["parent"] = 5225, + ["width"] = 16.77, + ["height"] = 10.03, + ["x1"] = 32.24, + ["y1"] = 48.13, + ["names"] = { + ["deDE"] = "Die Weltenwanderer", + ["enUS"] = "The Farstride", + ["esES"] = "El Vía Lejana", + ["ptBR"] = "O Passolargo", + ["zhCN"] = "远行之地", + }, + }, + [5531] = { + ["continent"] = 5225, + ["parent"] = 5225, + ["width"] = 16.07, + ["height"] = 19.01, + ["x1"] = 33.38, + ["y1"] = 28.97, + ["names"] = { + ["deDE"] = "Insel des Ewigen Herbstes", + ["enUS"] = "Isle of Eternal Autumn", + ["esES"] = "Isla del Otoño Eterno", + ["ptBR"] = "Ilha do Outono Eterno", + ["zhCN"] = "永秋之岛", + }, + }, + [5532] = { + ["continent"] = 5225, + ["parent"] = 5225, + ["width"] = 11.08, + ["height"] = 12.43, + ["x1"] = 52.05, + ["y1"] = 49.48, + ["names"] = { + ["deDE"] = "Anasterian Park", + ["enUS"] = "Anasterian Park", + ["esES"] = "Parque Anasteriano", + ["ptBR"] = "Parque Anasterian", + ["zhCN"] = "安纳斯特里亚公园", + }, + }, + [5533] = { + ["continent"] = 5225, + ["parent"] = 5225, + ["width"] = 10.48, + ["height"] = 12.87, + ["x1"] = 48.65, + ["y1"] = 85.48, + ["names"] = { + ["deDE"] = "Brinthilien", + ["enUS"] = "Brinthilien", + ["esES"] = "Brinthilien", + ["ptBR"] = "Brinthilien", + ["zhCN"] = "布林希尔林", + }, + }, + [5534] = { + ["continent"] = 5225, + ["parent"] = 5225, + ["width"] = 13.67, + ["height"] = 16.32, + ["x1"] = 16.92, + ["y1"] = 44.99, + ["names"] = { + ["deDE"] = "Ruinen von Nashal\'aran", + ["enUS"] = "Ruins of Nashal\'aran", + ["esES"] = "Ruinas de Nashal\'aran", + ["ptBR"] = "Ruínas de Nashal\'aran", + ["zhCN"] = "纳沙拉兰废墟", + }, + }, + [5537] = { + ["continent"] = 5536, + ["parent"] = 5536, + ["width"] = 9.78, + ["height"] = 10.33, + ["x1"] = 48.3, + ["y1"] = 65.79, + ["names"] = { + ["deDE"] = "Rosttorkamm", + ["enUS"] = "Rustgate Ridge", + ["esES"] = "Cima Rustgate", + ["ptBR"] = "Cume Rustgate", + ["zhCN"] = "锈门山脊", + }, + }, + [5538] = { + ["continent"] = 5536, + ["parent"] = 5536, + ["width"] = 8.48, + ["height"] = 9.73, + ["x1"] = 35.78, + ["y1"] = 61.15, + ["names"] = { + ["deDE"] = "Schwarzaschegruben", + ["enUS"] = "Black Ash Coalpits", + ["esES"] = "Pozos de Carbón Ceniza Negra", + ["ptBR"] = "Mina de Carvão Cinza Negra", + ["zhCN"] = "黑ç°çŸ¿å‘", + }, + }, + [5539] = { + ["continent"] = 5536, + ["parent"] = 5536, + ["width"] = 9.18, + ["height"] = 9.43, + ["x1"] = 53.29, + ["y1"] = 50.22, + ["names"] = { + ["deDE"] = "Schwarzascheminen", + ["enUS"] = "Black Ash Mine", + ["esES"] = "Mina Ceniza Negra", + ["ptBR"] = "Minha Cinza Negra", + ["zhCN"] = "黑ç°çŸ¿æ´ž", + }, + }, + [5540] = { + ["continent"] = 5536, + ["parent"] = 5536, + ["width"] = 5.29, + ["height"] = 6.59, + ["x1"] = 57.83, + ["y1"] = 24.1, + ["names"] = { + ["deDE"] = "Das Wasserloch", + ["enUS"] = "The Water Hole", + ["esES"] = "El Agujero de Agua", + ["ptBR"] = "O Poço de Ãgua", + ["zhCN"] = "æ°´æºåœ°", + }, + }, + [5541] = { + ["continent"] = 5536, + ["parent"] = 5536, + ["width"] = 9.28, + ["height"] = 11.23, + ["x1"] = 62.62, + ["y1"] = 82.41, + ["names"] = { + ["deDE"] = "Gazzik\'s Werkstatt", + ["enUS"] = "Gazzik\'s Workshop", + ["esES"] = "Taller de Gazzik", + ["ptBR"] = "Oficina de Gazzik", + ["zhCN"] = "加兹克工åŠ", + }, + }, + [5542] = { + ["continent"] = 5536, + ["parent"] = 5536, + ["width"] = 5.19, + ["height"] = 8.08, + ["x1"] = 54.89, + ["y1"] = 32.34, + ["names"] = { + ["deDE"] = "Venture Co. Slums", + ["enUS"] = "Venture Co. Slums", + ["esES"] = "Barrio Bajo Compañía de Exploración", + ["ptBR"] = "Favelas do Empreendimentos S.A.", + ["zhCN"] = "风险投资公å¸è´«æ°‘区", + }, + }, + [5543] = { + ["continent"] = 5536, + ["parent"] = 5536, + ["width"] = 8.68, + ["height"] = 10.33, + ["x1"] = 62.33, + ["y1"] = 58.46, + ["names"] = { + ["deDE"] = "Rosttorkamm Holzhof", + ["enUS"] = "Rustgate Lumber Yard", + ["esES"] = "Patio de Madera Rustgate", + ["ptBR"] = "Depósito de Madeira Rustgate", + ["zhCN"] = "锈门木æåœº", + }, + }, + [5546] = { + ["continent"] = 5225, + ["parent"] = 5225, + ["width"] = 7.58, + ["height"] = 8.83, + ["x1"] = 57.19, + ["y1"] = 71.33, + ["names"] = { + ["deDE"] = "Der Letzte Runenstein", + ["enUS"] = "The Last Runestone", + ["esES"] = "La Última Piedra Rúnica", + ["ptBR"] = "A Última Pedra Rúnica", + ["zhCN"] = "最åŽçš„符文石", + }, + }, + [5548] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 5.29, + ["height"] = 6.44, + ["x1"] = 47.55, + ["y1"] = 34.96, + ["names"] = { + ["deDE"] = "Bruchklippen Mine", + ["enUS"] = "Broken Cliff Mine", + ["esES"] = "Mina Roca Quebrada", + ["ptBR"] = "Mina do Penhasco Quebrado", + ["zhCN"] = "断崖矿井", + }, + }, + [5558] = { + ["continent"] = 5557, + ["parent"] = 5557, + ["width"] = 9.28, + ["height"] = 8.83, + ["x1"] = 55.34, + ["y1"] = 64.45, + ["names"] = { + ["deDE"] = "Der Fels der Verwüstung", + ["enUS"] = "The Rock of Desolation", + ["esES"] = "La Roca de la Desolación", + ["ptBR"] = "A Pedra da Desolação", + ["zhCN"] = "è’芜巨岩", + }, + }, + [5562] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 6.29, + ["height"] = 8.53, + ["x1"] = 27.3, + ["y1"] = 12.8, + ["names"] = { + ["deDE"] = "Möwenflügel Wrack", + ["enUS"] = "Gullwing Wreckage", + ["esES"] = "Restos de Alas de Gaviota", + ["ptBR"] = "Destroços de Gullwing", + ["zhCN"] = "鸥翼之é—", + }, + }, + [5563] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 7.58, + ["height"] = 13.02, + ["x1"] = 41.42, + ["y1"] = 21.63, + ["names"] = { + ["deDE"] = "Buchtpiratenlager", + ["enUS"] = "Bilgerat Compound", + ["esES"] = "Complejo Bilgerat", + ["ptBR"] = "Composto Bilgerat", + ["zhCN"] = "舱底鼠è¥åœ°", + }, + }, + [5564] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 6.49, + ["height"] = 5.84, + ["x1"] = 70.11, + ["y1"] = 77.32, + ["names"] = { + ["deDE"] = "SI:7 Außenposten", + ["enUS"] = "SI:7 Outpost", + ["esES"] = "Avanzada SI:7", + ["ptBR"] = "Posto Avançado SI:7", + ["zhCN"] = "军情七处哨站", + }, + }, + [5565] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 8.28, + ["height"] = 8.08, + ["x1"] = 53.64, + ["y1"] = 49.25, + ["names"] = { + ["deDE"] = "Ruinen von Brisenheim", + ["enUS"] = "Ruins of Breezehaven", + ["esES"] = "Ruinas de Brisa Refugio", + ["ptBR"] = "Ruínas de Breezehaven", + ["zhCN"] = "微风港的废墟", + }, + }, + [5566] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 5.69, + ["height"] = 7.49, + ["x1"] = 46.96, + ["y1"] = 45.36, + ["names"] = { + ["deDE"] = "Quakendes Plateau", + ["enUS"] = "Croaking Plateau", + ["esES"] = "Meseta Croak", + ["ptBR"] = "Planalto Coaxante", + ["zhCN"] = "蛙鸣高原", + }, + }, + [5567] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 5.49, + ["height"] = 5.69, + ["x1"] = 47.85, + ["y1"] = 57.93, + ["names"] = { + ["deDE"] = "Langstons Obsthain", + ["enUS"] = "Langston Orchard", + ["esES"] = "Huerta Langston", + ["ptBR"] = "Pomar Langston", + ["zhCN"] = "朗斯顿果园", + }, + }, + [5568] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 8.88, + ["height"] = 10.93, + ["x1"] = 39.07, + ["y1"] = 52.62, + ["names"] = { + ["deDE"] = "Trauermoorsee", + ["enUS"] = "Sorrowmore Lake", + ["esES"] = "Lago Dolormás", + ["ptBR"] = "Lago Sorrowmore", + ["zhCN"] = "悲痛湖", + }, + }, + [5569] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 7.19, + ["height"] = 16.92, + ["x1"] = 35.03, + ["y1"] = 37.05, + ["names"] = { + ["deDE"] = "Huscherdickicht", + ["enUS"] = "Scurrying Thicket", + ["esES"] = "Matorral Escurridizo", + ["ptBR"] = "Capão Corrido", + ["zhCN"] = "疾奔树林", + }, + }, + [5570] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 9.68, + ["height"] = 13.32, + ["x1"] = 56.94, + ["y1"] = 63.4, + ["names"] = { + ["deDE"] = "Sturmschmied Burg", + ["enUS"] = "Stormwrought Castle", + ["esES"] = "Castillo Forjatormenta", + ["ptBR"] = "Castelo Stormwrought", + ["zhCN"] = "风暴城堡", + }, + }, + [5571] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 7.68, + ["height"] = 7.78, + ["x1"] = 55.34, + ["y1"] = 74.55, + ["names"] = { + ["deDE"] = "Sturmwirkerspitze", + ["enUS"] = "Stormreaver Spire", + ["esES"] = "Cima Reaver Tormenta", + ["ptBR"] = "Pináculo Stormreaver", + ["zhCN"] = "暴掠之塔", + }, + }, + [5572] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 6.99, + ["height"] = 32.78, + ["x1"] = 30.14, + ["y1"] = 65.34, + ["names"] = { + ["deDE"] = "Windfels Klippen", + ["enUS"] = "Windrock Cliffs", + ["esES"] = "Acantilados Roca Viento", + ["ptBR"] = "Penhascos de Windrock", + ["zhCN"] = "风岩崖", + }, + }, + [5573] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 13.57, + ["height"] = 7.49, + ["x1"] = 52.69, + ["y1"] = 86.68, + ["names"] = { + ["deDE"] = "Tückische Felsklippen", + ["enUS"] = "Treacherous Crags", + ["esES"] = "Gargantas Traicioneras", + ["ptBR"] = "Penhascos Traiçoeiros", + ["zhCN"] = "险æ¶å³­å£", + }, + }, + [5574] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 5.09, + ["height"] = 8.53, + ["x1"] = 39.87, + ["y1"] = 62.95, + ["names"] = { + ["deDE"] = "Vander Hofgut", + ["enUS"] = "Vander Farmstead", + ["esES"] = "Granja Vander", + ["ptBR"] = "Granja Vander", + ["zhCN"] = "范德农场", + }, + }, + [5575] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 4.39, + ["height"] = 7.78, + ["x1"] = 35.63, + ["y1"] = 66.17, + ["names"] = { + ["deDE"] = "Anwesen der Grahans", + ["enUS"] = "Grahan Estate", + ["esES"] = "Mansión Grahan", + ["ptBR"] = "Mansão Grahan", + ["zhCN"] = "格拉汉庄园", + }, + }, + [5576] = { + ["continent"] = 5561, + ["parent"] = 5561, + ["width"] = 5.69, + ["height"] = 9.88, + ["x1"] = 71.11, + ["y1"] = 47.6, + ["names"] = { + ["deDE"] = "Sturmbruchspitze", + ["enUS"] = "Stormbreaker Point", + ["esES"] = "Punto Rompedor de Tormentas", + ["ptBR"] = "Ponto Stormbreaker", + ["zhCN"] = "暴掠角", + }, + }, + [5582] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 9.98, + ["height"] = 6.44, + ["x1"] = 47.01, + ["y1"] = 81.96, + ["names"] = { + ["deDE"] = "Karawanenstraße", + ["enUS"] = "Merchant\'s Highroad", + ["esES"] = "Camino Alto del Mercader", + ["ptBR"] = "Estrada do Comerciante", + ["zhCN"] = "商旅之路", + }, + }, + [5583] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 7.39, + ["height"] = 7.63, + ["x1"] = 46.71, + ["y1"] = 58.61, + ["names"] = { + ["deDE"] = "Bernhain", + ["enUS"] = "Ambershire", + ["esES"] = "Ambershire", + ["ptBR"] = "Ambershire", + ["zhCN"] = "安伯郡", + }, + }, + [5585] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 4.89, + ["height"] = 5.54, + ["x1"] = 32.88, + ["y1"] = 60.55, + ["names"] = { + ["deDE"] = "Bernholz Feste", + ["enUS"] = "Amberwood Keep", + ["esES"] = "Fortaleza Amberwood", + ["ptBR"] = "Fortaleza Amberwood", + ["zhCN"] = "安伯ä¼å¾·åŸŽå ¡", + }, + }, + [5587] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 9.78, + ["height"] = 11.08, + ["x1"] = 76.35, + ["y1"] = 86.38, + ["names"] = { + ["deDE"] = "Kristallfälle", + ["enUS"] = "Crystal Falls", + ["esES"] = "Cascadas Cristalinas", + ["ptBR"] = "Cataratas Cristalinas", + ["zhCN"] = "水晶瀑布", + }, + }, + [5588] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 4.39, + ["height"] = 6.29, + ["x1"] = 56.99, + ["y1"] = 58.83, + ["names"] = { + ["deDE"] = "Weingut Krähenfurt", + ["enUS"] = "Crawford Winery", + ["esES"] = "Bodega Crawford", + ["ptBR"] = "Adega Crawford", + ["zhCN"] = "克劳ç¦å¾·é…’厂", + }, + }, + [5589] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 5.09, + ["height"] = 7.93, + ["x1"] = 38.77, + ["y1"] = 43.34, + ["names"] = { + ["deDE"] = "Holzfällerlager von Nordwind", + ["enUS"] = "Northwind Logging Camp", + ["esES"] = "Campamento de Tala Viento Norte", + ["ptBR"] = "Acampamento Madeireiro de Vento Norte", + ["zhCN"] = "åŒ—é£Žé¢†ä¼æœ¨è¥åœ°", + }, + }, + [5590] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 5.89, + ["height"] = 6.59, + ["x1"] = 32.88, + ["y1"] = 26.05, + ["names"] = { + ["deDE"] = "Hexenzirkel", + ["enUS"] = "Witch Coven", + ["esES"] = "aquelarre de brujas", + ["ptBR"] = "Conciliábulo das Bruxas", + ["zhCN"] = "女巫集会所", + }, + }, + [5591] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 5.29, + ["height"] = 9.13, + ["x1"] = 74, + ["y1"] = 31.06, + ["names"] = { + ["deDE"] = "Ruinen von Birkenhain", + ["enUS"] = "Ruins of Birkhaven", + ["esES"] = "Ruinas de Birkhaven", + ["ptBR"] = "Ruinas de Birkhaven", + ["zhCN"] = "比尔克港的废墟", + }, + }, + [5593] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 13.47, + ["height"] = 30.39, + ["x1"] = 84.18, + ["y1"] = 19.69, + ["names"] = { + ["deDE"] = "Scherwald Steinbruch", + ["enUS"] = "Sherwood Quarry", + ["esES"] = "Cantera Sherwood", + ["ptBR"] = "Pedreira Sherwood", + ["zhCN"] = "èˆä¼å¾·é‡‡çŸ³åœº", + }, + }, + [5594] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 5.49, + ["height"] = 21.26, + ["x1"] = 82.98, + ["y1"] = 47.9, + ["names"] = { + ["deDE"] = "Schwarzfelsbresche", + ["enUS"] = "Blackrock Breach", + ["esES"] = "Brecha Roca Negra", + ["ptBR"] = "Brecha Rocha Negra", + ["zhCN"] = "黑石裂å£", + }, + }, + [5595] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 5.69, + ["height"] = 13.62, + ["x1"] = 67.91, + ["y1"] = 69.24, + ["names"] = { + ["deDE"] = "Grimmensee", + ["enUS"] = "Grimmen Lake", + ["esES"] = "Lago Grimmen", + ["ptBR"] = "Lago Grimmen", + ["zhCN"] = "格里门湖", + }, + }, + [5596] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 8.28, + ["height"] = 9.88, + ["x1"] = 62.23, + ["y1"] = 23.8, + ["names"] = { + ["deDE"] = "Klostergärten", + ["enUS"] = "Abbey Gardens", + ["esES"] = "Jardines de la Abadía", + ["ptBR"] = "Jardins da Abadia", + ["zhCN"] = "ä¿®é“院花园", + }, + }, + [5597] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 6.39, + ["height"] = 8.08, + ["x1"] = 21.26, + ["y1"] = 44.16, + ["names"] = { + ["deDE"] = "Hafen von Stillherz", + ["enUS"] = "Stillheart Port", + ["esES"] = "Puerto Corazón Quieto", + ["ptBR"] = "Porto Stillheart", + ["zhCN"] = "é™å¿ƒæ¸¯", + }, + }, + [5598] = { + ["continent"] = 406, + ["parent"] = 406, + ["width"] = 5.79, + ["height"] = 8.68, + ["x1"] = 49.6, + ["y1"] = 71.56, + ["names"] = { + ["deDE"] = "Irdener Ring", + ["enUS"] = "Earthen Ring", + ["esES"] = "Anillo de Tierra", + ["ptBR"] = "Elo Terroso", + ["zhCN"] = "大地之环", + }, + }, + [5603] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 5.89, + ["height"] = 5.84, + ["x1"] = 53.74, + ["y1"] = 56.81, + ["names"] = { + ["deDE"] = "Dun Kithas", + ["enUS"] = "Dun Kithas", + ["esES"] = "Dun Kithas", + ["ptBR"] = "Dun Kithas", + ["zhCN"] = "丹基塔斯", + }, + }, + [5606] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 19.26, + ["height"] = 18.26, + ["x1"] = 55.14, + ["y1"] = 83.08, + ["names"] = { + ["deDE"] = "Die Finstersenke", + ["enUS"] = "The Grim Hollow", + ["esES"] = "El Abismo Sombrío", + ["ptBR"] = "O Abismo Sombrio", + ["zhCN"] = "冷酷谷", + }, + }, + [5607] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 5.99, + ["height"] = 7.63, + ["x1"] = 54.19, + ["y1"] = 48.13, + ["names"] = { + ["deDE"] = "Kithas See", + ["enUS"] = "Lake Kithas", + ["esES"] = "Lago Kithas", + ["ptBR"] = "Lago Kithas", + ["zhCN"] = "基塔斯湖", + }, + }, + [5608] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 3.99, + ["height"] = 5.54, + ["x1"] = 56.19, + ["y1"] = 70.13, + ["names"] = { + ["deDE"] = "Schieferbarts Schmiede", + ["enUS"] = "Slatebeard\'s Forge", + ["esES"] = "Forja Barbas de Pizarra", + ["ptBR"] = "Forja de Slatebeard", + ["zhCN"] = "岩须的锻炉", + }, + }, + [5609] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 2.59, + ["height"] = 9.58, + ["x1"] = 45.21, + ["y1"] = 56.29, + ["names"] = { + ["deDE"] = "Der Hochpass", + ["enUS"] = "The High Pass", + ["esES"] = "El Paso Alto", + ["ptBR"] = "Passagem Alta", + ["zhCN"] = "高处å°å¾„", + }, + }, + [5611] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 6.49, + ["height"] = 10.03, + ["x1"] = 60.03, + ["y1"] = 66.24, + ["names"] = { + ["deDE"] = "Sal\'Galaz Minen", + ["enUS"] = "Sal\'Galaz Mines", + ["esES"] = "Minas Sal\'Galaz", + ["ptBR"] = "Minas Sal\'Galaz", + ["zhCN"] = "塞尔加拉兹矿井", + }, + }, + [5612] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 3.99, + ["height"] = 5.09, + ["x1"] = 46.91, + ["y1"] = 35.33, + ["names"] = { + ["deDE"] = "Ostgrat Außenposten", + ["enUS"] = "East Ridge Outpost", + ["esES"] = "Puesto de la Cima Este", + ["ptBR"] = "Posto Ferrugem Leste", + ["zhCN"] = "东脊哨站", + }, + }, + [5613] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 5.39, + ["height"] = 4.19, + ["x1"] = 44.51, + ["y1"] = 42.22, + ["names"] = { + ["deDE"] = "Baggoths Bollwerk", + ["enUS"] = "Baggoth\'s Rampart", + ["esES"] = "Murallas de Baggoth", + ["ptBR"] = "Muralha de Baggoth", + ["zhCN"] = "巴格斯农场", + }, + }, + [5614] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 6.09, + ["height"] = 5.24, + ["x1"] = 42.76, + ["y1"] = 17.14, + ["names"] = { + ["deDE"] = "Ruinen der Stolgaz Feste", + ["enUS"] = "Ruins of Stolgaz Keep", + ["esES"] = "Ruinas de la Fortaleza Stolgaz", + ["ptBR"] = "Ruínas da Fortaleza Stolgaz", + ["zhCN"] = "斯托尔加兹è¦å¡žåºŸå¢Ÿ", + }, + }, + [5615] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 4.99, + ["height"] = 7.49, + ["x1"] = 61.78, + ["y1"] = 44.16, + ["names"] = { + ["deDE"] = "Groldans Ausgrabung", + ["enUS"] = "Groldan\'s Excavation", + ["esES"] = "Excavación Groldan", + ["ptBR"] = "Escavação Groldan", + ["zhCN"] = "格罗丹挖掘场", + }, + }, + [5616] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 5.89, + ["height"] = 7.49, + ["x1"] = 54.64, + ["y1"] = 15.42, + ["names"] = { + ["deDE"] = "Zarm\'geth Festung", + ["enUS"] = "Zarm\'geth Stronghold", + ["esES"] = "Bastión Zarm\'geth", + ["ptBR"] = "Bastilha Zarm\'geth", + ["zhCN"] = "扎姆盖斯è¦å¡ž", + }, + }, + [5617] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 3.29, + ["height"] = 4.49, + ["x1"] = 50.05, + ["y1"] = 21.26, + ["names"] = { + ["deDE"] = "Geth\'kar", + ["enUS"] = "Geth\'kar", + ["esES"] = "Geth\'kar", + ["ptBR"] = "Geth\'kar", + ["zhCN"] = "ç›–æ–¯å¡å°”", + }, + }, + [5618] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 2.99, + ["height"] = 4.34, + ["x1"] = 50.4, + ["y1"] = 10.4, + ["names"] = { + ["deDE"] = "Zarm\'geth Spitze", + ["enUS"] = "Zarm\'geth Point", + ["esES"] = "Cima Zarm\'geth", + ["ptBR"] = "Local Zarm\'geth", + ["zhCN"] = "扎姆盖斯角", + }, + }, + [5619] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 5.59, + ["height"] = 5.69, + ["x1"] = 59.48, + ["y1"] = 32.04, + ["names"] = { + ["deDE"] = "Außenposten der Splitterklingen", + ["enUS"] = "Shatterblade Post", + ["esES"] = "Puesto de Cuchilla Rota", + ["ptBR"] = "Posto Quebra Espada", + ["zhCN"] = "岩须港", + }, + }, + [5621] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 3.09, + ["height"] = 5.99, + ["x1"] = 60.73, + ["y1"] = 14.97, + ["names"] = { + ["deDE"] = "Brangars Verlassene Farm", + ["enUS"] = "Brangar\'s Folly", + ["esES"] = "Locura de Brangar", + ["ptBR"] = "Insensatez de Brangar", + ["zhCN"] = "布兰加之愚", + }, + }, + [5622] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 4.89, + ["height"] = 6.29, + ["x1"] = 30.39, + ["y1"] = 42.96, + ["names"] = { + ["deDE"] = "Turm von Magilous", + ["enUS"] = "Tower of Magilou", + ["esES"] = "Torre de Magilou", + ["ptBR"] = "Torres de Magilou", + ["zhCN"] = "玛çªéœ²ä¹‹å¡”", + }, + }, + [5623] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 3.99, + ["height"] = 4.79, + ["x1"] = 66.67, + ["y1"] = 45.36, + ["names"] = { + ["deDE"] = "Grotte der Borstenbärte", + ["enUS"] = "Bristlewhisker Cavern", + ["esES"] = "Cueva Bigotocuerno", + ["ptBR"] = "Caverna Bigotocorno", + ["zhCN"] = "刺须洞穴", + }, + }, + [5625] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 3.89, + ["height"] = 4.79, + ["x1"] = 54.24, + ["y1"] = 32.04, + ["names"] = { + ["deDE"] = "Nordkammspitze", + ["enUS"] = "Northridge Point", + ["esES"] = "Punto de la Cuenca del Norte", + ["ptBR"] = "Ponto de Norte", + ["zhCN"] = "北山岗哨", + }, + }, + [5626] = { + ["continent"] = 5581, + ["parent"] = 5581, + ["width"] = 9.58, + ["height"] = 15.42, + ["x1"] = 83.73, + ["y1"] = 70.43, + ["names"] = { + ["deDE"] = "Aschenfallpass", + ["enUS"] = "Cinderfall Pass", + ["esES"] = "Paso Caído de Cenizas", + ["ptBR"] = "Passagem das Cinzas", + ["zhCN"] = "ç°çƒ¬å°å¾„", + }, + }, + [5630] = { + ["continent"] = 5602, + ["parent"] = 5602, + ["width"] = 3.09, + ["height"] = 5.54, + ["x1"] = 57.44, + ["y1"] = 39.45, + ["names"] = { + ["deDE"] = "Gerstenkamm Hofstätte", + ["enUS"] = "Barleycrest Farmstead", + ["esES"] = "Granja Barleycrest", + ["ptBR"] = "Fazenda Barleycrest", + ["zhCN"] = "麦冠农场", + }, + }, +} diff --git a/data/init.lua b/data/init.lua new file mode 100644 index 0000000..5a5499d --- /dev/null +++ b/data/init.lua @@ -0,0 +1,598 @@ + +if not MTH_DS then + MTH_DS = {} +end + +MTH_DS.version = "1.0" +MTH_DS.loaded = false + +local function MTH_DS_MarkNotInGameRangedWeapons() + if not MTH_DS_AmmoItems then return end + for _, item in pairs(MTH_DS_AmmoItems) do + if type(item) == "table" then + local name = string.lower(tostring(item.name or "")) + if string.find(name, "monster - ", 1, true) == 1 or string.find(name, "90 green", 1, true) == 1 then + local subtype = string.lower(tostring(item.subtype or "")) + if subtype == "bow" or subtype == "bows" + or subtype == "gun" or subtype == "rifle" or subtype == "rifles" + or subtype == "crossbow" or subtype == "crossbows" or subtype == "cross bow" or subtype == "cross-bow" + then + item.notingame = true + end + end + end + end +end + +MTH_DS_MarkNotInGameRangedWeapons() + +local function MTH_DS_EnsurePetSpellsTrainerGrowl() + if not MTH_DS_PetSpells then return end + if type(MTH_DS_PetSpells.allSpells) ~= "table" then MTH_DS_PetSpells.allSpells = {} end + if type(MTH_DS_PetSpells.byAbility) ~= "table" then MTH_DS_PetSpells.byAbility = {} end + if type(MTH_DS_PetSpells.abilitiesDiscovered) ~= "table" then MTH_DS_PetSpells.abilitiesDiscovered = {} end + + for _, row in ipairs(MTH_DS_PetSpells.allSpells) do + if type(row) == "table" and not row.learnMethod then + row.learnMethod = "beast" + end + end + + local hasGrowlAbility = false + local hasGreatStaminaAbility = false + local hasNaturalArmorAbility = false + local hasFireResistanceAbility = false + local hasFrostResistanceAbility = false + local hasArcaneResistanceAbility = false + local hasNatureResistanceAbility = false + local hasShadowResistanceAbility = false + for i = 1, table.getn(MTH_DS_PetSpells.abilitiesDiscovered) do + if MTH_DS_PetSpells.abilitiesDiscovered[i] == "Growl" then + hasGrowlAbility = true + elseif MTH_DS_PetSpells.abilitiesDiscovered[i] == "Great Stamina" then + hasGreatStaminaAbility = true + elseif MTH_DS_PetSpells.abilitiesDiscovered[i] == "Natural Armor" then + hasNaturalArmorAbility = true + elseif MTH_DS_PetSpells.abilitiesDiscovered[i] == "Fire Resistance" then + hasFireResistanceAbility = true + elseif MTH_DS_PetSpells.abilitiesDiscovered[i] == "Frost Resistance" then + hasFrostResistanceAbility = true + elseif MTH_DS_PetSpells.abilitiesDiscovered[i] == "Arcane Resistance" then + hasArcaneResistanceAbility = true + elseif MTH_DS_PetSpells.abilitiesDiscovered[i] == "Nature Resistance" then + hasNatureResistanceAbility = true + elseif MTH_DS_PetSpells.abilitiesDiscovered[i] == "Shadow Resistance" then + hasShadowResistanceAbility = true + end + if hasGrowlAbility and hasGreatStaminaAbility and hasNaturalArmorAbility and hasFireResistanceAbility and hasFrostResistanceAbility and hasArcaneResistanceAbility and hasNatureResistanceAbility and hasShadowResistanceAbility then break end + end + if not hasGrowlAbility then + table.insert(MTH_DS_PetSpells.abilitiesDiscovered, "Growl") + end + if not hasGreatStaminaAbility then + table.insert(MTH_DS_PetSpells.abilitiesDiscovered, "Great Stamina") + end + if not hasNaturalArmorAbility then + table.insert(MTH_DS_PetSpells.abilitiesDiscovered, "Natural Armor") + end + if not hasFireResistanceAbility then + table.insert(MTH_DS_PetSpells.abilitiesDiscovered, "Fire Resistance") + end + if not hasFrostResistanceAbility then + table.insert(MTH_DS_PetSpells.abilitiesDiscovered, "Frost Resistance") + end + if not hasArcaneResistanceAbility then + table.insert(MTH_DS_PetSpells.abilitiesDiscovered, "Arcane Resistance") + end + if not hasNatureResistanceAbility then + table.insert(MTH_DS_PetSpells.abilitiesDiscovered, "Nature Resistance") + end + if not hasShadowResistanceAbility then + table.insert(MTH_DS_PetSpells.abilitiesDiscovered, "Shadow Resistance") + end + table.sort(MTH_DS_PetSpells.abilitiesDiscovered) + + local growlRows = { + { id = 2649, rank = "Rank 1", rankNumber = 1, trainLevel = 1, effect = "(63) Threat Value: 50" }, + { id = 14916, rank = "Rank 2", rankNumber = 2, trainLevel = 10, effect = "(63) Threat Value: 65" }, + { id = 14917, rank = "Rank 3", rankNumber = 3, trainLevel = 20, effect = "(63) Threat Value: 110" }, + { id = 14918, rank = "Rank 4", rankNumber = 4, trainLevel = 30, effect = "(63) Threat Value: 170" }, + { id = 14919, rank = "Rank 5", rankNumber = 5, trainLevel = 40, effect = "(63) Threat Value: 240" }, + { id = 14920, rank = "Rank 6", rankNumber = 6, trainLevel = 50, effect = "(63) Threat Value: 320" }, + { id = 14921, rank = "Rank 7", rankNumber = 7, trainLevel = 60, effect = "(63) Threat Value: 415" }, + } + + local allById = {} + for i, row in ipairs(MTH_DS_PetSpells.allSpells) do + if type(row) == "table" and row.id then + allById[row.id] = i + end + end + + local growlSpells = {} + for i = 1, table.getn(growlRows) do + local src = growlRows[i] + local row = { + ability = "Growl", + castTime = "Instant", + categoryCooldown = "5 seconds", + cooldown = "n/a", + cost = "10 mana", + description = "Taunt the target, increasing the likelihood the creature will focus attacks on you.", + effects = { src.effect }, + icon = "Ability_Physical_Taunt", + id = src.id, + learnMethod = "trainer", + name = "Growl", + range = "5 yards (Combat Range)", + rank = src.rank, + rankNumber = src.rankNumber, + school = "Physical", + sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id), + trainLevel = src.trainLevel, + } + table.insert(growlSpells, row) + local idx = allById[src.id] + if idx then + MTH_DS_PetSpells.allSpells[idx] = row + else + table.insert(MTH_DS_PetSpells.allSpells, row) + end + end + + local staminaRows = { + { id = 4187, rank = "Rank 1", rankNumber = 1, trainLevel = 10, description = "Stamina increased by 3.", effect = "(6) Apply Aura #29: Mod Stat (2) Value: 3" }, + { id = 4188, rank = "Rank 2", rankNumber = 2, trainLevel = 12, description = "Stamina increased by 5.", effect = "(6) Apply Aura #29: Mod Stat (2) Value: 5" }, + { id = 4189, rank = "Rank 3", rankNumber = 3, trainLevel = 18, description = "Stamina increased by 7.", effect = "(6) Apply Aura #29: Mod Stat (2) Value: 7" }, + { id = 4190, rank = "Rank 4", rankNumber = 4, trainLevel = 24, description = "Stamina increased by 10.", effect = "(6) Apply Aura #29: Mod Stat (2) Value: 10" }, + { id = 4191, rank = "Rank 5", rankNumber = 5, trainLevel = 30, description = "Stamina increased by 13.", effect = "(6) Apply Aura #29: Mod Stat (2) Value: 13" }, + { id = 4192, rank = "Rank 6", rankNumber = 6, trainLevel = 36, description = "Stamina increased by 17.", effect = "(6) Apply Aura #29: Mod Stat (2) Value: 17" }, + { id = 4193, rank = "Rank 7", rankNumber = 7, trainLevel = 42, description = "Stamina increased by 21.", effect = "(6) Apply Aura #29: Mod Stat (2) Value: 21" }, + { id = 4194, rank = "Rank 8", rankNumber = 8, trainLevel = 48, description = "Stamina increased by 26.", effect = "(6) Apply Aura #29: Mod Stat (2) Value: 26" }, + { id = 5048, rank = "Rank 9", rankNumber = 9, trainLevel = 54, description = "Stamina increased by 32.", effect = "(36) Learn Spell Great Stamina" }, + { id = 5049, rank = "Rank 10", rankNumber = 10, trainLevel = 60, description = "Stamina increased by 40.", effect = "(36) Learn Spell Great Stamina" }, + } + + for i, row in ipairs(MTH_DS_PetSpells.allSpells) do + if type(row) == "table" and row.id then + allById[row.id] = i + end + end + + local staminaSpells = {} + for i = 1, table.getn(staminaRows) do + local src = staminaRows[i] + local row = { + ability = "Great Stamina", + castTime = "Passive", + categoryCooldown = "n/a", + cooldown = "n/a", + cost = "None", + description = src.description, + effects = { src.effect }, + icon = "Spell_Nature_UnyeildingStamina", + id = src.id, + learnMethod = "trainer", + name = "Great Stamina", + range = "0 yards (Self Only)", + rank = src.rank, + rankNumber = src.rankNumber, + school = "Physical", + sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id), + trainLevel = src.trainLevel, + } + table.insert(staminaSpells, row) + local idx = allById[src.id] + if idx then + MTH_DS_PetSpells.allSpells[idx] = row + else + table.insert(MTH_DS_PetSpells.allSpells, row) + end + end + + for i, row in ipairs(MTH_DS_PetSpells.allSpells) do + if type(row) == "table" and row.id then + allById[row.id] = i + end + end + + local armorRows = { + { id = 24545, rank = "Rank 1", rankNumber = 1, trainLevel = 10, description = "Armor increased by 50.", effect = "(6) Apply Aura #22: Mod Resistance (1) Value: 50" }, + { id = 24549, rank = "Rank 2", rankNumber = 2, trainLevel = 12, description = "Armor increased by 100.", effect = "(6) Apply Aura #22: Mod Resistance (1) Value: 100" }, + { id = 24550, rank = "Rank 3", rankNumber = 3, trainLevel = 18, description = "Armor increased by 160.", effect = "(6) Apply Aura #22: Mod Resistance (1) Value: 160" }, + { id = 24551, rank = "Rank 4", rankNumber = 4, trainLevel = 24, description = "Armor increased by 240.", effect = "(6) Apply Aura #22: Mod Resistance (1) Value: 240" }, + { id = 24552, rank = "Rank 5", rankNumber = 5, trainLevel = 30, description = "Armor increased by 330.", effect = "(6) Apply Aura #22: Mod Resistance (1) Value: 330" }, + { id = 24553, rank = "Rank 6", rankNumber = 6, trainLevel = 36, description = "Armor increased by 430.", effect = "(6) Apply Aura #22: Mod Resistance (1) Value: 430" }, + { id = 24554, rank = "Rank 7", rankNumber = 7, trainLevel = 42, description = "Armor increased by 550.", effect = "(6) Apply Aura #22: Mod Resistance (1) Value: 550" }, + { id = 24555, rank = "Rank 8", rankNumber = 8, trainLevel = 48, description = "Armor increased by 675.", effect = "(6) Apply Aura #22: Mod Resistance (1) Value: 675" }, + { id = 24629, rank = "Rank 9", rankNumber = 9, trainLevel = 54, description = "Armor increased by 810.", effect = "(6) Apply Aura #22: Mod Resistance (1) Value: 810" }, + { id = 24630, rank = "Rank 10", rankNumber = 10, trainLevel = 60, description = "Armor increased by 1000.", effect = "(6) Apply Aura #22: Mod Resistance (1) Value: 1000" }, + } + + local armorSpells = {} + for i = 1, table.getn(armorRows) do + local src = armorRows[i] + local row = { + ability = "Natural Armor", + castTime = "Passive", + categoryCooldown = "n/a", + cooldown = "n/a", + cost = "None", + description = src.description, + effects = { src.effect }, + icon = "Spell_Nature_SpiritArmor", + id = src.id, + learnMethod = "trainer", + name = "Natural Armor", + range = "0 yards (Self Only)", + rank = src.rank, + rankNumber = src.rankNumber, + school = "Physical", + sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id), + trainLevel = src.trainLevel, + } + table.insert(armorSpells, row) + local idx = allById[src.id] + if idx then + MTH_DS_PetSpells.allSpells[idx] = row + else + table.insert(MTH_DS_PetSpells.allSpells, row) + end + end + + for i, row in ipairs(MTH_DS_PetSpells.allSpells) do + if type(row) == "table" and row.id then + allById[row.id] = i + end + end + + local fireResRows = { + { id = 23992, rank = "Rank 1", rankNumber = 1, trainLevel = 20, description = "Increases Fire resistance by 30.", effect = "(6) Apply Aura #22: Mod Resistance (4) Value: 30" }, + { id = 24439, rank = "Rank 2", rankNumber = 2, trainLevel = 30, description = "Increases Fire resistance by 60.", effect = "(6) Apply Aura #22: Mod Resistance (4) Value: 60" }, + { id = 24444, rank = "Rank 3", rankNumber = 3, trainLevel = 40, description = "Increases Fire resistance by 90.", effect = "(6) Apply Aura #22: Mod Resistance (4) Value: 90" }, + { id = 24445, rank = "Rank 4", rankNumber = 4, trainLevel = 50, description = "Increases Fire resistance by 120.", effect = "(6) Apply Aura #22: Mod Resistance (4) Value: 120" }, + } + + local fireResSpells = {} + for i = 1, table.getn(fireResRows) do + local src = fireResRows[i] + local row = { + ability = "Fire Resistance", + castTime = "Passive", + categoryCooldown = "n/a", + cooldown = "n/a", + cost = "None", + description = src.description, + effects = { src.effect }, + icon = "Spell_Fire_FireArmor", + id = src.id, + learnMethod = "trainer", + name = "Fire Resistance", + range = "0 yards (Self Only)", + rank = src.rank, + rankNumber = src.rankNumber, + school = "Physical", + sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id), + trainLevel = src.trainLevel, + } + table.insert(fireResSpells, row) + local idx = allById[src.id] + if idx then + MTH_DS_PetSpells.allSpells[idx] = row + else + table.insert(MTH_DS_PetSpells.allSpells, row) + end + end + + for i, row in ipairs(MTH_DS_PetSpells.allSpells) do + if type(row) == "table" and row.id then + allById[row.id] = i + end + end + + local frostResRows = { + { id = 24446, rank = "Rank 1", rankNumber = 1, trainLevel = 20, description = "Increases Frost resistance by 30.", effect = "(6) Apply Aura #22: Mod Resistance (16) Value: 30" }, + { id = 24447, rank = "Rank 2", rankNumber = 2, trainLevel = 30, description = "Increases Frost resistance by 60.", effect = "(6) Apply Aura #22: Mod Resistance (16) Value: 60" }, + { id = 24448, rank = "Rank 3", rankNumber = 3, trainLevel = 40, description = "Increases Frost resistance by 90.", effect = "(6) Apply Aura #22: Mod Resistance (16) Value: 90" }, + { id = 24449, rank = "Rank 4", rankNumber = 4, trainLevel = 50, description = "Increases Frost resistance by 120.", effect = "(6) Apply Aura #22: Mod Resistance (16) Value: 120" }, + } + + local frostResSpells = {} + for i = 1, table.getn(frostResRows) do + local src = frostResRows[i] + local row = { + ability = "Frost Resistance", + castTime = "Passive", + categoryCooldown = "n/a", + cooldown = "n/a", + cost = "None", + description = src.description, + effects = { src.effect }, + icon = "Spell_Frost_FrostWard", + id = src.id, + learnMethod = "trainer", + name = "Frost Resistance", + range = "0 yards (Self Only)", + rank = src.rank, + rankNumber = src.rankNumber, + school = "Physical", + sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id), + trainLevel = src.trainLevel, + } + table.insert(frostResSpells, row) + local idx = allById[src.id] + if idx then + MTH_DS_PetSpells.allSpells[idx] = row + else + table.insert(MTH_DS_PetSpells.allSpells, row) + end + end + + for i, row in ipairs(MTH_DS_PetSpells.allSpells) do + if type(row) == "table" and row.id then + allById[row.id] = i + end + end + + local arcaneResRows = { + { id = 24493, rank = "Rank 1", rankNumber = 1, trainLevel = 20, description = "Increases Arcane resistance by 30.", effect = "(6) Apply Aura #22: Mod Resistance (64) Value: 30" }, + { id = 24497, rank = "Rank 2", rankNumber = 2, trainLevel = 30, description = "Increases Arcane resistance by 60.", effect = "(6) Apply Aura #22: Mod Resistance (64) Value: 60" }, + { id = 24500, rank = "Rank 3", rankNumber = 3, trainLevel = 40, description = "Increases Arcane resistance by 90.", effect = "(6) Apply Aura #22: Mod Resistance (64) Value: 90" }, + { id = 24501, rank = "Rank 4", rankNumber = 4, trainLevel = 50, description = "Increases Arcane resistance by 120.", effect = "(6) Apply Aura #22: Mod Resistance (64) Value: 120" }, + } + + local arcaneResSpells = {} + for i = 1, table.getn(arcaneResRows) do + local src = arcaneResRows[i] + local row = { + ability = "Arcane Resistance", + castTime = "Passive", + categoryCooldown = "n/a", + cooldown = "n/a", + cost = "None", + description = src.description, + effects = { src.effect }, + icon = "Spell_Nature_StarFall", + id = src.id, + learnMethod = "trainer", + name = "Arcane Resistance", + range = "0 yards (Self Only)", + rank = src.rank, + rankNumber = src.rankNumber, + school = "Physical", + sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id), + trainLevel = src.trainLevel, + } + table.insert(arcaneResSpells, row) + local idx = allById[src.id] + if idx then + MTH_DS_PetSpells.allSpells[idx] = row + else + table.insert(MTH_DS_PetSpells.allSpells, row) + end + end + + for i, row in ipairs(MTH_DS_PetSpells.allSpells) do + if type(row) == "table" and row.id then + allById[row.id] = i + end + end + + local natureResRows = { + { id = 24492, rank = "Rank 1", rankNumber = 1, trainLevel = 20, description = "Increases Nature resistance by 30.", effect = "(6) Apply Aura #22: Mod Resistance (8) Value: 30" }, + { id = 24502, rank = "Rank 2", rankNumber = 2, trainLevel = 30, description = "Increases Nature resistance by 60.", effect = "(6) Apply Aura #22: Mod Resistance (8) Value: 60" }, + { id = 24503, rank = "Rank 3", rankNumber = 3, trainLevel = 40, description = "Increases Nature resistance by 90.", effect = "(6) Apply Aura #22: Mod Resistance (8) Value: 90" }, + { id = 24504, rank = "Rank 4", rankNumber = 4, trainLevel = 50, description = "Increases Nature resistance by 120.", effect = "(6) Apply Aura #22: Mod Resistance (8) Value: 120" }, + } + + local natureResSpells = {} + for i = 1, table.getn(natureResRows) do + local src = natureResRows[i] + local row = { + ability = "Nature Resistance", + castTime = "Passive", + categoryCooldown = "n/a", + cooldown = "n/a", + cost = "None", + description = src.description, + effects = { src.effect }, + icon = "Spell_Nature_ResistNature", + id = src.id, + learnMethod = "trainer", + name = "Nature Resistance", + range = "0 yards (Self Only)", + rank = src.rank, + rankNumber = src.rankNumber, + school = "Physical", + sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id), + trainLevel = src.trainLevel, + } + table.insert(natureResSpells, row) + local idx = allById[src.id] + if idx then + MTH_DS_PetSpells.allSpells[idx] = row + else + table.insert(MTH_DS_PetSpells.allSpells, row) + end + end + + for i, row in ipairs(MTH_DS_PetSpells.allSpells) do + if type(row) == "table" and row.id then + allById[row.id] = i + end + end + + local shadowResRows = { + { id = 24488, rank = "Rank 1", rankNumber = 1, trainLevel = 20, description = "Increases Shadow resistance by 30.", effect = "(6) Apply Aura #22: Mod Resistance (32) Value: 30" }, + { id = 24505, rank = "Rank 2", rankNumber = 2, trainLevel = 30, description = "Increases Shadow resistance by 60.", effect = "(6) Apply Aura #22: Mod Resistance (32) Value: 60" }, + { id = 24506, rank = "Rank 3", rankNumber = 3, trainLevel = 40, description = "Increases Shadow resistance by 90.", effect = "(6) Apply Aura #22: Mod Resistance (32) Value: 90" }, + { id = 24507, rank = "Rank 4", rankNumber = 4, trainLevel = 50, description = "Increases Shadow resistance by 120.", effect = "(6) Apply Aura #22: Mod Resistance (32) Value: 120" }, + } + + local shadowResSpells = {} + for i = 1, table.getn(shadowResRows) do + local src = shadowResRows[i] + local row = { + ability = "Shadow Resistance", + castTime = "Passive", + categoryCooldown = "n/a", + cooldown = "n/a", + cost = "None", + description = src.description, + effects = { src.effect }, + icon = "Spell_Shadow_AntiShadow", + id = src.id, + learnMethod = "trainer", + name = "Shadow Resistance", + range = "0 yards (Self Only)", + rank = src.rank, + rankNumber = src.rankNumber, + school = "Physical", + sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id), + trainLevel = src.trainLevel, + } + table.insert(shadowResSpells, row) + local idx = allById[src.id] + if idx then + MTH_DS_PetSpells.allSpells[idx] = row + else + table.insert(MTH_DS_PetSpells.allSpells, row) + end + end + + table.sort(MTH_DS_PetSpells.allSpells, function(a, b) + local aa = (a and a.ability) or "" + local bb = (b and b.ability) or "" + if aa ~= bb then return aa < bb end + local ar = (a and a.rankNumber) or 0 + local br = (b and b.rankNumber) or 0 + if ar ~= br then return ar < br end + return ((a and a.id) or 0) < ((b and b.id) or 0) + end) + + MTH_DS_PetSpells.byAbility["Growl"] = { + ability = "Growl", + searchUrl = "https://database.turtlecraft.gg/?search=Growl", + spellCount = table.getn(growlSpells), + spells = growlSpells, + } + + MTH_DS_PetSpells.byAbility["Great Stamina"] = { + ability = "Great Stamina", + searchUrl = "https://database.turtlecraft.gg/?search=Great+Stamina", + spellCount = table.getn(staminaSpells), + spells = staminaSpells, + } + + MTH_DS_PetSpells.byAbility["Natural Armor"] = { + ability = "Natural Armor", + searchUrl = "https://database.turtlecraft.gg/?search=Natural+Armor", + spellCount = table.getn(armorSpells), + spells = armorSpells, + } + + MTH_DS_PetSpells.byAbility["Fire Resistance"] = { + ability = "Fire Resistance", + searchUrl = "https://database.turtlecraft.gg/?search=Fire+Resistance", + spellCount = table.getn(fireResSpells), + spells = fireResSpells, + } + + MTH_DS_PetSpells.byAbility["Frost Resistance"] = { + ability = "Frost Resistance", + searchUrl = "https://database.turtlecraft.gg/?search=Frost+Resistance", + spellCount = table.getn(frostResSpells), + spells = frostResSpells, + } + + MTH_DS_PetSpells.byAbility["Arcane Resistance"] = { + ability = "Arcane Resistance", + searchUrl = "https://database.turtlecraft.gg/?search=Arcane+Resistance", + spellCount = table.getn(arcaneResSpells), + spells = arcaneResSpells, + } + + MTH_DS_PetSpells.byAbility["Nature Resistance"] = { + ability = "Nature Resistance", + searchUrl = "https://database.turtlecraft.gg/?search=Nature+Resistance", + spellCount = table.getn(natureResSpells), + spells = natureResSpells, + } + + MTH_DS_PetSpells.byAbility["Shadow Resistance"] = { + ability = "Shadow Resistance", + searchUrl = "https://database.turtlecraft.gg/?search=Shadow+Resistance", + spellCount = table.getn(shadowResSpells), + spells = shadowResSpells, + } + + MTH_DS_PetSpells.totalSpellRows = table.getn(MTH_DS_PetSpells.allSpells) +end + +function MTH_DS_OnLoad() + if MTH_DS.loaded then + return + end + + -- Database files loaded via TOC + -- Check if all tables are present + + if MTH_DS_Beasts and MTH_DS_Vendors and MTH_DS_AmmoItems and MTH_DS_Zones and MTH_DS_Families then + MTH_DS_EnsurePetSpellsTrainerGrowl() + MTH_DS.loaded = true + + -- Count entries + MTH_DS.beastCount = 0 + for _ in pairs(MTH_DS_Beasts) do + MTH_DS.beastCount = MTH_DS.beastCount + 1 + end + + MTH_DS.vendorCount = 0 + for _ in pairs(MTH_DS_Vendors) do + MTH_DS.vendorCount = MTH_DS.vendorCount + 1 + end + + MTH_DS.ammoItemCount = 0 + for _ in pairs(MTH_DS_AmmoItems) do + MTH_DS.ammoItemCount = MTH_DS.ammoItemCount + 1 + end + + MTH_DS.zoneCount = 0 + for _ in pairs(MTH_DS_Zones) do + MTH_DS.zoneCount = MTH_DS.zoneCount + 1 + end + + MTH_DS.familyCount = 0 + for _ in pairs(MTH_DS_Families) do + MTH_DS.familyCount = MTH_DS.familyCount + 1 + end + + MTH_DS.petSpellRowCount = 0 + if MTH_DS_PetSpells and MTH_DS_PetSpells.allSpells then + for _ in pairs(MTH_DS_PetSpells.allSpells) do + MTH_DS.petSpellRowCount = MTH_DS.petSpellRowCount + 1 + end + end + + -- Delayed message to ensure chat frame is ready + local frame = CreateFrame("Frame") + frame:RegisterEvent("PLAYER_ENTERING_WORLD") + frame:SetScript("OnEvent", function() + this:UnregisterAllEvents() + end) + else + local missing = {} + if not MTH_DS_Beasts then table.insert(missing, "MTH_DS_Beasts") end + if not MTH_DS_Vendors then table.insert(missing, "MTH_DS_Vendors") end + if not MTH_DS_AmmoItems then table.insert(missing, "MTH_DS_AmmoItems") end + if not MTH_DS_Zones then table.insert(missing, "MTH_DS_Zones") end + if not MTH_DS_Families then table.insert(missing, "MTH_DS_Families") end + local msg = "|cFFFF3333[MTH] ERROR:|r Database failed to load! Missing: " .. table.concat(missing, ", ") + local frame = CreateFrame("Frame") + frame:RegisterEvent("PLAYER_ENTERING_WORLD") + frame:SetScript("OnEvent", function() + this:UnregisterAllEvents() + MTH:Print(msg, "error") + end) + end +end diff --git a/img/node.tga b/img/node.tga new file mode 100644 index 0000000000000000000000000000000000000000..540f7f42fc42460dbc77f3938e0464e8be412c7d GIT binary patch literal 4140 zcmc(i%}&EG49D9_laMC$yxn%$Bo1)hai@VQA&~eIr=EC*Jsaok5%wbR1jRpyhgzkL zJ6fg4OKLm*JGSG7B*_we7K!{_=$mCc9*+~DTxce=5L!B74iDMHV#}%{_EKjTtd)__ z#h28IExKrjj*YLWU3M8gl`5@XV20?6Su;Y~^blSR=epO@STkH`EvS#949L zMs(0+jR7C*VC_-k*d*U=xXMQdUDg=z!H$g|JN~ijFZTJP2ZYYaYOMYvwMq{R)*wFO zgZ3e6yh-QuUxy#efyQX0?@!e4uZuyn`v>?u664@m)I9uS^xnT5e$@M?^C8;(8+;y# zEpZq!7uY)8_kK~rzp`eCnylS*!>Dx)7Yq7>sYi((y%b&6^xktFz5XWl=+)RIo_sgK dftbTXcGmyj&yO$L*W#&oxW9V4Dekt{$v2wTM9u&J literal 0 HcmV?d00001 diff --git a/init/api.xml b/init/api.xml new file mode 100644 index 0000000..c6fa655 --- /dev/null +++ b/init/api.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/init/compat.xml b/init/compat.xml new file mode 100644 index 0000000..314f140 --- /dev/null +++ b/init/compat.xml @@ -0,0 +1,4 @@ + + + + diff --git a/init/env.xml b/init/env.xml new file mode 100644 index 0000000..b2363b9 --- /dev/null +++ b/init/env.xml @@ -0,0 +1,4 @@ + + + + diff --git a/init/libs.xml b/init/libs.xml new file mode 100644 index 0000000..2161052 --- /dev/null +++ b/init/libs.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/init/modules.xml b/init/modules.xml new file mode 100644 index 0000000..2ecfd0f --- /dev/null +++ b/init/modules.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/init/skins.xml b/init/skins.xml new file mode 100644 index 0000000..227d434 --- /dev/null +++ b/init/skins.xml @@ -0,0 +1,4 @@ + + + + diff --git a/modules/autobuy/engine.lua b/modules/autobuy/engine.lua new file mode 100644 index 0000000..3f3c74b --- /dev/null +++ b/modules/autobuy/engine.lua @@ -0,0 +1,1259 @@ +------------------------------------------------------ +-- MetaHunt: AutoBuy Engine +-- Foundation for vendor auto-buy logic (food + ammo) +------------------------------------------------------ + +MTH_AutoBuyEngine = MTH_AutoBuyEngine or {} + +local Engine = MTH_AutoBuyEngine +local AB_AMMO_BY_NAME = nil +local AB_FOOD_DIET_BY_ITEM_ID = nil + +local function AB_ParseItemIdFromLink(link) + if type(link) ~= "string" then + return nil + end + local _, _, itemId = string.find(link, "item:(%d+)") + if not itemId then + return nil + end + return tonumber(itemId) or itemId +end + +local function AB_GetAmmoSubtypeByItemId(itemId) + if not itemId then + return nil + end + local row = MTH_DS_AmmoItems and MTH_DS_AmmoItems[itemId] or nil + if type(row) ~= "table" then + row = MTH_DS_AmmoItems and MTH_DS_AmmoItems[tonumber(itemId) or -1] or nil + end + if type(row) ~= "table" then + return nil + end + local subtype = string.lower(tostring(row.subtype or "")) + if subtype == "arrow" or subtype == "bullet" then + return subtype + end + return nil +end + +local function AB_GetAmmoLookupByName() + if type(AB_AMMO_BY_NAME) == "table" then + return AB_AMMO_BY_NAME + end + AB_AMMO_BY_NAME = {} + local data = MTH_DS_AmmoItems or {} + for id, row in pairs(data) do + if type(row) == "table" then + local nameKey = string.lower(tostring(row.name or "")) + local subtype = string.lower(tostring(row.subtype or "")) + if nameKey ~= "" and (subtype == "arrow" or subtype == "bullet") then + if type(AB_AMMO_BY_NAME[nameKey]) ~= "table" then + AB_AMMO_BY_NAME[nameKey] = { + itemId = tonumber(id) or id, + subtype = subtype, + } + end + end + end + end + return AB_AMMO_BY_NAME +end + +local function AB_NormalizeDietToken(token) + local value = string.lower(tostring(token or "")) + value = string.gsub(value, "^%s+", "") + value = string.gsub(value, "%s+$", "") + return value +end + +local function AB_GetFoodDietMap() + if type(AB_FOOD_DIET_BY_ITEM_ID) == "table" then + return AB_FOOD_DIET_BY_ITEM_ID + end + + AB_FOOD_DIET_BY_ITEM_ID = {} + if type(FOM_Foods) ~= "table" then + return AB_FOOD_DIET_BY_ITEM_ID + end + + for dietName, itemIds in pairs(FOM_Foods) do + local normalizedDiet = AB_NormalizeDietToken(dietName) + if normalizedDiet ~= "" and type(itemIds) == "table" then + for _, itemId in ipairs(itemIds) do + local numericId = tonumber(itemId) + if numericId then + AB_FOOD_DIET_BY_ITEM_ID[numericId] = normalizedDiet + end + end + end + end + + return AB_FOOD_DIET_BY_ITEM_ID +end + +local function AB_GetItemFoodLevel(itemId) + if itemId and type(MTH_FEED_GetFoodLevel) == "function" then + local ok, level = pcall(MTH_FEED_GetFoodLevel, itemId) + local numeric = ok and tonumber(level) or nil + if numeric and numeric > 0 then + return numeric + end + end + if not itemId or type(GetItemInfo) ~= "function" then + return 1 + end + local _, _, _, itemLevel, minLevel = GetItemInfo(itemId) + local resolved = tonumber(minLevel) or tonumber(itemLevel) or 1 + if resolved < 1 then resolved = 1 end + return resolved +end + +local function AB_GetItemMaxStack(itemId, fallbackStack) + local fallback = math.floor(tonumber(fallbackStack) or 1) + if fallback < 1 then + fallback = 1 + end + + local numericId = tonumber(itemId) + if not numericId or type(GetItemInfo) ~= "function" then + if fallback < 20 then + return 20 + end + return fallback + end + + local _, _, _, _, _, _, _, maxStack = GetItemInfo(numericId) + if (not maxStack or tonumber(maxStack) == nil or tonumber(maxStack) < 1) and type(MTH_PrimeItemCache) == "function" then + pcall(MTH_PrimeItemCache, numericId) + _, _, _, _, _, _, _, maxStack = GetItemInfo(numericId) + end + maxStack = math.floor(tonumber(maxStack) or 0) + if maxStack < 1 then + if fallback < 20 then + return 20 + end + return fallback + end + + return maxStack +end + +local function AB_IsFoodExceptionRejected(itemId) + if not itemId then + return false + end + if type(MTH_FEED_IsExceptionReject) ~= "function" then + return false + end + local ok, rejected = pcall(MTH_FEED_IsExceptionReject, itemId) + return ok and rejected and true or false +end + +local function AB_IsFoodCompatibleByRules(petLevel, foodLevel) + local numericPetLevel = tonumber(petLevel) or 1 + local numericFoodLevel = tonumber(foodLevel) or 0 + if numericPetLevel < 1 then + numericPetLevel = 1 + end + + if type(MTH_FEED_GetFoodCompatibilityWindow) == "function" then + local ok, minAcceptedLevel, maxAcceptedLevel = pcall(MTH_FEED_GetFoodCompatibilityWindow, numericPetLevel) + if ok then + local minLevel = tonumber(minAcceptedLevel) + local maxLevel = tonumber(maxAcceptedLevel) + if minLevel and numericFoodLevel < minLevel then + return false + end + if maxLevel and numericFoodLevel > maxLevel then + return false + end + return true + end + end + + if type(MTH_FEED_IsFoodCompatible) == "function" then + local ok, isCompatible = pcall(MTH_FEED_IsFoodCompatible, petLevel, foodLevel) + if ok then + return isCompatible and true or false + end + end + local minAcceptedLevel = numericPetLevel - 15 + if minAcceptedLevel < 1 then + minAcceptedLevel = 1 + end + return numericFoodLevel >= minAcceptedLevel and numericFoodLevel <= numericPetLevel +end + +local function AB_NormalizeFamilyToken(text) + local value = string.lower(tostring(text or "")) + value = string.gsub(value, "%b()", " ") + value = string.gsub(value, "[^%a]", "") + return value +end + +local function AB_SingularizeFamilyToken(token) + local value = tostring(token or "") + if string.len(value) <= 3 then + return value + end + if string.sub(value, -3) == "ves" and string.len(value) > 4 then + return string.sub(value, 1, -4) .. "f" + end + if string.sub(value, -3) == "ies" and string.len(value) > 4 then + return string.sub(value, 1, -4) .. "y" + end + if string.sub(value, -3) == "xes" and string.len(value) > 4 then + return string.sub(value, 1, -3) + end + if string.sub(value, -4) == "ches" and string.len(value) > 5 then + return string.sub(value, 1, -3) + end + if string.sub(value, -4) == "shes" and string.len(value) > 5 then + return string.sub(value, 1, -3) + end + if string.sub(value, -3) == "zes" and string.len(value) > 4 then + return string.sub(value, 1, -3) + end + if string.sub(value, -3) == "ses" then + return string.sub(value, 1, -3) + end + if string.sub(value, -1) == "s" then + return string.sub(value, 1, -2) + end + return value +end + +local function AB_FindFamilyDietRow(familyName) + if type(MTH_DS_Families) ~= "table" then + return nil, nil + end + + local needle = AB_NormalizeFamilyToken(familyName) + if needle == "" then + return nil, nil + end + local needleSingular = AB_SingularizeFamilyToken(needle) + + for key, row in pairs(MTH_DS_Families) do + local candidate = AB_NormalizeFamilyToken(key) + if candidate ~= "" then + local candidateSingular = AB_SingularizeFamilyToken(candidate) + if candidate == needle or candidate == needleSingular or candidateSingular == needle or candidateSingular == needleSingular then + return row, tostring(key) + end + end + end + + return nil, nil +end + +local function AB_GetMerchantFoodsSnapshot(merchantSnapshot) + if type(MTH_GetMerchantFoodsByDiet) == "function" then + local ok, result = pcall(MTH_GetMerchantFoodsByDiet) + if ok and type(result) == "table" then + return result + end + end + + local dietMap = AB_GetFoodDietMap() + local result = { + byDiet = {}, + items = {}, + unknown = {}, + } + + if type(merchantSnapshot) ~= "table" or type(merchantSnapshot.items) ~= "table" then + return result + end + + for i = 1, table.getn(merchantSnapshot.items) do + local row = merchantSnapshot.items[i] + if type(row) == "table" and row.itemId then + local numericId = tonumber(row.itemId) + local diet = numericId and dietMap[numericId] or nil + local foodRow = { + index = row.index, + id = numericId, + name = row.name, + link = row.link, + diet = diet, + price = row.price, + stack = row.stackCount, + available = row.numAvailable, + } + if diet then + if type(result.byDiet[diet]) ~= "table" then + result.byDiet[diet] = {} + end + table.insert(result.byDiet[diet], foodRow) + table.insert(result.items, foodRow) + else + table.insert(result.unknown, foodRow) + end + end + end + + return result +end + +local function AB_CollectPetRows(scope) + if type(MTH_PETS_RefreshCurrentPet) == "function" then + pcall(MTH_PETS_RefreshCurrentPet) + end + + local rows = {} + local petsRoot = type(MTH_PETS_GetRootStore) == "function" and MTH_PETS_GetRootStore() or nil + local petStore = petsRoot and petsRoot.petStore or nil + local activeById = petStore and petStore.activeById or nil + local stableSlotIndex = petStore and petStore.stableSlotIndex or nil + + local currentInfo = type(MTH_GetCurrentPetInfo) == "function" and MTH_GetCurrentPetInfo() or nil + if currentInfo and currentInfo.liveExists then + local currentName = tostring(currentInfo.name or "") + if currentName ~= "" then + table.insert(rows, { + name = currentName, + family = tostring(currentInfo.family or ""), + level = tonumber(currentInfo.level) or 0, + slot = 0, + }) + end + end + + if scope == "all" then + for slot = 1, 4 do + local petId = (type(stableSlotIndex) == "table") and stableSlotIndex[slot] or nil + local row = (petId and type(activeById) == "table") and activeById[petId] or nil + if type(row) ~= "table" and type(activeById) == "table" then + for _, candidate in pairs(activeById) do + if type(candidate) == "table" and tonumber(candidate.stableSlot) == slot then + row = candidate + break + end + end + end + if type(row) == "table" and tostring(row.name or "") ~= "" then + table.insert(rows, { + name = tostring(row.name or ""), + family = tostring(row.family or ""), + level = tonumber(row.level) or 0, + slot = slot, + }) + end + end + end + + if scope ~= "all" and table.getn(rows) > 1 then + local filtered = {} + for i = 1, table.getn(rows) do + if rows[i].slot == 0 then + table.insert(filtered, rows[i]) + break + end + end + rows = filtered + end + + return rows +end + +local function AB_SelectBestFoodRowForDiet(dietRows, petLevel) + if type(dietRows) ~= "table" then + return nil, 0 + end + + local numericPetLevel = tonumber(petLevel) or 1 + if numericPetLevel < 1 then + numericPetLevel = 1 + end + + local bestRow = nil + local bestLevel = -1 + for i = 1, table.getn(dietRows) do + local row = dietRows[i] + if type(row) == "table" and row.id and row.index then + if AB_IsFoodExceptionRejected(row.id) then + -- learned exception: skip this item from autobuy candidates + else + local level = AB_GetItemFoodLevel(tonumber(row.id)) + local isCompatible = AB_IsFoodCompatibleByRules(numericPetLevel, level) + local underPetCap = (tonumber(level) or 0) <= numericPetLevel + if isCompatible and underPetCap and level >= bestLevel then + if level > bestLevel then + bestLevel = level + bestRow = row + elseif bestRow == nil then + bestRow = row + end + end + end + end + end + + if bestRow then + return bestRow, bestLevel + end + + return nil, 0 +end + +local function AB_CountDietItemsInBags(dietName) + local normalizedDiet = AB_NormalizeDietToken(dietName) + if normalizedDiet == "" then + return 0 + end + + local dietMap = AB_GetFoodDietMap() + local getSlots = _G and _G["GetContainerNumSlots"] or nil + local getLink = _G and _G["GetContainerItemLink"] or nil + local getInfo = _G and _G["GetContainerItemInfo"] or nil + if type(getSlots) ~= "function" or type(getLink) ~= "function" or type(getInfo) ~= "function" then + return 0 + end + + local count = 0 + for bag = 0, 4 do + local slots = tonumber(getSlots(bag)) or 0 + for slot = 1, slots do + local link = getLink(bag, slot) + local itemId = AB_ParseItemIdFromLink(link) + local numericId = tonumber(itemId) + if numericId and dietMap[numericId] == normalizedDiet then + local _, itemCount = getInfo(bag, slot) + itemCount = math.floor(tonumber(itemCount) or 1) + if itemCount < 1 then + itemCount = 1 + end + count = count + itemCount + end + end + end + + return count +end + +local function AB_ResolveAmmoByName(name) + if type(name) ~= "string" or name == "" then + return nil, nil + end + local lookup = AB_GetAmmoLookupByName() + local row = lookup[string.lower(name)] + if type(row) ~= "table" then + return nil, nil + end + return row.itemId, row.subtype +end + +local function AB_DefaultRule() + return { + enabled = false, + itemId = nil, + stacks = 1, + } +end + +local function AB_EnsureRuleList(bucket) + if type(bucket) ~= "table" then + bucket = {} + end + if type(bucket.rules) ~= "table" then + bucket.rules = {} + end + for i = 1, 3 do + if type(bucket.rules[i]) ~= "table" then + bucket.rules[i] = AB_DefaultRule() + end + if bucket.rules[i].enabled == nil then + bucket.rules[i].enabled = false + end + if bucket.rules[i].stacks == nil then + bucket.rules[i].stacks = 1 + end + end + return bucket +end + +local function AB_Log(message, severity) + if type(MTH_Log) == "function" then + MTH_Log("[AutoBuy] " .. tostring(message or ""), severity) + end +end + +local function AB_Trace(message) + return +end + +function Engine:GetConfigStore() + if MTH and MTH.GetModuleSavedVariables then + local store = MTH:GetModuleSavedVariables("autobuy") + if type(store) == "table" then + return store + end + end + MTH_AutoBuy_Saved = MTH_AutoBuy_Saved or {} + return MTH_AutoBuy_Saved +end + +function Engine:EnsureDefaults() + local store = self:GetConfigStore() + if store.enabled == nil then + store.enabled = true + end + if store.debug == nil then + store.debug = false + end + if type(store.food) ~= "table" then + store.food = {} + end + if type(store.ammo) ~= "table" then + store.ammo = {} + end + if type(store.projectiles) ~= "table" then + store.projectiles = {} + end + if type(store.petFood) ~= "table" then + store.petFood = {} + end + if store.food.enabled == nil then + store.food.enabled = true + end + if store.ammo.enabled == nil then + store.ammo.enabled = true + end + if store.projectiles.enabled == nil then + store.projectiles.enabled = true + end + if store.petFood.enabled == nil then + store.petFood.enabled = false + end + if store.petFood.stacks == nil then + store.petFood.stacks = 1 + end + if store.petFood.scope == nil then + store.petFood.scope = "current" + end + store.petFood.stacks = math.floor(tonumber(store.petFood.stacks) or 1) + if store.petFood.stacks < 0 then + store.petFood.stacks = 0 + end + if store.petFood.scope ~= "current" and store.petFood.scope ~= "all" then + store.petFood.scope = "current" + end + if store.food.minStock == nil then + store.food.minStock = 200 + end + if store.ammo.minStock == nil then + store.ammo.minStock = 1000 + end + if store.food.preferredDiet == nil then + store.food.preferredDiet = nil + end + if store.ammo.preferredType == nil then + store.ammo.preferredType = nil + end + store.projectiles.arrows = AB_EnsureRuleList(store.projectiles.arrows) + store.projectiles.bullets = AB_EnsureRuleList(store.projectiles.bullets) + return store +end + +function Engine:GetBuyableProjectileItems(subtype) + local wanted = string.lower(tostring(subtype or "")) + if wanted ~= "arrow" and wanted ~= "bullet" then + return {} + end + + local result = {} + local data = MTH_DS_AmmoItems or {} + for itemId, row in pairs(data) do + if type(row) == "table" and string.lower(tostring(row.subtype or "")) == wanted then + local vendorCount = 0 + if type(row.vendors) == "table" then + for _ in pairs(row.vendors) do + vendorCount = vendorCount + 1 + break + end + end + if vendorCount > 0 then + table.insert(result, { + itemId = tonumber(itemId) or itemId, + name = tostring(row.name or ("Item " .. tostring(itemId))), + level = tonumber(row.level) or 0, + reqlevel = tonumber(row.reqlevel) or 0, + }) + end + end + end + + table.sort(result, function(a, b) + if (a.level or 0) ~= (b.level or 0) then + return (a.level or 0) < (b.level or 0) + end + if (a.reqlevel or 0) ~= (b.reqlevel or 0) then + return (a.reqlevel or 0) < (b.reqlevel or 0) + end + if tostring(a.name or "") ~= tostring(b.name or "") then + return tostring(a.name or "") < tostring(b.name or "") + end + return tonumber(a.itemId or 0) < tonumber(b.itemId or 0) + end) + + return result +end + +function Engine:IsEnabled() + local store = self:EnsureDefaults() + return store.enabled and true or false +end + +function Engine:IsDebugEnabled() + local store = self:EnsureDefaults() + return store.debug and true or false +end + +function Engine:IsTraceEnabled() + if self:IsDebugEnabled() then + return true + end + if MTH and MTH.debug then + return true + end + return false +end + +function Engine:Debug(message) + if self:IsTraceEnabled() then + AB_Log(message, "debug") + end +end + +function Engine:Init() + self:EnsureDefaults() + self:Debug("engine initialized") +end + +function Engine:ScanMerchant() + if type(GetMerchantNumItems) ~= "function" then + return { count = 0, items = {} } + end + + local result = { + count = GetMerchantNumItems() or 0, + items = {}, + } + + for index = 1, result.count do + local name, _, price, stackCount, numAvailable = GetMerchantItemInfo(index) + local link = GetMerchantItemLink(index) + local itemId = AB_ParseItemIdFromLink(link) + local subtype = AB_GetAmmoSubtypeByItemId(itemId) + if (not itemId or not subtype) and type(name) == "string" and name ~= "" then + local byNameItemId, byNameSubtype = AB_ResolveAmmoByName(name) + if byNameItemId and byNameSubtype then + itemId = byNameItemId + subtype = byNameSubtype + end + end + result.items[index] = { + index = index, + name = name, + link = link, + itemId = itemId, + subtype = subtype, + price = price, + stackCount = stackCount, + numAvailable = numAvailable, + } + end + + return result +end + +function Engine:BuildFoodPlan(merchantSnapshot) + local store = self:EnsureDefaults() + local plan = { + enabled = store.petFood and store.petFood.enabled and true or false, + actions = {}, + reason = "", + debugLines = {}, + } + + local function dbg(line) + table.insert(plan.debugLines, tostring(line or "")) + end + + if not plan.enabled then + plan.reason = "petfood-disabled" + dbg("petFood option disabled") + return plan + end + + local targetStacks = math.floor(tonumber(store.petFood.stacks) or 0) + if targetStacks <= 0 then + plan.reason = "petfood-quantity-zero" + dbg("petFood target stacks <= 0") + return plan + end + + local merchantFoods = AB_GetMerchantFoodsSnapshot(merchantSnapshot) + if type(merchantFoods) ~= "table" or type(merchantFoods.byDiet) ~= "table" then + plan.reason = "merchant-foods-missing" + dbg("merchant food scan missing") + return plan + end + + local scope = store.petFood.scope == "all" and "all" or "current" + local petRows = AB_CollectPetRows(scope) + if table.getn(petRows) == 0 then + plan.reason = "no-pets-in-scope" + dbg("no pets in scope=" .. tostring(scope)) + return plan + end + + local families = {} + for i = 1, table.getn(petRows) do + local pet = petRows[i] + local dsRow, familyKey = AB_FindFamilyDietRow(pet.family) + if type(dsRow) == "table" and type(dsRow.food) == "table" and table.getn(dsRow.food) > 0 and familyKey then + if type(families[familyKey]) ~= "table" then + families[familyKey] = { + key = familyKey, + petCount = 0, + levelCap = tonumber(pet.level) or 0, + foodList = dsRow.food, + petNames = {}, + candidates = {}, + } + end + local family = families[familyKey] + family.petCount = family.petCount + 1 + local petName = tostring(pet.name or "") + if petName ~= "" then + table.insert(family.petNames, petName) + end + local petLevel = tonumber(pet.level) or 0 + if family.levelCap <= 0 then + family.levelCap = petLevel + elseif petLevel > family.levelCap then + family.levelCap = petLevel + end + else + dbg("skip pet with unknown family diet: " .. tostring(pet.name or "?") .. " family='" .. tostring(pet.family or "") .. "'") + end + end + + local familyList = {} + local dietCoverage = {} + for _, family in pairs(families) do + for order = 1, table.getn(family.foodList or {}) do + local diet = AB_NormalizeDietToken(family.foodList[order]) + if diet ~= "" then + local bestRow, bestLevel = AB_SelectBestFoodRowForDiet(merchantFoods.byDiet[diet], family.levelCap) + if bestRow then + table.insert(family.candidates, { + diet = diet, + order = order, + bestLevel = bestLevel, + }) + dietCoverage[diet] = (dietCoverage[diet] or 0) + 1 + end + end + end + if table.getn(family.candidates) > 0 then + table.insert(familyList, family) + else + dbg("family has no vendor diet options: " .. tostring(family.key)) + end + end + + if table.getn(familyList) == 0 then + plan.reason = "no-family-food-match" + dbg("no families have compatible vendor foods") + return plan + end + + table.sort(familyList, function(a, b) + if (a.petCount or 0) ~= (b.petCount or 0) then + return (a.petCount or 0) > (b.petCount or 0) + end + if table.getn(a.candidates or {}) ~= table.getn(b.candidates or {}) then + return table.getn(a.candidates or {}) < table.getn(b.candidates or {}) + end + return tostring(a.key or "") < tostring(b.key or "") + end) + + local assignedPetCountByDiet = {} + local chosenByFamily = {} + for i = 1, table.getn(familyList) do + local family = familyList[i] + local bestChoice = nil + for j = 1, table.getn(family.candidates) do + local candidate = family.candidates[j] + local scoreA = (assignedPetCountByDiet[candidate.diet] or 0) + (family.petCount or 0) + local scoreB = dietCoverage[candidate.diet] or 0 + local scoreC = candidate.bestLevel or 0 + local scoreD = -(candidate.order or 99) + if not bestChoice then + bestChoice = { + candidate = candidate, + scoreA = scoreA, + scoreB = scoreB, + scoreC = scoreC, + scoreD = scoreD, + } + else + local better = false + if scoreA > bestChoice.scoreA then + better = true + elseif scoreA == bestChoice.scoreA and scoreB > bestChoice.scoreB then + better = true + elseif scoreA == bestChoice.scoreA and scoreB == bestChoice.scoreB and scoreC > bestChoice.scoreC then + better = true + elseif scoreA == bestChoice.scoreA and scoreB == bestChoice.scoreB and scoreC == bestChoice.scoreC and scoreD > bestChoice.scoreD then + better = true + end + if better then + bestChoice = { + candidate = candidate, + scoreA = scoreA, + scoreB = scoreB, + scoreC = scoreC, + scoreD = scoreD, + } + end + end + end + + if bestChoice and bestChoice.candidate then + chosenByFamily[family.key] = { + diet = bestChoice.candidate.diet, + levelCap = family.levelCap, + petCount = family.petCount, + petNames = family.petNames, + } + assignedPetCountByDiet[bestChoice.candidate.diet] = (assignedPetCountByDiet[bestChoice.candidate.diet] or 0) + (family.petCount or 0) + dbg("family " .. tostring(family.key) + .. " -> diet " .. tostring(bestChoice.candidate.diet) + .. " (pets=" .. tostring(family.petCount) + .. ", levelCap=" .. tostring(family.levelCap) + .. ")") + end + end + + local buckets = {} + for familyKey, chosen in pairs(chosenByFamily) do + local diet = chosen.diet + if type(buckets[diet]) ~= "table" then + buckets[diet] = { + diet = diet, + petCount = 0, + familyCount = 0, + levelCap = chosen.levelCap, + petNames = {}, + } + end + local bucket = buckets[diet] + bucket.petCount = bucket.petCount + (chosen.petCount or 0) + bucket.familyCount = bucket.familyCount + 1 + if type(chosen.petNames) == "table" then + for n = 1, table.getn(chosen.petNames) do + local candidateName = tostring(chosen.petNames[n] or "") + if candidateName ~= "" then + local exists = false + for e = 1, table.getn(bucket.petNames) do + if bucket.petNames[e] == candidateName then + exists = true + break + end + end + if not exists then + table.insert(bucket.petNames, candidateName) + end + end + end + end + if tonumber(chosen.levelCap) and tonumber(chosen.levelCap) > 0 then + if (tonumber(bucket.levelCap) or 0) <= 0 or tonumber(chosen.levelCap) > tonumber(bucket.levelCap) then + bucket.levelCap = tonumber(chosen.levelCap) + end + end + dbg("bucket diet=" .. tostring(diet) .. " add family=" .. tostring(familyKey)) + end + + for dietName, bucket in pairs(buckets) do + local bestRow, bestLevel = AB_SelectBestFoodRowForDiet(merchantFoods.byDiet[dietName], bucket.levelCap) + if bestRow and bestRow.index then + local stackCount = math.floor(tonumber(bestRow.stack) or 1) + if stackCount < 1 then stackCount = 1 end + local bagStackSize = AB_GetItemMaxStack(bestRow.id, stackCount) + local targetItems = targetStacks * bagStackSize + local ownedItems = AB_CountDietItemsInBags(dietName) + local deficitItems = targetItems - ownedItems + local deficit = 0 + if deficitItems > 0 then + deficit = math.ceil(deficitItems / stackCount) + end + dbg("diet=" .. tostring(dietName) + .. " targetStacks=" .. tostring(targetStacks) + .. " bagStackSize=" .. tostring(bagStackSize) + .. " vendorStackSize=" .. tostring(stackCount) + .. " targetItems=" .. tostring(targetItems) + .. " ownedItems=" .. tostring(ownedItems) + .. " deficitItems=" .. tostring(deficitItems) + .. " deficit=" .. tostring(deficit) + .. " selectedLevel=" .. tostring(bestLevel) + .. " levelCap=" .. tostring(bucket.levelCap) + .. " pets=" .. tostring(bucket.petCount) + .. " families=" .. tostring(bucket.familyCount)) + if deficit > 0 then + table.insert(plan.actions, { + index = bestRow.index, + quantityStacks = deficit, + quantityItems = deficit * stackCount, + unitPrice = tonumber(bestRow.price) or 0, + itemId = tonumber(bestRow.id) or bestRow.id, + itemLink = bestRow.link, + itemName = bestRow.name, + diet = dietName, + targetStacks = targetStacks, + bagStackSize = bagStackSize, + vendorStackSize = stackCount, + targetItems = targetItems, + ownedItems = ownedItems, + deficitItems = deficitItems, + deficit = deficit, + stackCount = stackCount, + petCount = bucket.petCount, + familyCount = bucket.familyCount, + petNames = bucket.petNames, + }) + end + else + dbg("diet " .. tostring(dietName) .. " has no compatible row at levelCap=" .. tostring(bucket.levelCap)) + end + end + + if table.getn(plan.actions) == 0 then + plan.reason = "no-deficit" + else + plan.reason = "ok" + end + + return plan +end + +function Engine:BuildAmmoPlan(merchantSnapshot) + local store = self:EnsureDefaults() + local plan = { + enabled = store.projectiles and store.projectiles.enabled and true or false, + actions = {}, + reason = "", + debugLines = {}, + } + + local function dbg(line) + table.insert(plan.debugLines, tostring(line or "")) + end + + if not plan.enabled then + plan.reason = "projectiles-disabled" + dbg("projectiles option disabled") + return plan + end + + if type(merchantSnapshot) ~= "table" or type(merchantSnapshot.items) ~= "table" then + plan.reason = "merchant-snapshot-missing" + dbg("merchant snapshot missing") + return plan + end + + local merchantByItemId = {} + local hasAmmoVendor = false + for i = 1, table.getn(merchantSnapshot.items) do + local row = merchantSnapshot.items[i] + if type(row) == "table" and row.itemId and row.subtype then + dbg("merchant row index=" .. tostring(row.index) .. " itemId=" .. tostring(row.itemId) .. " subtype=" .. tostring(row.subtype) .. " stack=" .. tostring(row.stackCount)) + merchantByItemId[tonumber(row.itemId) or row.itemId] = row + hasAmmoVendor = true + end + end + + if not hasAmmoVendor then + plan.reason = "not-ammo-vendor" + dbg("merchant has no recognized arrow/bullet rows") + return plan + end + + local function countStacksInBags(itemId) + if not itemId then + return 0 + end + local count = 0 + local getSlots = _G and _G["GetContainerNumSlots"] or nil + local getLink = _G and _G["GetContainerItemLink"] or nil + if type(getSlots) ~= "function" or type(getLink) ~= "function" then + return 0 + end + for bag = 0, 4 do + local slots = tonumber(getSlots(bag)) or 0 + for slot = 1, slots do + local link = getLink(bag, slot) + local bagItemId = AB_ParseItemIdFromLink(link) + if bagItemId and tonumber(bagItemId) == tonumber(itemId) then + count = count + 1 + end + end + end + return count + end + + local function appendRuleActions(bucket, subtype) + if type(bucket) ~= "table" or type(bucket.rules) ~= "table" then + dbg(subtype .. " rules bucket missing") + return + end + for i = 1, table.getn(bucket.rules) do + local rule = bucket.rules[i] + if type(rule) == "table" and rule.enabled and rule.itemId then + local desiredStacks = math.floor(tonumber(rule.stacks) or 0) + if desiredStacks > 0 then + local merchantRow = merchantByItemId[tonumber(rule.itemId) or rule.itemId] + if merchantRow and merchantRow.subtype == subtype then + local ownedStacks = countStacksInBags(rule.itemId) + local deficit = desiredStacks - ownedStacks + dbg(subtype .. "#" .. tostring(i) .. " item=" .. tostring(rule.itemId) + .. " desired=" .. tostring(desiredStacks) + .. " owned=" .. tostring(ownedStacks) + .. " deficit=" .. tostring(deficit)) + if deficit > 0 then + local merchantStack = math.floor(tonumber(merchantRow.stackCount) or 1) + if merchantStack < 1 then merchantStack = 1 end + local quantityStacks = deficit + local quantityItems = quantityStacks * merchantStack + table.insert(plan.actions, { + index = merchantRow.index, + quantityStacks = quantityStacks, + quantityItems = quantityItems, + unitPrice = tonumber(merchantRow.price) or 0, + itemId = rule.itemId, + itemLink = merchantRow.link, + itemName = merchantRow.name, + subtype = subtype, + desiredStacks = desiredStacks, + ownedStacks = ownedStacks, + deficit = deficit, + stackCount = merchantStack, + }) + end + else + dbg(subtype .. "#" .. tostring(i) .. " item=" .. tostring(rule.itemId) .. " not sold by this vendor") + end + else + dbg(subtype .. "#" .. tostring(i) .. " desired stacks <= 0") + end + else + dbg(subtype .. "#" .. tostring(i) .. " disabled or no item") + end + end + end + + appendRuleActions(store.projectiles.arrows, "arrow") + appendRuleActions(store.projectiles.bullets, "bullet") + + if table.getn(plan.actions) == 0 then + plan.reason = "no-deficit" + else + plan.reason = "ok" + end + + return plan +end + +function Engine:ExecutePlan(plan) + if type(plan) ~= "table" or type(plan.actions) ~= "table" then + return 0, 0 + end + local executedActions = 0 + local requestedStacks = 0 + for _, action in ipairs(plan.actions) do + if type(action) == "table" and action.index then + local stacksToBuy = math.floor(tonumber(action.quantityStacks) or 0) + if stacksToBuy > 0 then + for i = 1, stacksToBuy do + BuyMerchantItem(action.index) + end + executedActions = executedActions + 1 + requestedStacks = requestedStacks + stacksToBuy + end + end + end + return executedActions, requestedStacks +end + +local function AB_AnnouncePurchases(actions) + if type(actions) ~= "table" or table.getn(actions) == 0 then + return + end + + local function formatMoney(money) + if MTH and MTH.FormatMoney then + return MTH:FormatMoney(money) + end + if type(MTH_FormatMoney) == "function" then + return MTH_FormatMoney(money) + end + return tostring(tonumber(money) or 0) + end + + for i = 1, table.getn(actions) do + local action = actions[i] + if type(action) == "table" then + local stacks = math.floor(tonumber(action.quantityStacks) or 0) + if stacks > 0 then + local linkText = tostring(action.itemLink or "") + if linkText == "" then + linkText = tostring(action.itemName or ("item:" .. tostring(action.itemId or "?"))) + end + local totalPrice = (tonumber(action.unitPrice) or 0) * stacks + local petsSuffix = "" + if type(action.petNames) == "table" and table.getn(action.petNames) > 0 then + local names = {} + for n = 1, table.getn(action.petNames) do + local petName = tostring(action.petNames[n] or "") + if petName ~= "" then + table.insert(names, petName) + end + end + if table.getn(names) > 0 then + table.sort(names) + if table.getn(names) == 1 then + petsSuffix = " " .. tostring(names[1]) .. " likes this !" + else + petsSuffix = " " .. table.concat(names, ", ") .. " like this !" + end + end + end + local line = "[AutoBuy] Bought " .. tostring(stacks) + .. " stack" .. (stacks > 1 and "s" or "") + .. " of " .. linkText + .. " for " .. formatMoney(totalPrice) .. "." + .. petsSuffix + if MTH and MTH.Print then + MTH:Print(line) + elseif type(MTH_Log) == "function" then + MTH_Log(line) + end + end + end + end +end + +function Engine:OnMerchantEvent(eventName) + if eventName == "MERCHANT_SHOW" then + self._merchantSessionExecuted = false + AB_Trace("merchant session reset on MERCHANT_SHOW") + elseif eventName == "MERCHANT_UPDATE" and self._merchantSessionExecuted then + AB_Trace("skip MERCHANT_UPDATE: session already executed") + return + end + + self:Debug("OnMerchantEvent enter event=" .. tostring(eventName)) + AB_Trace("OnMerchantEvent " .. tostring(eventName)) + if not self:IsEnabled() then + self:Debug("OnMerchantEvent abort: engine store disabled") + local store = self:EnsureDefaults() + AB_Trace("abort: store.enabled=" .. tostring(store and store.enabled) + .. " projectiles.enabled=" .. tostring(store and store.projectiles and store.projectiles.enabled)) + return + end + + local snapshot = self:ScanMerchant() + self:Debug("snapshot count=" .. tostring(snapshot and snapshot.count or 0)) + AB_Trace("merchant item count=" .. tostring(snapshot and snapshot.count or 0)) + local foodPlan = self:BuildFoodPlan(snapshot) + local ammoPlan = self:BuildAmmoPlan(snapshot) + + self:Debug(tostring(eventName) .. " merchant items=" .. tostring(snapshot.count) + .. " foodActions=" .. tostring(table.getn(foodPlan.actions or {})) + .. " ammoActions=" .. tostring(table.getn(ammoPlan.actions or {}))) + self:Debug("ammoPlan reason=" .. tostring(ammoPlan.reason or "")) + AB_Trace("ammoPlan reason=" .. tostring(ammoPlan.reason or "") + .. " actions=" .. tostring(table.getn(ammoPlan.actions or {}))) + AB_Trace("foodPlan reason=" .. tostring(foodPlan.reason or "") + .. " actions=" .. tostring(table.getn(foodPlan.actions or {}))) + for i = 1, table.getn(foodPlan.debugLines or {}) do + self:Debug("foodPlan " .. tostring(foodPlan.debugLines[i])) + end + for i = 1, table.getn(ammoPlan.debugLines or {}) do + self:Debug("ammoPlan " .. tostring(ammoPlan.debugLines[i])) + end + + local totalRequestedStacks = 0 + if table.getn(foodPlan.actions or {}) > 0 then + for i = 1, table.getn(foodPlan.actions or {}) do + local action = foodPlan.actions[i] + self:Debug("food action#" .. tostring(i) + .. " idx=" .. tostring(action.index) + .. " item=" .. tostring(action.itemId) + .. " diet=" .. tostring(action.diet) + .. " qtyStacks=" .. tostring(action.quantityStacks) + .. " targetStacks=" .. tostring(action.targetStacks) + .. " ownedStacks=" .. tostring(action.ownedStacks) + .. " deficit=" .. tostring(action.deficit) + .. " pets=" .. tostring(action.petCount) + .. " families=" .. tostring(action.familyCount)) + end + local _, requestedStacks = self:ExecutePlan(foodPlan) + totalRequestedStacks = totalRequestedStacks + requestedStacks + AB_Trace("executed food requestedStacks=" .. tostring(requestedStacks)) + if requestedStacks > 0 then + AB_AnnouncePurchases(foodPlan.actions) + end + else + self:Debug("no food actions to execute") + AB_Trace("no food actions to execute") + end + + if table.getn(ammoPlan.actions or {}) > 0 then + for i = 1, table.getn(ammoPlan.actions or {}) do + local action = ammoPlan.actions[i] + self:Debug("action#" .. tostring(i) + .. " idx=" .. tostring(action.index) + .. " item=" .. tostring(action.itemId) + .. " qtyStacks=" .. tostring(action.quantityStacks) + .. " qtyItems=" .. tostring(action.quantityItems) + .. " desiredStacks=" .. tostring(action.desiredStacks) + .. " ownedStacks=" .. tostring(action.ownedStacks) + .. " deficit=" .. tostring(action.deficit) + .. " stackCount=" .. tostring(action.stackCount)) + end + local executedActions, requestedStacks = self:ExecutePlan(ammoPlan) + totalRequestedStacks = totalRequestedStacks + requestedStacks + AB_Trace("executed actions=" .. tostring(executedActions) .. " requestedStacks=" .. tostring(requestedStacks)) + if requestedStacks > 0 then + AB_AnnouncePurchases(ammoPlan.actions) + end + self:Debug("executed ammo actions=" .. tostring(table.getn(ammoPlan.actions or {}))) + else + self:Debug("no ammo actions to execute") + AB_Trace("no actions to execute") + end + + if totalRequestedStacks > 0 then + self._merchantSessionExecuted = true + end +end + +function Engine:OnMerchantClosed() + self._merchantSessionExecuted = false + AB_Trace("merchant session reset on MERCHANT_CLOSED") +end + +function MTH_AutoBuy_GetMerchantSnapshot() + return Engine:ScanMerchant() +end + +function MTH_AutoBuy_GetProjectileItems(subtype) + return Engine:GetBuyableProjectileItems(subtype) +end diff --git a/modules/autobuy/loader.xml b/modules/autobuy/loader.xml new file mode 100644 index 0000000..981b896 --- /dev/null +++ b/modules/autobuy/loader.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/modules/autobuy/module.lua b/modules/autobuy/module.lua new file mode 100644 index 0000000..0c33d3b --- /dev/null +++ b/modules/autobuy/module.lua @@ -0,0 +1,161 @@ +------------------------------------------------------ +-- MetaHunt: AutoBuy Module +-- Standalone module shell for vendor auto-buy features. +------------------------------------------------------ + +local MTH_AutoBuy = { + name = "autobuy", + enabled = false, + version = "1.0.3", + events = { + "VARIABLES_LOADED", + "MERCHANT_SHOW", + "MERCHANT_UPDATE", + "MERCHANT_CLOSED", + }, + initialized = false, +} + +local function AB_Log(message, severity) + if type(MTH_Log) == "function" then + MTH_Log("[AutoBuy] " .. tostring(message or ""), severity) + end +end + +local AB_Engine + +local function AB_Trace(message) + return +end + +local function AB_EnsureBridgeFrame(moduleRef) + if MTH_AutoBuy._bridgeFrame then + return MTH_AutoBuy._bridgeFrame + end + + local frame = CreateFrame("Frame", "MTH_AutoBuyBridgeFrame") + if not frame then + AB_Log("failed to create bridge frame", "error") + return nil + end + + frame:RegisterEvent("VARIABLES_LOADED") + frame:RegisterEvent("MERCHANT_SHOW") + frame:RegisterEvent("MERCHANT_UPDATE") + frame:RegisterEvent("MERCHANT_CLOSED") + frame:SetScript("OnEvent", function(_, eventName) + eventName = eventName or event + local engine = AB_Engine() + if not engine then + AB_Trace("bridge event=" .. tostring(eventName) .. " engine missing") + return + end + + if eventName == "VARIABLES_LOADED" then + engine:Init() + local store = engine:EnsureDefaults() + AB_Trace("bridge VARIABLES_LOADED store.enabled=" .. tostring(store and store.enabled) + .. " projectiles.enabled=" .. tostring(store and store.projectiles and store.projectiles.enabled)) + return + end + + if eventName == "MERCHANT_SHOW" or eventName == "MERCHANT_UPDATE" then + AB_Trace("bridge " .. tostring(eventName) .. " -> engine") + engine:OnMerchantEvent(eventName) + elseif eventName == "MERCHANT_CLOSED" then + AB_Trace("bridge MERCHANT_CLOSED -> engine") + if engine.OnMerchantClosed then + engine:OnMerchantClosed() + end + end + end) + + MTH_AutoBuy._bridgeFrame = frame + MTH_AutoBuy._bridgeActive = true + return frame +end + +AB_Engine = function() + return MTH_AutoBuyEngine +end + +function MTH_AutoBuy:init() + local engine = AB_Engine() + if not engine then + AB_Log("init failed: engine missing", "error") + AB_Trace("init failed: engine missing") + return + end + engine:Init() + AB_EnsureBridgeFrame(self) + self.initialized = true + AB_Trace("init done") + if self.enabled then + AB_Log("module initialized", "debug") + end +end + +function MTH_AutoBuy:setEnabled(enabled) + if not self.initialized then + self:init() + end + local engine = AB_Engine() + if not engine then + AB_Trace("setEnabled ignored: engine missing") + return + end + local store = engine:EnsureDefaults() + store.enabled = enabled and true or false + AB_EnsureBridgeFrame(self) + AB_Trace("setEnabled store.enabled=" .. tostring(store.enabled)) +end + +function MTH_AutoBuy:onEvent(event) + local engine = AB_Engine() + if not engine then + AB_Log("onEvent ignored: engine missing, event=" .. tostring(event), "debug") + AB_Trace("engine missing, event=" .. tostring(event)) + return + end + if self._bridgeActive and (event == "MERCHANT_SHOW" or event == "MERCHANT_UPDATE" or event == "MERCHANT_CLOSED") then + return + end + + AB_Log("onEvent event=" .. tostring(event) .. " module.enabled=" .. tostring(self.enabled), "debug") + + if event == "VARIABLES_LOADED" then + engine:Init() + AB_EnsureBridgeFrame(self) + AB_Log("onEvent VARIABLES_LOADED -> engine init", "debug") + local store = engine:EnsureDefaults() + AB_Trace("VARIABLES_LOADED module.enabled=" .. tostring(self.enabled) + .. " store.enabled=" .. tostring(store and store.enabled) + .. " projectiles.enabled=" .. tostring(store and store.projectiles and store.projectiles.enabled)) + return + end + + if not self.enabled then + AB_Log("onEvent ignored: module disabled", "debug") + if event == "MERCHANT_SHOW" or event == "MERCHANT_UPDATE" then + AB_Trace(tostring(event) .. " ignored: module disabled") + end + return + end + + if event == "MERCHANT_SHOW" or event == "MERCHANT_UPDATE" then + AB_Log("onEvent merchant event forwarded: " .. tostring(event), "debug") + AB_Trace("forwarding " .. tostring(event) .. " to engine") + engine:OnMerchantEvent(event) + elseif event == "MERCHANT_CLOSED" then + AB_Trace("forwarding MERCHANT_CLOSED to engine") + if engine.OnMerchantClosed then + engine:OnMerchantClosed() + end + end +end + +function MTH_AutoBuy:cleanup() + AB_Log("module cleanup", "debug") +end + +MTH:RegisterModule("autobuy", MTH_AutoBuy) diff --git a/modules/chronometer/data-hunter.lua b/modules/chronometer/data-hunter.lua new file mode 100644 index 0000000..0a55e7c --- /dev/null +++ b/modules/chronometer/data-hunter.lua @@ -0,0 +1,78 @@ +local BS = setmetatable({}, { + __index = function(_, key) + if MTH and MTH.LocalizeSpell then + return MTH:LocalizeSpell(key) + end + return key + end, +}) + +function MTH_ChronometerHunterInstallData(engine) + if not engine then + return + end + + local _, class = UnitClass("player") + if class == "HUNTER" then + engine:AddGroup(1, true, "MAGENTA") + engine:AddGroup(2, false, "MAROON") + engine:AddGroup(3, false, "GREEN") + + engine:AddTimer(engine.SPELL, BS["Bestial Wrath"], 18, 0, 1, 1, { cr = "RED" }) + engine:AddTimer(engine.SPELL, BS["Concussive Shot"], 4, 1, 0, 0, { cr = "BLUE", ea = { [BS["Improved Concussive Shot"]] = 4 } }) + engine:AddTimer(engine.SPELL, BS["Counterattack"], 5, 1, 0, 0, { cr = "ORANGE" }) + engine:AddTimer(engine.SPELL, BS["Deterrence"], 10, 0, 1, 1, { cr = "ORANGE" }) + engine:AddTimer(engine.SPELL, BS["Explosive Trap"], 60, 0, 0, 0, { gr = 1, cr = "ORANGE", rc = true, ea = { [BS["Explosive Trap Effect"]] = 60 } }) + engine:AddTimer(engine.SPELL, BS["Freezing Trap"], 60, 0, 0, 0, { gr = 1, cr = "CYAN", rc = true, ea = { [BS["Freezing Trap Effect"]] = 60 } }) + engine:AddTimer(engine.SPELL, BS["Frost Trap"], 60, 0, 0, 0, { gr = 1, cr = "CYAN", rc = true, ea = { [BS["Frost Trap Aura"]] = 60 } }) + engine:AddTimer(engine.SPELL, BS["Flare"], 30, 0, 0, 0, { cr = "YELLOW", rc = true }) + engine:AddTimer(engine.SPELL, BS["Hunter's Mark"], 120, 1, 0, 0, { cr = "RED", rc = true }) + engine:AddTimer(engine.SPELL, BS["Immolation Trap"], 60, 0, 0, 0, { gr = 1, cr = "ORANGE", ea = { [BS["Immolation Trap Effect"]] = 60 } }) + engine:AddTimer(engine.SPELL, BS["Rapid Fire"], 15, 0, 1, 1, { cr = "RED" }) + engine:AddTimer(engine.SPELL, BS["Scare Beast"], 10, 1, 0, 0, { cr = "BLUE", rc = true, d = { rs = 5 } }) + engine:AddTimer(engine.SPELL, BS["Scatter Shot"], 4, 1, 0, 0, { cr = "MAROON" }) + engine:AddTimer(engine.SPELL, BS["Scorpid Sting"], 20, 1, 0, 0, { gr = 2, cr = "PURPLE", rc = true }) + engine:AddTimer(engine.SPELL, BS["Serpent Sting"], 15, 1, 0, 0, { gr = 2, cr = "GREEN", rc = true }) + engine:AddTimer(engine.SPELL, BS["Viper Sting"], 8, 1, 0, 0, { gr = 2, cr = "CYAN", rc = true }) + engine:AddTimer(engine.SPELL, BS["Wing Clip"], 10, 1, 0, 0, { cr = "YELLOW", rc = true, ea = { [BS["Improved Wing Clip"]] = 10 } }) + engine:AddTimer(engine.SPELL, BS["Wyvern Sting"], 12, 1, 0, 0, { gr = 2, cr = "MAROON", rc = true }) + engine:AddTimer(engine.SPELL, BS["Feed Pet"], 20, 0, 0, 0, { cr = "MAROON" }) + + engine:AddTimer(engine.EVENT, BS["Explosive Trap Effect"], 20, 1, 0, 1, { gr = 1, cr = "ORANGE", a = 1, xn = BS["Explosive Trap"] }) + engine:AddTimer(engine.EVENT, BS["Freezing Trap Effect"], 10, 1, 0, 1, { gr = 1, cr = "CYAN", a = 1, d = { rs = 5, tn = BS["Clever Traps"], tb = 0.15, tp = 1 }, xn = BS["Freezing Trap"] }) + engine:AddTimer(engine.EVENT, BS["Frost Trap Aura"], 30, 0, 0, 0, { gr = 1, cr = "CYAN", a = 1, d = { tn = BS["Clever Traps"], tb = 0.15, tp = 1 }, xn = BS["Frost Trap"] }) + engine:AddTimer(engine.EVENT, BS["Immolation Trap Effect"], 15, 1, 0, 1, { gr = 1, cr = "ORANGE", a = 1, xn = BS["Immolation Trap"] }) + engine:AddTimer(engine.EVENT, BS["Improved Concussive Shot"], 3, 1, 0, 0, { a = 1, cr = "BLUE", xn = BS["Concussive Shot"] }) + engine:AddTimer(engine.EVENT, BS["Improved Wing Clip"], 5, 1, 0, 0, { a = 1, cr = "YELLOW", xn = BS["Wing Clip"] }) + engine:AddTimer(engine.EVENT, BS["Piercing Shot"], 8, 1, 0, 1, { a = 1, cr = "PURPLE", tx = "Interface\\Icons\\Ability_Hunter_DisarmingShot" }) + engine:AddTimer(engine.EVENT, BS["Quick Shots"], 12, 0, 1, 1, { a = 1, cr = "MAROON", tx = "Interface\\Icons\\Ability_Hunter_Quickshot" }) + engine:AddTimer(engine.EVENT, BS["Scorpid Poison"], 10, 1, 0, 0, { gr = 3, cr = "PURPLE", a = 1, dn = 1 }) + end + + local _, race = UnitRace("player") + if race == "Dwarf" then + engine:AddTimer(engine.SPELL, BS["Stoneform"], 8, 0, 1, 1, { cl = "RACIAL" }) + elseif race == "Human" then + engine:AddTimer(engine.SPELL, BS["Perception"], 20, 0, 1, 1, { cl = "RACIAL" }) + elseif race == "Orc" then + engine:AddTimer(engine.SPELL, BS["Blood Fury"], 15, 0, 1, 1, { cl = "RACIAL" }) + elseif race == "Tauren" then + engine:AddTimer(engine.SPELL, BS["War Stomp"], 2, 0, 0, 0, { cl = "RACIAL" }) + elseif race == "Troll" then + engine:AddTimer(engine.SPELL, BS["Berserking"], 10, 0, 1, 1, { cl = "RACIAL" }) + elseif race == "Scourge" then + engine:AddTimer(engine.SPELL, BS["Will of the Forsaken"], 5, 0, 1, 1, { cl = "RACIAL" }) + end + + if class == "PALADIN" or class == "WARRIOR" or class == "ROGUE" or class == "SHAMAN" then + engine:AddTimer(engine.EVENT, BS["Holy Strength"], 15, 0, 1, 1, { cr = "YELLOW", a = 1, cl = "COMMON" }) + end + engine:AddTimer(engine.EVENT, BS["Unstable Power"], 20, 0, 1, 1, { a = 1, cr = "CYAN", cl = "COMMON" }) + engine:AddTimer(engine.EVENT, BS["Ephemeral Power"], 15, 0, 1, 1, { a = 1, cr = "CYAN", cl = "COMMON" }) + engine:AddTimer(engine.EVENT, BS["Mind Quickening"], 20, 0, 1, 1, { a = 1, cr = "CYAN", cl = "COMMON", xn = BS["Critical Mass"] }) +end + +if MTH_ChronometerHunter and MTH_ChronometerHunter.dataSetup then + table.insert(MTH_ChronometerHunter.dataSetup, MTH_ChronometerHunterInstallData) +end + diff --git a/modules/chronometer/engine.lua b/modules/chronometer/engine.lua new file mode 100644 index 0000000..3993952 --- /dev/null +++ b/modules/chronometer/engine.lua @@ -0,0 +1,1379 @@ +local Chronometer = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceHook-2.1", "CandyBar-2.0") +local BS = { + GetSpellIcon = function(self, spellName) + local cacheOwner = type(self) == "table" and self or {} + local name = tostring(spellName or "") + if name == "" then + return "Interface\\Icons\\INV_Misc_QuestionMark" + end + + local iconCache = rawget(cacheOwner, "_iconCache") + if type(iconCache) ~= "table" then + iconCache = {} + rawset(cacheOwner, "_iconCache", iconCache) + end + if iconCache[name] then + return iconCache[name] + end + + if type(GetSpellName) == "function" and type(GetSpellTexture) == "function" then + local spellBookType = BOOKTYPE_SPELL or "spell" + local i = 1 + while true do + local okName, n = pcall(GetSpellName, i, spellBookType) + if not okName or not n then + break + end + if n == name then + local okTex, tex = pcall(GetSpellTexture, i, spellBookType) + if okTex and tex then + iconCache[name] = tex + return tex + end + break + end + i = i + 1 + end + end + + iconCache[name] = "Interface\\Icons\\INV_Misc_QuestionMark" + return "Interface\\Icons\\INV_Misc_QuestionMark" + end, +} +BS = setmetatable(BS, { + __index = function(_, key) + if MTH and MTH.LocalizeSpell then + return MTH:LocalizeSpell(key) + end + return key + end, +}) + +MTH_ChronometerHunter = Chronometer + +Chronometer.SPELL = 1 +Chronometer.EVENT = 2 +Chronometer.rightclick = false +Chronometer.dataSetup = {} + +local latins = { I = 1, II = 2, III = 3, IV = 4, V = 5, VI = 6, VII = 7, VIII = 8, IX = 9, X = 10, XI = 11, XII = 12, XIII = 13, XIV = 14 } +local BAR_GROUP = "MTHChronometer" +local PARSER_OWNER = "MTHChronometerHunter" + +local COLOR_NAME_MAP = { + white = { 1.0, 1.0, 1.0 }, + black = { 0.0, 0.0, 0.0 }, + blue = { 0.0, 0.0, 1.0 }, + magenta = { 1.0, 0.0, 1.0 }, + cyan = { 0.0, 1.0, 1.0 }, + green = { 0.0, 1.0, 0.0 }, + yellow = { 1.0, 1.0, 0.0 }, + orange = { 1.0, 0.5, 0.0 }, + red = { 1.0, 0.0, 0.0 }, + gray = { 0.5, 0.5, 0.5 }, + forest = { 0.0, 0.5, 0.0 }, + maroon = { 0.5, 0.0, 0.0 }, + navy = { 0.0, 0.0, 0.5 }, + olive = { 0.5, 0.5, 0.0 }, + purple = { 0.5, 0.0, 0.5 }, + teal = { 0.0, 0.5, 0.5 }, +} + +local DEFAULTS = { + growup = false, + reverse = false, + fadeonkill = true, + fadeonfade = true, + barposition = {}, + ghost = 0, + selfbars = true, + barwidth = 220, + barheight = 16, + spacing = 0, + barscale = 1, + iconposition = "LEFT", + textsize = 10, + textcolor = "white", + bgcolor = "teal", + barcolor = "gray", + bordercolor = "black", + bordertex = "None", + bgalpha = 0.5, + text = "$t", + onlyself = false, + disabledSpells = { + COMMON = { + ["Ephemeral Power"] = {}, + ["Mind Quickening"] = {}, + ["Unstable Power"] = {}, + }, + RACIAL = {}, + HUNTER = {}, + }, +} + +local function seedDefaultCommonDisabled(commonBucket) + if type(commonBucket) ~= "table" then + return + end + local names = { + "Ephemeral Power", + "Mind Quickening", + "Unstable Power", + } + for i = 1, table.getn(names) do + local baseName = names[i] + if commonBucket[baseName] == nil then + commonBucket[baseName] = {} + end + if MTH and MTH.LocalizeSpell then + local localized = MTH:LocalizeSpell(baseName) + if localized and localized ~= "" and commonBucket[localized] == nil then + commonBucket[localized] = {} + end + end + end +end + +local function shallowCopy(source) + local dest = {} + for key, value in pairs(source) do + dest[key] = value + end + return dest +end + +local function ensureProfile() + if not MTH or not MTH.GetModuleSavedVariables then + return shallowCopy(DEFAULTS) + end + + local store = MTH:GetModuleSavedVariables("chronometer") + if type(store.profile) ~= "table" then + store.profile = {} + end + local profile = store.profile + + for key, value in pairs(DEFAULTS) do + if profile[key] == nil then + if type(value) == "table" then + profile[key] = shallowCopy(value) + else + profile[key] = value + end + end + end + + if type(profile.disabledSpells) ~= "table" then + profile.disabledSpells = {} + end + if type(profile.disabledSpells.COMMON) ~= "table" then + profile.disabledSpells.COMMON = {} + end + seedDefaultCommonDisabled(profile.disabledSpells.COMMON) + if type(profile.disabledSpells.RACIAL) ~= "table" then + profile.disabledSpells.RACIAL = {} + end + if type(profile.disabledSpells.HUNTER) ~= "table" then + profile.disabledSpells.HUNTER = {} + end + + return profile +end + +local function MTH_CHRON_IsPersistedEnabled() + if MTH and MTH.IsModuleEnabled then + return MTH:IsModuleEnabled("chronometer", true) and true or false + end + + if type(MTH_SavedVariables) == "table" then + if type(MTH_SavedVariables.modules) == "table" + and type(MTH_SavedVariables.modules.chronometer) == "table" + and MTH_SavedVariables.modules.chronometer.enabled ~= nil + then + return MTH_SavedVariables.modules.chronometer.enabled and true or false + end + + if type(MTH_SavedVariables.chronometer) == "table" + and MTH_SavedVariables.chronometer.enabled ~= nil + then + return MTH_SavedVariables.chronometer.enabled and true or false + end + end + + return true +end + +local function convertcolor(color) + if type(color) == "string" then + local key = string.lower(color) + local named = COLOR_NAME_MAP[key] + if named then + return { named[1], named[2], named[3] } + end + if string.len(key) == 6 then + local rr = tonumber(string.sub(key, 1, 2), 16) + local gg = tonumber(string.sub(key, 3, 4), 16) + local bb = tonumber(string.sub(key, 5, 6), 16) + if rr and gg and bb then + return { rr / 255, gg / 255, bb / 255 } + end + end + return { 1, 1, 1 } + elseif type(color) == "table" then + if type(color[1]) == "table" then + color = color[1] + end + local r = tonumber(color[1] or color.r) + local g = tonumber(color[2] or color.g) + local b = tonumber(color[3] or color.b) + if r and g and b then + return { r, g, b } + end + return { 1, 1, 1 } + else + return { 1, 1, 1 } + end +end + +local function parseRankNumber(text) + if not text or text == "" then + return 0 + end + local _, _, rankText = string.find(tostring(text), "(%d+)") + local rank = tonumber(rankText) + if rank then + return rank + end + local _, _, roman = string.find(tostring(text), "([IVX]+)") + if roman and latins[roman] then + return latins[roman] + end + return 0 +end + +local function getActionSpell(slot) + if GetActionText(slot) or not HasAction(slot) then + return nil, 0 + end + + if not Chronometer._scanTooltip then + Chronometer._scanTooltip = CreateFrame("GameTooltip", "MTH_ChronometerScanTooltip", UIParent, "GameTooltipTemplate") + Chronometer._scanTooltip:SetOwner(UIParent, "ANCHOR_NONE") + end + + local tooltip = Chronometer._scanTooltip + tooltip:ClearLines() + tooltip:SetAction(slot) + + local left1 = getglobal("MTH_ChronometerScanTooltipTextLeft1") + local left2 = getglobal("MTH_ChronometerScanTooltipTextLeft2") + local spellName = left1 and left1:GetText() or nil + if not spellName or spellName == "" then + return nil, 0 + end + + local rankText = left2 and left2:GetText() or "" + return spellName, parseRankNumber(rankText) +end + +local function callGlobal(name, a1, a2, a3, a4) + if type(name) ~= "string" or name == "" then + return nil + end + local fn = getglobal(name) + if type(fn) == "function" then + return fn(a1, a2, a3, a4) + end + return nil +end + +function Chronometer:MTH_Initialize() + if self._mth_initialized then + return + end + + self.profile = ensureProfile() + self.parser = ParserLib:GetInstance("1.1") + + self.COLOR_MAP = { + [0] = "olive", + [1] = "teal", + [2] = "purple", + [3] = "forest", + } + + self.anchor = self:CreateAnchor("MetaHunt Chronometer", 0, 1, 0) + self:RegisterCandyBarGroup(BAR_GROUP) + self:SetCandyBarGroupPoint(BAR_GROUP, "TOP", self.anchor, "BOTTOM", 0, 0) + + self._mth_initialized = true +end + +function Chronometer:MTH_Enable() + self._mth_explicitEnable = true + self:MTH_Initialize() + self:OnEnable() + self._mth_explicitEnable = nil +end + +function Chronometer:MTH_Disable() + self:OnDisable() +end + +function Chronometer:OnEnable() + if self._mth_enabled then + return + end + + if (not self._mth_explicitEnable) and (not MTH_CHRON_IsPersistedEnabled()) then + if self.parser then + self.parser:UnregisterAllEvents(PARSER_OWNER) + end + self:UnregisterAllEvents() + if self.CancelAllScheduledEvents then + self:CancelAllScheduledEvents() + end + self:UnhookAll() + if self.anchor then + self.anchor:Hide() + end + self.bars = nil + self.timers = nil + self.groups = nil + self.captive = nil + self.active = nil + self._mth_enabled = false + return + end + + self:MTH_Initialize() + self._mth_enabled = true + + self.profile = ensureProfile() + self:SetCandyBarGroupGrowth(BAR_GROUP, self.profile.growup and true or false) + self:SetCandyBarGroupVerticalSpacing(BAR_GROUP, self.profile.spacing or 0) + + self.groups = {} + self.timers = {} + self.bars = {} + for i = 1, 20 do + self.bars[i] = {} + end + + for _, setupFunc in pairs(self.dataSetup) do + setupFunc(self) + end + + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_COMBAT_FRIENDLY_DEATH", function(event, info) self:COMBAT_DEATH(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_COMBAT_HOSTILE_DEATH", function(event, info) self:COMBAT_DEATH(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_COMBAT_XP_GAIN", function(event, info) self:COMBAT_DEATH(event, info) end) + + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_AURA_GONE_SELF", function(event, info) self:SPELL_FADE(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_AURA_GONE_OTHER", function(event, info) self:SPELL_FADE(event, info) end) + + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE", function(event, info) self:SPELL_PERIODIC(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS", function(event, info) self:SPELL_PERIODIC(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS", function(event, info) self:SPELL_PERIODIC(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE", function(event, info) self:SPELL_PERIODIC(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS", function(event, info) self:SPELL_PERIODIC(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE", function(event, info) self:SPELL_PERIODIC(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS", function(event, info) self:SPELL_PERIODIC(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE", function(event, info) self:SPELL_PERIODIC(event, info) end) + + local enableRoM = false + for _, timer in pairs(self.timers[self.SPELL] or {}) do + if timer.x.rom or timer.x.romc then + enableRoM = true + break + end + end + if not enableRoM then + for _, timer in pairs(self.timers[self.EVENT] or {}) do + if timer.x.rom or timer.x.romc then + enableRoM = true + break + end + end + end + if enableRoM then + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_COMBAT_SELF_HITS", function(event, info) self:SELF_HITS(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_COMBAT_SELF_CRITS", function(event, info) self:SELF_CRITS(event, info) end) + end + + self.captive = {} + self.active = {} + if type(getglobal("UseAction")) == "function" then + self:Hook("UseAction") + end + if type(getglobal("CastSpell")) == "function" then + self:Hook("CastSpell") + end + if type(getglobal("CastSpellByName")) == "function" then + self:Hook("CastSpellByName") + end + if type(getglobal("SpellTargetUnit")) == "function" then + self:Hook("SpellTargetUnit") + end + if type(getglobal("TargetUnit")) == "function" then + self:Hook("TargetUnit") + end + if type(getglobal("SpellStopTargeting")) == "function" then + self:Hook("SpellStopTargeting") + end + if type(getglobal("SpellStopCasting")) == "function" then + self:Hook("SpellStopCasting") + end + if WorldFrame then + self:HookScript(WorldFrame, "OnMouseDown") + end + self:RegisterEvent("SPELLCAST_INTERRUPTED") + self:RegisterEvent("SPELLCAST_START") + self:RegisterEvent("SPELLCAST_STOP") + self:RegisterEvent("PLAYER_DEAD") + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_SELF_DAMAGE", function(event, info) self:SELF_DAMAGE(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF", function(event, info) self:SELF_DAMAGE(event, info) end) + self.parser:RegisterEvent(PARSER_OWNER, "CHAT_MSG_SPELL_FAILED_LOCALPLAYER", function(event, info) self:SPELL_FAILED(event, info) end) +end + +function Chronometer:OnDisable() + if not self._mth_enabled then + return + end + self._mth_enabled = false + + if self.bars then + for i = 1, 20 do + if self.bars[i].id then + self:StopCandyBar(self.bars[i].id) + end + end + end + if self.anchor then + self.anchor:Hide() + end + self.bars = nil + self.timers = nil + self.groups = nil + self.captive = nil + self.active = nil + + if self.parser then + self.parser:UnregisterAllEvents(PARSER_OWNER) + end + self:UnregisterAllEvents() + if self.CancelAllScheduledEvents then + self:CancelAllScheduledEvents() + end + self:UnhookAll() +end + +function Chronometer:AddGroup(id, forall, color) + if color then + self.groups[id] = { fa = forall, cr = color } + else + self.groups[id] = { fa = forall } + end +end + +function Chronometer:AddTimer(kind, name, duration, targeted, isgain, selforselect, extra) + if not self.timers[kind] then + self.timers[kind] = {} + end + if not self.timers[kind][name] then + self.timers[kind][name] = {} + end + if not extra then + extra = {} + end + targeted = (targeted and targeted > 0) and 1 or nil + isgain = (isgain and isgain > 0) and 1 or nil + selforselect = (selforselect and selforselect > 0) and 1 or nil + if not extra.cr then + if extra.gr and self.groups[extra.gr] and self.groups[extra.gr].cr then + extra.cr = self.groups[extra.gr].cr + else + local ccode = (targeted and 2 or 0) + (isgain and 1 or 0) + extra.cr = self.COLOR_MAP[ccode] + end + end + self.timers[kind][name] = { d = duration, k = { t = targeted, g = isgain, s = selforselect }, x = extra } +end + +function Chronometer:StartTimer(timer, name, target, rank, durmod) + target = self:DisambiguateTarget(timer, target) + local idTarget = target + + do + local sp = (BS and BS["Scorpid Poison"]) or "Scorpid Poison" + if name == sp and target and target ~= "none" then + local label = tostring(target) + local base = label + local pHash = string.find(label, " #", 1, true) + if pHash then + base = string.sub(base, 1, pHash - 1) + end + local pRt = string.find(base, " {RT", 1, true) + if pRt then + base = string.sub(base, 1, pRt - 1) + end + + local guid = self:SPGetGUIDForName(base) + if guid then + self._spGuidOrdinal = self._spGuidOrdinal or {} + self._spGuidOrdinal[base] = self._spGuidOrdinal[base] or {} + local ord = self._spGuidOrdinal[base][guid] + if not ord then + local used = {} + for _, orderValue in pairs(self._spGuidOrdinal[base]) do + if type(orderValue) == "number" then + used[orderValue] = true + end + end + local n = 1 + while used[n] do + n = n + 1 + end + ord = n + self._spGuidOrdinal[base][guid] = ord + end + idTarget = base .. " #" .. guid + target = base .. " #" .. ord + end + end + end + + local _, class = UnitClass("player") + local timerClass = timer.x.cl == nil and class or timer.x.cl + if self.profile.disabledSpells[timerClass] and self.profile.disabledSpells[timerClass][name] ~= nil then + return + end + + if not target then + target = "none" + end + if not rank then + rank = timer.r or 0 + end + if not durmod then + durmod = 0 + end + if timer.x.gr then + self:CleanGroup(timer.x.gr, target) + end + if timer.d == 0 then + return + end + if (not self.profile.selfbars) and (target == UnitName("player") or (target == "none" and timer.k.g)) then + return + end + if self.profile.onlyself and timer.k.t ~= nil and target ~= UnitName("player") then + return + end + + local id = name .. "-" .. idTarget + local slot = nil + for i = 20, 1, -1 do + if self.bars[i].id == id then + self:SetCandyBarFade(id, 0, false) + self:StopCandyBar(self.bars[i].id) + self:ReallyStopBar(self.bars[i].id) + break + end + end + for i = 1, 20 do + if not self.bars[i].id then + slot = i + break + end + end + if not slot then + slot = 20 + end + + self.bars[slot].id = id + self.bars[slot].timer = timer + self.bars[slot].name = name + self.bars[slot].rank = rank + self.bars[slot].target = target + self.bars[slot].group = timer.x.gr + + local duration = (timer.x.d and self:GetDuration(timer.d, timer.x.d, rank, timer.cp) or timer.d) + durmod + local text = target == "none" and name or self.profile.text + text = string.gsub(text, "$t", target) + text = string.gsub(text, "$s", name) + local icon = timer.x.tx or self:GetTexture(name, timer.x) + local color = convertcolor(timer.x.cr or self.profile.barcolor) + local fade = (timer.x.rc and self.profile.ghost) and self.profile.ghost or 0.5 + + self:RegisterCandyBar(id, duration, text, icon, color[1], color[2], color[3], 1, 0, 0) + self:RegisterCandyBarWithGroup(id, BAR_GROUP) + if self.profile.barscale then self:SetCandyBarScale(id, self.profile.barscale) end + if self.profile.barwidth then self:SetCandyBarWidth(id, self.profile.barwidth) end + if self.profile.barheight then self:SetCandyBarHeight(id, self.profile.barheight) end + if self.profile.iconposition then self:SetCandyBarIconPosition(id, self.profile.iconposition) end + if self.profile.spacing then self:SetCandyBarGroupVerticalSpacing(BAR_GROUP, self.profile.spacing) end + if self.profile.textsize then self:SetCandyBarFontSize(id, self.profile.textsize) end + if self.profile.textcolor then local c = convertcolor(self.profile.textcolor); self:SetCandyBarTextColor(id, c[1], c[2], c[3]) end + if self.profile.bgcolor then local c = convertcolor(self.profile.bgcolor); self:SetCandyBarBackgroundColor(id, c[1], c[2], c[3], self.profile.bgalpha) end + if self.profile.bordercolor then local c = convertcolor(self.profile.bordercolor); self:SetCandyBarBorderColor(id, c[1], c[2], c[3]) end + if self.profile.bordertex then self:SetCandyBarBorderTexture(id, self.profile.bordertex) end + self:SetCandyBarFade(id, fade, true) + self:SetCandyBarCompletion(id, self.StopBar, self, id) + self:SetCandyBarReversed(id, self.profile.reverse) + self:SetCandyBarOnClick(id, function(a1, a2, a3, a4, a5) self:CandyOnClick(a1, a2, a3, a4, a5) end, timer.x.rc, timer.x.mc) + self:StartCandyBar(id, true) +end + +function Chronometer:DisambiguateTarget(timer, t) + if not timer or not timer.x or not timer.x.dn then + return t + end + local base = tostring(t or "") + if base == "" then + return base + end + if string.find(base, " #", 1, true) or string.find(base, "{RT", 1, true) then + return base + end + + local unit = nil + if UnitExists("pettarget") and UnitName("pettarget") == base then unit = "pettarget" end + if UnitExists("target") and UnitName("target") == base and not unit then unit = "target" end + + local withIcon = nil + if unit then + local idx = GetRaidTargetIndex(unit) + if idx then + withIcon = base .. " {RT" .. idx .. "}" + end + end + + local function used(label) + for i = 1, 20 do + local bar = self.bars[i] + if bar and bar.id and bar.target == label then + if not timer.x.gr or (bar.timer and bar.timer.x and bar.timer.x.gr == timer.x.gr) then + return true + end + end + end + return false + end + + if withIcon and not used(withIcon) then return withIcon end + if not used(base) then return base end + for n = 2, 9 do + local candidate = base .. " #" .. n + if not used(candidate) then + return candidate + end + end + return base .. " #?" +end + +function Chronometer:GetDuration(duration, record, rank, cp) + if record.rt then duration = record.rt[rank] or duration end + if record.rs then duration = duration + (rank - 1) * record.rs end + if record.cp and cp then duration = duration + (cp - 1) * record.cp end + + if record.tn then + if type(record.tn) == "string" then + record.tn = self:GetTalentPosition(record.tn) + end + if record.tn then + local _, _, _, _, talentRank = GetTalentInfo(record.tn[1], record.tn[2]) + if talentRank and talentRank > 0 then + local gain = record.tt and record.tt[talentRank] or (record.tb + (talentRank - 1) * (record.ts or record.tb)) + duration = duration + (record.tp and (duration / 100) * gain or gain) + end + end + end + return duration +end + +function Chronometer:GetTexture(name, record) + if record.xn then + name = record.xn + end + record.tx = BS:GetSpellIcon(name) + return record.tx +end + +function Chronometer:GetTalentPosition(name) + for i = 1, GetNumTalentTabs() do + for j = 1, GetNumTalents(i) do + local talentName = GetTalentInfo(i, j) + if talentName == name then + return { i, j } + end + end + end +end + +function Chronometer:IsBanished(target) + for i = 1, 20 do + if self.bars[i].id and self.bars[i].target == target and self.bars[i].name == BS["Banish"] then + return true + end + end + return false +end + +function Chronometer:CleanGroup(group, target) + if not group or type(self.groups) ~= "table" then + return + end + local groupDef = self.groups[group] + if type(groupDef) ~= "table" then + return + end + local forall = groupDef.fa + for i = 20, 1, -1 do + if self.bars[i].group and self.bars[i].group == group then + if forall or self.bars[i].target == target then + self:StopCandyBar(self.bars[i].id) + self:StopBar(self.bars[i].id) + if not forall then break end + end + end + end +end + +function Chronometer:KillBar(name, unit) + for i = 20, 1, -1 do + if self.bars[i].id and self.bars[i].name == name then + if not unit then + if self.bars[i].timer.k.t then unit = UnitName("player") else unit = "none" end + end + if self.bars[i].target == unit then + self:StopCandyBar(self.bars[i].id) + return self:StopBar(self.bars[i].id) + end + end + end +end + +function Chronometer:KillBars(unit) + if unit and UnitExists("target") and UnitName("target") == unit and not UnitIsDeadOrGhost("target") then + return + end + for i = 20, 1, -1 do + if self.bars[i].id and (not unit or self.bars[i].target == unit) then + self:SetCandyBarFade(self.bars[i].id, 0.5, true) + self:StopCandyBar(self.bars[i].id) + self:ReallyStopBar(self.bars[i].id) + end + end +end + +function Chronometer:StopBar(id) + if self.profile.ghost and self.profile.ghost > 0 then + self:ScheduleEvent("MTHChronometerStop" .. id, self.ReallyStopBar, self.profile.ghost, self, id) + else + self:ReallyStopBar(id) + end +end + +function Chronometer:ReallyStopBar(id) + self:CancelScheduledEvent("MTHChronometerStop" .. id) + for i = 1, 20 do + if self.bars[i].id == id then + for key in pairs(self.bars[i]) do + self.bars[i][key] = nil + end + end + end + for i = 1, 19 do + if not self.bars[i].id then + local temp = self.bars[i] + for j = i + 1, 20 do + if self.bars[j].id then + self.bars[i] = self.bars[j] + self.bars[j] = temp + temp = nil + break + end + end + if temp then break end + end + end +end + +function Chronometer:CandyOnClick(id, button, reactive, middlecast) + if button == "RightButton" then + MouselookStart() + Chronometer.rightclick = true + self:ScheduleEvent(function() Chronometer.rightclick = false; Chronometer:CancelScheduledEvent("MTHChronometerCheckMouselook") end, 0.5) + self:ScheduleRepeatingEvent("MTHChronometerCheckMouselook", Chronometer.onClickStopCandyBar, 0.06, self, id) + elseif button == "MiddleButton" and middlecast then + for i = 1, 20 do + if self.bars[i].id == id then + return self:CastSpellOnUnit(middlecast, self.bars[i].target) + end + end + elseif button == "LeftButton" and reactive then + for i = 1, 20 do + if self.bars[i].id == id then + return self:CastSpellOnUnit(self.bars[i].name, self.bars[i].target) + end + end + end +end + +function Chronometer:onClickStopCandyBar(id) + if Chronometer.rightclick and not IsMouselooking() then + self:SetCandyBarFade(id, 0.5, true) + self:StopCandyBar(id) + self:StopBar(id) + end +end + +function Chronometer:CastSpellOnUnit(spell, unit) + local restore = nil + local hadtarget = UnitExists("target") + + if unit and unit ~= "none" then + if hadtarget and UnitName("target") ~= unit then + restore = true + end + TargetByName(unit, true) + end + + CastSpellByName(spell) + + if restore then + TargetLastTarget() + elseif not hadtarget then + ClearTarget() + end +end + +function Chronometer:RunTest() + if not self._mth_enabled then + self:MTH_Enable() + end + if type(self.bars) ~= "table" then + self.bars = {} + for i = 1, 20 do + self.bars[i] = self.bars[i] or {} + end + end + + for i = 1, 5 do + local name = "Test" .. i + local target = "Test" + local rank = 1 + local id = name .. "-" .. target + local slot = nil + + for j = 20, 1, -1 do + if self.bars[j].id == id then + self:SetCandyBarFade(id, 0, false) + self:StopCandyBar(self.bars[j].id) + self:ReallyStopBar(self.bars[j].id) + break + end + end + for j = 1, 20 do + if not self.bars[j].id then + slot = j + break + end + end + if not slot then + slot = 20 + end + + self.bars[slot].id = id + self.bars[slot].timer = nil + self.bars[slot].name = name + self.bars[slot].rank = rank + self.bars[slot].target = target + self.bars[slot].group = nil + + local duration = math.random() * 8.0 + 2.0 + local text = self.profile.text or "$t" + text = string.gsub(text, "$t", target) + text = string.gsub(text, "$s", name) + local icon = "Interface\\Icons\\Spell_Shadow_ManaBurn" + local color = convertcolor(self.profile.barcolor) + + self:RegisterCandyBar(id, duration, text, icon, color[1], color[2], color[3], 1, 0, 0) + self:RegisterCandyBarWithGroup(id, BAR_GROUP) + if self.profile.barscale then self:SetCandyBarScale(id, self.profile.barscale) end + if self.profile.barwidth then self:SetCandyBarWidth(id, self.profile.barwidth) end + if self.profile.barheight then self:SetCandyBarHeight(id, self.profile.barheight) end + if self.profile.iconposition then self:SetCandyBarIconPosition(id, self.profile.iconposition) end + if self.profile.spacing then self:SetCandyBarGroupVerticalSpacing(BAR_GROUP, self.profile.spacing) end + if self.profile.textsize then self:SetCandyBarFontSize(id, self.profile.textsize) end + if self.profile.textcolor then local c = convertcolor(self.profile.textcolor); self:SetCandyBarTextColor(id, c[1], c[2], c[3]) end + if self.profile.bgcolor then local c = convertcolor(self.profile.bgcolor); self:SetCandyBarBackgroundColor(id, c[1], c[2], c[3], self.profile.bgalpha) end + if self.profile.bordercolor then local c = convertcolor(self.profile.bordercolor); self:SetCandyBarBorderColor(id, c[1], c[2], c[3]) end + if self.profile.bordertex then self:SetCandyBarBorderTexture(id, self.profile.bordertex) end + if self.profile.ghost then self:SetCandyBarFade(id, self.profile.ghost, true) end + self:SetCandyBarCompletion(id, self.StopBar, self, id) + self:SetCandyBarReversed(id, self.profile.reverse) + self:SetCandyBarOnClick(id, function(a1, a2, a3, a4, a5) self:CandyOnClick(a1, a2, a3, a4, a5) end) + self:StartCandyBar(id, true) + end +end + +function Chronometer:ToggleAnchor() + if self.anchor:IsVisible() then + self.anchor:Hide() + else + self.anchor:Show() + end +end + +function Chronometer:CreateAnchor(text, r, g, b) + local frame = CreateFrame("Button", "MTHChronometerAnchor", UIParent) + frame:SetWidth(220) + frame:SetHeight(25) + frame.owner = self + frame:SetClampedToScreen(true) + frame:SetMovable(true) + frame:RegisterForDrag("LeftButton") + + local px = tonumber(self.profile and self.profile.barposition and self.profile.barposition.x) + local py = tonumber(self.profile and self.profile.barposition and self.profile.barposition.y) + if px ~= nil and py ~= nil then + frame:ClearAllPoints() + frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", px, py) + else + frame:SetPoint("CENTER", UIParent, "CENTER", 0, 50) + end + + frame:SetScript("OnDragStart", function(self) + self = self or this + if not self or not self.StartMoving then + return + end + self:StartMoving() + end) + frame:SetScript("OnDragStop", function(self) + self = self or this + if not self then + return + end + + if self.StopMovingOrSizing then + self:StopMovingOrSizing() + end + + local point, _, _, x, y = self:GetPoint() + x = tonumber(x) + y = tonumber(y) + if not x or not y then + return + end + + x = math.floor(x + 0.5) + y = math.floor(y + 0.5) + self:ClearAllPoints() + self:SetPoint(point or "TOPLEFT", UIParent, point or "TOPLEFT", x, y) + + if self.owner and self.owner.profile and type(self.owner.profile.barposition) == "table" then + self.owner.profile.barposition.x = x + self.owner.profile.barposition.y = y + end + end) + + frame:SetBackdrop({ + bgFile = "Interface/Tooltips/UI-Tooltip-Background", + edgeFile = "Interface/Tooltips/UI-Tooltip-Border", + tile = false, + tileSize = 16, + edgeSize = 16, + insets = { left = 5, right = 5, top = 5, bottom = 5 }, + }) + frame:SetBackdropColor(r, g, b, 0.6) + + frame.Text = frame:CreateFontString(nil, "OVERLAY") + frame.Text:SetFontObject(GameFontNormalSmall) + frame.Text:ClearAllPoints() + frame.Text:SetTextColor(1, 1, 1, 1) + frame.Text:SetWidth(220) + frame.Text:SetHeight(25) + frame.Text:SetPoint("TOPLEFT", frame, "TOPLEFT") + frame.Text:SetJustifyH("CENTER") + frame.Text:SetJustifyV("MIDDLE") + frame.Text:SetText(text) + frame:Hide() + + return frame +end + +function Chronometer:COMBAT_DEATH(event, info) + if not self.profile.fadeonkill then return end + if info.type == "experience" then + if info.source and not self:IsBanished(info.source) then + self:ScheduleEvent(self.KillBars, 0.5, self, info.source) + return + end + elseif info.victim ~= ParserLib_SELF then + self:ScheduleEvent(self.KillBars, 0.5, self, info.victim) + return + end +end + +function Chronometer:SPELL_FADE(event, info) + if not self.profile.fadeonfade then return end + if info.skill == BS["Banish"] then return end + if info.type == "fade" then + if info.victim == ParserLib_SELF then + return self:KillBar(info.skill) + end + return self:KillBar(info.skill, info.victim) + end +end + +function Chronometer:SPELL_PERIODIC(event, info) + local aura, rank, unit, isgain + + if info.type == "buff" then + isgain = 1 + elseif info.type == "debuff" then + isgain = nil + elseif not info.isDOT then + return + end + + if info.victim ~= ParserLib_SELF then + unit = info.victim + end + aura = info.skill + _, _, rank = string.find(aura, "%s([IVX]+)[^u]") + if rank then + rank = latins[rank] + aura = string.gsub(aura, "%s([IVX]+)[^u]", "") + end + + if aura == "Deep Wound" then + aura = "Deep Wounds" + elseif aura == "Piercing Shots" then + aura = "Piercing Shot" + end + + local timer = self.timers[self.EVENT] and self.timers[self.EVENT][aura] + if timer and timer.k.g == isgain and not info.isDOT and (timer.x.a or (timer.v and timer.v > GetTime())) then + if timer.k.t then + if not unit then unit = UnitName("player") end + if timer.k.s then + if timer.t and timer.t ~= unit then return end + else + if not UnitExists("target") or unit ~= UnitName("target") then return end + end + else + if not timer.k.s or not unit then unit = "none" else return end + end + timer.v = nil + timer.t = nil + self:StartTimer(timer, aura, unit, rank) + elseif timer and info.isDOT and not timer.x.a then + timer.v = nil + timer.t = nil + self:StartTimer(timer, aura, "none") + end +end + +function Chronometer:SELF_CRITS(event, info) + if info.type == "hit" and info.source == ParserLib_SELF and info.isCrit then + for i = 1, 20 do + if self.bars[i].id and self.bars[i].timer.x.romc then + self:StartTimer(self.bars[i].timer, self.bars[i].name, self.bars[i].target, self.bars[i].rank) + end + end + end +end + +function Chronometer:PLAYER_DEAD() + self.active = {} + self.captive = {} + + local unit = UnitName("player") + for i = 20, 1, -1 do + if self.bars[i].id and (self.bars[i].target == unit or (self.bars[i].target == "none" and self.bars[i].timer.k.g)) then + self:SetCandyBarFade(self.bars[i].id, 0.5, true) + self:StopCandyBar(self.bars[i].id) + self:ReallyStopBar(self.bars[i].id) + end + end +end + +function Chronometer:UseAction(slot, clicked, onself) + local name, rank = getActionSpell(slot) + if name then + local normalized = tostring(name) + normalized = string.gsub(normalized, "^%s+", "") + normalized = string.gsub(normalized, "%s+$", "") + local _, _, baseName = string.find(normalized, "^(.-)%s*%(.+%)$") + if baseName and baseName ~= "" then + normalized = baseName + end + + local timer = nil + if self.timers[self.SPELL] then + timer = self.timers[self.SPELL][normalized] or self.timers[self.SPELL][name] + end + if timer then + self:CatchSpellcast(timer, normalized, rank, onself) + end + end + if self.hooks and type(self.hooks["UseAction"]) == "function" then + return self.hooks["UseAction"](slot, clicked, onself) + end + return callGlobal("UseAction", slot, clicked, onself) +end + +function Chronometer:CastSpell(index, booktype) + local name, rankText = GetSpellName(index, booktype) + local timer = self.timers[self.SPELL] and self.timers[self.SPELL][name] + if timer then + self:CatchSpellcast(timer, name, parseRankNumber(rankText)) + end + if self.hooks and type(self.hooks["CastSpell"]) == "function" then + return self.hooks["CastSpell"](index, booktype) + end + return callGlobal("CastSpell", index, booktype) +end + +function Chronometer:CastSpellByName(text, onself) + local raw = tostring(text or "") + local name = raw + local rank = 0 + local _, _, spellName, paren = string.find(raw, "^(.-)%s*%((.-)%)$") + if spellName and paren then + name = spellName + rank = parseRankNumber(paren) + end + name = string.gsub(name, "^%s+", "") + name = string.gsub(name, "%s+$", "") + local timer = self.timers[self.SPELL] and self.timers[self.SPELL][name] + if timer then + self:CatchSpellcast(timer, name, rank, onself) + end + if self.hooks and type(self.hooks["CastSpellByName"]) == "function" then + return self.hooks["CastSpellByName"](text, onself) + end + return callGlobal("CastSpellByName", text, onself) +end + +function Chronometer:CatchSpellcast(timer, name, rank, onself) + local unit = nil + if timer.k.t then + if timer.k.s then + if onself and onself == 1 then + unit = UnitName("player") + elseif UnitExists("target") then + if timer.k.g then + if UnitIsFriend("player", "target") then unit = UnitName("target") end + else + if UnitCanAttack("player", "target") then unit = UnitName("target") end + end + end + else + if UnitExists("target") then unit = UnitName("target") else return end + end + else + unit = "none" + end + + local cp = GetComboPoints("player", "target") + if cp and cp > 0 then + timer.cp = cp + end + table.insert(self.captive, { t = timer, n = name, u = unit, r = rank }) +end + +function Chronometer:SpellTargetUnit(unit) + for _, captive in pairs(self.captive) do + if not captive.u then + captive.u = UnitName(unit) + end + end + if self.hooks and type(self.hooks["SpellTargetUnit"]) == "function" then + return self.hooks["SpellTargetUnit"](unit) + end + return callGlobal("SpellTargetUnit", unit) +end + +function Chronometer:TargetUnit(unit) + for _, captive in pairs(self.captive) do + if not captive.u then + captive.u = UnitName(unit) + end + end + if self.hooks and type(self.hooks["TargetUnit"]) == "function" then + return self.hooks["TargetUnit"](unit) + end + return callGlobal("TargetUnit", unit) +end + +function Chronometer:OnMouseDown(button) + for _, captive in pairs(self.captive) do + if not captive.u and button == "LeftButton" and UnitExists("mouseover") then + captive.u = UnitName("mouseover") + end + end + if self.hooks and WorldFrame and self.hooks[WorldFrame] and type(self.hooks[WorldFrame]["OnMouseDown"]) == "function" then + return self.hooks[WorldFrame]["OnMouseDown"](WorldFrame, button) + end + if WorldFrame and type(WorldFrame.GetScript) == "function" then + local script = WorldFrame:GetScript("OnMouseDown") + if type(script) == "function" and script ~= Chronometer.OnMouseDown then + return script(WorldFrame, button) + end + end + return nil +end + +function Chronometer:SPELLCAST_START() +end + +function Chronometer:SPELLCAST_STOP() + local captive = self.captive[1] + if captive then + if captive.t and captive.t.x.ea then + for name, valid in pairs(captive.t.x.ea) do + local eventTimer = self.timers and self.timers[Chronometer.EVENT] and self.timers[Chronometer.EVENT][name] + if eventTimer then + eventTimer.r = captive.r + eventTimer.v = GetTime() + valid + if captive.u ~= "none" then + eventTimer.t = captive.u + end + end + end + end + + if captive.u == "none" then + self:StartTimer(captive.t, captive.n, captive.u, captive.r) + else + self.active[captive.n] = { t = captive.t, n = captive.n, u = captive.u, r = captive.r } + self:ScheduleEvent(self.CompleteCast, 0.5, self, captive.n) + end + end + self.captive = {} +end + +function Chronometer:CompleteCast(name) + local active = self.active[name] + if active and active.t then + self:StartTimer(active.t, active.n, active.u, active.r, -0.5) + self.active[name] = nil + end +end + +function Chronometer:SpellStopCasting() + self.captive = {} + if self.hooks and type(self.hooks["SpellStopCasting"]) == "function" then + return self.hooks["SpellStopCasting"]() + end + return callGlobal("SpellStopCasting") +end + +function Chronometer:SpellStopTargeting() + if type(self.captive) == "table" and table.getn(self.captive) > 0 then + local kept = {} + for i = 1, table.getn(self.captive) do + local captive = self.captive[i] + if captive and captive.t and captive.t.k and not captive.t.k.t then + table.insert(kept, captive) + end + end + self.captive = kept + else + self.captive = {} + end + if self.hooks and type(self.hooks["SpellStopTargeting"]) == "function" then + return self.hooks["SpellStopTargeting"]() + end + return callGlobal("SpellStopTargeting") +end + +function Chronometer:SPELL_FAILED(event, info) + for k, captive in pairs(self.captive) do + if captive.n == info.skill then + table.remove(self.captive, k) + break + end + end +end + +function Chronometer:SPELLCAST_INTERRUPTED() + for _, active in pairs(self.active) do + if active.t and active.t.x.ea then + for name in pairs(active.t.x.ea) do + local eventTimer = self.timers and self.timers[Chronometer.EVENT] and self.timers[Chronometer.EVENT][name] + if eventTimer then + eventTimer.r = nil + eventTimer.v = nil + eventTimer.t = nil + end + end + end + end + self.active = {} +end + +function Chronometer:SELF_DAMAGE(event, info) + local active = self.active[info.skill] + if active and info.type == "miss" and info.victim == active.u then + if active.t and active.t.x.ea then + for name in pairs(active.t.x.ea) do + local eventTimer = self.timers and self.timers[Chronometer.EVENT] and self.timers[Chronometer.EVENT][name] + if eventTimer then + eventTimer.r = nil + eventTimer.v = nil + eventTimer.t = nil + end + end + end + self.active[info.skill] = nil + elseif active and info.type == "hit" and info.victim == active.u then + if active.t and active.t.x.ea then + for name in pairs(active.t.x.ea) do + for i = 1, 20 do + if self.bars[i].id and self.bars[i].id == name .. "-" .. info.victim then + self:StartTimer(self.bars[i].timer, self.bars[i].name, self.bars[i].target, self.bars[i].rank) + end + end + end + end + end +end + +function Chronometer:SELF_HITS(event, info) + if info.type == "hit" and info.source == ParserLib_SELF then + for i = 1, 20 do + if self.bars[i].id and self.bars[i].target == info.victim and self.bars[i].timer.x.rom then + self:StartTimer(self.bars[i].timer, self.bars[i].name, self.bars[i].target, self.bars[i].rank) + end + end + end +end + +function Chronometer:SPGetGUIDForName(base) + local name = tostring(base or "") + if name == "" then return nil end + + local function guidOf(unit) + if not UnitExists(unit) then + return nil + end + if UnitName(unit) ~= name then + return nil + end + if type(UnitGUID) == "function" then + local guid = UnitGUID(unit) + if guid and guid ~= "" then + return guid + end + end + return nil + end + + return guidOf("pettarget") or guidOf("target") or guidOf("mouseover") +end diff --git a/modules/chronometer/libs/AceAddon-2.0/AceAddon-2.0.lua b/modules/chronometer/libs/AceAddon-2.0/AceAddon-2.0.lua new file mode 100644 index 0000000..04ec5de --- /dev/null +++ b/modules/chronometer/libs/AceAddon-2.0/AceAddon-2.0.lua @@ -0,0 +1,1078 @@ +--[[ +Name: AceAddon-2.0 +Revision: $Rev: 17957 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceAddon-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceAddon-2.0 +Description: Base for all Ace addons to inherit from. +Dependencies: AceLibrary, AceOO-2.0, AceEvent-2.0, (optional) AceConsole-2.0 +]] + +local MAJOR_VERSION = "AceAddon-2.0" +local MINOR_VERSION = "$Revision: 17957 $" + +-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end + +local function safecall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) + local success, err = pcall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) + if not success then geterrorhandler()(err) end +end + +-- Localization +local STANDBY, TITLE, NOTES, VERSION, AUTHOR, DATE, CATEGORY, EMAIL, CREDITS, WEBSITE, COMMANDS, CATEGORIES, ABOUT, PRINT_ADDON_INFO +if GetLocale() == "deDE" then + STANDBY = "|cffff5050(Standby)|r" -- capitalized + + TITLE = "Titel" + NOTES = "Anmerkung" + VERSION = "Version" + AUTHOR = "Autor" + DATE = "Datum" + CATEGORY = "Kategorie" + EMAIL = "E-mail" + WEBSITE = "Webseite" + CREDITS = "Credits" -- fix + COMMANDS = "Commands" + + ABOUT = "\195\188ber" + PRINT_ADDON_INFO = "Gibt Addondaten aus" + + CATEGORIES = { + ["Action Bars"] = "Aktionsleisten", + ["Auction"] = "Auktion", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Schlachtfeld/PvP", + ["Buffs"] = "Buffs", + ["Chat/Communication"] = "Chat/Kommunikation", + ["Druid"] = "Druide", + ["Hunter"] = "Jäger", + ["Mage"] = "Magier", + ["Paladin"] = "Paladin", + ["Priest"] = "Priester", + ["Rogue"] = "Schurke", + ["Shaman"] = "Schamane", + ["Warlock"] = "Hexenmeister", + ["Warrior"] = "Krieger", + ["Healer"] = "Heiler", + ["Tank"] = "Tank", -- noone use "Brecher"... + ["Caster"] = "Caster", + ["Combat"] = "Kampf", + ["Compilations"] = "Compilations", -- whats that o_O + ["Data Export"] = "Datenexport", + ["Development Tools"] = "Entwicklungs Tools", + ["Guild"] = "Gilde", + ["Frame Modification"] = "Frame Modifikation", + ["Interface Enhancements"] = "Interface Verbesserungen", + ["Inventory"] = "Inventar", + ["Library"] = "Library", + ["Map"] = "Map", + ["Mail"] = "Mail", + ["Miscellaneous"] = "Diverses", + ["Quest"] = "Quest", + ["Raid"] = "Schlachtzug", + ["Tradeskill"] = "Handelsf\195\164higkeit", + ["UnitFrame"] = "UnitFrame", + } +elseif GetLocale() == "frFR" then + STANDBY = "|cffff5050(attente)|r" + + TITLE = "Titre" + NOTES = "Notes" + VERSION = "Version" + AUTHOR = "Auteur" + DATE = "Date" + CATEGORY = "Cat\195\169gorie" + EMAIL = "E-mail" + WEBSITE = "Site web" + CREDITS = "Credits" -- fix + COMMANDS = "Commands" + + ABOUT = "A propos" + PRINT_ADDON_INFO = "Afficher les informations sur l'addon" + + CATEGORIES = { + ["Action Bars"] = "Barres d'action", + ["Auction"] = "H\195\180tel des ventes", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Champs de bataille/JcJ", + ["Buffs"] = "Buffs", + ["Chat/Communication"] = "Chat/Communication", + ["Druid"] = "Druide", + ["Hunter"] = "Chasseur", + ["Mage"] = "Mage", + ["Paladin"] = "Paladin", + ["Priest"] = "Pr\195\170tre", + ["Rogue"] = "Voleur", + ["Shaman"] = "Chaman", + ["Warlock"] = "D\195\169moniste", + ["Warrior"] = "Guerrier", + ["Healer"] = "Soigneur", + ["Tank"] = "Tank", + ["Caster"] = "Casteur", + ["Combat"] = "Combat", + ["Compilations"] = "Compilations", + ["Data Export"] = "Exportation de donn\195\169es", + ["Development Tools"] = "Outils de d\195\169veloppement", + ["Guild"] = "Guilde", + ["Frame Modification"] = "Modification des fen\195\170tres", + ["Interface Enhancements"] = "Am\195\169liorations de l'interface", + ["Inventory"] = "Inventaire", + ["Library"] = "Biblioth\195\168ques", + ["Map"] = "Carte", + ["Mail"] = "Courrier", + ["Miscellaneous"] = "Divers", + ["Quest"] = "Qu\195\170tes", + ["Raid"] = "Raid", + ["Tradeskill"] = "M\195\169tiers", + ["UnitFrame"] = "Fen\195\170tres d'unit\195\169", + } +elseif GetLocale() == "koKR" then + STANDBY = "|cffff5050(사용가능)|r" + + TITLE = "제목" + NOTES = "노트" + VERSION = "버전" + AUTHOR = "저작ìž" + DATE = "ë‚ ì§œ" + CATEGORY = "분류" + EMAIL = "E-mail" + WEBSITE = "웹사ì´íЏ" + CREDITS = "Credits" -- fix + COMMANDS = "Commands" + + ABOUT = "ì •ë³´" + PRINT_ADDON_INFO = "애드온 ì •ë³´ 출력" + + CATEGORIES = { + ["Action Bars"] = "액션바", + ["Auction"] = "경매", + ["Audio"] = "ìŒí–¥", + ["Battlegrounds/PvP"] = "전장/PvP", + ["Buffs"] = "버프", + ["Chat/Communication"] = "대화/ì˜ì‚¬ì†Œí†µ", + ["Druid"] = "드루ì´ë“œ", + ["Hunter"] = "사냥꾼", + ["Mage"] = "마법사", + ["Paladin"] = "성기사", + ["Priest"] = "사제", + ["Rogue"] = "ë„ì ", + ["Shaman"] = "주술사", + ["Warlock"] = "í‘마법사", + ["Warrior"] = "전사", + ["Healer"] = "ížëŸ¬", + ["Tank"] = "탱커", + ["Caster"] = "ìºìŠ¤í„°", + ["Combat"] = "전투", + ["Compilations"] = "복합", + ["Data Export"] = "ìžë£Œ 출력", + ["Development Tools"] = "개발 ë„구", + ["Guild"] = "길드", + ["Frame Modification"] = "구조 변경", + ["Interface Enhancements"] = "ì¸í„°íŽ˜ì´ìФ ê°•í™”", + ["Inventory"] = "ì¸ë²¤í† ë¦¬", + ["Library"] = "ë¼ì´ë¸ŒëŸ¬ë¦¬", + ["Map"] = "ì§€ë„", + ["Mail"] = "우편", + ["Miscellaneous"] = "기타", + ["Quest"] = "퀘스트", + ["Raid"] = "공격대", + ["Tradeskill"] = "전문기술", + ["UnitFrame"] = "유닛 프레임", + } +elseif GetLocale() == "zhTW" then + STANDBY = "|cffff5050(待命)|r" + + TITLE = "標題" + NOTES = "註記" + VERSION = "版本" + AUTHOR = "作者" + DATE = "日期" + CATEGORY = "類別" + EMAIL = "E-mail" + WEBSITE = "網站" + CREDITS = "Credits" -- fix + COMMANDS = "Commands" + + ABOUT = "關於" + PRINT_ADDON_INFO = "顯示æ’件資訊" + + CATEGORIES = { + ["Action Bars"] = "動作列", + ["Auction"] = "æ‹è³£", + ["Audio"] = "音樂", + ["Battlegrounds/PvP"] = "戰場/PvP", + ["Buffs"] = "增益", + ["Chat/Communication"] = "èŠå¤©/通訊", + ["Druid"] = "德魯伊", + ["Hunter"] = "çµäºº", + ["Mage"] = "法師", + ["Paladin"] = "è–騎士", + ["Priest"] = "牧師", + ["Rogue"] = "盜賊", + ["Shaman"] = "薩滿", + ["Warlock"] = "術士", + ["Warrior"] = "戰士", + ["Healer"] = "治療者", + ["Tank"] = "å¦å…‹", + ["Caster"] = "施法者", + ["Combat"] = "戰鬥", + ["Compilations"] = "編輯", + ["Data Export"] = "資料匯出", + ["Development Tools"] = "開發工具", + ["Guild"] = "公會", + ["Frame Modification"] = "框架修改", + ["Interface Enhancements"] = "介é¢å¢žå¼·", + ["Inventory"] = "背包", + ["Library"] = "資料庫", + ["Map"] = "地圖", + ["Mail"] = "郵件", + ["Miscellaneous"] = "ç¶œåˆ", + ["Quest"] = "任務", + ["Raid"] = "團隊", + ["Tradeskill"] = "商業技能", + ["UnitFrame"] = "單使¡†æž¶", + } +elseif GetLocale() == "zhCN" then + STANDBY = "|cffff5050(\230\154\130\230\140\130)|r" + + TITLE = "\230\160\135\233\162\152" + NOTES = "\233\153\132\230\179\168" + VERSION = "\231\137\136\230\156\172" + AUTHOR = "\228\189\156\232\128\133" + DATE = "\230\151\165\230\156\159" + CATEGORY = "\229\136\134\231\177\187" + EMAIL = "\231\148\181\229\173\144\233\130\174\228\187\182" + WEBSITE = "\231\189\145\231\171\153" + CREDITS = "Credits" -- fix + COMMANDS = "Commands" + + ABOUT = "\229\133\179\228\186\142" + PRINT_ADDON_INFO = "\229\141\176\229\136\151\229\135\186\230\143\146\228\187\182\228\191\161\230\129\175" + + CATEGORIES = { + ["Action Bars"] = "\229\138\168\228\189\156\230\157\161", + ["Auction"] = "\230\139\141\229\141\150", + ["Audio"] = "\233\159\179\233\162\145", + ["Battlegrounds/PvP"] = "\230\136\152\229\156\186/PvP", + ["Buffs"] = "\229\162\158\231\155\138\233\173\148\230\179\149", + ["Chat/Communication"] = "\232\129\138\229\164\169/\228\186\164\230\181\129", + ["Druid"] = "\229\190\183\233\178\129\228\188\138", + ["Hunter"] = "\231\140\142\228\186\186", + ["Mage"] = "\230\179\149\229\184\136", + ["Paladin"] = "\229\156\163\233\170\145\229\163\171", + ["Priest"] = "\231\137\167\229\184\136", + ["Rogue"] = "\231\155\151\232\180\188", + ["Shaman"] = "\232\144\168\230\187\161\231\165\173\229\143\184", + ["Warlock"] = "\230\156\175\229\163\171", + ["Warrior"] = "\230\136\152\229\163\171", +-- ["Healer"] = "\230\178\187\231\150\151\228\191\157\233\154\156", +-- ["Tank"] = "\232\191\145\230\136\152\230\142\167\229\136\182", +-- ["Caster"] = "\232\191\156\231\168\139\232\190\147\229\135\186", + ["Combat"] = "\230\136\152\230\150\151", + ["Compilations"] = "\231\188\150\232\175\145", + ["Data Export"] = "\230\149\176\230\141\174\229\175\188\229\135\186", + ["Development Tools"] = "\229\188\128\229\143\145\229\183\165\229\133\183", + ["Guild"] = "\229\133\172\228\188\154", + ["Frame Modification"] = "\230\161\134\230\158\182\228\191\174\230\148\185", + ["Interface Enhancements"] = "\231\149\140\233\157\162\229\162\158\229\188\186", + ["Inventory"] = "\232\131\140\229\140\133", + ["Library"] = "\229\186\147", + ["Map"] = "\229\156\176\229\155\190", + ["Mail"] = "\233\130\174\228\187\182", + ["Miscellaneous"] = "\230\157\130\233\161\185", + ["Quest"] = "\228\187\187\229\138\161", + ["Raid"] = "\229\155\162\233\152\159", + ["Tradeskill"] = "\229\149\134\228\184\154\230\138\128\232\131\189", + ["UnitFrame"] = "\229\164\180\229\131\143\230\161\134\230\158\182", + } +elseif GetLocale() == "ruRU" then + STANDBY = "|cffff5050(в режиме ожиданиÑ)|r" + + TITLE = "Ðазвание" + NOTES = "ОпиÑание" + VERSION = "ВерÑиÑ" + AUTHOR = "Ðвтор" + DATE = "Дата" + CATEGORY = "КатегориÑ" + EMAIL = "E-mail" + WEBSITE = "Сайт" + CREDITS = "Титры" + COMMANDS = "Команды" + + ABOUT = "Об аддоне" + PRINT_ADDON_INFO = "Показать информацию об аддоне." + + CATEGORIES = { + ["Action Bars"] = "Панели команд", + ["Auction"] = "Ðукцион", + ["Audio"] = "Ðудио", + ["Battlegrounds/PvP"] = "ÐŸÐ¾Ð»Ñ Ñражений/PvP", + ["Buffs"] = "Баффы", + ["Chat/Communication"] = "Чат/КоммуникациÑ", + ["Druid"] = "Друид", + ["Hunter"] = "Охотник", + ["Mage"] = "Маг", + ["Paladin"] = "Паладин", + ["Priest"] = "Жрец", + ["Rogue"] = "Разбойник", + ["Shaman"] = "Шаман", + ["Warlock"] = "Чернокнижник", + ["Warrior"] = "Воин", + ["Healer"] = "Лекарь", + ["Tank"] = "Танк", + ["Caster"] = "КаÑтер", + ["Combat"] = "СражениÑ", + ["Compilations"] = "КомпилÑциÑ", + ["Data Export"] = "ЭкÑпорт данных", + ["Development Tools"] = "ИнÑтрументы разработчика", + ["Guild"] = "ГильдиÑ", + ["Frame Modification"] = "ÐœÐ¾Ð´Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ñ„Ñ€ÐµÐ¹Ð¼Ð¾Ð²", + ["Interface Enhancements"] = "Улучшение интерфейÑа", + ["Inventory"] = "Инвентарь", + ["Library"] = "Библиотеки", + ["Map"] = "Карта", + ["Mail"] = "Почта", + ["Miscellaneous"] = "Разное", + ["Quest"] = "ЗаданиÑ", + ["Raid"] = "Рейд", + ["Tradeskill"] = "УмениÑ", + ["UnitFrame"] = "Фреймы перÑонажей", + } + else -- enUS + STANDBY = "|cffff5050(standby)|r" + + TITLE = "Title" + NOTES = "Notes" + VERSION = "Version" + AUTHOR = "Author" + DATE = "Date" + CATEGORY = "Category" + EMAIL = "E-mail" + WEBSITE = "Website" + CREDITS = "Credits" + COMMANDS = "Commands" + + ABOUT = "About" + PRINT_ADDON_INFO = "Print out addon info" + + CATEGORIES = { + ["Action Bars"] = "Action Bars", + ["Auction"] = "Auction", + ["Audio"] = "Audio", + ["Battlegrounds/PvP"] = "Battlegrounds/PvP", + ["Buffs"] = "Buffs", + ["Chat/Communication"] = "Chat/Communication", + ["Druid"] = "Druid", + ["Hunter"] = "Hunter", + ["Mage"] = "Mage", + ["Paladin"] = "Paladin", + ["Priest"] = "Priest", + ["Rogue"] = "Rogue", + ["Shaman"] = "Shaman", + ["Warlock"] = "Warlock", + ["Warrior"] = "Warrior", + ["Healer"] = "Healer", + ["Tank"] = "Tank", + ["Caster"] = "Caster", + ["Combat"] = "Combat", + ["Compilations"] = "Compilations", + ["Data Export"] = "Data Export", + ["Development Tools"] = "Development Tools", + ["Guild"] = "Guild", + ["Frame Modification"] = "Frame Modification", + ["Interface Enhancements"] = "Interface Enhancements", + ["Inventory"] = "Inventory", + ["Library"] = "Library", + ["Map"] = "Map", + ["Mail"] = "Mail", + ["Miscellaneous"] = "Miscellaneous", + ["Quest"] = "Quest", + ["Raid"] = "Raid", + ["Tradeskill"] = "Tradeskill", + ["UnitFrame"] = "UnitFrame", + } +end + +setmetatable(CATEGORIES, { __index = function(self, key) -- case-insensitive + local lowerKey = string.lower(key) + for k,v in pairs(CATEGORIES) do + if string.lower(k) == lowerKey then + return v + end + end +end }) + +-- Create the library object + +local AceOO = AceLibrary("AceOO-2.0") +local AceAddon = AceOO.Class() +local AceEvent +local AceConsole +local AceModuleCore + +function AceAddon:ToString() + return "AceAddon" +end + +local function print(text) + DEFAULT_CHAT_FRAME:AddMessage(text) +end + +function AceAddon:ADDON_LOADED(name) + while table.getn(self.nextAddon) > 0 do + local addon = table.remove(self.nextAddon, 1) + table.insert(self.addons, addon) + if not self.addons[name] then + self.addons[name] = addon + end + self:InitializeAddon(addon, name) + end +end + +local function RegisterOnEnable(self) + if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage then -- HACK + AceAddon.playerLoginFired = true + end + if AceAddon.playerLoginFired then + AceAddon.addonsStarted[self] = true + if (type(self.IsActive) ~= "function" or self:IsActive()) and (not AceModuleCore or not AceModuleCore:IsModule(self) or AceModuleCore:IsModuleActive(self)) then + local current = self.class + while true do + if current == AceOO.Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable,mixin,self) + end + end + end + current = current.super + end + if type(self.OnEnable) == "function" then + safecall(self.OnEnable,self) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", self) + end + end + else + if not AceAddon.addonsToOnEnable then + AceAddon.addonsToOnEnable = {} + end + table.insert(AceAddon.addonsToOnEnable, self) + end +end + +local function stripSpaces(text) + if type(text) == "string" then + return (string.gsub(string.gsub(text, "^%s*(.-)%s*$", "%1"), "%s%s+", " ")) + end + return text +end + +function AceAddon:InitializeAddon(addon, name) + if addon.name == nil then + addon.name = name + end + if GetAddOnMetadata then + -- TOC checks + if addon.title == nil then + addon.title = GetAddOnMetadata(name, "Title") + end + if addon.title then + local num = string.find(addon.title, " |cff7fff7f %-Ace2%-|r$") + if num then + addon.title = string.sub(addon.title, 1, num - 1) + end + addon.title = stripSpaces(addon.title) + end + + if addon.notes == nil then + addon.notes = GetAddOnMetadata(name, "Notes") + addon.notes = stripSpaces(addon.notes) + end + if addon.version == nil then + addon.version = GetAddOnMetadata(name, "Version") + end + if addon.version then + if string.find(addon.version, "%$Revision: (%d+) %$") then + addon.version = string.gsub(addon.version, "%$Revision: (%d+) %$", "%1") + elseif string.find(addon.version, "%$Rev: (%d+) %$") then + addon.version = string.gsub(addon.version, "%$Rev: (%d+) %$", "%1") + elseif string.find(addon.version, "%$LastChangedRevision: (%d+) %$") then + addon.version = string.gsub(addon.version, "%$LastChangedRevision: (%d+) %$", "%1") + end + end + addon.version = stripSpaces(addon.version) + if addon.author == nil then + addon.author = GetAddOnMetadata(name, "Author") + addon.author = stripSpaces(addon.author) + end + if addon.credits == nil then + addon.credits = GetAddOnMetadata(name, "X-Credits") + addon.credits = stripSpaces(addon.credits) + end + if addon.date == nil then + addon.date = GetAddOnMetadata(name, "X-Date") or GetAddOnMetadata(name, "X-ReleaseDate") + end + if addon.date then + if string.find(addon.date, "%$Date: (.-) %$") then + addon.date = string.gsub(addon.date, "%$Date: (.-) %$", "%1") + elseif string.find(addon.date, "%$LastChangedDate: (.-) %$") then + addon.date = string.gsub(addon.date, "%$LastChangedDate: (.-) %$", "%1") + end + end + addon.date = stripSpaces(addon.date) + + if addon.category == nil then + addon.category = GetAddOnMetadata(name, "X-Category") + addon.category = stripSpaces(addon.category) + end + if addon.email == nil then + addon.email = GetAddOnMetadata(name, "X-eMail") or GetAddOnMetadata(name, "X-Email") + addon.email = stripSpaces(addon.email) + end + if addon.website == nil then + addon.website = GetAddOnMetadata(name, "X-Website") + addon.website = stripSpaces(addon.website) + end + if addon.commands == nil then + addon.commands = GetAddOnMetadata(name, "X-Commands") + addon.commands = stripSpaces(addon.commands) + end + end + local current = addon.class + while true do + if current == AceOO.Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedInitialize) == "function" then + mixin:OnEmbedInitialize(addon, name) + end + end + end + current = current.super + end + if type(addon.OnInitialize) == "function" then + safecall(addon.OnInitialize, addon, name) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonInitialized", addon) + end + RegisterOnEnable(addon) +end + +function AceAddon.prototype:PrintAddonInfo() + local x + if self.title then + x = "|cffffff7f" .. tostring(self.title) .. "|r" + elseif self.name then + x = "|cffffff7f" .. tostring(self.name) .. "|r" + else + x = "|cffffff7f<" .. tostring(self.class) .. " instance>|r" + end + if type(self.IsActive) == "function" then + if not self:IsActive() then + x = x .. " " .. STANDBY + end + end + if self.version then + x = x .. " - |cffffff7f" .. tostring(self.version) .. "|r" + end + if self.notes then + x = x .. " - " .. tostring(self.notes) + end + print(x) + if self.author then + print(" - |cffffff7f" .. AUTHOR .. ":|r " .. tostring(self.author)) + end + if self.credits then + print(" - |cffffff7f" .. CREDITS .. ":|r " .. tostring(self.credits)) + end + if self.date then + print(" - |cffffff7f" .. DATE .. ":|r " .. tostring(self.date)) + end + if self.category then + local category = CATEGORIES[self.category] + if category then + print(" - |cffffff7f" .. CATEGORY .. ":|r " .. category) + end + end + if self.email then + print(" - |cffffff7f" .. EMAIL .. ":|r " .. tostring(self.email)) + end + if self.website then + print(" - |cffffff7f" .. WEBSITE .. ":|r " .. tostring(self.website)) + end + if self.commands then + print(" - |cffffff7f" .. COMMANDS .. ":|r " .. tostring(self.commands)) + end +end + +local options +function AceAddon:GetAceOptionsDataTable(target) + if not options then + options = { + about = { + name = ABOUT, + desc = PRINT_ADDON_INFO, + type = "execute", + func = "PrintAddonInfo", + order = -1, + } + } + end + return options +end + +function AceAddon:PLAYER_LOGIN() + self.playerLoginFired = true + if self.addonsToOnEnable then + while table.getn(self.addonsToOnEnable) > 0 do + local addon = table.remove(self.addonsToOnEnable, 1) + self.addonsStarted[addon] = true + if (type(addon.IsActive) ~= "function" or addon:IsActive()) and (not AceModuleCore or not AceModuleCore:IsModule(addon) or AceModuleCore:IsModuleActive(addon)) then + local current = addon.class + while true do + if current == AceOO.Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnEmbedEnable) == "function" then + safecall(mixin.OnEmbedEnable,mixin,addon) + end + end + end + current = current.super + end + if type(addon.OnEnable) == "function" then + safecall(addon.OnEnable,addon) + end + if AceEvent then + AceEvent:TriggerEvent("Ace2_AddonEnabled", addon) + end + end + end + self.addonsToOnEnable = nil + end +end + +function AceAddon.prototype:Inject(t) + AceAddon:argCheck(t, 2, "table") + for k,v in pairs(t) do + self[k] = v + end +end + +function AceAddon.prototype:init() + if not AceEvent then + error(MAJOR_VERSION .. " requires AceEvent-2.0", 4) + end + AceAddon.super.prototype.init(self) + + self.super = self.class.prototype + + AceAddon:RegisterEvent("ADDON_LOADED", "ADDON_LOADED", true) + table.insert(AceAddon.nextAddon, self) +end + +function AceAddon.prototype:ToString() + local x + if type(self.title) == "string" then + x = self.title + elseif type(self.name) == "string" then + x = self.name + else + x = "<" .. tostring(self.class) .. " instance>" + end + if (type(self.IsActive) == "function" and not self:IsActive()) or (AceModuleCore and AceModuleCore:IsModule(addon) and AceModuleCore:IsModuleActive(addon)) then + x = x .. " " .. STANDBY + end + return x +end + +AceAddon.new = function(self, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20) + local class = AceAddon:pcall(AceOO.Classpool, self, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20) + return class:new() +end + +local function external(self, major, instance) + if major == "AceEvent-2.0" then + AceEvent = instance + + AceEvent:embed(self) + + self:RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN", true) + elseif major == "AceConsole-2.0" then + AceConsole = instance + + local slashCommands = { "/ace2" } + local _,_,_,enabled,loadable = GetAddOnInfo("Ace") + if not enabled or not loadable then + table.insert(slashCommands, "/ace") + end + local function listAddon(addon, depth) + if not depth then + depth = 0 + end + + local s = string.rep(" ", depth) .. " - " .. tostring(addon) + if rawget(addon, 'version') then + s = s .. " - |cffffff7f" .. tostring(addon.version) .. "|r" + end + if rawget(addon, 'slashCommand') then + s = s .. " |cffffff7f(" .. tostring(addon.slashCommand) .. ")|r" + end + print(s) + if type(rawget(addon, 'modules')) == "table" then + local i = 0 + for k,v in pairs(addon.modules) do + i = i + 1 + if i == 6 then + print(string.rep(" ", depth + 1) .. " - more...") + break + else + listAddon(v, depth + 1) + end + end + end + end + local function listNormalAddon(i) + local name,_,_,enabled,loadable = GetAddOnInfo(i) + if not loadable then + enabled = false + end + if self.addons[name] then + local addon = self.addons[name] + if not AceCoreAddon or not AceCoreAddon:IsModule(addon) then + listAddon(addon) + end + else + local s = " - " .. tostring(GetAddOnMetadata(i, "Title") or name) + local version = GetAddOnMetadata(i, "Version") + if version then + if string.find(version, "%$Revision: (%d+) %$") then + version = string.gsub(version, "%$Revision: (%d+) %$", "%1") + elseif string.find(version, "%$Rev: (%d+) %$") then + version = string.gsub(version, "%$Rev: (%d+) %$", "%1") + elseif string.find(version, "%$LastChangedRevision: (%d+) %$") then + version = string.gsub(version, "%$LastChangedRevision: (%d+) %$", "%1") + end + s = s .. " - |cffffff7f" .. version .. "|r" + end + if not enabled then + s = s .. " |cffff0000(disabled)|r" + end + if IsAddOnLoadOnDemand(i) then + s = s .. " |cff00ff00[LoD]|r" + end + print(s) + end + end + local function mySort(alpha, bravo) + return tostring(alpha) < tostring(bravo) + end + AceConsole.RegisterChatCommand(self, slashCommands, { + desc = "AddOn development framework", + name = "Ace2", + type = "group", + args = { + about = { + desc = "Get information about Ace2", + name = "About", + type = "execute", + func = function() + print("|cffffff7fAce2|r - |cffffff7f2.0." .. string.gsub(MINOR_VERSION, "%$Revision: (%d+) %$", "%1") .. "|r - AddOn development framework") + print(" - |cffffff7f" .. AUTHOR .. ":|r Ace Development Team") + print(" - |cffffff7f" .. WEBSITE .. ":|r http://www.wowace.com/") + end + }, + list = { + desc = "List addons", + name = "List", + type = "group", + args = { + ace2 = { + desc = "List addons using Ace2", + name = "Ace2", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local AceCoreAddon = AceLibrary:HasInstance("AceCoreAddon-2.0") and AceLibrary("AceCoreAddon-2.0") + table.sort(self.addons, mySort) + for _,v in ipairs(self.addons) do + if not AceCoreAddon or not AceCoreAddon:IsModule(v) then + listAddon(v) + end + end + end + }, + all = { + desc = "List all addons", + name = "All", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local AceCoreAddon = AceLibrary:HasInstance("AceCoreAddon-2.0") and AceLibrary("AceCoreAddon-2.0") + local count = GetNumAddOns() + for i = 1, count do + listNormalAddon(i) + end + end + }, + enabled = { + desc = "List all enabled addons", + name = "Enabled", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local AceCoreAddon = AceLibrary:HasInstance("AceCoreAddon-2.0") and AceLibrary("AceCoreAddon-2.0") + local count = GetNumAddOns() + for i = 1, count do + local _,_,_,enabled,loadable = GetAddOnInfo(i) + if enabled and loadable then + listNormalAddon(i) + end + end + end + }, + disabled = { + desc = "List all disabled addons", + name = "Disabled", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local AceCoreAddon = AceLibrary:HasInstance("AceCoreAddon-2.0") and AceLibrary("AceCoreAddon-2.0") + local count = GetNumAddOns() + for i = 1, count do + local _,_,_,enabled,loadable = GetAddOnInfo(i) + if not enabled or not loadable then + listNormalAddon(i) + end + end + end + }, + lod = { + desc = "List all LoadOnDemand addons", + name = "LoadOnDemand", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local AceCoreAddon = AceLibrary:HasInstance("AceCoreAddon-2.0") and AceLibrary("AceCoreAddon-2.0") + local count = GetNumAddOns() + for i = 1, count do + if IsAddOnLoadOnDemand(i) then + listNormalAddon(i) + end + end + end + }, + ace1 = { + desc = "List all addons using Ace1", + name = "Ace 1.x", + type = "execute", + func = function() + print("|cffffff7fAddon list:|r") + local count = GetNumAddOns() + for i = 1, count do + local dep1, dep2, dep3, dep4 = GetAddOnDependencies(i) + if dep1 == "Ace" or dep2 == "Ace" or dep3 == "Ace" or dep4 == "Ace" then + listNormalAddon(i) + end + end + end + }, + libs = { + desc = "List all libraries using AceLibrary", + name = "Libraries", + type = "execute", + func = function() + if type(AceLibrary) == "table" and type(AceLibrary.libs) == "table" then + print("|cffffff7fLibrary list:|r") + for name, data in pairs(AceLibrary.libs) do + local s + if data.minor then + s = " - " .. tostring(name) .. "." .. tostring(data.minor) + else + s = " - " .. tostring(name) + end + if AceLibrary(name).slashCommand then + s = s .. " |cffffff7f(" .. tostring(AceLibrary(name).slashCommand) .. "|cffffff7f)" + end + print(s) + end + end + end + }, + search = { + desc = "Search by name", + name = "Search", + type = "text", + usage = "", + input = true, + get = false, + set = function(...) + for i,v in ipairs(arg) do + arg[i] = string.lower(string.gsub(string.gsub(v, '%*', '.*'), '%%', '%%%%')) + end + local count = GetNumAddOns() + for i = 1, count do + local name = GetAddOnInfo(i) + local good = true + for _,v in ipairs(arg) do + if not string.find(string.lower(name), v) then + good = false + break + end + end + if good then + listNormalAddon(i) + end + end + end + } + }, + }, + enable = { + desc = "Enable addon", + name = "Enable", + type = "text", + usage = "", + get = false, + set = function(text) + local name,title,_,_,_,reason = GetAddOnInfo(text) + if reason == "MISSING" then + print(string.format("|cffffff7fAce2:|r AddOn %q does not exist", text)) + else + EnableAddOn(text) + print(string.format("|cffffff7fAce2:|r %s is now enabled", title or name)) + end + end, + }, + disable = { + desc = "Disable addon", + name = "Disable", + type = "text", + usage = "", + get = false, + set = function(text) + local name,title,_,_,_,reason = GetAddOnInfo(text) + if reason == "MISSING" then + print(string.format("|cffffff7fAce2:|r AddOn %q does not exist", text)) + else + DisableAddOn(text) + print(string.format("|cffffff7fAce2:|r %s is now disabled", title or name)) + end + end, + }, + load = { + desc = "Load addon", + name = "Load", + type = "text", + usage = "", + get = false, + set = function(text) + local name,title,_,_,loadable,reason = GetAddOnInfo(text) + if reason == "MISSING" then + print(string.format("|cffffff7fAce2:|r AddOn %q does not exist.", text)) + elseif not loadable then + print(string.format("|cffffff7fAce2:|r AddOn %q is not loadable. Reason: %s", text, reason)) + else + LoadAddOn(text) + print(string.format("|cffffff7fAce2:|r %s is now loaded", title or name)) + end + end + }, + info = { + desc = "Display information", + name = "Information", + type = "execute", + func = function() + local mem, threshold = gcinfo() + print(string.format(" - |cffffff7fMemory usage [|r%.3f MiB|cffffff7f]|r", mem / 1024)) + print(string.format(" - |cffffff7fThreshold [|r%.3f MiB|cffffff7f]|r", threshold / 1024)) + print(string.format(" - |cffffff7fFramerate [|r%.0f fps|cffffff7f]|r", GetFramerate())) + local bandwidthIn, bandwidthOut, latency = GetNetStats() + bandwidthIn, bandwidthOut = floor(bandwidthIn * 1024), floor(bandwidthOut * 1024) + print(string.format(" - |cffffff7fLatency [|r%.0f ms|cffffff7f]|r", latency)) + print(string.format(" - |cffffff7fBandwidth in [|r%.0f B/s|cffffff7f]|r", bandwidthIn)) + print(string.format(" - |cffffff7fBandwidth out [|r%.0f B/s|cffffff7f]|r", bandwidthOut)) + print(string.format(" - |cffffff7fTotal addons [|r%d|cffffff7f]|r", GetNumAddOns())) + print(string.format(" - |cffffff7fAce2 addons [|r%d|cffffff7f]|r", table.getn(self.addons))) + local ace = 0 + local enabled = 0 + local disabled = 0 + local lod = 0 + for i = 1, GetNumAddOns() do + local dep1, dep2, dep3, dep4 = GetAddOnDependencies(i) + if dep1 == "Ace" or dep2 == "Ace" or dep3 == "Ace" or dep4 == "Ace" then + ace = ace + 1 + end + if IsAddOnLoadOnDemand(i) then + lod = lod + 1 + end + local _,_,_,IsActive,loadable = GetAddOnInfo(i) + if not IsActive or not loadable then + disabled = disabled + 1 + else + enabled = enabled + 1 + end + end + print(string.format(" - |cffffff7fAce 1.x addons [|r%d|cffffff7f]|r", ace)) + print(string.format(" - |cffffff7fLoadOnDemand addons [|r%d|cffffff7f]|r", lod)) + print(string.format(" - |cffffff7fenabled addons [|r%d|cffffff7f]|r", enabled)) + print(string.format(" - |cffffff7fdisabled addons [|r%d|cffffff7f]|r", disabled)) + local libs = 0 + if type(AceLibrary) == "table" and type(AceLibrary.libs) == "table" then + for _ in pairs(AceLibrary.libs) do + libs = libs + 1 + end + end + print(string.format(" - |cffffff7fAceLibrary instances [|r%d|cffffff7f]|r", libs)) + end + } + } + }) + elseif major == "AceModuleCore-2.0" then + AceModuleCore = instance + end +end + +local function activate(self, oldLib, oldDeactivate) + AceAddon = self + + if oldLib then + self.playerLoginFired = oldLib.playerLoginFired or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage + self.addonsToOnEnable = oldLib.addonsToOnEnable + self.addons = oldLib.addons + self.nextAddon = oldLib.nextAddon + self.addonsStarted = oldLib.addonsStarted + end + if not self.addons then + self.addons = {} + end + if not self.nextAddon then + self.nextAddon = {} + end + if not self.addonsStarted then + self.addonsStarted = {} + end + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceAddon, MAJOR_VERSION, MINOR_VERSION, activate, nil, external) +AceAddon = AceLibrary(MAJOR_VERSION) diff --git a/modules/chronometer/libs/AceEvent-2.0/AceEvent-2.0.lua b/modules/chronometer/libs/AceEvent-2.0/AceEvent-2.0.lua new file mode 100644 index 0000000..c654568 --- /dev/null +++ b/modules/chronometer/libs/AceEvent-2.0/AceEvent-2.0.lua @@ -0,0 +1,973 @@ +--[[ +Name: AceEvent-2.0 +Revision: $Rev: 17803 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceEvent-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceEvent-2.0 +Description: Mixin to allow for event handling, scheduling, and inter-addon + communication. +Dependencies: AceLibrary, AceOO-2.0 +]] + +local MAJOR_VERSION = "AceEvent-2.0" +local MINOR_VERSION = "$Revision: 17803 $" + +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end + +local AceOO = AceLibrary:GetInstance("AceOO-2.0") +local Mixin = AceOO.Mixin +local AceEvent = Mixin { + "RegisterEvent", + "RegisterAllEvents", + "UnregisterEvent", + "UnregisterAllEvents", + "TriggerEvent", + "ScheduleEvent", + "ScheduleRepeatingEvent", + "CancelScheduledEvent", + "CancelAllScheduledEvents", + "IsEventRegistered", + "IsEventScheduled", + "RegisterBucketEvent", + "UnregisterBucketEvent", + "UnregisterAllBucketEvents", + "IsBucketEventRegistered", + } + +local table_setn +do + local version = GetBuildInfo() + if string.find(version, "^2%.") then + -- 2.0.0 + table_setn = function() end + else + table_setn = table.setn + end +end + +local weakKey = {__mode="k"} +local new, del +do + local list = setmetatable({}, weakKey) + function new() + local t = next(list) + if t then + list[t] = nil + return t + else + return {} + end + end + + function del(t) + setmetatable(t, nil) + for k in pairs(t) do + t[k] = nil + end + list[t] = true + end +end + +local FAKE_NIL +local RATE + +local eventsWhichHappenOnce = { + PLAYER_LOGIN = true, + AceEvent_FullyInitialized = true, + VARIABLES_LOADED = true, + PLAYER_LOGOUT = true, +} + +local registeringFromAceEvent +function AceEvent:RegisterEvent(event, method, once) + AceEvent:argCheck(event, 2, "string") + if self == AceEvent and not registeringFromAceEvent then + AceEvent:argCheck(method, 3, "function") + self = method + else + AceEvent:argCheck(method, 3, "string", "function", "nil", "boolean", "number") + if type(method) == "boolean" or type(method) == "number" then + AceEvent:argCheck(once, 4, "nil") + once, method = method, event + end + end + AceEvent:argCheck(once, 4, "number", "boolean", "nil") + if eventsWhichHappenOnce[event] then + once = true + end + local throttleRate + if type(once) == "number" then + throttleRate, once = once + end + if not method then + method = event + end + if type(method) == "string" and type(self[method]) ~= "function" then + AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method) + else + assert(type(method) == "function" or type(method) == "string") + end + + local AceEvent_registry = AceEvent.registry + if not AceEvent_registry[event] then + AceEvent_registry[event] = new() + AceEvent.frame:RegisterEvent(event) + end + + local remember = true + if AceEvent_registry[event][self] then + remember = false + end + AceEvent_registry[event][self] = method + + local AceEvent_onceRegistry = AceEvent.onceRegistry + if once then + if not AceEvent_onceRegistry then + AceEvent.onceRegistry = new() + AceEvent_onceRegistry = AceEvent.onceRegistry + end + if not AceEvent_onceRegistry[event] then + AceEvent_onceRegistry[event] = new() + end + AceEvent_onceRegistry[event][self] = true + else + if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then + AceEvent_onceRegistry[event][self] = nil + if not next(AceEvent_onceRegistry[event]) then + AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) + end + end + end + + local AceEvent_throttleRegistry = AceEvent.throttleRegistry + if throttleRate then + if not AceEvent_throttleRegistry then + AceEvent.throttleRegistry = new() + AceEvent_throttleRegistry = AceEvent.throttleRegistry + end + if not AceEvent_throttleRegistry[event] then + AceEvent_throttleRegistry[event] = new() + end + if AceEvent_throttleRegistry[event][self] then + AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) + end + AceEvent_throttleRegistry[event][self] = setmetatable(new(), weakKey) + local t = AceEvent_throttleRegistry[event][self] + t[RATE] = throttleRate + else + if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] then + if AceEvent_throttleRegistry[event][self] then + AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) + end + if not next(AceEvent_throttleRegistry[event]) then + AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) + end + end + end + + if remember then + AceEvent:TriggerEvent("AceEvent_EventRegistered", self, event) + end +end + +local ALL_EVENTS + +function AceEvent:RegisterAllEvents(method) + if self == AceEvent then + AceEvent:argCheck(method, 1, "function") + self = method + else + AceEvent:argCheck(method, 1, "string", "function") + if type(method) == "string" and type(self[method]) ~= "function" then + AceEvent:error("Cannot register all events to method %q, it does not exist", method) + end + end + + local AceEvent_registry = AceEvent.registry + if not AceEvent_registry[ALL_EVENTS] then + AceEvent_registry[ALL_EVENTS] = new() + AceEvent.frame:RegisterAllEvents() + end + + AceEvent_registry[ALL_EVENTS][self] = method +end + +local _G = getfenv(0) +local memstack, timestack = {}, {} +local memdiff, timediff +function AceEvent:TriggerEvent(event, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + AceEvent:argCheck(event, 2, "string") + local AceEvent_registry = AceEvent.registry + if (not AceEvent_registry[event] or not next(AceEvent_registry[event])) and (not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS])) then + return + end + local _G_event = _G.event + _G.event = event + local lastEvent = AceEvent.currentEvent + AceEvent.currentEvent = event + + local AceEvent_onceRegistry = AceEvent.onceRegistry + local AceEvent_debugTable = AceEvent.debugTable + if AceEvent_onceRegistry and AceEvent_onceRegistry[event] then + local tmp = new() + for obj, method in pairs(AceEvent_onceRegistry[event]) do + tmp[obj] = AceEvent_registry[event] and AceEvent_registry[event][obj] or nil + end + local obj = next(tmp) + while obj do + local mem, time + if AceEvent_debugTable then + if not AceEvent_debugTable[event] then + AceEvent_debugTable[event] = new() + end + if not AceEvent_debugTable[event][obj] then + AceEvent_debugTable[event][obj] = new() + AceEvent_debugTable[event][obj].mem = 0 + AceEvent_debugTable[event][obj].time = 0 + AceEvent_debugTable[event][obj].count = 0 + end + if memdiff then + table.insert(memstack, memdiff) + table.insert(timestack, timediff) + end + memdiff, timediff = 0, 0 + mem, time = gcinfo(), GetTime() + end + local method = tmp[obj] + AceEvent.UnregisterEvent(obj, event) + if type(method) == "string" then + local obj_method = obj[method] + if obj_method then + obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + elseif method then -- function + method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + if AceEvent_debugTable then + local dmem, dtime = memdiff, timediff + mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff + AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem + AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time + AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 + + memdiff, timediff = table.remove(memstack), table.remove(timestack) + if memdiff then + memdiff = memdiff + mem + dmem + timediff = timediff + time + dtime + end + end + tmp[obj] = nil + obj = next(tmp) + end + del(tmp) + end + + local AceEvent_throttleRegistry = AceEvent.throttleRegistry + local throttleTable = AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] + if AceEvent_registry[event] then + local tmp = new() + for obj, method in pairs(AceEvent_registry[event]) do + tmp[obj] = method + end + local obj = next(tmp) + while obj do + local method = tmp[obj] + local continue = false + if throttleTable and throttleTable[obj] then + local a1 = a1 + if a1 == nil then + a1 = FAKE_NIL + end + if not throttleTable[obj][a1] or GetTime() - throttleTable[obj][a1] >= throttleTable[obj][RATE] then + throttleTable[obj][a1] = GetTime() + else + continue = true + end + end + if not continue then + local mem, time + if AceEvent_debugTable then + if not AceEvent_debugTable[event] then + AceEvent_debugTable[event] = new() + end + if not AceEvent_debugTable[event][obj] then + AceEvent_debugTable[event][obj] = new() + AceEvent_debugTable[event][obj].mem = 0 + AceEvent_debugTable[event][obj].time = 0 + AceEvent_debugTable[event][obj].count = 0 + end + if memdiff then + table.insert(memstack, memdiff) + table.insert(timestack, timediff) + end + memdiff, timediff = 0, 0 + mem, time = gcinfo(), GetTime() + end + if type(method) == "string" then + local obj_method = obj[method] + if obj_method then + obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + elseif method then -- function + method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + if AceEvent_debugTable then + local dmem, dtime = memdiff, timediff + mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff + AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem + AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time + AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 + + memdiff, timediff = table.remove(memstack), table.remove(timestack) + if memdiff then + memdiff = memdiff + mem + dmem + timediff = timediff + time + dtime + end + end + end + tmp[obj] = nil + obj = next(tmp) + end + del(tmp) + end + if AceEvent_registry[ALL_EVENTS] then + local tmp = new() + for obj, method in pairs(AceEvent_registry[ALL_EVENTS]) do + tmp[obj] = method + end + local obj = next(tmp) + while obj do + local method = tmp[obj] + local mem, time + if AceEvent_debugTable then + if not AceEvent_debugTable[event] then + AceEvent_debugTable[event] = new() + end + if not AceEvent_debugTable[event][obj] then + AceEvent_debugTable[event][obj] = new() + AceEvent_debugTable[event][obj].mem = 0 + AceEvent_debugTable[event][obj].time = 0 + AceEvent_debugTable[event][obj].count = 0 + end + if memdiff then + table.insert(memstack, memdiff) + table.insert(timestack, timediff) + end + memdiff, timediff = 0, 0 + mem, time = gcinfo(), GetTime() + end + if type(method) == "string" then + local obj_method = obj[method] + if obj_method then + obj_method(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + elseif method then -- function + method(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + end + if AceEvent_debugTable then + local dmem, dtime = memdiff, timediff + mem, time = gcinfo() - mem - memdiff, GetTime() - time - timediff + AceEvent_debugTable[event][obj].mem = AceEvent_debugTable[event][obj].mem + mem + AceEvent_debugTable[event][obj].time = AceEvent_debugTable[event][obj].time + time + AceEvent_debugTable[event][obj].count = AceEvent_debugTable[event][obj].count + 1 + + memdiff, timediff = table.remove(memstack), table.remove(timestack) + if memdiff then + memdiff = memdiff + mem + dmem + timediff = timediff + time + dtime + end + end + tmp[obj] = nil + obj = next(tmp) + end + del(tmp) + end + _G.event = _G_event + AceEvent.currentEvent = lastEvent +end + +-- local accessors +local getn = table.getn +local tinsert = table.insert +local tremove = table.remove +local floor = math.floor +local GetTime = GetTime +local next = next +local pairs = pairs +local unpack = unpack + +local delayRegistry +local tmp = {} +local function OnUpdate() + local t = GetTime() + for k,v in pairs(delayRegistry) do + tmp[k] = true + end + for k in pairs(tmp) do + local v = delayRegistry[k] + if v then + local v_time = v.time + if not v_time then + delayRegistry[k] = del(v) + elseif v_time <= t then + local v_repeatDelay = v.repeatDelay + if v_repeatDelay then + -- use the event time, not the current time, else timing inaccuracies add up over time + v.time = v_time + v_repeatDelay + end + local event = v.event + local mem, time + if AceEvent_debugTable then + mem, time = gcinfo(), GetTime() + end + if type(event) == "function" then + event(unpack(v)) + else + AceEvent:TriggerEvent(event, unpack(v)) + end + if AceEvent_debugTable then + mem, time = gcinfo() - mem, GetTime() - time + v.mem = v.mem + mem + v.timeSpent = v.timeSpent + time + v.count = v.count + 1 + end + if not v_repeatDelay then + local x = delayRegistry[k] + if x and x.time == v_time then -- check if it was manually reset + delayRegistry[k] = del(v) + end + end + end + end + end + for k in pairs(tmp) do + tmp[k] = nil + end + if not next(delayRegistry) then + AceEvent.frame:Hide() + end +end + +local function ScheduleEvent(self, repeating, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + local id + if type(event) == "string" or type(event) == "table" then + if type(event) == "table" then + if not delayRegistry or not delayRegistry[event] then + AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") + end + end + if type(delay) ~= "number" then + id, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 + AceEvent:argCheck(event, 3, "string", "function", --[[ so message is right ]] "number") + AceEvent:argCheck(delay, 4, "number") + self:CancelScheduledEvent(id) + end + else + AceEvent:argCheck(event, 2, "string", "function") + AceEvent:argCheck(delay, 3, "number") + end + + if not delayRegistry then + AceEvent.delayRegistry = new() + delayRegistry = AceEvent.delayRegistry + AceEvent.frame:SetScript("OnUpdate", OnUpdate) + end + local t + if type(id) == "table" then + for k in pairs(id) do + id[k] = nil + end + t = id + else + t = new() + end + t[1] = a1 + t[2] = a2 + t[3] = a3 + t[4] = a4 + t[5] = a5 + t[6] = a6 + t[7] = a7 + t[8] = a8 + t[9] = a9 + t[10] = a10 + t[11] = a11 + t[12] = a12 + t[13] = a13 + t[14] = a14 + t[15] = a15 + t[16] = a16 + t[17] = a17 + t[18] = a18 + t[19] = a19 + t[20] = a20 + table_setn(t, 20) + t.event = event + t.time = GetTime() + delay + t.self = self + t.id = id or t + t.repeatDelay = repeating and delay + if AceEvent_debugTable then + t.mem = 0 + t.count = 0 + t.timeSpent = 0 + end + delayRegistry[t.id] = t + AceEvent.frame:Show() + return t.id +end + +function AceEvent:ScheduleEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if type(event) == "string" or type(event) == "table" then + if type(event) == "table" then + if not delayRegistry or not delayRegistry[event] then + AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") + end + end + if type(delay) ~= "number" then + AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") + AceEvent:argCheck(a1, 4, "number") + end + else + AceEvent:argCheck(event, 2, "string", "function") + AceEvent:argCheck(delay, 3, "number") + end + + return ScheduleEvent(self, false, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) +end + +function AceEvent:ScheduleRepeatingEvent(event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if type(event) == "string" or type(event) == "table" then + if type(event) == "table" then + if not delayRegistry or not delayRegistry[event] then + AceEvent:error("Bad argument #2 to `ScheduleEvent'. Improper id table fed in.") + end + end + if type(delay) ~= "number" then + AceEvent:argCheck(delay, 3, "string", "function", --[[ so message is right ]] "number") + AceEvent:argCheck(a1, 4, "number") + end + else + AceEvent:argCheck(event, 2, "string", "function") + AceEvent:argCheck(delay, 3, "number") + end + + return ScheduleEvent(self, true, event, delay, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) +end + +function AceEvent:CancelScheduledEvent(t) + AceEvent:argCheck(t, 2, "string", "table") + if delayRegistry then + local v = delayRegistry[t] + if v then + delayRegistry[t] = del(v) + if not next(delayRegistry) then + AceEvent.frame:Hide() + end + return true + end + end + return false +end + +function AceEvent:IsEventScheduled(t) + AceEvent:argCheck(t, 2, "string", "table") + if delayRegistry then + local v = delayRegistry[t] + if v then + return true, v.time - GetTime() + end + end + return false, nil +end + +function AceEvent:UnregisterEvent(event) + AceEvent:argCheck(event, 2, "string") + local AceEvent_registry = AceEvent.registry + if AceEvent_registry[event] and AceEvent_registry[event][self] then + AceEvent_registry[event][self] = nil + local AceEvent_onceRegistry = AceEvent.onceRegistry + if AceEvent_onceRegistry and AceEvent_onceRegistry[event] and AceEvent_onceRegistry[event][self] then + AceEvent_onceRegistry[event][self] = nil + if not next(AceEvent_onceRegistry[event]) then + AceEvent_onceRegistry[event] = del(AceEvent_onceRegistry[event]) + end + end + local AceEvent_throttleRegistry = AceEvent.throttleRegistry + if AceEvent_throttleRegistry and AceEvent_throttleRegistry[event] and AceEvent_throttleRegistry[event][self] then + AceEvent_throttleRegistry[event][self] = del(AceEvent_throttleRegistry[event][self]) + if not next(AceEvent_throttleRegistry[event]) then + AceEvent_throttleRegistry[event] = del(AceEvent_throttleRegistry[event]) + end + end + if not next(AceEvent_registry[event]) then + AceEvent_registry[event] = del(AceEvent_registry[event]) + if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then + AceEvent.frame:UnregisterEvent(event) + end + end + else + if self == AceEvent then + error(string.format("Cannot unregister event %q. Improperly unregistering from AceEvent-2.0.", event), 2) + else + AceEvent:error("Cannot unregister event %q. %q is not registered with it.", event, self) + end + end + AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) +end + +function AceEvent:UnregisterAllEvents() + local AceEvent_registry = AceEvent.registry + if AceEvent_registry[ALL_EVENTS] and AceEvent_registry[ALL_EVENTS][self] then + AceEvent_registry[ALL_EVENTS][self] = nil + if not next(AceEvent_registry[ALL_EVENTS]) then + del(AceEvent_registry[ALL_EVENTS]) + AceEvent.frame:UnregisterAllEvents() + for k,v in pairs(AceEvent_registry) do + if k ~= ALL_EVENTS then + AceEvent.frame:RegisterEvent(k) + end + end + AceEvent_registry[event] = nil + end + end + local first = true + for event, data in pairs(AceEvent_registry) do + if first then + if AceEvent_registry.AceEvent_EventUnregistered then + event = "AceEvent_EventUnregistered" + else + first = false + end + end + local x = data[self] + data[self] = nil + if x and event ~= ALL_EVENTS then + if not next(data) then + del(data) + if not AceEvent_registry[ALL_EVENTS] or not next(AceEvent_registry[ALL_EVENTS]) then + AceEvent.frame:UnregisterEvent(event) + end + AceEvent_registry[event] = nil + end + AceEvent:TriggerEvent("AceEvent_EventUnregistered", self, event) + end + if first then + event = nil + end + end + if AceEvent.onceRegistry then + for event, data in pairs(AceEvent.onceRegistry) do + data[self] = nil + end + end +end + +function AceEvent:CancelAllScheduledEvents() + if delayRegistry then + for k,v in pairs(delayRegistry) do + if v.self == self then + delayRegistry[k] = del(v) + end + end + if not next(delayRegistry) then + AceEvent.frame:Hide() + end + end +end + +function AceEvent:IsEventRegistered(event) + AceEvent:argCheck(event, 2, "string") + local AceEvent_registry = AceEvent.registry + if self == AceEvent then + return AceEvent_registry[event] and next(AceEvent_registry[event]) and true or false + end + if AceEvent_registry[event] and AceEvent_registry[event][self] then + return true, AceEvent_registry[event][self] + end + return false, nil +end + +local bucketfunc +function AceEvent:RegisterBucketEvent(event, delay, method) + AceEvent:argCheck(event, 2, "string", "table") + if type(event) == "table" then + for k,v in pairs(event) do + if type(k) ~= "number" then + AceEvent:error("All keys to argument #2 to `RegisterBucketEvent' must be numbers.") + elseif type(v) ~= "string" then + AceEvent:error("All values to argument #2 to `RegisterBucketEvent' must be strings.") + end + end + end + AceEvent:argCheck(delay, 3, "number") + if AceEvent == self then + AceEvent:argCheck(method, 4, "function") + self = method + else + if type(event) == "string" then + AceEvent:argCheck(method, 4, "string", "function", "nil") + if not method then + method = event + end + else + AceEvent:argCheck(method, 4, "string", "function") + end + + if type(method) == "string" and type(self[method]) ~= "function" then + AceEvent:error("Cannot register event %q to method %q, it does not exist", event, method) + end + end + if not AceEvent.buckets then + AceEvent.buckets = new() + end + if not AceEvent.buckets[event] then + AceEvent.buckets[event] = new() + end + if not AceEvent.buckets[event][self] then + AceEvent.buckets[event][self] = new() + AceEvent.buckets[event][self].current = new() + AceEvent.buckets[event][self].self = self + else + AceEvent.CancelScheduledEvent(self, AceEvent.buckets[event][self].id) + end + local bucket = AceEvent.buckets[event][self] + bucket.method = method + + local func = function(arg1) + bucket.run = true + if arg1 then + bucket.current[arg1] = true + end + end + AceEvent.buckets[event][self].func = func + if type(event) == "string" then + AceEvent.RegisterEvent(self, event, func) + else + for _,v in ipairs(event) do + AceEvent.RegisterEvent(self, v, func) + end + end + if not bucketfunc then + bucketfunc = function(bucket) + local current = bucket.current + local method = bucket.method + local self = bucket.self + if bucket.run then + if type(method) == "string" then + self[method](self, current) + elseif method then -- function + method(current) + end + for k in pairs(current) do + current[k] = nil + k = nil + end + bucket.run = false + end + end + end + bucket.id = AceEvent.ScheduleRepeatingEvent(self, bucketfunc, delay, bucket) +end + +function AceEvent:IsBucketEventRegistered(event) + AceEvent:argCheck(event, 2, "string", "table") + return AceEvent.buckets and AceEvent.buckets[event] and AceEvent.buckets[event][self] +end + +function AceEvent:UnregisterBucketEvent(event) + AceEvent:argCheck(event, 2, "string", "table") + if not AceEvent.buckets or not AceEvent.buckets[event] or not AceEvent.buckets[event][self] then + AceEvent:error("Cannot unregister bucket event %q. %q is not registered with it.", event, self) + end + + local bucket = AceEvent.buckets[event][self] + + if type(event) == "string" then + AceEvent.UnregisterEvent(self, event) + else + for _,v in ipairs(event) do + AceEvent.UnregisterEvent(self, v) + end + end + AceEvent:CancelScheduledEvent(bucket.id) + + del(bucket.current) + AceEvent.buckets[event][self] = del(AceEvent.buckets[event][self]) + if not next(AceEvent.buckets[event]) then + AceEvent.buckets[event] = del(AceEvent.buckets[event]) + end +end + +function AceEvent:UnregisterAllBucketEvents() + if not AceEvent.buckets or not next(AceEvent.buckets) then + return + end + for k,v in pairs(AceEvent.buckets) do + if v == self then + AceEvent.UnregisterBucketEvent(self, k) + k = nil + end + end +end + +function AceEvent:OnEmbedDisable(target) + self.UnregisterAllEvents(target) + + self.CancelAllScheduledEvents(target) + + self.UnregisterAllBucketEvents(target) +end + +function AceEvent:EnableDebugging() + if not self.debugTable then + self.debugTable = new() + + if delayRegistry then + for k,v in pairs(self.delayRegistry) do + if not v.mem then + v.mem = 0 + v.count = 0 + v.timeSpent = 0 + end + end + end + end +end + +function AceEvent:IsFullyInitialized() + return self.postInit or false +end + +function AceEvent:IsPostPlayerLogin() + return self.playerLogin or false +end + +function AceEvent:activate(oldLib, oldDeactivate) + AceEvent = self + + if oldLib then + self.onceRegistry = oldLib.onceRegistry + self.throttleRegistry = oldLib.throttleRegistry + self.delayRegistry = oldLib.delayRegistry + self.buckets = oldLib.buckets + self.registry = oldLib.registry + self.frame = oldLib.frame + self.debugTable = oldLib.debugTable + self.playerLogin = oldLib.pew or DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.defaultLanguage and true + self.postInit = oldLib.postInit or self.playerLogin and ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r and true + self.ALL_EVENTS = oldLib.ALL_EVENTS + self.FAKE_NIL = oldLib.FAKE_NIL + self.RATE = oldLib.RATE + end + if not self.registry then + self.registry = {} + end + if not self.frame then + self.frame = CreateFrame("Frame", "AceEvent20Frame") + end + if not self.ALL_EVENTS then + self.ALL_EVENTS = {} + end + if not self.FAKE_NIL then + self.FAKE_NIL = {} + end + if not self.RATE then + self.RATE = {} + end + ALL_EVENTS = self.ALL_EVENTS + FAKE_NIL = self.FAKE_NIL + RATE = self.RATE + local inPlw = false + local blacklist = { + UNIT_INVENTORY_CHANGED = true, + BAG_UPDATE = true, + ITEM_LOCK_CHANGED = true, + ACTIONBAR_SLOT_CHANGED = true, + } + self.frame:SetScript("OnEvent", function() + local event = event + if event == "PLAYER_ENTERING_WORLD" then + inPlw = false + elseif event == "PLAYER_LEAVING_WORLD" then + inPlw = true + end + if event and (not inPlw or not blacklist[event]) then + self:TriggerEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) + end + end) + if self.delayRegistry then + delayRegistry = self.delayRegistry + self.frame:SetScript("OnUpdate", OnUpdate) + end + + self:UnregisterAllEvents() + self:CancelAllScheduledEvents() + + registeringFromAceEvent = true + self:RegisterEvent("LOOT_OPENED", function() + SendAddonMessage("LOOT_OPENED", "", "RAID") + end) + registeringFromAceEvent = nil + + if not self.playerLogin then + registeringFromAceEvent = true + self:RegisterEvent("PLAYER_LOGIN", function() + self.playerLogin = true + end, true) + registeringFromAceEvent = nil + end + + if not self.postInit then + local isReload = true + local function func() + self.postInit = true + self:TriggerEvent("AceEvent_FullyInitialized") + if self.registry["CHAT_MSG_CHANNEL_NOTICE"] and self.registry["CHAT_MSG_CHANNEL_NOTICE"][self] then + self:UnregisterEvent("CHAT_MSG_CHANNEL_NOTICE") + end + if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then + self:UnregisterEvent("MEETINGSTONE_CHANGED") + end + if self.registry["MINIMAP_ZONE_CHANGED"] and self.registry["MINIMAP_ZONE_CHANGED"][self] then + self:UnregisterEvent("MINIMAP_ZONE_CHANGED") + end + if self.registry["LANGUAGE_LIST_CHANGED"] and self.registry["LANGUAGE_LIST_CHANGED"][self] then + self:UnregisterEvent("LANGUAGE_LIST_CHANGED") + end + end + registeringFromAceEvent = true + local f = function() + self.playerLogin = true + self:ScheduleEvent("AceEvent_FullyInitialized", func, 1) + end + self:RegisterEvent("MEETINGSTONE_CHANGED", f, true) + self:RegisterEvent("CHAT_MSG_CHANNEL_NOTICE", function() + self:ScheduleEvent("AceEvent_FullyInitialized", func, 0.05) + end) + self:RegisterEvent("LANGUAGE_LIST_CHANGED", function() + if self.registry["MEETINGSTONE_CHANGED"] and self.registry["MEETINGSTONE_CHANGED"][self] then + registeringFromAceEvent = true + self:UnregisterEvent("MEETINGSTONE_CHANGED") + self:RegisterEvent("MINIMAP_ZONE_CHANGED", f, true) + registeringFromAceEvent = nil + end + end) + self:ScheduleEvent("AceEvent_FullyInitialized", func, 10) + registeringFromAceEvent = nil + end + + self.super.activate(self, oldLib, oldDeactivate) + if oldLib then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceEvent, MAJOR_VERSION, MINOR_VERSION, AceEvent.activate) +AceEvent = AceLibrary(MAJOR_VERSION) diff --git a/modules/chronometer/libs/AceHook-2.1/AceHook-2.1.lua b/modules/chronometer/libs/AceHook-2.1/AceHook-2.1.lua new file mode 100644 index 0000000..8beaf66 --- /dev/null +++ b/modules/chronometer/libs/AceHook-2.1/AceHook-2.1.lua @@ -0,0 +1,569 @@ +--[[ +Name: AceHook-2.1 +Revision: $Rev: 17638 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceHook-2.1 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceHook-2.1 +Description: Mixin to allow for safe hooking of functions, methods, and scripts. +Dependencies: AceLibrary, AceOO-2.0 +]] + +local MAJOR_VERSION = "AceHook-2.1" +local MINOR_VERSION = "$Revision: 17638 $" + +-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0") end + +--[[--------------------------------------------------------------------------------- + Create the library object +----------------------------------------------------------------------------------]] + +local AceOO = AceLibrary:GetInstance("AceOO-2.0") +local AceHook = AceOO.Mixin { + "Hook", + "HookScript", + "SecureHook", + "Unhook", + "UnhookAll", + "HookReport", + "IsHooked", + } + +--[[--------------------------------------------------------------------------------- + Library Definitions +----------------------------------------------------------------------------------]] + +local protFuncs = { + CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true, + TurnOrActionStart = true, TurnOrActionStop = true, + PitchUpStart = true, PitchUpStop = true, + PitchDownStart = true, PitchDownStop = true, + MoveBackwardStart = true, MoveBackwardStop = true, + MoveForwardStart = true, MoveForwardStop = true, + Jump = true, StrafeLeftStart = true, + StrafeLeftStop = true, StrafeRightStart = true, + StrafeRightStop = true, ToggleMouseMove = true, + ToggleRun = true, TurnLeftStart = true, + TurnLeftStop = true, TurnRightStart = true, + TurnRightStop = true, +} + +local function issecurevariable(x) + if protFuncs[x] then + return 1 + else + return nil + end +end + +local _G = getfenv(0) + +local function hooksecurefunc(arg1, arg2, arg3) + if type(arg1) == "string" then + arg1, arg2, arg3 = _G, arg1, arg2 + end + local orig = arg1[arg2] + arg1[arg2] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + local x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 = orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + + arg3(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + + return x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,x16,x17,x18,x19,x20 + end +end + +local protectedScripts = { + OnClick = true, +} + + +local handlers, scripts, actives, registry + +--[[--------------------------------------------------------------------------------- + Private definitions (Not exposed) +----------------------------------------------------------------------------------]] + +local new, del +do + local list = setmetatable({}, {__mode = "k"}) + function new() + local t = next(list) + if not t then + return {} + end + list[t] = nil + return t + end + + function del(t) + setmetatable(t, nil) + for k in pairs(t) do + t[k] = nil + end + list[t] = true + end +end + +local function createFunctionHook(self, func, handler, orig, secure) + if not secure then + if type(handler) == "string" then + -- The handler is a method, need to self it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + else + return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + else + return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + end + else + -- secure hooks don't call the original method + if type(handler) == "string" then + -- The handler is a method, need to self it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + end + end +end + +local function createMethodHook(self, object, method, handler, orig, secure, script) + if script then + if type(handler) == "string" then + local uid + uid = function() + if actives[uid] then + return self[handler](self, object) + else + return orig() + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function() + if actives[uid] then + return handler(object) + else + return orig() + end + end + return uid + end + elseif not secure then + if type(handler) == "string" then + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + else + return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + else + return orig(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + end + else + -- secure hooks don't call the original method + if type(handler) == "string" then + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return self[handler](self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + else + -- The handler is a function, just call it + local uid + uid = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + if actives[uid] then + return handler(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20) + end + end + return uid + end + end +end + +local function hookFunction(self, func, handler, secure) + local orig = _G[func] + + if not orig or type(orig) ~= "function" then + AceHook:error("Attempt to hook a non-existant function %q", func) + end + + if not handler then + handler = func + end + + if registry[self][func] then + local uid = registry[self][func] + + if actives[uid] then + -- We have an active hook from this source. Don't multi-hook + AceHook:error("%q already has an active hook from this source.", func) + end + + if handlers[uid] == handler then + -- The hook is inactive, so reactivate it + actives[uid] = true + return + else + AceHook:error("There is a stale hook for %q can't hook or reactivate.", func) + end + end + + if type(handler) == "string" then + if type(self[handler]) ~= "function" then + AceHook:error("Could not find the the handler %q when hooking function %q", handler, func) + end + elseif type(handler) ~= "function" then + AceHook:error("Could not find the handler you supplied when hooking %q", func) + end + + local uid = createFunctionHook(self, func, handler, orig, secure) + registry[self][func] = uid + actives[uid] = true + handlers[uid] = handler + + if not secure then + _G[func] = uid + self.hooks[func] = orig + else + hooksecurefunc(func, uid) + end +end + +local function unhookFunction(self, func) + if not registry[self][func] then + AceHook:error("Tried to unhook %q which is not currently hooked.", func) + end + + local uid = registry[self][func] + + if actives[uid] then + -- See if we own the global function + if self.hooks[func] and _G[func] == uid then + _G[func] = self.hooks[func] + self.hooks[func] = nil + registry[self][func] = nil + handlers[uid] = nil + scripts[uid] = nil + actives[uid] = nil + -- Magically all-done + else + actives[uid] = nil + end + end +end + +local function hookMethod(self, obj, method, handler, script, secure) + if not handler then + handler = method + end + + if not obj or type(obj) ~= "table" then + AceHook:error("The object you supplied could not be found, or isn't a table.") + end + + local uid = registry[self][obj] and registry[self][obj][method] + if uid then + if actives[uid] then + -- We have an active hook from this source. Don't multi-hook + AceHook:error("%q already has an active hook from this source.", method) + end + + if handlers[uid] == handler then + -- The hook is inactive, reactivate it. + actives[uid] = true + return + else + AceHook:error("There is a stale hook for %q can't hook or reactivate.", method) + end + end + + if type(handler) == "string" then + if type(self[handler]) ~= "function" then + AceHook:error("Could not find the handler %q you supplied when hooking method %q", handler, method) + end + elseif type(handler) ~= "function" then + AceHook:error("Could not find the handler you supplied when hooking method %q", method) + end + + local orig + if script then + if not obj.GetScript then + AceHook:error("The object you supplied does not have a GetScript method.") + end + if not obj:HasScript(method) then + AceHook:error("The object you supplied doesn't allow the %q method.", method) + end + + orig = obj:GetScript(method) + if type(orig) ~= "function" then + -- Sometimes there is not a original function for a script. + orig = function() end + end + else + orig = obj[method] + end + if not orig then + AceHook:error("Could not find the method or script %q you are trying to hook.", method) + end + + if not registry[self][obj] then + registry[self][obj] = new() + end + + if not self.hooks[obj] then + self.hooks[obj] = new() + end + + local uid = createMethodHook(self, obj, method, handler, orig, secure, script) + registry[self][obj][method] = uid + actives[uid] = true + handlers[uid] = handler + scripts[uid] = script and true or nil + + if script then + obj:SetScript(method, uid) + self.hooks[obj][method] = orig + elseif not secure then + obj[method] = uid + self.hooks[obj][method] = orig + else + hooksecurefunc(obj, method, uid) + end +end + +local function unhookMethod(self, obj, method) + if not registry[self][obj] or not registry[self][obj][method] then + AceHook:error("Attempt to unhook a method %q that is not currently hooked.", method) + return + end + + local uid = registry[self][obj][method] + + if actives[uid] then + if scripts[uid] then -- If this is a script + if obj:GetScript(method) == uid then + -- We own the script. Revert to normal. + obj:SetScript(method, self.hooks[obj][method]) + self.hooks[obj][method] = nil + registry[self][obj][method] = nil + handlers[uid] = nil + scripts[uid] = nil + actives[uid] = nil + else + actives[uid] = nil + end + else + if self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then + -- We own the method. Revert to normal. + obj[method] = self.hooks[obj][method] + self.hooks[obj][method] = nil + registry[self][obj][method] = nil + handlers[uid] = nil + actives[uid] = nil + else + actives[uid] = nil + end + end + end + if self.hooks[obj] and not next(self.hooks[obj]) then + self.hooks[obj] = del(self.hooks[obj]) + end + if not next(registry[self][obj]) then + registry[self][obj] = del(registry[self][obj]) + end +end + +-- ("function" [, handler] [, hookSecure]) or (object, "method" [, handler] [, hookSecure]) +function AceHook:Hook(object, method, handler, hookSecure) + if type(object) == "string" then + method, handler, hookSecure = object, method, handler + if handler == true then + handler, hookSecure = nil, true + end + if not hookSecure and issecurevariable(method) then + AceHook:error("Attempt to hook secure function %q. Use `SecureHook' or add `true' to the argument list to override.", method) + end + AceHook:argCheck(handler, 3, "function", "string", "nil") + AceHook:argCheck(hookSecure, 4, "boolean", "nil") + hookFunction(self, method, handler, false) + else + if handler == true then + handler, hookSecure = nil, true + end + if not hookSecure and issecurevariable(object, method) then + AceHook:error("Attempt to hook secure method %q. Use `SecureHook' or add `true' to the argument list to override.", method) + end + AceHook:argCheck(object, 2, "table") + AceHook:argCheck(method, 3, "string") + AceHook:argCheck(handler, 4, "function", "string", "nil") + AceHook:argCheck(hookSecure, 5, "boolean", "nil") + hookMethod(self, object, method, handler, false, false) + end +end + +-- ("function", handler) or (object, "method", handler) +function AceHook:SecureHook(object, method, handler) + if type(object) == "string" then + method, handler = object, method + AceHook:argCheck(handler, 3, "function", "string", "nil") + hookFunction(self, method, handler, true) + else + AceHook:argCheck(object, 2, "table") + AceHook:argCheck(method, 3, "string") + AceHook:argCheck(handler, 4, "function", "string", "nil") + hookMethod(self, object, method, handler, false, true) + end +end + +function AceHook:HookScript(frame, script, handler) + AceHook:argCheck(frame, 2, "table") + if not frame[0] or type(frame.IsFrameType) ~= "function" then + AceHook:error("Bad argument #2 to `HookScript'. Expected frame.") + end + AceHook:argCheck(script, 3, "string") + AceHook:argCheck(handler, 4, "function", "string", "nil") + hookMethod(self, frame, script, handler, true, false) +end + +-- ("function") or (object, "method") +function AceHook:IsHooked(obj, method) + if type(obj) == "string" then + if registry[self][obj] and actives[registry[self][obj]] then + return true, handlers[registry[self][obj]] + end + else + AceHook:argCheck(obj, 2, "string", "table") + AceHook:argCheck(method, 3, "string") + if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then + return true, handlers[registry[self][obj][method]] + end + end + + return false, nil +end + +-- ("function") or (object, "method") +function AceHook:Unhook(obj, method) + if type(obj) == "string" then + unhookFunction(self, obj) + else + AceHook:argCheck(obj, 2, "string", "table") + AceHook:argCheck(method, 3, "string") + unhookMethod(self, obj, method) + end +end + +function AceHook:UnhookAll() + for key, value in pairs(registry[self]) do + if type(key) == "table" then + for method in pairs(value) do + self:Unhook(key, method) + end + else + self:Unhook(key) + end + end +end + +function AceHook:HookReport() + DEFAULT_CHAT_FRAME:AddMessage("This is a list of all active hooks for this object:") + if not next(registry[self]) then + DEFAULT_CHAT_FRAME:AddMessage("No hooks") + end + + for key, value in pairs(registry[self]) do + if type(value) == "table" then + for method, uid in pairs(value) do + DEFAULT_CHAT_FRAME:AddMessage(string.format("object: %s method: %q |cff%s|r%s", tostring(key), method, actives[uid] and "00ff00Active" or "ffff00Inactive", not self.hooks[key][method] and " |cff7f7fff-Secure-|r" or "")) + end + else + DEFAULT_CHAT_FRAME:AddMessage(string.format("function: %q |cff%s|r%s", tostring(key), actives[value] and "00ff00Active" or "ffff00Inactive", not self.hooks[key] and " |cff7f7fff-Secure-|r" or "")) + end + end +end + +function AceHook:OnInstanceInit(object) + if not object.hooks then + object.hooks = new() + end + if not registry[object] then + registry[object] = new() + end +end + +AceHook.OnManualEmbed = AceHook.OnInstanceInit + +function AceHook:OnEmbedDisable(target) + self.UnhookAll(target) +end + +local function activate(self, oldLib, oldDeactivate) + AceHook = self + + self.handlers = oldLib and oldLib.handlers or {} + self.registry = oldLib and oldLib.registry or {} + self.scripts = oldLib and oldLib.scripts or {} + self.actives = oldLib and oldLib.actives or {} + + handlers = self.handlers + registry = self.registry + scripts = self.scripts + actives = self.actives + + AceHook.super.activate(self, oldLib, oldDeactivate) + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceHook, MAJOR_VERSION, MINOR_VERSION, activate) diff --git a/modules/chronometer/libs/AceLibrary/AceLibrary.lua b/modules/chronometer/libs/AceLibrary/AceLibrary.lua new file mode 100644 index 0000000..0f2b237 --- /dev/null +++ b/modules/chronometer/libs/AceLibrary/AceLibrary.lua @@ -0,0 +1,757 @@ +--[[ +Name: AceLibrary +Revision: $Rev: 17722 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Iriel (iriel@vigilance-committee.org) + Tekkub (tekkub@gmail.com) + Revision: $Rev: 17722 $ +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceLibrary +SVN: http://svn.wowace.com/root/trunk/Ace2/AceLibrary +Description: Versioning library to handle other library instances, upgrading, + and proper access. + It also provides a base for libraries to work off of, providing + proper error tools. It is handy because all the errors occur in the + file that called it, not in the library file itself. +Dependencies: None +]] + +local ACELIBRARY_MAJOR = "AceLibrary" +local ACELIBRARY_MINOR = "$Revision: 17722 $" + +if loadstring("return function(...) return ... end") and AceLibrary and AceLibrary:HasInstance(ACELIBRARY_MAJOR) then return end -- lua51 check +local table_setn +do + local version = GetBuildInfo() + if string.find(version, "^2%.") then + -- 2.0.0 + table_setn = function() end + else + table_setn = table.setn + end +end + +local string_gfind = string.gmatch or string.gfind + +local _G = getfenv(0) +local previous = _G[ACELIBRARY_MAJOR] +if previous and not previous:IsNewVersion(ACELIBRARY_MAJOR, ACELIBRARY_MINOR) then return end + +local function safecall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) + local success, err = pcall(func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) + if not success then geterrorhandler()(err) end +end + +-- @table AceLibrary +-- @brief System to handle all versioning of libraries. +local AceLibrary = {} +local AceLibrary_mt = {} +setmetatable(AceLibrary, AceLibrary_mt) + +local tmp +local function error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if type(self) ~= "table" then + _G.error(string.format("Bad argument #1 to `error' (table expected, got %s)", type(self)), 2) + end + if not tmp then + tmp = {} + else + for k in pairs(tmp) do tmp[k] = nil end + table_setn(tmp, 0) + end + + table.insert(tmp, a1) + table.insert(tmp, a2) + table.insert(tmp, a3) + table.insert(tmp, a4) + table.insert(tmp, a5) + table.insert(tmp, a6) + table.insert(tmp, a7) + table.insert(tmp, a8) + table.insert(tmp, a9) + table.insert(tmp, a10) + table.insert(tmp, a11) + table.insert(tmp, a12) + table.insert(tmp, a13) + table.insert(tmp, a14) + table.insert(tmp, a15) + table.insert(tmp, a16) + table.insert(tmp, a17) + table.insert(tmp, a18) + table.insert(tmp, a19) + table.insert(tmp, a20) + + local stack = debugstack() + if not message then + local _,_,second = string.find(stack, "\n(.-)\n") + message = "error raised! " .. second + else + for i = 1,table.getn(tmp) do + tmp[i] = tostring(tmp[i]) + end + for i = 1,10 do + table.insert(tmp, "nil") + end + message = string.format(message, unpack(tmp)) + end + + if getmetatable(self) and getmetatable(self).__tostring then + message = string.format("%s: %s", tostring(self), message) + elseif type(rawget(self, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self:GetLibraryVersion()) then + message = string.format("%s: %s", self:GetLibraryVersion(), message) + elseif type(rawget(self, 'class')) == "table" and type(rawget(self.class, 'GetLibraryVersion')) == "function" and AceLibrary:HasInstance(self.class:GetLibraryVersion()) then + message = string.format("%s: %s", self.class:GetLibraryVersion(), message) + end + + local first = string.gsub(stack, "\n.*", "") + local file = string.gsub(first, ".*\\(.*).lua:%d+: .*", "%1") + file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + + local i = 0 + for s in string_gfind(stack, "\n([^\n]*)") do + i = i + 1 + if not string.find(s, file .. "%.lua:%d+:") then + file = string.gsub(s, "^.*\\(.*).lua:%d+: .*", "%1") + file = string.gsub(file, "([%(%)%.%*%+%-%[%]%?%^%$%%])", "%%%1") + break + end + end + local j = 0 + for s in string_gfind(stack, "\n([^\n]*)") do + j = j + 1 + if j > i and not string.find(s, file .. "%.lua:%d+:") then + _G.error(message, j + 1) + return + end + end + _G.error(message, 2) + return +end + +local function assert(self, condition, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if not condition then + if not message then + local stack = debugstack() + local _,_,second = string.find(stack, "\n(.-)\n") + message = "assertion failed! " .. second + end + error(self, message, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + return + end + return condition +end + +local function argCheck(self, arg, num, kind, kind2, kind3, kind4, kind5) + if type(num) ~= "number" then + error(self, "Bad argument #3 to `argCheck' (number expected, got %s)", type(num)) + elseif type(kind) ~= "string" then + error(self, "Bad argument #4 to `argCheck' (string expected, got %s)", type(kind)) + end + local errored = false + arg = type(arg) + if arg ~= kind and arg ~= kind2 and arg ~= kind3 and arg ~= kind4 and arg ~= kind5 then + local _,_,func = string.find(debugstack(), "`argCheck'.-([`<].-['>])") + if not func then + _,_,func = string.find(debugstack(), "([`<].-['>])") + end + if kind5 then + error(self, "Bad argument #%s to %s (%s, %s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, kind5, arg) + elseif kind4 then + error(self, "Bad argument #%s to %s (%s, %s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, kind4, arg) + elseif kind3 then + error(self, "Bad argument #%s to %s (%s, %s, or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, kind3, arg) + elseif kind2 then + error(self, "Bad argument #%s to %s (%s or %s expected, got %s)", tonumber(num) or 0/0, func, kind, kind2, arg) + else + error(self, "Bad argument #%s to %s (%s expected, got %s)", tonumber(num) or 0/0, func, kind, arg) + end + end +end + +local function pcall(self, func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = _G.pcall(func, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + if not a1 then + error(self, string.gsub(a2, ".-%.lua:%d-: ", "")) + else + return a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 + end +end + +local recurse = {} +local function addToPositions(t, major) + if not AceLibrary.positions[t] or AceLibrary.positions[t] == major then + rawset(t, recurse, true) + AceLibrary.positions[t] = major + for k,v in pairs(t) do + if type(v) == "table" and not rawget(v, recurse) then + addToPositions(v, major) + end + if type(k) == "table" and not rawget(k, recurse) then + addToPositions(k, major) + end + end + local mt = getmetatable(t) + if mt and not rawget(mt, recurse) then + addToPositions(mt, major) + end + rawset(t, recurse, nil) + end +end + +local function svnRevisionToNumber(text) + if type(text) == "string" then + if string.find(text, "^%$Revision: (%d+) %$$") then + return tonumber((string.gsub(text, "^%$Revision: (%d+) %$$", "%1"))) + elseif string.find(text, "^%$Rev: (%d+) %$$") then + return tonumber((string.gsub(text, "^%$Rev: (%d+) %$$", "%1"))) + elseif string.find(text, "^%$LastChangedRevision: (%d+) %$$") then + return tonumber((string.gsub(text, "^%$LastChangedRevision: (%d+) %$$", "%1"))) + end + elseif type(text) == "number" then + return text + end + return nil +end + +local crawlReplace +do + local recurse = {} + local function func(t, to, from) + if recurse[t] then + return + end + recurse[t] = true + local mt = getmetatable(t) + setmetatable(t, nil) + rawset(t, to, rawget(t, from)) + rawset(t, from, nil) + for k,v in pairs(t) do + if v == from then + t[k] = to + elseif type(v) == "table" then + if not recurse[v] then + func(v, to, from) + end + end + + if type(k) == "table" then + if not recurse[k] then + func(k, to, from) + end + end + end + setmetatable(t, mt) + if mt then + if mt == from then + setmetatable(t, to) + elseif not recurse[mt] then + func(mt, to, from) + end + end + end + function crawlReplace(t, to, from) + func(t, to, from) + for k in pairs(recurse) do + recurse[k] = nil + end + end +end + +-- @function destroyTable +-- @brief remove all the contents of a table +-- @param t table to destroy +local function destroyTable(t) + setmetatable(t, nil) + for k,v in pairs(t) do t[k] = nil end + table_setn(t, 0) +end + +local function isFrame(frame) + return type(frame) == "table" and type(rawget(frame, 0)) == "userdata" and type(rawget(frame, 'IsFrameType')) == "function" and getmetatable(frame) and type(rawget(getmetatable(frame), '__index')) == "function" +end + +local new, del +do + local tables = setmetatable({}, {__mode = "k"}) + + function new() + local t = next(tables) + if t then + tables[t] = nil + return t + else + return {} + end + end + + function del(t, depth) + if depth and depth > 0 then + for k,v in pairs(t) do + if type(v) == "table" and not isFrame(v) then + del(v, depth - 1) + end + end + end + destroyTable(t) + tables[t] = true + end +end + +-- @function copyTable +-- @brief Create a shallow copy of a table and return it. +-- @param from The table to copy from +-- @return A shallow copy of the table +local function copyTable(from) + local to = new() + for k,v in pairs(from) do to[k] = v end + table_setn(to, table.getn(from)) + setmetatable(to, getmetatable(from)) + return to +end + +-- @function deepTransfer +-- @brief Fully transfer all data, keeping proper previous table +-- backreferences stable. +-- @param to The table with which data is to be injected into +-- @param from The table whose data will be injected into the first +-- @param saveFields If available, a shallow copy of the basic data is saved +-- in here. +-- @param list The account of table references +-- @param list2 The current status on which tables have been traversed. +local deepTransfer +do + -- @function examine + -- @brief Take account of all the table references to be shared + -- between the to and from tables. + -- @param to The table with which data is to be injected into + -- @param from The table whose data will be injected into the first + -- @param list An account of the table references + local function examine(to, from, list, major) + list[from] = to + for k,v in pairs(from) do + if rawget(to, k) and type(from[k]) == "table" and type(to[k]) == "table" and not list[from[k]] then + if from[k] == to[k] then + list[from[k]] = to[k] + elseif AceLibrary.positions[from[v]] ~= major and AceLibrary.positions[from[v]] then + list[from[k]] = from[k] + elseif not list[from[k]] then + examine(to[k], from[k], list, major) + end + end + end + return list + end + + function deepTransfer(to, from, saveFields, major, list, list2) + setmetatable(to, nil) + local createdList + if not list then + createdList = true + list = new() + list2 = new() + examine(to, from, list, major) + end + list2[to] = to + for k,v in pairs(to) do + if type(rawget(from, k)) ~= "table" or type(v) ~= "table" or isFrame(v) then + if saveFields then + saveFields[k] = v + end + to[k] = nil + elseif v ~= _G then + if saveFields then + saveFields[k] = copyTable(v) + end + end + end + for k in pairs(from) do + if rawget(to, k) and to[k] ~= from[k] and AceLibrary.positions[to[k]] == major and from[k] ~= _G then + if not list2[to[k]] then + deepTransfer(to[k], from[k], nil, major, list, list2) + end + to[k] = list[to[k]] or list2[to[k]] + else + rawset(to, k, from[k]) + end + end + table_setn(to, table.getn(from)) + setmetatable(to, getmetatable(from)) + local mt = getmetatable(to) + if mt then + if list[mt] then + setmetatable(to, list[mt]) + elseif mt.__index and list[mt.__index] then + mt.__index = list[mt.__index] + end + end + destroyTable(from) + if createdList then + del(list) + del(list2) + end + end +end + +-- @method TryToLoadStandalone +-- @brief Attempt to find and load a standalone version of the requested library +-- @param major A string representing the major version +-- @return If library is found, return values from the call to LoadAddOn are returned +-- If the library has been requested previously, nil is returned. +local function TryToLoadStandalone(major) + if not AceLibrary.scannedlibs then AceLibrary.scannedlibs = {} end + if AceLibrary.scannedlibs[major] then return end + + AceLibrary.scannedlibs[major] = true + + local name, _, _, enabled, loadable = GetAddOnInfo(major) + if loadable then + return LoadAddOn(name) + end + + for i=1,GetNumAddOns() do + if GetAddOnMetadata(i, "X-AceLibrary-"..major) then + local name, _, _, enabled, loadable = GetAddOnInfo(i) + if loadable then + return LoadAddOn(name) + end + end + end +end + +-- @method IsNewVersion +-- @brief Obtain whether the supplied version would be an upgrade to the +-- current version. This allows for bypass code in library +-- declaration. +-- @param major A string representing the major version +-- @param minor An integer or an svn revision string representing the minor version +-- @return whether the supplied version would be newer than what is +-- currently available. +function AceLibrary:IsNewVersion(major, minor) + argCheck(self, major, 2, "string") + TryToLoadStandalone(major) + + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(string.format("Bad argument #3 to `IsNewVersion'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) + end + end + argCheck(self, minor, 3, "number") + local data = self.libs[major] + if not data then + return true + end + return data.minor < minor +end + +-- @method HasInstance +-- @brief Returns whether an instance exists. This allows for optional support of a library. +-- @param major A string representing the major version. +-- @param minor (optional) An integer or an svn revision string representing the minor version. +-- @return Whether an instance exists. +function AceLibrary:HasInstance(major, minor) + argCheck(self, major, 2, "string") + TryToLoadStandalone(major) + + if minor then + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(string.format("Bad argument #3 to `HasInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) + end + end + argCheck(self, minor, 3, "number") + if not self.libs[major] then + return + end + return self.libs[major].minor == minor + end + return self.libs[major] and true +end + +-- @method GetInstance +-- @brief Returns the library with the given major/minor version. +-- @param major A string representing the major version. +-- @param minor (optional) An integer or an svn revision string representing the minor version. +-- @return The library with the given major/minor version. +function AceLibrary:GetInstance(major, minor) + argCheck(self, major, 2, "string") + TryToLoadStandalone(major) + + local data = self.libs[major] + if not data then + _G.error(string.format("Cannot find a library instance of %s.", major), 2) + return + end + if minor then + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(string.format("Bad argument #3 to `GetInstance'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) + end + end + argCheck(self, minor, 2, "number") + if data.minor ~= minor then + _G.error(string.format("Cannot find a library instance of %s, minor version %d.", major, minor), 2) + return + end + end + return data.instance +end + +-- Syntax sugar. AceLibrary("FooBar-1.0") +AceLibrary_mt.__call = AceLibrary.GetInstance + +local donothing + +local AceEvent + +-- @method Register +-- @brief Registers a new version of a given library. +-- @param newInstance the library to register +-- @param major the major version of the library +-- @param minor the minor version of the library +-- @param activateFunc (optional) A function to be called when the library is +-- fully activated. Takes the arguments +-- (newInstance [, oldInstance, oldDeactivateFunc]). If +-- oldInstance is given, you should probably call +-- oldDeactivateFunc(oldInstance). +-- @param deactivateFunc (optional) A function to be called by a newer library's +-- activateFunc. +-- @param externalFunc (optional) A function to be called whenever a new +-- library is registered. +function AceLibrary:Register(newInstance, major, minor, activateFunc, deactivateFunc, externalFunc) + argCheck(self, newInstance, 2, "table") + argCheck(self, major, 3, "string") + if type(minor) == "string" then + local m = svnRevisionToNumber(minor) + if m then + minor = m + else + _G.error(string.format("Bad argument #4 to `Register'. Must be a number or SVN revision string. %q is not appropriate", minor), 2) + end + end + argCheck(self, minor, 4, "number") + if math.floor(minor) ~= minor or minor < 0 then + error(self, "Bad argument #4 to `Register' (integer >= 0 expected, got %s)", minor) + end + argCheck(self, activateFunc, 5, "function", "nil") + argCheck(self, deactivateFunc, 6, "function", "nil") + argCheck(self, externalFunc, 7, "function", "nil") + if not deactivateFunc then + if not donothing then + donothing = function() end + end + deactivateFunc = donothing + end + local data = self.libs[major] + if not data then + -- This is new + local instance = copyTable(newInstance) + crawlReplace(instance, instance, newInstance) + destroyTable(newInstance) + if AceLibrary == newInstance then + self = instance + AceLibrary = instance + end + self.libs[major] = { + instance = instance, + minor = minor, + deactivateFunc = deactivateFunc, + externalFunc = externalFunc, + } + rawset(instance, 'GetLibraryVersion', function(self) + return major, minor + end) + if not rawget(instance, 'error') then + rawset(instance, 'error', error) + end + if not rawget(instance, 'assert') then + rawset(instance, 'assert', assert) + end + if not rawget(instance, 'argCheck') then + rawset(instance, 'argCheck', argCheck) + end + if not rawget(instance, 'pcall') then + rawset(instance, 'pcall', pcall) + end + addToPositions(instance, major) + if activateFunc then + safecall(activateFunc, instance, nil, nil) -- no old version, so explicit nil + end + + if externalFunc then + for k,data in pairs(self.libs) do + if k ~= major then + safecall(externalFunc, instance, k, data.instance) + end + end + end + + for k,data in pairs(self.libs) do + if k ~= major and data.externalFunc then + safecall(data.externalFunc, data.instance, major, instance) + end + end + if major == "AceEvent-2.0" then + AceEvent = instance + end + if AceEvent then + AceEvent.TriggerEvent(self, "AceLibrary_Register", major, instance) + end + + return instance + end + local instance = data.instance + if minor <= data.minor then + -- This one is already obsolete, raise an error. + _G.error(string.format("Obsolete library registered. %s is already registered at version %d. You are trying to register version %d. Hint: if not AceLibrary:IsNewVersion(%q, %d) then return end", major, data.minor, minor, major, minor), 2) + return + end + -- This is an update + local oldInstance = new() + + addToPositions(newInstance, major) + local isAceLibrary = (AceLibrary == newInstance) + local old_error, old_assert, old_argCheck, old_pcall + if isAceLibrary then + self = instance + AceLibrary = instance + + old_error = instance.error + old_assert = instance.assert + old_argCheck = instance.argCheck + old_pcall = instance.pcall + + self.error = error + self.assert = assert + self.argCheck = argCheck + self.pcall = pcall + end + deepTransfer(instance, newInstance, oldInstance, major) + crawlReplace(instance, instance, newInstance) + local oldDeactivateFunc = data.deactivateFunc + data.minor = minor + data.deactivateFunc = deactivateFunc + data.externalFunc = externalFunc + rawset(instance, 'GetLibraryVersion', function(self) + return major, minor + end) + if not rawget(instance, 'error') then + rawset(instance, 'error', error) + end + if not rawget(instance, 'assert') then + rawset(instance, 'assert', assert) + end + if not rawget(instance, 'argCheck') then + rawset(instance, 'argCheck', argCheck) + end + if not rawget(instance, 'pcall') then + rawset(instance, 'pcall', pcall) + end + if isAceLibrary then + for _,v in pairs(self.libs) do + local i = type(v) == "table" and v.instance + if type(i) == "table" then + if not rawget(i, 'error') or i.error == old_error then + rawset(i, 'error', error) + end + if not rawget(i, 'assert') or i.assert == old_assert then + rawset(i, 'assert', assert) + end + if not rawget(i, 'argCheck') or i.argCheck == old_argCheck then + rawset(i, 'argCheck', argCheck) + end + if not rawget(i, 'pcall') or i.pcall == old_pcall then + rawset(i, 'pcall', pcall) + end + end + end + end + if activateFunc then + safecall(activateFunc, instance, oldInstance, oldDeactivateFunc) + else + safecall(oldDeactivateFunc, oldInstance) + end + del(oldInstance) + + if externalFunc then + for k,data in pairs(self.libs) do + if k ~= major then + safecall(externalFunc, instance, k, data.instance) + end + end + end + + return instance +end + +local iter +function AceLibrary:IterateLibraries() + if not iter then + local function iter(t, k) + k = next(t, k) + if not k then + return nil + else + return k, t[k].instance + end + end + end + return iter, self.libs, nil +end + +-- @function Activate +-- @brief The activateFunc for AceLibrary itself. Called when +-- AceLibrary properly registers. +-- @param self Reference to AceLibrary +-- @param oldLib (optional) Reference to an old version of AceLibrary +-- @param oldDeactivate (optional) Function to deactivate the old lib +local function activate(self, oldLib, oldDeactivate) + if not self.libs then + if oldLib then + self.libs = oldLib.libs + self.scannedlibs = oldLib.scannedlibs + end + if not self.libs then + self.libs = {} + end + if not self.scannedlibs then + self.scannedlibs = {} + end + end + if not self.positions then + if oldLib then + self.positions = oldLib.positions + end + if not self.positions then + self.positions = setmetatable({}, { __mode = "k" }) + end + end + + -- Expose the library in the global environment + _G[ACELIBRARY_MAJOR] = self + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +if not previous then + previous = AceLibrary +end +if not previous.libs then + previous.libs = {} +end +AceLibrary.libs = previous.libs +if not previous.positions then + previous.positions = setmetatable({}, { __mode = "k" }) +end +AceLibrary.positions = previous.positions +AceLibrary:Register(AceLibrary, ACELIBRARY_MAJOR, ACELIBRARY_MINOR, activate) diff --git a/modules/chronometer/libs/AceOO-2.0/AceOO-2.0.lua b/modules/chronometer/libs/AceOO-2.0/AceOO-2.0.lua new file mode 100644 index 0000000..659be0f --- /dev/null +++ b/modules/chronometer/libs/AceOO-2.0/AceOO-2.0.lua @@ -0,0 +1,1004 @@ +--[[ +Name: AceOO-2.0 +Revision: $Rev: 17638 $ +Developed by: The Ace Development Team (http://www.wowace.com/index.php/The_Ace_Development_Team) +Inspired By: Ace 1.x by Turan (turan@gryphon.com) +Website: http://www.wowace.com/ +Documentation: http://www.wowace.com/index.php/AceOO-2.0 +SVN: http://svn.wowace.com/root/trunk/Ace2/AceOO-2.0 +Description: Library to provide an object-orientation framework. +Dependencies: AceLibrary +]] + +local MAJOR_VERSION = "AceOO-2.0" +local MINOR_VERSION = "$Revision: 17638 $" + +-- This ensures the code is only executed if the libary doesn't already exist, or is a newer version +if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end + +if loadstring("return function(...) return ... end") and AceLibrary:HasInstance(MAJOR_VERSION) then return end -- lua51 check +local table_setn +do + local version = GetBuildInfo() + if string.find(version, "^2%.") then + -- 2.0.0 + table_setn = function() end + else + table_setn = table.setn + end +end + +local AceOO = { + error = AceLibrary.error, + argCheck = AceLibrary.argCheck +} + +-- @function getuid +-- @brief Obtain a unique string identifier for the object in question. +-- @param t The object to obtain the uid for. +-- @return The uid string. +local function pad(cap) + return string.rep('0', 8 - string.len(cap)) .. cap +end +local function getuid(t) + local mt = getmetatable(t) + setmetatable(t, nil) + local str = tostring(t) + setmetatable(t, mt) + local _,_,cap = string.find(str, '[^:]*: 0x(.*)$') + if cap then return pad(cap) end + _,_,cap = string.find(str, '[^:]*: (.*)$') + if cap then return pad(cap) end +end + +local function getlibrary(o) + if type(o) == "table" then + return o + elseif type(o) == "string" then + if not AceLibrary:HasInstance(o) then + AceOO:error("Library %q does not exist.", o) + end + return AceLibrary(o) + end +end + +-- @function Factory +-- @brief Construct a factory for the creation of objects. +-- @param obj The object whose init method will be called on the new factory +-- object. +-- @param newobj The object whose init method will be called on the new +-- objects that the Factory creates, to initialize them. +-- @param (a1..a20) Arguments which will be passed to obj.init() in addition +-- to the Factory object. +-- @return The new factory which creates a newobj when its new method is called, +-- or when it is called directly (__call metamethod). +local Factory +do + local function new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + local t = {} + local uid = getuid(t) + local l = getlibrary + obj:init(t, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), + l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), + l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), + l(a20)) + t.uid = uid + return t + end + + local function createnew(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, + a19, a20) + local o = self.prototype + local l = getlibrary + return new(o, l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), + l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), + l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), + l(a20)) + end + + function Factory(obj, newobj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, + a19, a20) + local t = new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + t.prototype = newobj + t.new = createnew + getmetatable(t).__call = t.new + return t + end +end + + +local function objtostring(self) + if self.ToString then + return self:ToString() + elseif self.GetLibraryVersion then + return (self:GetLibraryVersion()) + elseif self.super then + local s = "Sub-" .. tostring(self.super) + local first = true + if self.interfaces then + for interface in pairs(self.interfaces) do + if first then + s = s .. "(" .. tostring(interface) + first = false + else + s = s .. ", " .. tostring(interface) + end + end + end + if self.mixins then + for mixin in pairs(self.mixins) do + if first then + s = s .. tostring(mixin) + first = false + else + s = s .. ", " .. tostring(mixin) + end + end + end + if first then + if self.uid then + return s .. ":" .. self.uid + else + return s + end + else + return s .. ")" + end + else + return self.uid and 'Subclass:' .. self.uid or 'Subclass' + end +end + +-- @table Object +-- @brief Base of all objects, including Class. +-- +-- @method init +-- @brief Initialize a new object. +-- @param newobject The object to initialize +-- @param class The class to make newobject inherit from +local Object +do + Object = {} + function Object:init(newobject, class) + local parent = class or self + if not rawget(newobject, 'uid') then + newobject.uid = getuid(newobject) + end + local mt = { + __index = parent, + __tostring = objtostring, + } + setmetatable(newobject, mt) + end + Object.uid = getuid(Object) + setmetatable(Object, { __tostring = function() return 'Object' end }) +end + +local Interface + +local function validateInterface(object, interface) + if not object.class and object.prototype then + object = object.prototype + end + for k,v in pairs(interface.interface) do + if tostring(type(object[k])) ~= v then + return false + end + end + if interface.superinterfaces then + for superinterface in pairs(interface.superinterfaces) do + if not validateInterface(object, superinterface) then + return false + end + end + end + if type(object.class) == "table" and rawequal(object.class.prototype, object) then + if not object.class.interfaces then + rawset(object.class, 'interfaces', {}) + end + object.class.interfaces[interface] = true + elseif type(object.class) == "table" and type(object.class.prototype) == "table" then + validateInterface(object.class.prototype, interface) + -- check if class is proper, thus preventing future checks. + end + return true +end + +-- @function inherits +-- @brief Return whether an Object or Class inherits from a given +-- parent. +-- @param object Object or Class to check +-- @param parent Parent to test inheritance from +-- @return whether an Object or Class inherits from a given +-- parent. +local function inherits(object, parent) + object = getlibrary(object) + if type(parent) == "string" then + if not AceLibrary:HasInstance(parent) then + return false + else + parent = AceLibrary(parent) + end + end + AceOO:argCheck(parent, 2, "table") + if type(object) ~= "table" then + return false + end + local current + if object.class then + current = object.class + else + current = object + end + if type(current) ~= "table" then + return false + end + if rawequal(current, parent) then + return true + end + if parent.class then + while true do + if rawequal(current, Object) then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if rawequal(mixin, parent) then + return true + end + end + end + if current.interfaces then + for interface in pairs(current.interfaces) do + if rawequal(interface, parent) then + return true + end + end + end + current = current.super + if type(current) ~= "table" then + break + end + end + + local isInterface = false + local curr = parent.class + while true do + if rawequal(curr, Object) then + break + elseif rawequal(curr, Interface) then + isInterface = true + break + end + curr = curr.super + if type(curr) ~= "table" then + break + end + end + return isInterface and validateInterface(object, parent) + else + while true do + if rawequal(current, parent) then + return true + elseif rawequal(current, Object) then + return false + end + current = current.super + if type(current) ~= "table" then + return false + end + end + end +end + +-- @table Class +-- @brief An object factory which sets up inheritence and supports +-- 'mixins'. +-- +-- @metamethod Class call +-- @brief Call ClassFactory:new() to create a new class. +-- +-- @method Class new +-- @brief Construct a new object. +-- @param (a1..a20) Arguments to pass to the object init function. +-- @return The new object. +-- +-- @method Class init +-- @brief Initialize a new class. +-- @param parent Superclass. +-- @param (a1..a20) Mixins. +-- +-- @method Class ToString +-- @return A string representing the object, in this case 'Class'. +local initStatus +local Class +local Mixin +local autoEmbed = false +local function traverseInterfaces(bit, total) + if bit.superinterfaces then + for interface in pairs(bit.superinterfaces) do + if not total[interface] then + total[interface] = true + traverseInterfaces(interface, total) + end + end + end +end +local class_new +do + Class = Factory(Object, setmetatable({}, {__index = Object}), Object) + Class.super = Object + + local function protostring(t) + return '<' .. tostring(t.class) .. ' prototype>' + end + local function classobjectstring(t) + if t.ToString then + return t:ToString() + elseif t.GetLibraryVersion then + return (t:GetLibraryVersion()) + else + return '<' .. tostring(t.class) .. ' instance>' + end + end + local function classobjectequal(self, other) + if type(self) == "table" and self.Equals then + return self:Equals(other) + elseif type(other) == "table" and other.Equals then + return other:Equals(self) + elseif type(self) == "table" and self.CompareTo then + return self:CompareTo(other) == 0 + elseif type(other) == "table" and other.CompareTo then + return other:CompareTo(self) == 0 + else + return rawequal(self, other) + end + end + local function classobjectlessthan(self, other) + if type(self) == "table" and self.IsLessThan then + return self:IsLessThan(other) + elseif type(other) == "table" and other.IsLessThanOrEqualTo then + return not other:IsLessThanOrEqualTo(self) + elseif type(self) == "table" and self.CompareTo then + return self:CompareTo(other) < 0 + elseif type(other) == "table" and other.CompareTo then + return other:CompareTo(self) > 0 + elseif type(other) == "table" and other.IsLessThan and other.Equals then + return other:Equals(self) or other:IsLessThan(self) + else + AceOO:error("cannot compare two objects") + end + end + local function classobjectlessthanequal(self, other) + if type(self) == "table" and self.IsLessThanOrEqualTo then + return self:IsLessThanOrEqualTo(other) + elseif type(other) == "table" and other.IsLessThan then + return not other:IsLessThan(self) + elseif type(self) == "table" and self.CompareTo then + return self:CompareTo(other) <= 0 + elseif type(other) == "table" and other.CompareTo then + return other:CompareTo(self) >= 0 + elseif type(self) == "table" and self.IsLessThan and self.Equals then + return self:Equals(other) or self:IsLessThan(other) + else + AceOO:error("cannot compare two incompatible objects") + end + end + local function classobjectadd(self, other) + if type(self) == "table" and self.Add then + return self:Add(other) + else + AceOO:error("cannot add two incompatible objects") + end + end + local function classobjectsub(self, other) + if type(self) == "table" and self.Subtract then + return self:Subtract(other) + else + AceOO:error("cannot subtract two incompatible objects") + end + end + local function classobjectunm(self, other) + if type(self) == "table" and self.UnaryNegation then + return self:UnaryNegation(other) + else + AceOO:error("attempt to negate an incompatible object") + end + end + local function classobjectmul(self, other) + if type(self) == "table" and self.Multiply then + return self:Multiply(other) + else + AceOO:error("cannot multiply two incompatible objects") + end + end + local function classobjectdiv(self, other) + if type(self) == "table" and self.Divide then + return self:Divide(other) + else + AceOO:error("cannot divide two incompatible objects") + end + end + local function classobjectpow(self, other) + if type(self) == "table" and self.Exponent then + return self:Exponent(other) + else + AceOO:error("cannot exponentiate two incompatible objects") + end + end + local function classobjectconcat(self, other) + if type(self) == "table" and self.Concatenate then + return self:Concatenate(other) + else + AceOO:error("cannot concatenate two incompatible objects") + end + end + function class_new(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + if self.virtual then + AceOO:error("Cannot instantiate a virtual class.") + end + + local o = self.prototype + local newobj = {} + if o.class and o.class.instancemeta then + setmetatable(newobj, o.class.instancemeta) + else + Object:init(newobj, o) + end + + if self.interfaces and not self.interfacesVerified then + -- Verify the interfaces + + for interface in pairs(self.interfaces) do + for field,kind in pairs(interface.interface) do + if tostring(type(newobj[field])) ~= kind then + AceOO:error("Class did not satisfy all interfaces. %q is required to be a %s. It is a %s", field, kind, tostring(type(newobj[field]))) + end + end + end + self.interfacesVerified = true + end + local tmp = initStatus + initStatus = newobj + newobj:init(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + if initStatus then + initStatus = tmp + AceOO:error("Initialization not completed, be sure to call the superclass's init method.") + return + end + initStatus = tmp + return newobj + end + local classmeta = { + __tostring = objtostring, + __call = function(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + return self:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, + a13, a14, a15, a16, a17, a18, a19, a20) + end, + } + function Class:init(newclass, parent, a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, + a17, a18, a19, a20) + parent = parent or self + + local total + + if parent.class then + total = { + parent, a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, + a17, a18, a19, a20 + } + parent = self + else + total = { + a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, + a17, a18, a19, a20 + } + end + if not inherits(parent, Class) then + AceOO:error("Classes must inherit from a proper class") + end + if parent.sealed then + AceOO:error("Cannot inherit from a sealed class") + end + for i,v in ipairs(total) do + if inherits(v, Mixin) and v.class then + if not newclass.mixins then + newclass.mixins = {} + end + if newclass.mixins[v] then + AceOO:error("Cannot explicitly inherit from the same mixin twice") + end + newclass.mixins[v] = true + elseif inherits(v, Interface) and v.class then + if not newclass.interfaces then + newclass.interfaces = {} + end + if newclass.interfaces[v] then + AceOO:error("Cannot explicitly inherit from the same interface twice") + end + newclass.interfaces[v] = true + else + AceOO:error("Classes can only inherit from one or zero classes and any number of mixins or interfaces") + end + end + if parent.interfaces then + if newclass.interfaces then + for interface in pairs(parent.interfaces) do + newclass.interfaces[interface] = true + end + else + newclass.interfaces = parent.interfaces + end + end + for k in pairs(total) do + total[k] = nil + end + table_setn(total, 0) + + newclass.super = parent + + newclass.prototype = setmetatable(total, { + __index = parent.prototype, + __tostring = protostring, + }) + total = nil + + newclass.instancemeta = { + __index = newclass.prototype, + __tostring = classobjectstring, + __eq = classobjectequal, + __lt = classobjectlessthan, + __le = classobjectlessthanequal, + __add = classobjectadd, + __sub = classobjectsub, + __unm = classobjectunm, + __mul = classobjectmul, + __div = classobjectdiv, + __pow = classobjectpow, + __concat = classobjectconcat, + } + + setmetatable(newclass, classmeta) + + newclass.new = class_new + + if newclass.mixins then + -- Fold in the mixins + local err, msg + for mixin in pairs(newclass.mixins) do + local ret + autoEmbed = true + ret, msg = pcall(mixin.embed, mixin, newclass.prototype) + autoEmbed = false + if not ret then + err = true + break + end + end + + if err then + local pt = newclass.prototype + for k,v in pairs(pt) do + pt[k] = nil + end + + -- method conflict + AceOO:error(msg) + end + end + + newclass.prototype.class = newclass + + if newclass.interfaces then + for interface in pairs(newclass.interfaces) do + traverseInterfaces(interface, newclass.interfaces) + end + end + if newclass.mixins then + for mixin in pairs(newclass.mixins) do + if mixin.interfaces then + if not newclass.interfaces then + newclass.interfaces = {} + end + for interface in pairs(mixin.interfaces) do + newclass.interfaces[interface] = true + end + end + end + end + end + function Class:ToString() + if type(self.GetLibraryVersion) == "function" then + return (self:GetLibraryVersion()) + else + return "Class" + end + end + + local tmp + function Class.prototype:init() + if rawequal(self, initStatus) then + initStatus = nil + else + AceOO:error("Improper self passed to init. You must do MyClass.super.prototype.init(self, ...)", 2) + end + self.uid = getuid(self) + local current = self.class + while true do + if current == Class then + break + end + if current.mixins then + for mixin in pairs(current.mixins) do + if type(mixin.OnInstanceInit) == "function" then + mixin:OnInstanceInit(self) + end + end + end + current = current.super + end + end +end + + +-- @object ClassFactory +-- @brief A factory for creating classes. Rarely used directly. +local ClassFactory = Factory(Object, Class, Object) + +function Class:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, + a12, a13, a14, a15, a16, a17, a18, a19, a20) + local x = ClassFactory:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, + a12, a13, a14, a15, a16, a17, a18, a19, a20) + if AceOO.classes then + AceOO.classes[x] = true + end + return x +end +getmetatable(Class).__call = Class.new + +-- @class Mixin +-- @brief A class to create mixin objects, which contain methods that get +-- "mixed in" to class prototypes. +-- +-- @object Mixin prototype +-- @brief The prototype that mixin objects inherit their methods from. +-- +-- @method Mixin prototype embed +-- @brief Mix in the methods of our object which are listed in our interface +-- to the supplied target table. +-- +-- @method Mixin prototype init +-- @brief Initialize the mixin object. +-- @param newobj The new object we're initializing. +-- @param interface The interface we implement (the list of methods our +-- prototype provides which should be mixed into the target +-- table by embed). +do + Mixin = Class() + function Mixin:ToString() + if self.GetLibraryVersion then + return (self:GetLibraryVersion()) + else + return 'Mixin' + end + end + local function _Embed(state, field, target) + field = next(state.export, field) + if field == nil then + return + end + + if rawget(target, field) or (target[field] and target[field] ~= state[field]) then + AceOO:error("Method conflict in attempt to mixin. Field %q", field) + end + + target[field] = state[field] + + local ret,msg = pcall(_Embed, state, field, target) + if not ret then + -- Mix in the next method according to the defined interface. If that + -- fails due to a conflict, re-raise to back out the previous mixed + -- methods. + + target[field] = nil + AceOO:error(msg) + end + end + function Mixin.prototype:embed(target) + local mt = getmetatable(target) + setmetatable(target, nil) + local err, msg = pcall(_Embed, self, nil, target) + if not err then + setmetatable(target, mt) + AceOO:error(msg) + return + end + if type(self.embedList) == "table" then + self.embedList[target] = true + end + if type(target.class) ~= "table" then + target[self] = true + end + if not autoEmbed and type(self.OnManualEmbed) == "function" then + self:OnManualEmbed(target) + end + setmetatable(target, mt) + end + + function Mixin.prototype:activate(oldLib, oldDeactivate) + if oldLib and oldLib.embedList then + for target in pairs(oldLib.embedList) do + local mt = getmetatable(target) + setmetatable(target, nil) + for field in pairs(oldLib.export) do + target[field] = nil + end + setmetatable(target, mt) + end + self.embedList = oldLib.embedList + for target in pairs(self.embedList) do + self:embed(target) + end + else + self.embedList = setmetatable({}, {__mode="k"}) + end + end + + function Mixin.prototype:init(export, a1, a2, a3, a4, a5, a6, a7, a8, a9, + a10, a11, a12, a13, a14, a15, a16, + a17, a18, a19, a20) + AceOO:argCheck(export, 2, "table") + for k,v in pairs(export) do + if type(k) ~= "number" then + AceOO:error("All keys to argument #2 must be numbers.") + elseif type(v) ~= "string" then + AceOO:error("All values to argument #2 must be strings.") + end + end + local num = table.getn(export) + for i = 1, num do + local v = export[i] + export[i] = nil + export[v] = true + end + table_setn(export, 0) + local interfaces + if a1 then + local l = getlibrary + interfaces = { l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8), + l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16), + l(a17), l(a18), l(a19), l(a20) } + for _,v in ipairs(interfaces) do + if not v.class or not inherits(v, Interface) then + AceOO:error("Mixins can inherit only from interfaces") + end + end + local num = table.getn(interfaces) + for i = 1, num do + local v = interfaces[i] + interfaces[i] = nil + interfaces[v] = true + end + table_setn(interfaces, 0) + for interface in pairs(interfaces) do + traverseInterfaces(interface, interfaces) + end + for interface in pairs(interfaces) do + for field,kind in pairs(interface.interface) do + if kind ~= "nil" then + local good = false + for bit in pairs(export) do + if bit == field then + good = true + break + end + end + if not good then + AceOO:error("Mixin does not fully accommodate field %q", field) + end + end + end + end + end + self.super = Mixin.prototype + Mixin.super.prototype.init(self) + self.export = export + self.interfaces = interfaces + end +end + +-- @class Interface +-- @brief A class to create interfaces, which contain contracts that classes +-- which inherit from this must comply with. +-- +-- @object Interface prototype +-- @brief The prototype that interface objects must adhere to. +-- +-- @method Interface prototype init +-- @brief Initialize the mixin object. +-- @param interface The interface we contract (the hash of fields forced). +-- @param (a1..a20) Superinterfaces +do + Interface = Class() + function Interface:ToString() + if self.GetLibraryVersion then + return (self:GetLibraryVersion()) + else + return 'Instance' + end + end + function Interface.prototype:init(interface, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + Interface.super.prototype.init(self) + AceOO:argCheck(interface, 2, "table") + for k,v in pairs(interface) do + if type(k) ~= "string" then + AceOO:error("All keys to argument #2 must be numbers.") + elseif type(v) ~= "string" then + AceOO:error("All values to argument #2 must be strings.") + elseif v ~= "nil" and v ~= "string" and v ~= "number" and v ~= "table" and v ~= "function" then + AceOO:error('All values to argument #2 must either be "nil", "string", "number", "table", or "function".') + end + end + local l = getlibrary + a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20 = l(a1), l(a2), l(a3), l(a4), l(a5), l(a6), l(a7), l(a8), l(a9), l(a10), l(a11), l(a12), l(a13), l(a14), l(a15), l(a16), l(a17), l(a18), l(a19), l(a20) + if a1 then + self.superinterfaces = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20} + for k,v in ipairs(self.superinterfaces) do + if not inherits(v, Interface) or not v.class then + AceOO:error('Cannot provide a non-Interface to inherit from') + end + end + local num = table.getn(self.superinterfaces) + for i = 1, num do + local v = self.superinterfaces[i] + self.superinterfaces[i] = nil + self.superinterfaces[v] = true + end + table_setn(self.superinterfaces, 0) + end + self.interface = interface + end +end + +-- @function Classpool +-- @brief Obtain a read only class from our pool of classes, indexed by the +-- superclass and mixins. +-- @param sc The superclass of the class we want. +-- @param (m1..m20) Mixins of the class we want's objects. +-- @return A read only class from the class pool. +local Classpool +do + local pool = setmetatable({}, {__mode = 'v'}) + local function newindex(k, v) + AceOO:error('Attempt to modify a read-only class.') + end + local function protonewindex(k, v) + AceOO:error('Attempt to modify a read-only class prototype.') + end + local function ts(bit) + if type(bit) ~= "table" then + return tostring(bit) + elseif getmetatable(bit) and bit.__tostring then + return tostring(bit) + elseif type(bit.GetLibraryVersion) == "function" then + return bit:GetLibraryVersion() + else + return tostring(bit) + end + end + local t + local function getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, + m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20) + if not t then t = {} end + if sc then if sc.uid then table.insert(t, sc.uid) else AceOO:error("%s is not an appropriate class/mixin", ts(sc)) end + if m1 then if m1.uid then table.insert(t, m1.uid) else AceOO:error("%s is not an appropriate mixin", ts(m1)) end + if m2 then if m2.uid then table.insert(t, m2.uid) else AceOO:error("%s is not an appropriate mixin", ts(m2)) end + if m3 then if m3.uid then table.insert(t, m3.uid) else AceOO:error("%s is not an appropriate mixin", ts(m3)) end + if m4 then if m4.uid then table.insert(t, m4.uid) else AceOO:error("%s is not an appropriate mixin", ts(m4)) end + if m5 then if m5.uid then table.insert(t, m5.uid) else AceOO:error("%s is not an appropriate mixin", ts(m5)) end + if m6 then if m6.uid then table.insert(t, m6.uid) else AceOO:error("%s is not an appropriate mixin", ts(m6)) end + if m7 then if m7.uid then table.insert(t, m7.uid) else AceOO:error("%s is not an appropriate mixin", ts(m7)) end + if m8 then if m8.uid then table.insert(t, m8.uid) else AceOO:error("%s is not an appropriate mixin", ts(m8)) end + if m9 then if m9.uid then table.insert(t, m9.uid) else AceOO:error("%s is not an appropriate mixin", ts(m9)) end + if m10 then if m10.uid then table.insert(t, m10.uid) else AceOO:error("%s is not an appropriate mixin", ts(m10)) end + if m11 then if m11.uid then table.insert(t, m11.uid) else AceOO:error("%s is not an appropriate mixin", ts(m11)) end + if m12 then if m12.uid then table.insert(t, m12.uid) else AceOO:error("%s is not an appropriate mixin", ts(m12)) end + if m13 then if m13.uid then table.insert(t, m13.uid) else AceOO:error("%s is not an appropriate mixin", ts(m13)) end + if m14 then if m14.uid then table.insert(t, m14.uid) else AceOO:error("%s is not an appropriate mixin", ts(m14)) end + if m15 then if m15.uid then table.insert(t, m15.uid) else AceOO:error("%s is not an appropriate mixin", ts(m15)) end + if m16 then if m16.uid then table.insert(t, m16.uid) else AceOO:error("%s is not an appropriate mixin", ts(m16)) end + if m17 then if m17.uid then table.insert(t, m17.uid) else AceOO:error("%s is not an appropriate mixin", ts(m17)) end + if m18 then if m18.uid then table.insert(t, m18.uid) else AceOO:error("%s is not an appropriate mixin", ts(m18)) end + if m19 then if m19.uid then table.insert(t, m19.uid) else AceOO:error("%s is not an appropriate mixin", ts(m19)) end + if m20 then if m20.uid then table.insert(t, m20.uid) else AceOO:error("%s is not an appropriate mixin", ts(m20)) end + end end end end end end end end end end end end end end end end end end end end end + table.sort(t) + local uid = table.concat(t, '') + for k in pairs(t) do t[k] = nil end + table_setn(t, 0) + return uid + end + local classmeta + function Classpool(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, + m10, m11, m12, m13, m14, m15, m16, + m17, m18, m19, m20) + local l = getlibrary + sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = l(sc), l(m1), l(m2), l(m3), l(m4), l(m5), l(m6), l(m7), l(m8), l(m9), l(m10), l(m11), l(m12), l(m13), l(m14), l(m15), l(m16), l(m17), l(m18), l(m19), l(m20) + if sc and sc.class then + sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20 = Class, sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19 + end + sc = sc or Class + local key = getcomplexuid(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, m16, m17, m18, m19, m20) + if not pool[key] then + local class = Class(sc, m1, m2, m3, m4, m5, m6, m7, m8, m9, + m10, m11, m12, m13, m14, m15, m16, m17, + m18, m19, m20) + if not classmeta then + classmeta = {} + local mt = getmetatable(class) + for k,v in pairs(mt) do + classmeta[k] = v + end + classmeta.__newindex = newindex + end + -- Prevent the user from adding methods to this class. + -- NOTE: I'm not preventing modifications of existing class members, + -- but it's likely that only a truly malicious user will be doing so. + class.sealed = true + setmetatable(class, classmeta) + getmetatable(class.prototype).__newindex = protonewindex + pool[key] = class + end + return pool[key] + end +end + +AceOO.Factory = Factory +AceOO.Object = Object +AceOO.Class = Class +AceOO.Mixin = Mixin +AceOO.Interface = Interface +AceOO.Classpool = Classpool +AceOO.inherits = inherits + +-- Library handling bits + +local function activate(self, oldLib, oldDeactivate) + AceOO = self + Factory = self.Factory + Object = self.Object + Class = self.Class + ClassFactory.prototype = Class + Mixin = self.Mixin + Interface = self.Interface + Classpool = self.Classpool + + if oldLib then + self.classes = oldLib.classes + end + if not self.classes then + self.classes = setmetatable({}, {__mode="k"}) + else + for class in pairs(self.classes) do + class.new = class_new + end + end + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(AceOO, MAJOR_VERSION, MINOR_VERSION, activate) +AceOO = AceLibrary(MAJOR_VERSION) diff --git a/modules/chronometer/libs/CandyBar-2.0/CandyBar-2.0.lua b/modules/chronometer/libs/CandyBar-2.0/CandyBar-2.0.lua new file mode 100644 index 0000000..eee14f8 --- /dev/null +++ b/modules/chronometer/libs/CandyBar-2.0/CandyBar-2.0.lua @@ -0,0 +1,1912 @@ +--[[ +Name: CandyBar-2.0 +Revision: 16003 +Author: Ammo (wouter@muze.nl) +Backport to vanilla: laytya (@gmail.com) +Website: https://github.com/laytya/LibCandyBar/ +Documentation: http://web.archive.org/web/20070314234510/http://wowace.com/wiki/CandyBar-2.0 +Description: A timer bars library. +Dependencies: AceLibrary, AceOO-2.0, PaintChips-2.0 +]] +local match = string.match +local getn,setn,tinsert = table.getn, table.setn,table.insert +local vmajor, vminor = "CandyBar-2.0", "$Revision: 16011 $" + +if not AceLibrary then error(vmajor .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(vmajor, vminor) then return end + +if not AceLibrary:HasInstance("AceOO-2.0") then error(vmajor .. " requires AceOO-2.0") end +--if not AceLibrary:HasInstance("PaintChips-2.0") then error(vmajor .. " requires PaintChips-2.0") end + +local paint,compost = nil,nil + +if AceLibrary:HasInstance("Compost-2.0") then compost = AceLibrary:GetInstance("Compost-2.0") end + +local AceOO = AceLibrary:GetInstance("AceOO-2.0") +local Mixin = AceOO.Mixin +local CandyBar = Mixin { + "RegisterCandyBar", + "UnregisterCandyBar", + "IsCandyBarRegistered", + "StartCandyBar", + "StopCandyBar", + "PauseCandyBar", + "CandyBarStatus", + "SetCandyBarTexture", + "SetCandyBarTime", + "SetCandyBarColor", + "SetCandyBarText", + "SetCandyBarIcon", + "SetCandyBarIconPosition", + "SetCandyBarWidth", + "SetCandyBarHeight", + "SetCandyBarBackgroundColor", + "SetCandyBarBorderColor", + "SetCandyBarBorderTexture", + "SetCandyBarTextColor", + "SetCandyBarTimerTextColor", + "SetCandyBarFontSize", + "SetCandyBarPoint", + "GetCandyBarPoint", + "GetCandyBarCenter", + "GetCandyBarOffsets", + "GetCandyBarEffectiveScale", + "SetCandyBarGradient", + "SetCandyBarScale", + "SetCandyBarTimeFormat", + "SetCandyBarTimeLeft", + "SetCandyBarCompletion", + "SetCandyBarFade", + "RegisterCandyBarGroup", + "UnregisterCandyBarGroup", + "IsCandyBarGroupRegistered", + "SetCandyBarGroupPoint", + "SetCandyBarGroupGrowth", + "SetCandyBarGroupVerticalSpacing", + "UpdateCandyBarGroup", + "GetCandyBarNextBarPointInGroup", + "RegisterCandyBarWithGroup", + "UnregisterCandyBarWithGroup", + "IsCandyBarRegisteredWithGroup", + "SetCandyBarReversed", + "IsCandyBarReversed", + "SetCandyBarOnClick", + "SetCandyBarOnSizeGroup", +} + +local defaults = { + texture = "Interface\\TargetingFrame\\UI-StatusBar", + width = 200, + height = 16, + scale = 1, + point = "CENTER", + rframe = UIParent, + rpoint = "CENTER", + iconpos = "LEFT", + xoffset = 0, + yoffset = 0, + fontsize = 11, + color = {1, 0, 1, 1}, + bgcolor = {0, 0.5, 0.5, 0.5}, + bordercolor = {1,1,1,1}, + bordertexture = "Interface\\None", + textcolor = {1, 1, 1, 1}, + timertextcolor = {1, 1, 1, 1}, + stayonscreen = false, +} + +local getArgs +do + local numargs + local function _get(t, str, i, ...) + if i<=numargs then + return t[format("%s%d", str, i)], _get(t, str, i+1, unpack(arg)) + end + return (unpack(arg)) + end + + function getArgs(t, str, ...) + numargs = t[str.."#" or 0] + return _get(t,str,1, unpack(arg)) + end +end + +local function setArgs(t, str, ...) + local n = getn(arg) + for i=1,n do + t[format("%s%d",str,i)]=arg[i] + end + for i=n+1, (t[str.."#"] or 0) do + t[format("%s%d",str,i)]=nil + end + t[str.."#"] = n +end + +local new, del +if compost then + new = function() return compost:Acquire() end + del = function(t) compost:Reclaim(t) end +else + local list = setmetatable({}, {__mode = "k"}) + function new() + local t = next(list) + if not t then + return {} + end + list[t] = nil + return t + end + + function del(t) + setmetatable(t, nil) + for k in pairs(t) do + t[k] = nil + end + setn(t,0) + list[t] = true + end +end + +-- Registers a new candy bar +-- name - A unique identifier for your bar. +-- time - Time for the bar +-- text - text displayed on the bar [defaults to the name if not set] +-- icon - icon off the bar [optional] +-- c1 - c10 - color of the bar [optional] +-- returns true on a succesful register +function CandyBar:Register(name, time, text, icon, c1, c2, c3, c4, ...) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(time, 3, "number") + CandyBar:argCheck(text, 4, "string", "nil") + CandyBar:argCheck(icon, 5, "string", "nil") + CandyBar:argCheck(c1, 6, "string", "number", "nil") + CandyBar:argCheck(c2, 7, "string", "number", "nil") + CandyBar:argCheck(c3, 8, "string", "number", "nil") + CandyBar:argCheck(c4, 9, "string", "number", "nil") + if not text then text = name end + if CandyBar.handlers[name] then + self:Unregister(name) + end + local handler = new() + handler.name, handler.time, handler.text, handler.icon = name, time, text or name, icon + handler.texture = defaults.texture + local c1Type = type(c1) + if c1Type ~= "number" and not paint then + CandyBar:error("You need the PaintChips-2.0 library if you don't pass in RGB pairs as colors.") + end + if c1Type == "nil" or (c1Type ~= "number" and paint and not paint:GetRGBPercent(c1)) then + c1 = "green" + end + handler.color = new() + if c1Type == "number" then + handler.color[1] = c1 + handler.color[2] = c2 + handler.color[3] = c3 + else + local _ + _, handler.color[1], handler.color[2], handler.color[3] = paint:GetRGBPercent(c1) + end + handler.color[4] = 1 + handler.running = nil + handler.endtime = 0 + handler.reversed = nil + CandyBar.handlers[name] = handler + handler.frame = CandyBar:AcquireBarFrame(name) + if (c1Type == "number" and c4) or (c1Type == "string" and c2) then + CandyBar:SetGradient(name, c1, c2, c3, c4, unpack(arg)) + end + handler.stayonscreen = defaults.stayonscreen + return true +end + + +-- Removes a candy bar +-- a1 - a10 handlers that you wish to remove +-- returns true upon sucessful removal +function CandyBar:Unregister(a1, ...) + CandyBar:argCheck(a1, 2, "string") + if not CandyBar.handlers[a1] then + return + end + CandyBar:UnregisterWithGroup(a1) + CandyBar:ReleaseBarFrame(a1) + local handler = CandyBar.handlers[a1] + CandyBar.handlers[a1] = nil + if handler.color then + handler.color = del(handler.color) + end + if handler.bgcolor then + handler.bgcolor = del(handler.bgcolor) + end + if handler.textcolor then + handler.textcolor = del(handler.textcolor) + end + if handler.timertextcolor then + handler.timertextcolor = del(handler.timertextcolor) + end + if handler.gradienttable then + for i,v in ipairs(handler.gradienttable) do + v = del(v) + end + handler.gradienttable = del(handler.gradienttable) + end + handler = del(handler) + if getn(arg)>0 then + CandyBar:Unregister(unpack(arg)) + elseif not CandyBar:HasHandlers() then + CandyBar.frame:Hide() + end + return true +end + +-- Checks if a candy bar is registered +-- Args: name - name of the candybar +-- returns true if a the candybar is registered +function CandyBar:IsRegistered(name) + CandyBar:argCheck(name, 2, "string") + if CandyBar.handlers[name] then + return true + end + return false +end + +-- Start a bar +-- Args: name - the candybar you want to start +-- fireforget [optional] - pass true if you want the bar to unregister upon completion +-- returns true if succesful +function CandyBar:Start(name, fireforget) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(fireforget, 3, "boolean", "nil") + local handler = CandyBar.handlers[name] + if not handler then + return + end + local t = GetTime() + if handler.paused then + local pauseoffset = t - handler.pausetime + handler.endtime = handler.endtime + pauseoffset + handler.starttime = handler.starttime + pauseoffset + elseif handler.elapsed and not handler.running then + handler.endtime = t + handler.time - handler.elapsed + handler.starttime = t - handler.elapsed + else + -- bar hasn't elapsed a second. + handler.elapsed = 0 + handler.endtime = t + handler.time + handler.starttime = t + end + handler.fireforget = fireforget + handler.running = true + handler.paused = nil + handler.fading = nil + CandyBar:AcquireBarFrame(name) -- this will reset the barframe incase we were fading out when it was restarted + handler.frame:Show() + if handler.group then + CandyBar:UpdateGroup(handler.group) -- update the group + end + CandyBar.frame:Show() + return true +end + +-- Stop a bar +-- Args: name - the candybar you want to stop +-- returns true if succesful +function CandyBar:Stop(name) + CandyBar:argCheck(name, 2, "string") + + local handler = CandyBar.handlers[name] + + if not handler then + return + end + + handler.running = nil + handler.paused = nil + handler.elapsed = 0 + + if handler.fadeout then + handler.frame.spark:Hide() + if not handler.stayonscreen then + handler.fading = true + handler.fadeelapsed = 0 + local t = GetTime() + if handler.endtime > t then + handler.endtime = t + end + end + else + handler.frame:Hide() + handler.starttime = nil + handler.endtime = 0 + if handler.group then + CandyBar:UpdateGroup(handler.group) + end + if handler.fireforget then + return CandyBar:Unregister(name) + end + end + if not CandyBar:HasHandlers() then + CandyBar.frame:Hide() + end + return true +end + +-- Pause a bar +-- Name - the candybar you want to pause +-- returns true if succesful +function CandyBar:Pause(name) + CandyBar:argCheck(name, 2, "string") + local handler = CandyBar.handlers[name] + if not handler then + return + end + handler.pausetime = GetTime() + handler.paused = true + handler.running = nil +end + +-- Query a timer's status +-- Args: name - the schedule you wish to look up +-- Returns: registered - true if a schedule exists with this name +-- time - time for this bar +-- elapsed - time elapsed for this bar +-- running - true if this schedule is currently running +function CandyBar:Status(name) + CandyBar:argCheck(name, 2, "string") + local handler = CandyBar.handlers[name] + if not handler then + return + end + return true, handler.time, handler.elapsed, handler.running, handler.paused +end + + +-- Set the time for a bar. +-- Args: name - the candybar name +-- time - the new time for this bar +-- returns true if succesful +function CandyBar:SetTime(name, time) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(time, 3, "number") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + handler.time = time + if handler.starttime and handler.endtime then + handler.endtime = handler.starttime + time + end + return true +end + +-- Set the time left for a bar. +-- Args: name - the candybar name +-- time - time left on the bar +-- returns true if succesful + +function CandyBar:SetTimeLeft(name, time) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(time, 3, "number") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + if handler.time < time or time < 0 then + return + end + + local e = handler.time - time + if handler.starttime and handler.endtime then + local d = handler.elapsed - e + handler.starttime = handler.starttime + d + handler.endtime = handler.endtime + d + end + + handler.elapsed = e + + if handler.group then + CandyBar:UpdateGroup(handler.group) + end + + return true +end + +-- Sets smooth coloring of the bar depending on time elapsed +-- Args: name - the candybar name +-- c1 - c10 color order of the gradient +-- returns true when succesful +local cachedgradient = new() +function CandyBar:SetGradient(name, c1, c2, ...) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(c1, 3, "string", "number", "nil") + CandyBar:argCheck(c2, 4, "string", "number", "nil") + + local handler = CandyBar.handlers[name] + + if not handler then + return + end + + local gtable = new() + local gradientid = nil + local gmax = 0 + -- We got string values passed in, which means they're not rgb values + -- directly, but a color most likely registered with paintchips + if type(c1) == "string" then + if not paint then + CandyBar:error("You need the PaintChips-2.0 library if you don't pass in RGB pairs as colors.") + end + if not paint:GetRGBPercent(c1) then c1 = "green" end + if not paint:GetRGBPercent(c2) then c2 = "red" end + + gtable[1] = new() + gtable[2] = new() + + gradientid = c1 .. "_" .. c2 + local _ + _, gtable[1][1], gtable[1][2], gtable[1][3] = paint:GetRGBPercent(c1) + _, gtable[2][1], gtable[2][2], gtable[2][3] = paint:GetRGBPercent(c2) + gmax = 2 + for i = 1, getn(arg) do + local c = arg[i] + if not c or not paint:GetRGBPercent(c) then + break + end + gmax = gmax + 1 + gtable[gmax] = new() + local _ + _, gtable[gmax][1], gtable[gmax][2], gtable[gmax][3] = paint:GetRGBPercent(c) + gradientid = gradientid .. "_" .. c + end + + elseif type(c1) == "number" then + -- It's a number, which means we should expect r,g,b values + local n = getn(arg) -- select("#", ...) + if n < 4 then CandyBar:error("Not enough extra arguments to :SetGradient, need at least 2 RGB pairs.") end + gtable[1] = new() + gtable[1][1] = c1 + gtable[1][2] = c2 + gtable[1][3] = arg[1] -- select(1, ...) + gradientid = string.format("%d%d%d", c1, c2, gtable[1][3]) + gmax = 1 + + local i = 2,2 + while i < n do --for i = 2, n, 3 do + local r, g, b = arg[i], arg[i+1], arg[i+3] --select(i, ...) + if r and g and b then + gmax = gmax + 1 + gtable[gmax] = new() + gtable[gmax][1], gtable[gmax][2], gtable[gmax][3] = r, g, b + gradientid = string.format("%s_%d%d%d", gradientid, r, g, b) + else + break + end + i = i + 3 + end + end + + for i = 1, gmax do + if not gtable[i][4] then + gtable[i][4] = 1 + end + gtable[i][5] = (i-1) / (gmax-1) + end + + if handler.gradienttable then + for i,v in ipairs(handler.gradienttable) do + v = del(v) + end + handler.gradienttable = del(handler.gradienttable) + end + handler.gradienttable = gtable + handler.gradient = true + handler.gradientid = gradientid + if not cachedgradient[gradientid] then + cachedgradient[gradientid] = new() + end + handler.frame.statusbar:SetStatusBarColor(unpack(gtable[1], 1, 4)) + return true +end + +local function setColor(color, alpha, b, a) + CandyBar:argCheck(color, 3, "string", "number") + local ctable = new() + local _, rr, gg, bb, aa + if type(color) == "string" then + if not paint then + CandyBar:error("You need the PaintChips-2.0 library if you don't pass in RGB pairs as colors.") + end + if not paint:GetRGBPercent(color) then + return + end + CandyBar:argCheck(alpha, 4, "number", "nil") + _, rr, gg, bb = paint:GetRGBPercent(color) + aa = alpha and alpha or 1 + else + CandyBar:argCheck(alpha, 4, "number") + CandyBar:argCheck(b, 5, "number") + CandyBar:argCheck(a, 6, "number", "nil") + rr, gg, bb = color, alpha, b + aa = a and a or 1 + end + tinsert(ctable,rr) + tinsert(ctable,gg) + tinsert(ctable,bb) + tinsert(ctable,aa) + return ctable +end + +-- Set the color of the bar +-- Args: name - the candybar name +-- color - new color of the bar +-- alpha - new alpha of the bar +-- Setting the color will override smooth settings. +function CandyBar:SetColor(name, color, alpha, b, a) + CandyBar:argCheck(name, 2, "string") + local handler = CandyBar.handlers[name] + if not handler then + return + end + local t = setColor(color, alpha, b, a) + if not t then return end + + if handler.color then + handler.color = del(handler.color) + end + handler.color = t + handler.gradient = nil + + handler.frame.statusbar:SetStatusBarColor(unpack(t, 1, 4)) + return true +end + +-- Set the color of background of the bar +-- Args: name - the candybar name +-- color - new color of the bar +-- alpha - new alpha of the bar +-- Setting the color will override smooth settings. +function CandyBar:SetBackgroundColor(name, color, alpha, b, a) + CandyBar:argCheck(name, 2, "string") + local handler = CandyBar.handlers[name] + if not handler then + return + end + + local t = setColor(color, alpha, b, a) + if not t then return end + + if handler.bgcolor then + handler.bgcolor = del(handler.bgcolor) + end + handler.bgcolor = t + handler.frame.statusbarbg:SetStatusBarColor(unpack(t, 1, 4)) + handler.frame:SetBackdropColor(unpack(t, 1, 4)) + + return true +end + +-- Set the color of border of the bar +-- Args: name - the candybar name +-- color - new color of the bar border +-- alpha - new alpha of the bar border +-- Setting the color will override smooth settings. +function CandyBar:SetBorderColor(name, color, alpha, b, a) + CandyBar:argCheck(name, 2, "string") + local handler = CandyBar.handlers[name] + if not handler then + return + end + + local t = setColor(color, alpha, b, a) + if not t then return end + + if handler.bordercolor then + handler.bordercolor = del(handler.bordercolor) + end + handler.bordercolor = t + handler.frame:SetBackdropBorderColor(unpack(t, 1, 4)) + return true +end + + + +-- Set the color for the bar text +-- Args: name - name of the candybar +-- color - new color of the text +-- alpha - new alpha of the text +-- returns true when succesful +function CandyBar:SetTextColor(name, color, alpha, b, a) + CandyBar:argCheck(name, 2, "string") + local handler = CandyBar.handlers[name] + if not handler then + return + end + + local t = setColor(color, alpha, b, a) + if not t then return end + + if handler.textcolor then + handler.textcolor = del(handler.textcolor) + end + handler.textcolor = t + handler.frame.text:SetTextColor(unpack(t, 1, 4)) + + return true +end + +-- Set the color for the timer text +-- Args: name - name of the candybar +-- color - new color of the text +-- alpha - new alpha of the text +-- returns true when succesful +function CandyBar:SetTimerTextColor(name, color, alpha, b, a) + CandyBar:argCheck(name, 2, "string") + local handler = CandyBar.handlers[name] + if not handler then + return + end + + local t = setColor(color, alpha, b, a) + if not t then return end + + if handler.timertextcolor then + handler.timertextcolor = del(handler.timertextcolor) + end + handler.timertextcolor = t + handler.frame.timertext:SetTextColor(unpack(t, 1, 4)) + + return true +end + +-- Set the text for the bar +-- Args: name - name of the candybar +-- text - text to set it to +-- returns true when succesful +function CandyBar:SetText(name, text) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(text, 3, "string") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + handler.text = text + handler.frame.text:SetText(text) + + return true +end + +-- Set the fontsize +-- Args: name - name of the candybar +-- fontsize - new fontsize +-- returns true when succesful +function CandyBar:SetFontSize(name, fontsize) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(fontsize, 3, "number") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + local font, _, style = GameFontHighlight:GetFont() + local timertextwidth = fontsize * 3.6 + local width = handler.width or defaults.width + local f = handler.frame + + handler.fontsize = fontsize + f.timertext:SetFont(font, fontsize, style) + f.text:SetFont(font, fontsize, style) + f.timertext:SetWidth(timertextwidth) + f.text:SetWidth((width - timertextwidth) * .9) + + return true +end + + +-- Set the point where a bar should be anchored +-- Args: name -- name of the bar +-- point -- anchor point +-- rframe -- relative frame +-- rpoint -- relative point +-- xoffset -- x offset +-- yoffset -- y offset +-- returns true when succesful +function CandyBar:SetPoint(name, point, rframe, rpoint, xoffset, yoffset) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(point, 3, "string") + CandyBar:argCheck(rframe, 4, "table", "string", "nil") + CandyBar:argCheck(rpoint, 5, "string", "nil") + CandyBar:argCheck(xoffset, 6, "number", "nil") + CandyBar:argCheck(yoffset, 7, "number", "nil") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + handler.point = point + handler.rframe = rframe + handler.rpoint = rpoint + handler.xoffset = xoffset + handler.yoffset = yoffset + + handler.frame:ClearAllPoints() + handler.frame:SetPoint(point, rframe, rpoint, xoffset, yoffset) + + return true +end + +function CandyBar:GetPoint(name) + CandyBar:argCheck(name, 2, "string") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + return handler.point, handler.rframe, handler.rpoint, handler.xoffset, handler.yoffset +end + +function CandyBar:GetCenter(name) + CandyBar:argCheck(name, 2, "string") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + return handler.frame:GetCenter() +end + +function CandyBar:GetOffsets(name) + CandyBar:argCheck(name, 2, "string") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + local bottom = handler.frame:GetBottom() + local top = handler.frame:GetTop() + local left = handler.frame:GetLeft() + local right = handler.frame:GetRight() + + return left, top, bottom, right +end + +function CandyBar:GetEffectiveScale(name) + CandyBar:argCheck(name, 2, "string") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + return handler.frame:GetEffectiveScale() +end + +-- Set the width for a bar +-- Args: name - name of the candybar +-- width - new width of the candybar +-- returns true when succesful +function CandyBar:SetWidth(name, width) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(width, 3, "number") + + local handler = CandyBar.handlers[name] + if not CandyBar.handlers[name] then + return + end + + local height = handler.height or defaults.height + local fontsize = handler.fontsize or defaults.fontsize + local timertextwidth = fontsize * 3.6 + local f = handler.frame + f:SetWidth(width + height) + f.statusbar:SetWidth(width) + f.statusbarbg:SetWidth(width) + + f.timertext:SetWidth(timertextwidth) + f.text:SetWidth((width - timertextwidth) * .9) + + handler.width = width + + return true +end + +-- Set the height for a bar +-- Args: name - name of the candybar +-- height - new height for the bar +-- returs true when succesful +function CandyBar:SetHeight(name, height) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(height, 3, "number") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + local width = handler.width or defaults.width + local f = handler.frame + + f:SetWidth(width + height) + f:SetHeight(height) + f.icon:SetWidth(height) + f.icon:SetHeight(height) + f.statusbar:SetHeight(height) + f.statusbarbg:SetHeight(height) + f.spark:SetHeight(height + 25) + + f.statusbarbg:SetPoint("TOPLEFT", f, "TOPLEFT", height, 0) + f.statusbar:SetPoint("TOPLEFT", f, "TOPLEFT", height, 0) + + handler.height = height + + return true +end + +-- Set the scale for a bar +-- Args: name - name of the candybar +-- scale - new scale of the bar +-- returns true when succesful +function CandyBar:SetScale(name, scale) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(scale, 3, "number") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + handler.scale = scale + + handler.frame:SetScale(scale) + + return true +end + +-- Set the time formatting function for a bar +-- Args: name - name of the candybar +-- func - function that returns the formatted string +-- a1-a10 - optional arguments to that function +-- returns true when succesful + +function CandyBar:SetTimeFormat(name, func, ...) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(func, 3, "function") + + local handler = CandyBar.handlers[name] + + if not handler then + return + end + handler.timeformat = func + setArgs(handler, "timeformat", unpack(arg)) + + return true +end + +-- Set the completion function for a bar +-- Args: name - name of the candybar +-- func - function to call upon ending of the bar +-- a1 - a10 - arguments to pass to the function +-- returns true when succesful +function CandyBar:SetCompletion(name, func, ...) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(func, 3, "function") + + local handler = CandyBar.handlers[name] + + if not handler then + return + end + handler.completion = func + setArgs(handler, "completion", unpack(arg)) + + return true +end + +local function onClick() + CandyBar:OnClick() +end + +-- Set the on click function for a bar +-- Args: name - name of the candybar +-- func - function to call when the bar is clicked +-- a1 - a10 - arguments to pass to the function +-- returns true when succesful +function CandyBar:SetOnClick(name, func, ...) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(func, 3, "function", "nil") + + local handler = CandyBar.handlers[name] + + if not handler then + return + end + handler.onclick = func + setArgs(handler, "onclick", unpack(arg)) + + local frame = handler.frame + if func then + -- enable mouse + frame:EnableMouse(true) + frame:RegisterForClicks("LeftButtonUp", "RightButtonUp", "MiddleButtonUp", "Button4Up", "Button5Up", "RightButtonDown") + frame:SetScript("OnClick", onClick) + frame.icon:EnableMouse(true) + frame.icon:RegisterForClicks("LeftButtonUp", "RightButtonUp", "MiddleButtonUp", "Button4Up", "Button5Up", "RightButtonDown") + frame.icon:SetScript("OnClick", onClick) + else + frame:EnableMouse(false) + frame:RegisterForClicks() + frame:SetScript("OnClick", nil) + frame.icon:EnableMouse(false) + frame.icon:RegisterForClicks() + frame.icon:SetScript("OnClick", nil) + end + + return true + +end + +-- Set the "on size" function for a group +-- Args: name - name of the candybar +-- func - function to call when a group changes size +-- ... - arguments to pass to the function +-- (the new size of the bar, in pixels, will be appended last) +-- returns true when succesful +function CandyBar:SetOnSizeGroup(name, func, ...) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(func, 3, "function", "nil") + + local group = assert(CandyBar.groups[name]) + + group.onsize = func + setArgs(group, "onsize", unpack(arg)) +end + + +-- Set the texture for a bar +-- Args: name - name of the candybar +-- texture - new texture, if passed nil, the texture is reset to default +-- returns true when succesful +function CandyBar:SetTexture(name, texture) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(texture, 3, "string", "nil") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + if not texture then + texture = defaults.texture + end + + handler.texture = texture + + handler.frame.statusbar:SetStatusBarTexture(texture) + handler.frame.statusbarbg:SetStatusBarTexture(texture) + + return true +end + +-- Set the texture for a bar +-- Args: name - name of the candybar +-- texture - new texture, if passed nil, the texture is reset to default +-- returns true when succesful +function CandyBar:SetBorderTexture(name, texture) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(texture, 3, "string", "nil") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + if not texture then + texture = defaults.bordertexture + end + + local bgcolor = handler.bgcolor or defaults.bgcolor + local bordercolor = handler.bordercolor or defaults.bordercolor + + handler.bordertexture = texture + local backdrop = handler.frame:GetBackdrop() + backdrop.edgefile = texture + handler.frame:SetBackdrop(backdrop) + handler.frame:SetBackdropBorderColor(unpack(bordercolor, 1, 4)) + handler.frame:SetBackdropColor(unpack(bgcolor, 1, 4)) + + return true +end + +-- Set the icon on a bar +-- Args: name - name of the candybar +-- icon - icon path, nil removes the icon +-- left, right, top, bottom - optional texture coordinates +-- returns true when succesful +function CandyBar:SetIcon(name, icon, left, right, top, bottom) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(icon, 3, "string", "nil") + CandyBar:argCheck(left, 4, "number", "nil") + CandyBar:argCheck(right, 5, "number", "nil") + CandyBar:argCheck(top, 6, "number", "nil") + CandyBar:argCheck(bottom, 7, "number", "nil") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + handler.icon = icon + + if not icon then + handler.frame.icon:Hide() + else + left = left or 0.07 + right = right or 0.93 + top = top or 0.07 + bottom = bottom or 0.93 + handler.frame.icon:SetNormalTexture(icon) + handler.frame.icon:GetNormalTexture():SetTexCoord(left, right, top, bottom) + handler.frame.icon:Show() + end + + return true +end + +-- Set the icon position on bar +-- Args: name - name of the candybar +-- position - icon position, "LEFT" or "RIGHT" +-- returns true when succesful +function CandyBar:SetIconPosition(name, position) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(position, 3, "string", "LEFT", "RIGHT") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + handler.iconpos = position + if handler.running then + handler.frame.icon:SetPoint("LEFT", handler.frame, position, 0, 0) + end + return true +end + +-- Sets the fading style of a candybar +-- args: name - name of the candybar +-- time - duration of the fade (default .5 seconds), negative to keep the bar on screen +-- returns true when succesful +function CandyBar:SetFade(name, time) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(time, 3, "number") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + handler.fadetime = time + handler.fadeout = true + handler.stayonscreen = (handler.fadetime < 0) + + return true +end + +function CandyBar:SetReversed(name, reversed) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(reversed, 3, "boolean", "nil") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + handler.reversed = reversed + return true +end + +function CandyBar:IsReversed(name) + CandyBar:argCheck(name, 2, "string") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + + return handler.reversed +end + + +-- Registers a candybar with a certain candybar group +-- args: name - name of the candybar +-- group - group to register the bar with +-- returns true when succesful +function CandyBar:RegisterWithGroup(name, group) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(group, 3, "string") + + local handler = CandyBar.handlers[name] + local gtable = CandyBar.groups[group] + if not handler or not gtable then + return + end + + CandyBar:UnregisterWithGroup(name) + + tinsert(gtable.bars, name) + -- CandyBar.groups[group].bars[name] = name + handler.group = group + CandyBar:UpdateGroup(group) + + return true +end + +-- Unregisters a candybar from its group +-- args: name - name of the candybar +-- returns true when succesful + +function CandyBar:UnregisterWithGroup(name) + CandyBar:argCheck(name, 2, "string") + + local handler = CandyBar.handlers[name] + if not handler then + return + end + --if not CandyBar.handlers[name].group then return end + + local group = handler.group + local gtable = CandyBar.groups[group] + if not gtable then + return + end + + for k,v in pairs(gtable.bars) do + if v == name then + table.remove(gtable.bars, k) + end + end + -- CandyBar.groups[group].bars[name] = nil + handler.group = nil + + CandyBar:UpdateGroup(group) + + return true +end + +-- Register a Candybar group +-- Args: name - name of the candybar group +-- returns true when succesful +function CandyBar:RegisterGroup(name) + CandyBar:argCheck(name, 2, "string") + + if CandyBar.groups[name] then + return + end + + local t = new() + + t.point = "CENTER" + t.rframe = UIParent + t.rpoint = "CENTER" + t.xoffset = 0 + t.yoffset = 0 + t.bars = new() + t.height = -1 + + CandyBar.groups[name] = t + return true +end + +-- Unregister a candybar group +-- Args: a1-a2 candybar group ids +-- returns true when succesful +function CandyBar:UnregisterGroup(a1, ...) + CandyBar:argCheck(a1, 2, "string") + if not CandyBar.groups[a1] then + return + end + CandyBar.groups[a1].bars = del(CandyBar.groups[a1].bars) + CandyBar.groups[a1] = del(CandyBar.groups[a1]) + + if getn(arg) > 0 then + CandyBar:UnregisterGroup(unpack(arg)) + end + + return true +end + +-- Checks if a group is registered +-- Args: name - Candybar group +-- returns true if the candybar group is registered +function CandyBar:IsGroupRegistered(name) + CandyBar:argCheck(name, 2, "string") + return CandyBar.groups[name] and true or false +end + +-- Checks if a bar is registered with a group +-- Args: name - Candybar name +-- group - group id [optional] +-- returns true is the candybar is registered with a/the group +function CandyBar:IsRegisteredWithGroup(name, group) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(group, 3, "string", "nil") + local handler = CandyBar.handlers[name] + if not handler then + return + end + + if group then + if not CandyBar.groups[group] then + return false + end + if handler.group == group then + return true + end + elseif handler.group then + return true + end + return false +end + + +-- Set the point for a CandyBargroup +-- point -- anchor point +-- rframe -- relative frame +-- rpoint -- relative point +-- xoffset [optional] -- x offset +-- yoffset [optional] -- y offset +-- The first bar of the group will be anchored at the anchor. +-- returns true when succesful +function CandyBar:SetGroupPoint(name, point, rframe, rpoint, xoffset, yoffset) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(point, 3, "string") + CandyBar:argCheck(rframe, 4, "string", "table", "nil") + CandyBar:argCheck(rpoint, 5, "string", "nil") + CandyBar:argCheck(xoffset, 6, "number", "nil") + CandyBar:argCheck(yoffset, 6, "number", "nil") + + local group = CandyBar.groups[name] + if not group then + return + end + + group.point = point + group.rframe = rframe + group.rpoint = rpoint + group.xoffset = xoffset + group.yoffset = yoffset + CandyBar:UpdateGroup(name) + return true +end + +-- SetGroupGrowth - sets the group to grow up or down +-- Args: name - name of the candybar group +-- growup - true if growing up, false for growing down +-- returns true when succesful +function CandyBar:SetGroupGrowth(name, growup) + CandyBar:argCheck(name, 2, "string") + CandyBar:argCheck(growup, 3, "boolean") + + local group = CandyBar.groups[name] + if not group then + return + end + + group.growup = growup + + CandyBar:UpdateGroup(name) + + return true +end + +-- SetGroupVerticalSpacing - sets a vertical spacing between the bars of the group +-- Args: name - name of the candybar group +-- spacing - y offset for the bars +-- returns true when succesful +function CandyBar:SetGroupVerticalSpacing(name, spacing) + CandyBar:argCheck(name, 2, "string"); + CandyBar:argCheck(spacing, 3, "number"); + + local group = CandyBar.groups[name] + if not group then + return + end + + group.vertspacing = spacing; + + CandyBar:UpdateGroup(name) + + return true +end + +local mysort = function(a, b) + return CandyBar.handlers[a].endtime < CandyBar.handlers[b].endtime +end +function CandyBar:SortGroup(name) + local group = CandyBar.groups[name] + if not group then + return + end + table.sort(group.bars, mysort) +end + +-- internal method +-- UpdateGroup - updates the location of bars in a group +-- Args: name - name of the candybar group +-- returns true when succesful + +function CandyBar:UpdateGroup(name) + local group = CandyBar.groups[name] + if not CandyBar.groups[name] then + return + end + + local point = group.point + local rframe = group.rframe + local rpoint = group.rpoint + local xoffset = group.xoffset + local yoffset = group.yoffset + local m = -1 + if group.growup then + m = 1 + end + local vertspacing = group.vertspacing or 0 + + local bar = 0 + local barh = 0 + + CandyBar:SortGroup(name) + + for c,n in pairs(group.bars) do + local handler = CandyBar.handlers[n] + if handler then + if handler.frame:IsShown() then + CandyBar:SetPoint(n, point, rframe, rpoint, xoffset, yoffset + (m * bar)) + barh = handler.height or defaults.height + bar = bar + barh + vertspacing + end + end + end + + if group.height ~= bar then + group.height = bar + if group.onsize then + group.onsize(getArgs(group, "onsize", bar)) + end + end + + return true +end + +function CandyBar:GetNextBarPointInGroup(name) + CandyBar:argCheck(name, 2, "string") + + local group = CandyBar.groups[name] + if not CandyBar.groups[name] then + return + end + + local xoffset = group.xoffset + local yoffset = group.yoffset + local m = -1 + if group.growup then + m = 1 + end + + local bar = 0 + local barh = 0 + + local vertspacing = group.vertspacing or 0 + + for c,n in pairs(group.bars) do + local handler = CandyBar.handlers[n] + if handler then + if handler.frame:IsShown() then + barh = handler.height or defaults.height + bar = bar + barh + vertspacing + end + end + end + + return xoffset, yoffset + (m * bar) +end + +-- Internal Method +-- Update a bar on screen +function CandyBar:Update(name) + local handler = CandyBar.handlers[name] + if not handler then + return + end + + local t = handler.time - handler.elapsed + handler.slow = t>11 + + local timetext + if handler.timeformat then + timetext = handler.timeformat(t, getArgs(handler, "timeformat")) + else + local h = floor(t/3600) + local m = t - (h*3600) + m = floor(m/60) + local s = t - ((h*3600) + (m*60)) + if h > 0 then + timetext = string.format("%d:%02d", h, m) + elseif m > 0 then + timetext = string.format("%d:%02d", m, floor(s)) + elseif s < 10 then + timetext = string.format("%1.1f", s) + else + timetext = string.format("%.0f", floor(s)) + end + end + handler.frame.timertext:SetText(timetext) + + local perc = t / handler.time + + local reversed = handler.reversed + handler.frame.statusbar:SetValue(reversed and 1-perc or perc) + + local width = handler.width or defaults.width + + local sp = width * perc + sp = reversed and -sp or sp + handler.frame.spark:SetPoint("CENTER", handler.frame.statusbar, reversed and "RIGHT" or "LEFT", sp, 0) + + if handler.gradient then + local p = floor( (handler.elapsed / handler.time) * 100 ) / 100 + local currentGradient = cachedgradient[handler.gradientid][p] + if currentGradient==nil then + -- find the appropriate start/end + local gstart, gend, gp + for i = 1, getn(handler.gradienttable) - 1 do + if handler.gradienttable[i][5] < p and p <= handler.gradienttable[i+1][5] then + -- the bounds are to assure no divide by zero error here. + + gstart = handler.gradienttable[i] + gend = handler.gradienttable[i+1] + gp = (p - gstart[5]) / (gend[5] - gstart[5]) + end + end + if gstart and gend then + -- calculate new gradient + currentGradient = new() + for i = 1, 4 do + -- these may be the same.. but I'm lazy to make sure. + tinsert(currentGradient, gstart[i]*(1-gp) + gend[i]*(gp)) + end + cachedgradient[handler.gradientid][p] = currentGradient + end + end + if currentGradient~=nil then + handler.frame.statusbar:SetStatusBarColor(unpack(currentGradient)) + end + end +end + +-- Intenal Method +-- Fades the bar out when it's complete. +function CandyBar:UpdateFade(name) + local handler = CandyBar.handlers[name] + if not handler then + return + end + if not handler.fading then + return + end + if handler.stayonscreen then + return + end + + -- if the fade is done go and keel the bar. + if handler.fadeelapsed > handler.fadetime then + handler.fading = nil + handler.starttime = nil + handler.endtime = 0 + handler.frame:Hide() + if handler.group then + CandyBar:UpdateGroup(handler.group) + end + if handler.fireforget then + return CandyBar:Unregister(name) + end + else -- we're fading, set the alpha for the texts, statusbar and background. fade from default to 0 in the time given. + local t = handler.fadetime - CandyBar.handlers[name].fadeelapsed + local p = t / handler.fadetime + local color = handler.color or defaults.color + local bgcolor = handler.bgcolor or defaults.bgcolor + local bordercolor = handler.bordercolor or defaults.bordercolor + local textcolor = handler.textcolor or defaults.textcolor + local timertextcolor = handler.timertextcolor or defaults.timertextcolor + local colora = color[4] * p + local bgcolora = bgcolor[4] * p + local bordercolora = bordercolor[4] * p + local textcolora = textcolor[4] * p + local timertextcolora = timertextcolor[4] * p + + handler.frame.statusbarbg:SetStatusBarColor(bgcolor[1], bgcolor[2], bgcolor[3], bgcolora) + handler.frame.statusbar:SetStatusBarColor(color[1], color[2], color[3], colora) + handler.frame.text:SetTextColor(textcolor[1], textcolor[2], textcolor[3], textcolora) + handler.frame.timertext:SetTextColor(timertextcolor[1], timertextcolor[2], timertextcolor[3], timertextcolora) + handler.frame:SetBackdropBorderColor(bordercolor[1], bordercolor[2], bordercolor[3], bordercolora) + handler.frame:SetBackdropColor(bgcolor[1], bgcolor[2], bgcolor[3], bgcolora) + + handler.frame.icon:SetAlpha(p) + end + return true +end +do + local backdrop = { insets = {} } + local backdrop_insets = backdrop.insets + +-- Internal Method +-- Create and return a new bar frame, recycles where needed +-- Name - which candybar is this for +-- Returns the frame + function CandyBar:AcquireBarFrame(name) + local handler = CandyBar.handlers[name] + if not handler then + return + end + + local f = handler.frame + + local color = handler.color or defaults.color + local bgcolor = handler.bgcolor or defaults.bgcolor + local bordercolor = handler.bordercolor or defaults.bordercolor + local bordertexture = handler.bordertexture or defaults.bordertexture + local icon = handler.icon or nil + local iconpos = handler.iconpos or defaults.iconpos + local texture = handler.texture or defaults.texture + local width = handler.width or defaults.width + local height = handler.height or defaults.height + local point = handler.point or defaults.point + local rframe = handler.rframe or defaults.rframe + local rpoint = handler.rpoint or defaults.rpoint + local xoffset = handler.xoffset or defaults.xoffset + local yoffset = handler.yoffset or defaults.yoffset + local text = handler.text or defaults.text + local fontsize = handler.fontsize or defaults.fontsize + local textcolor = handler.textcolor or defaults.textcolor + local timertextcolor = handler.timertextcolor or defaults.timertextcolor + local scale = handler.scale or defaults.scale + if not scale then + scale = 1 + end + local timertextwidth = fontsize * 3.6 + local font, _, style = GameFontHighlight:GetFont() + + if not f and getn(CandyBar.framepool) > 0 then + f = table.remove(CandyBar.framepool) + end + + if not f then + f = CreateFrame("Button", nil, UIParent) + end + f:Hide() + f.owner = name + -- yes we add the height to the width for the icon. + f:SetWidth(width + 10) + f:SetHeight(height + 10) + f:ClearAllPoints() + f:SetPoint(point, rframe, rpoint, xoffset, yoffset) + -- disable mouse + f:EnableMouse(false) + f:RegisterForClicks() + f:SetScript("OnClick", nil) + f:SetScale(scale) + -- LaYt + backdrop.bgFile = "Interface\\Tooltips\\UI-Tooltip-Background" + backdrop.tile = true + backdrop.tileSize = 16 + backdrop.edgeFile = bordertexture + backdrop.edgeSize = 16 + backdrop_insets.left = 4 + backdrop_insets.right = 4 + backdrop_insets.top = 4 + backdrop_insets.bottom = 4 + f:SetBackdrop(backdrop) + f:SetBackdropBorderColor(unpack(bordercolor, 1, 4)) + f:SetBackdropColor(unpack(bgcolor, 1, 4)) + + if not f.icon then + f.icon = CreateFrame("Button", nil, f) + end + f.icon:ClearAllPoints() + f.icon.owner = name + f.icon:EnableMouse(false) + f.icon:RegisterForClicks() + f.icon:SetScript("OnClick", nil) + -- an icno is square and the height of the bar, so yes 2x height there + f.icon:SetHeight(height) + f.icon:SetWidth(height) + local delta = 0 -- height/2 + 2 + if iconpos == "LEFT" then + f.icon:SetPoint("RIGHT", f, "LEFT", -2, 0) + + else --L["Right"] + f.icon:SetPoint("LEFT", f, "RIGHT", 2, 0) + delta = -delta + end + -- f.icon:SetPoint("LEFT", f, iconpos, 0, 0) + f.icon:SetNormalTexture(icon) + if f.icon:GetNormalTexture() then + f.icon:GetNormalTexture():SetTexCoord( 0.07, 0.93, 0.07, 0.93) + end + f.icon:SetAlpha(1) + f.icon:Show() + + if not f.statusbarbg then + f.statusbarbg = CreateFrame("StatusBar", nil, f) + f.statusbarbg:SetFrameLevel(f.statusbarbg:GetFrameLevel() - 1) + end + f.statusbarbg:ClearAllPoints() + f.statusbarbg:SetHeight(height) + f.statusbarbg:SetWidth(width) + -- offset the height of the frame on the x-axis for the icon. + -- f.statusbarbg:SetPoint("TOPLEFT", f, "TOPLEFT", height, 0) + f.statusbarbg:SetPoint('CENTER',f,'CENTER', delta, 0) + f.statusbarbg:SetStatusBarTexture(texture) + f.statusbarbg:SetStatusBarColor(bgcolor[1],bgcolor[2],bgcolor[3],bgcolor[4]) + f.statusbarbg:SetMinMaxValues(0,100) + f.statusbarbg:SetValue(100) + + if not f.statusbar then + f.statusbar = CreateFrame("StatusBar", nil, f) + end + f.statusbar:ClearAllPoints() + f.statusbar:SetHeight(height) + f.statusbar:SetWidth(width) + -- offset the height of the frame on the x-axis for the icon. + -- f.statusbar:SetPoint("TOPLEFT", f, "TOPLEFT", height, 0) + f.statusbar:SetPoint('CENTER',f,'CENTER', delta, 0) + f.statusbar:SetStatusBarTexture(texture) + f.statusbar:SetStatusBarColor(color[1], color[2], color[3], color[4]) + f.statusbar:SetMinMaxValues(0,1) + f.statusbar:SetValue(1) + + + if not f.spark then + f.spark = f.statusbar:CreateTexture(nil, "OVERLAY") + end + f.spark:SetTexture("Interface\\CastingBar\\UI-CastingBar-Spark") + f.spark:SetWidth(16) + f.spark:SetHeight(height + 25) + f.spark:SetBlendMode("ADD") + f.spark:Show() + + if not f.timertext then + f.timertext = f.statusbar:CreateFontString(nil, "OVERLAY") + end + f.timertext:SetFontObject(GameFontHighlight) + f.timertext:SetFont(font, fontsize, style) + f.timertext:SetHeight(height) + f.timertext:SetWidth(timertextwidth) + f.timertext:SetPoint("LEFT", f.statusbar, "LEFT", 0, 0) + f.timertext:SetJustifyH("RIGHT") + f.timertext:SetText("") + f.timertext:SetTextColor(timertextcolor[1], timertextcolor[2], timertextcolor[3], timertextcolor[4]) + + if not f.text then + f.text = f.statusbar:CreateFontString(nil, "OVERLAY") + end + f.text:SetFontObject(GameFontHighlight) + f.text:SetFont(font, fontsize, style) + f.text:SetHeight(height) + f.text:SetWidth((width - timertextwidth) *.9) + f.text:SetPoint("RIGHT", f.statusbar, "RIGHT", 0, 0) + f.text:SetJustifyH("LEFT") + f.text:SetText(text) + f.text:SetTextColor(textcolor[1], textcolor[2], textcolor[3], textcolor[4]) + + if handler.onclick then + f:EnableMouse(true) + f:RegisterForClicks("LeftButtonUp", "RightButtonUp", "MiddleButtonUp", "Button4Up", "Button5Up", "RightButtonDown") + f:SetScript("OnClick", onClick) + f.icon:EnableMouse(true) + f.icon:RegisterForClicks("LeftButtonUp", "RightButtonUp", "MiddleButtonUp", "Button4Up", "Button5Up", "RightButtonDown") + f.icon:SetScript("OnClick", onClick) + end + + return f + end +end + +-- Internal Method +-- Releases a bar frame into the pool +-- Name - which candybar's frame are we're releasing +-- Returns true when succesful +function CandyBar:ReleaseBarFrame(name) + local handler = CandyBar.handlers[name] + if not handler then + return + end + if not handler.frame then + return + end + handler.frame:Hide() + tinsert(CandyBar.framepool, handler.frame) + return true +end + +-- Internal Method +-- Executes the OnClick function of a bar +function CandyBar:OnClick() + if not this.owner then + return + end + local handler = CandyBar.handlers[this.owner] + if not handler then + return + end + if not handler.onclick then + return + end + -- pass the name of the handlers first, and the button clicked as the second argument + local button = arg1 + handler.onclick(this.owner, button, getArgs(handler, "onclick")) + return true +end + +-- Internal Method +-- on update handler +local lastSlow = 0 +function CandyBar:OnUpdate() + local doslow + lastSlow = lastSlow + arg1 + if lastSlow > 0.04 then + doslow = true + lastSlow = 0 + end + + local t + for i,v in pairs(this.owner.handlers) do + if not t then t = GetTime() end + if (not doslow) and v.slow then + -- nada + elseif v.running then + v.elapsed = t - v.starttime + if v.endtime <= t then + local c = this.owner.handlers[i] + if c.completion then + if not c.completion(getArgs(c, "completion")) then + this.owner:Stop(i) + end + else + this.owner:Stop(i) + end + else + this.owner:Update(i) + end + elseif v.fading and not v.stayonscreen then + v.fadeelapsed = (t - v.endtime) + this.owner:UpdateFade(i) + end + end +end + +-- Internal Method +-- returns true if we have any handlers +function CandyBar:HasHandlers() + return next(CandyBar.handlers) and true +end + +------------------------------ +-- Mixins Methods -- +------------------------------ + +CandyBar.IsCandyBarRegistered = CandyBar.IsRegistered +CandyBar.StartCandyBar = CandyBar.Start +CandyBar.StopCandyBar = CandyBar.Stop +CandyBar.PauseCandyBar = CandyBar.Pause +CandyBar.CandyBarStatus = CandyBar.Status +CandyBar.SetCandyBarTexture = CandyBar.SetTexture +CandyBar.SetCandyBarTime = CandyBar.SetTime +CandyBar.SetCandyBarColor = CandyBar.SetColor +CandyBar.SetCandyBarText = CandyBar.SetText +CandyBar.SetCandyBarIcon = CandyBar.SetIcon +CandyBar.SetCandyBarIconPosition = CandyBar.SetIconPosition +CandyBar.SetCandyBarBackgroundColor = CandyBar.SetBackgroundColor +CandyBar.SetCandyBarBorderColor = CandyBar.SetBorderColor +CandyBar.SetCandyBarBorderTexture = CandyBar.SetBorderTexture +CandyBar.SetCandyBarTextColor = CandyBar.SetTextColor +CandyBar.SetCandyBarTimerTextColor = CandyBar.SetTimerTextColor +CandyBar.SetCandyBarFontSize = CandyBar.SetFontSize +CandyBar.SetCandyBarPoint = CandyBar.SetPoint +CandyBar.GetCandyBarPoint = CandyBar.GetPoint +CandyBar.GetCandyBarCenter = CandyBar.GetCenter +CandyBar.GetCandyBarOffsets = CandyBar.GetOffsets +CandyBar.GetCandyBarEffectiveScale = CandyBar.GetEffectiveScale +CandyBar.SetCandyBarScale = CandyBar.SetScale +CandyBar.SetCandyBarTimeFormat = CandyBar.SetTimeFormat +CandyBar.SetCandyBarTimeLeft = CandyBar.SetTimeLeft +CandyBar.SetCandyBarCompletion = CandyBar.SetCompletion +CandyBar.RegisterCandyBarGroup = CandyBar.RegisterGroup +CandyBar.UnregisterCandyBarGroup = CandyBar.UnregisterGroup +CandyBar.IsCandyBarGroupRegistered = CandyBar.IsGroupRegistered +CandyBar.SetCandyBarGroupPoint = CandyBar.SetGroupPoint +CandyBar.SetCandyBarGroupGrowth = CandyBar.SetGroupGrowth +CandyBar.SetCandyBarGroupVerticalSpacing = CandyBar.SetGroupVerticalSpacing +CandyBar.UpdateCandyBarGroup = CandyBar.UpdateGroup +CandyBar.GetCandyBarNextBarPointInGroup = CandyBar.GetNextBarPointInGroup +CandyBar.SetCandyBarOnClick = CandyBar.SetOnClick +CandyBar.SetCandyBarFade = CandyBar.SetFade +CandyBar.RegisterCandyBarWithGroup = CandyBar.RegisterWithGroup +CandyBar.UnregisterCandyBarWithGroup = CandyBar.UnregisterWithGroup +CandyBar.IsCandyBarRegisteredWithGroup = CandyBar.IsRegisteredWithGroup +CandyBar.SetCandyBarReversed = CandyBar.SetReversed +CandyBar.IsCandyBarReversed = CandyBar.IsReversed +CandyBar.SetCandyBarOnClick = CandyBar.SetOnClick +CandyBar.SetCandyBarHeight = CandyBar.SetHeight +CandyBar.SetCandyBarWidth = CandyBar.SetWidth +CandyBar.SetCandyBarOnSizeGroup = CandyBar.SetOnSizeGroup + +function CandyBar:RegisterCandyBar(name, time, text, icon, ...) + if not CandyBar.addons[self] then + CandyBar.addons[self] = new() + end + CandyBar.addons[self][name] = CandyBar:Register(name, time, text, icon, unpack(arg)) +end + +function CandyBar:UnregisterCandyBar(a1, ...) + CandyBar:argCheck(a1, 2, "string") + if CandyBar.addons[self] then + CandyBar.addons[self][a1] = nil + end + CandyBar:Unregister(a1) + if getn(arg)>0 then + self:UnregisterCandyBar(unpack(arg)) + end +end + +function CandyBar:OnEmbedDisable(target) + if self.addons[target] then + for i in pairs(self.addons[target]) do + self:Unregister(i) + end + end +end + +-------------------------------- +-- Load this bitch! -- +-------------------------------- + +local function activate(self, oldLib, oldDeactivate) + CandyBar = self + + self.frame = oldLib and (oldLib.frame or oldLib.var and oldLib.var.frame) + if not self.frame then + self.frame = CreateFrame("Frame") + self.frame:Hide() + self.frame.name = "CandyBar-2.0 Frame" + end + + self.handlers = oldLib and (oldLib.handlers or oldLib.var and oldLib.var.handlers) or {} + self.groups = oldLib and (oldLib.groups or oldLib.var and oldLib.var.groups) or {} + self.framepool = oldLib and (oldLib.framepool or oldLib.var and oldLib.var.framepool) or {} + self.addons = oldLib and (oldLib.addons or oldLib.var and oldLib.var.addons) or {} + + self.frame:SetScript("OnUpdate", self.OnUpdate) + self.frame.owner = self + + self:activate(oldLib, oldDeactivate) + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +local function external(self, major, instance) + if major == "PaintChips-2.0" then + paint = instance + end +end + +AceLibrary:Register(CandyBar, vmajor, vminor, activate, nil, external) + diff --git a/modules/chronometer/libs/ParserLib/FixGlobalString.lua b/modules/chronometer/libs/ParserLib/FixGlobalString.lua new file mode 100644 index 0000000..7d766b5 --- /dev/null +++ b/modules/chronometer/libs/ParserLib/FixGlobalString.lua @@ -0,0 +1,32 @@ +-- Add a function to fix some global strings. +if GetLocale() == "koKR" then + function FixGlobalString(pName, str) + -- wrong global strings + -- "%1$s|1ì´;ê°€; %3$sì—게 %2$s|1ì„;를; 사용했습니다." <- "%s|1ì´;ê°€; %3$sì—게 %2$s|1ì„;를; 사용했습니다." + if (pName == "OPEN_LOCK_OTHER") + or (pName == "SIMPLEPERFORMOTHEROTHER") + or (pName == "SPELLPERFORMGOOTHERTARGETTED") + or (pName == "PERIODICAURADAMAGEOTHEROTHER") + or (pName == "PERIODICAURADAMAGESELFOTHER") + or (pName == "SIMPLECASTOTHEROTHER") + or (pName == "SIMPLECASTOTHERSELF") + or (pName == "SIMPLEPERFORMOTHERSELF") + or (pName == "SPELLDURABILITYDAMAGEALLOTHERSELF") + or (pName == "SPELLDURABILITYDAMAGEOTHERSELF") + or (pName == "SPELLMISSOTHEROTHER") then + str = string.gsub(str, "%%s", "%%1%$s"); -- by creep7 + elseif (pName == "COMBATLOG_HONORGAIN") then + str = "%1$s|1ì´;ê°€; 죽었습니다. 계급: %2$s (ì˜ˆìƒ ëª…ì˜ˆ ì ìˆ˜: %3$d)"; -- by creep7 + end + + --problematic strings + -- %sì˜ + return string.gsub(str, "(%%%d?$?s)(ì˜)", "%1% %2") -- by mundi4 + end +else + function FixGlobalString(pName, str) + --problematic strings + -- %s's + return str + end +end diff --git a/modules/chronometer/libs/ParserLib/ParserLib.lua b/modules/chronometer/libs/ParserLib/ParserLib.lua new file mode 100644 index 0000000..62a7984 --- /dev/null +++ b/modules/chronometer/libs/ParserLib/ParserLib.lua @@ -0,0 +1,2577 @@ +--[[ +Name: ParserLib +Revision: $Revision: 15186 $ +Author(s): rophy (rophy123@gmail.com) +Website: http://www.wowace.com/index.php/ParserLib +Documentation: http://www.wowace.com/index.php/ParserLib +SVN: http://svn.wowace.com/root/trunk/ParserLib +Description: An embedded combat log parser, which works on all localizations. +Dependencies: CompostLib (optional) or Compost-2.0 (optional). +]] + +--------------------------------------------------------------------------- +-- To Get an instance of ParserLib, call this: +-- local parser = ParserLib:GetInstance(version) +-- where the version is the variable 'vmajor' you see here. +--------------------------------------------------------------------------- +local vmajor, vminor = "1.1", tonumber(string.sub("$Revision: 15186 $", 12, -3)) + +local stubvarname = "TekLibStub" +local libvarname = "ParserLib" + +-- Check to see if an update is needed +-- if not then just return out now before we do anything +local libobj = getglobal(libvarname) +if libobj and not libobj:NeedsUpgraded(vmajor, vminor) then return end + +local lua51 = loadstring("return function(...) return ... end") and true or false +local string_gmatch = lua51 and string.gmatch or string.gfind + +local function print(msg, r, g, b) + if type(msg) ~= 'string' then msg = tostring(msg) end + ChatFrame1:AddMessage(string.format("<%s-%s-%s> %s", libvarname, vmajor, vminor, msg), r, g, b) +end + +--------------------------------------------------------------------------- +-- Embedded Library Registration Stub +-- Written by Iriel +-- Version 0.1 - 2006-03-05 +-- Modified by Tekkub +--------------------------------------------------------------------------- + +local stubobj = getglobal(stubvarname) +if not stubobj then + stubobj = {} + setglobal(stubvarname, stubobj) + + -- Instance replacement method, replace contents of old with that of new + function stubobj:ReplaceInstance(old, new) + for k,v in pairs(old) do old[k]=nil end + for k,v in pairs(new) do old[k]=v end + end + + -- Get a new copy of the stub + function stubobj:NewStub(name) + local newStub = {} + self:ReplaceInstance(newStub, self) + newStub.libName = name + newStub.lastVersion = '' + newStub.versions = {} + return newStub + end + + + -- Get instance version + function stubobj:NeedsUpgraded(vmajor, vminor) + local versionData = self.versions[vmajor] + if not versionData or versionData.minor < vminor then return true end + end + + + -- Get instance version + function stubobj:GetInstance(version) + if not version then version = self.lastVersion end + local versionData = self.versions[version] + if not versionData then print(string.format("<%s> Cannot find library version: %s", self.libName, version or "")) return end + return versionData.instance + end + + + -- Register new instance + function stubobj:Register(newInstance) + local version,minor = newInstance:GetLibraryVersion() + self.lastVersion = version + local versionData = self.versions[version] + if not versionData then + -- This one is new! + versionData = { + instance = newInstance, + minor = minor, + old = {}, + } + self.versions[version] = versionData + newInstance:LibActivate(self) + return newInstance + end + -- This is an update + local oldInstance = versionData.instance + local oldList = versionData.old + versionData.instance = newInstance + versionData.minor = minor + local skipCopy = newInstance:LibActivate(self, oldInstance, oldList) + table.insert(oldList, oldInstance) + if not skipCopy then + for i, old in ipairs(oldList) do self:ReplaceInstance(old, newInstance) end + end + return newInstance + end +end + + +if not libobj then + libobj = stubobj:NewStub(libvarname) + setglobal(libvarname, libobj) +end + + +local lib = {} + + +-- Return the library's current version +function lib:GetLibraryVersion() + return vmajor, vminor +end + + +-- Activate a new instance of this library +function lib:LibActivate(stub, oldLib, oldList) + local maj, min = self:GetLibraryVersion() + + if oldLib then + local omaj, omin = oldLib:GetLibraryVersion() + ---------------------------------------------------- + -- ********************************************** -- + -- **** Copy over any old data you need here **** -- + -- ********************************************** -- + ---------------------------------------------------- + self.frame = oldLib.frame + self:OnLoad() + + if omin < 11 and oldLib.clients then + for event in pairs(oldLib.clients) do + for i in pairs(oldLib.clients[event]) do + if type(oldLib.clients[event][i]["func"]) == "string" then + oldLib.clients[event][i]["func"] = getglobal(oldLib.clients[event][i]["func"]) + end + end + end + end + self.clients = oldLib.clients + + + else + --------------------------------------------------- + -- ********************************************* -- + -- **** Do any initialization you need here **** -- + -- ********************************************* -- + --------------------------------------------------- + self:OnLoad() + end + -- nil return makes stub do object copy +end + + + + + + + +---------------------------------------------- +-- *ParserLib Public Methods* -- +---------------------------------------------- + +-- Register an event to ParserLib. +function lib:RegisterEvent(addonID, event, handler) + + local eventExist + for i, v in pairs(self.supportedEvents) do + if v == event then + eventExist = true + break + end + end + + if not eventExist then + self:Print( string.format("Event %s is not supported. (AddOnID %s)", event, addonID), 1, 0, 0 ) + return + end + + + -- self:Print(string.format("Registering %s for addon %s.", event, addonID) ); -- debug + + if type(handler) == "string" then handler = getglobal(handler) end + + -- if not handler then self:Print("nil handler from " .. addonID, 1, 0, 0) end -- debug + + if self.clients[event] == nil then + self.clients[event] = {}; + end + + -- load pattern list & info when event is registering + self:PreLoadPatternInfo(event); + + table.insert(self.clients[event], { ["id"]=addonID, ["func"]=handler } ); + self.frame:RegisterEvent(event); + +end + +-- Check if you have registered an event. +function lib:IsEventRegistered(addonID, event) + if self.clients[event] then + for i, v in pairs(self.clients[event]) do + if v.id == addonID then return true end + end + end +end + +-- Unregister an event. +function lib:UnregisterEvent(addonID, event) + local empty = true + + + if not self.clients[event] then return end + + for i, v in pairs(self.clients[event]) do + if v.id == addonID then + -- self:Print( format("Removing %s from %s", v.id, event) ) -- debug + table.remove(self.clients[event], i) + else + empty = false + end + end + + if empty then + -- self:Print("Unregistering event " .. event) -- debug + self.frame:UnregisterEvent(event) + self.clients[event] = nil + end +end + +-- Unregister all events. +function lib:UnregisterAllEvents(addonID) + local event, index, empty; + + for event in pairs(self.clients) do + empty = true; + + for i, v in pairs(self.clients[event]) do + if v.id == addonID then + -- self:Print( format("Removing %s for %s", v.id, event) ) -- debug + table.remove(self.clients[event], i) +-- self.clients[event][index] = nil; + else + empty = false; + end + end + + if empty then + -- self:Print("Unregistering event " .. event) -- debug + self.frame:UnregisterEvent(event); + self.clients[event] = nil; + end + + end + +end + +-- Parse custom messages, check documentation.html for more info. +function lib:Deformat(text, pattern) + if not self.customPatterns then self.customPatterns = {} end + if not self.customPatterns[pattern] then + self.customPatterns[pattern] = self:Curry(pattern) + end + return self.customPatterns[pattern](text) +end + +-------------------------------------------------------- +-- Methods to control ParserLib behaviour -- +-------------------------------------------------------- + +-- Use CompostLib or not? +-- Not that at 1.1-23 CompostLib no longer slows down the speed of ParserLib, +-- because I found out that I was calling CompostLib:Recycle too frequently, which can be avoided. +function lib:UseCompost(flag) + self.vars.noCompost = not flag +end + + +--------------------------------------------------- +-- *End of ParserLib Public Methods* -- +--------------------------------------------------- + + +---------------------------------------------- +-- ParserLib Private Methods +---------------------------------------------- + +-- Constants +ParserLib_SELF = 103 +ParserLib_MELEE = 112 +ParserLib_DAMAGESHIELD = 113 + +-- lib.timing = true -- timer + +-- Stub function called by frame.OnEvent +local function ParserOnEvent() lib:OnEvent() end + +-- Sort the pattern so that they can be parsed in a correct sequence, will only do once for each registered event. +local function PatternCompare(a, b) + + local pa = getglobal(a) + local pb = getglobal(b) + + if not pa then ChatFrame1:AddMessage("|cffff0000Nil pattern: ".. a.."|r") end + if not pb then ChatFrame1:AddMessage("|cffff0000Nil pattern: ".. b.."|r") end + + local ca=0 + for _ in string_gmatch(pa, "%%%d?%$?[sd]") do ca=ca+1 end + local cb=0 + for _ in string_gmatch(pb, "%%%d?%$?[sd]") do cb=cb+1 end + + pa = string.gsub(pa, "%%%d?%$?[sd]", "") + pb = string.gsub(pb, "%%%d?%$?[sd]", "") + + if string.len(pa) == string.len(pb) then + return ca < cb; + else + return string.len(pa) > string.len(pb) + end + +end + +local FindString = { + [0] = function(m,p,t) return string.find(m,p), t end, + [1] = function(m,p,t) _,_,t[1] = string.find(m,p) if t[1] then return true, t else return false, t end end, + [2] = function(m,p,t) _,_,t[1],t[2] = string.find(m,p) if t[2] then return true, t else return false, t end end, + [3] = function(m,p,t) _,_,t[1],t[2],t[3] = string.find(m,p) if t[3] then return true, t else return false, t end end, + [4] = function(m,p,t) _,_,t[1],t[2],t[3],t[4] = string.find(m,p) if t[4] then return true, t else return false, t end end, + [5] = function(m,p,t) _,_,t[1],t[2],t[3],t[4],t[5] = string.find(m,p) if t[5] then return true, t else return false, t end end, + [6] = function(m,p,t) _,_,t[1],t[2],t[3],t[4],t[5],t[6] = string.find(m,p) if t[6] then return true, t else return false, t end end, + [7] = function(m,p,t) _,_,t[1],t[2],t[3],t[4],t[5],t[6],t[7] = string.find(m,p) if t[7] then return true, t else return false, t end end, + [8] = function(m,p,t) _,_,t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8] = string.find(m,p) if t[8] then return true, t else return false, t end end, + [9] = function(m,p,t) _,_,t[1],t[2],t[3],t[4],t[5],t[6],t[7],t[8],t[9] = string.find(m,p) if t[9] then return true, t else return false, t end end, +} + +-- Currently supported event list. +lib.supportedEvents = { + + "CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_HITS", + "CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_MISSES", + "CHAT_MSG_COMBAT_CREATURE_VS_PARTY_HITS", + "CHAT_MSG_COMBAT_CREATURE_VS_PARTY_MISSES", + "CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS", + "CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES", + "CHAT_MSG_COMBAT_FACTION_CHANGE", + "CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS", + "CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES", + "CHAT_MSG_COMBAT_FRIENDLY_DEATH", + "CHAT_MSG_COMBAT_HONOR_GAIN", + "CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS", + "CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES", + "CHAT_MSG_COMBAT_HOSTILE_DEATH", + "CHAT_MSG_COMBAT_PARTY_HITS", + "CHAT_MSG_COMBAT_PARTY_MISSES", + "CHAT_MSG_COMBAT_PET_HITS", + "CHAT_MSG_COMBAT_PET_MISSES", + "CHAT_MSG_COMBAT_SELF_HITS", + "CHAT_MSG_COMBAT_SELF_MISSES", + "CHAT_MSG_COMBAT_XP_GAIN", + "CHAT_MSG_SPELL_AURA_GONE_OTHER", + "CHAT_MSG_SPELL_AURA_GONE_SELF", + "CHAT_MSG_SPELL_AURA_GONE_PARTY", + "CHAT_MSG_SPELL_BREAK_AURA", + "CHAT_MSG_SPELL_CREATURE_VS_CREATURE_BUFF", + "CHAT_MSG_SPELL_CREATURE_VS_CREATURE_DAMAGE", + "CHAT_MSG_SPELL_CREATURE_VS_PARTY_BUFF", + "CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE", + "CHAT_MSG_SPELL_CREATURE_VS_SELF_BUFF", + "CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE", + "CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS", + "CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF", + "CHAT_MSG_SPELL_FAILED_LOCALPLAYER", + "CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF", + "CHAT_MSG_SPELL_FRIENDLYPLAYER_DAMAGE", + "CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF", + "CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE", + "CHAT_MSG_SPELL_ITEM_ENCHANTMENTS", + "CHAT_MSG_SPELL_PARTY_BUFF", + "CHAT_MSG_SPELL_PARTY_DAMAGE", + "CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS", + "CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE", + "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS", + "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE", + "CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS", + "CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE", + "CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS", + "CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE", + "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS", + "CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE", + "CHAT_MSG_SPELL_PET_BUFF", + "CHAT_MSG_SPELL_PET_DAMAGE", + "CHAT_MSG_SPELL_SELF_BUFF", + "CHAT_MSG_SPELL_SELF_DAMAGE", + "CHAT_MSG_SPELL_TRADESKILLS", + +} + +function lib:GetCompost() + + if self.vars.noCompost then + + if not self.myCompost then + self.myCompost = { + Recycle = function() return {} end, + Acquire = function() return {} end, + Reclaim = function() end, + } + end + return self.myCompost + + else + + if not self.compost then + if AceLibrary and AceLibrary:HasInstance("Compost-2.0") then + self.compost = AceLibrary:GetInstance("Compost-2.0") + elseif CompostLib then + self.compost = CompostLib:GetInstance("compost-1") + else + -- Cannot find the existance of CompostLib + self.vars.noCompost = true + return self:GetCompost() + end + end + return self.compost + + end + +end + +-- Convert "%s hits %s for %d." to "(.+) hits (.+) for (%d+)." +-- Will additionaly return the sequence of tokens, for example: +-- "%2$s reflects %3$d %4$s damage to %1$s." will return: +-- "(.-) reflects (%+) (.-) damage to (.-)%.", 4 1 2 3. +-- ( [1]=2,[2]=3,[3]=4,[4]=1 Reverting indexes and become [1]=4, [2]=[1],[3]=2,[4]=3. ) +function lib:ConvertPattern(pattern, anchor) + + local seq + + -- Add % to escape all magic characters used in LUA pattern matching, except $ and % + pattern = string.gsub(pattern,"([%^%(%)%.%[%]%*%+%-%?])","%%%1"); + + -- Do these AFTER escaping the magic characters. + pattern = string.gsub(pattern,"%%s","(.-)"); -- %s to (.-) + pattern = string.gsub(pattern,"%%d","(%-?%%d+)"); -- %d to (%d+) + + if string.find(pattern,"%$") then + seq = {}; -- fills with ordered list of $s as they appear + local idx = 1; -- incremental index into field[] + + local tmpSeq = {} + for i in string_gmatch(pattern,"%%(%d)%$.") do + tmpSeq[idx] = tonumber(i); + idx = idx + 1 + end + for i, j in ipairs(tmpSeq) do + seq[j] = i + end + table.setn(seq, table.getn(tmpSeq)) + pattern = string.gsub(pattern,"%%%d%$s","(.-)"); -- %1$s to (.-) + pattern = string.gsub(pattern,"%%%d%$d","(%-?%%d+)"); -- %1$d to (%d+) + end + + -- Escape $ now. + pattern = string.gsub(pattern,"%$","%%$"); + + -- Anchor tag can improve string.find() performance by 100%. + if anchor then pattern = "^"..pattern end + + -- If the pattern ends with (.-), replace it with (.+), or the capsule will be lost. + if string.sub(pattern, -4) == "(.-)" then + pattern = string.sub(pattern, 0, -5) .. "(.+)"; + end + + if not seq then return pattern end + + return pattern, seq[1], seq[2], seq[3], seq[4], seq[5], seq[6], seq[7], seq[8], seq[9], seq[10] +end + +function lib:OnLoad() + + -- Both table starts out empty, and load the data only when required. + self.eventTable = {} + self.patternTable = {} + + + self.vars = {} + + if self.timing then + self.timer = { + ParseMessage_LoadPatternList = 0, + ParseMessage_FindPattern = 0, + ParseMessage_FindPattern_Regexp = 0, + ParseMessage_FindPattern_Regexp_FindString = 0, + ParseMessage_FindPattern_LoadPatternInfo = 0, + ParseMessage_ParseInformation = 0, + ParseMessage_ParseTrailers = 0, + ParseMessage_ConvertTypes = 0, + NotifyClients = 0, + Compost_Acquire = 0, + Compost_Recycle = 0, + Compost_Reclaim = 0, + } + end + + if not self.clients then self.clients = {} end + + if not self.frame then + self.frame = CreateFrame("FRAME", "ParserLibFrame") + self.frame:SetScript("OnEvent", ParserOnEvent ) + self.frame:Hide() + end + + +end + +function lib:OnEvent(e, a1) + + if not e then e = event end + if not a1 then a1 = arg1 end + + -- self:Print("Event: |cff3333ff"..e.."|r") -- debug + + -- Titan Honor+ was changing the global events... just change it back. + if e == "CHAT_MSG_HONORPLUS" then e = "CHAT_MSG_COMBAT_HONOR_GAIN" end + + + if self:ParseMessage(a1, e) then + +-- local timer = GetTime() -- timer + self:NotifyClients(e) +-- self.timer.NotifyClients = self.timer.NotifyClients + GetTime() - timer -- timer + + end + + +end + +function lib:NotifyClients(event) + + if not self.clients or not self.clients[event] then + -- self:Print(event .. " has no client to notify.") -- debug + return + end + + -- Noneed to recycle the table if there is only one client. + if table.getn(self.clients[event]) == 1 then + -- self:Print(event .. ", calling " .. self.clients[event][1].id) -- debug + self.clients[event][1].func(event, self.info) + return + end + +-- local timer = GetTime() -- timer + local info = self:GetCompost():Acquire() +-- self.timer.Compost_Acquire = GetTime() - timer + self.timer.Compost_Acquire -- timer + + for i, client in pairs(self.clients[event]) do + -- self:Print(event .. ", calling " .. client.id) -- debug + + -- I can just do a compost:Recycle() here, but I hope this can improve the performance. + for j in pairs(info) do if not self.info[j] then info[j] = nil end end + for j, v in pairs(self.info) do info[j] = v end + + client.func(event, info) + end + +-- timer = GetTime() -- timer + self:GetCompost():Reclaim(info) +-- self.timer.reclaim = GetTime() - timer + self.timer.reclaim -- timer + +end + +lib.Print = print + +-- load pattern list & info +function lib:PreLoadPatternInfo(event) + if not event then return end + + if not self.eventTable[event] then self.eventTable[event] = self:LoadPatternList(event) end + local list = self.eventTable[event] + if list then + for i, v in pairs(list) do + if not self.patternTable[v] then self.patternTable[v] = self:LoadPatternInfo(v) end + end + end +end + +-- message : the arg1 in the event +-- event : name says it all. +-- info : the table which will store the passed result, so THIS IS THE OUTPUT. +-- return : true if pattern found and parsed, nil otherwise. +function lib:ParseMessage(message, event) + +-- -- local currTime -- timer + + +-- currTime = GetTime() -- timer + if not self.eventTable[event] then self.eventTable[event] = self:LoadPatternList(event) end -- loaded by registering already + local list = self.eventTable[event] +-- self.timer.ParseMessage_LoadPatternList = self.timer.ParseMessage_LoadPatternList + GetTime() - currTime -- timer + + if not list then return end + + -- Get the table to store parsed results. + if not self.info then +-- timer = GetTime() -- timer + self.info = self:GetCompost():Acquire() +-- self.timer.Compost_Acquire = GetTime() - timer + self.timer.Compost_Acquire -- timer + else +-- timer = GetTime() -- timer + self.info = self:GetCompost():Recycle(self.info) +-- self.timer.Compost_Recycle = GetTime() - timer + self.timer.Compost_Recycle -- timer + end + + +-- currTime = GetTime() -- timer + local pattern = self:FindPattern(message, list) +-- self.timer.ParseMessage_FindPattern = GetTime() - currTime + self.timer.ParseMessage_FindPattern -- timer + + + if not pattern then + -- create "unknown" event type. + self.info.type = "unknown" + self.info.message = message + return true + end + +-- currTime = GetTime() -- timer + self:ParseInformation(pattern) +-- self.timer.ParseMessage_ParseInformation = GetTime() - currTime + self.timer.ParseMessage_ParseInformation -- timer + + +-- currTime = GetTime() -- timer + if self.info.type == "hit" or self.info.type == "environment" then + self:ParseTrailers(message) + end +-- self.timer.ParseMessage_ParseTrailers = GetTime() - currTime + self.timer.ParseMessage_ParseTrailers -- timer + +-- currTime = GetTime() -- timer + self:ConvertTypes(self.info) +-- self.timer.ParseMessage_ConvertTypes = GetTime() - currTime + self.timer.ParseMessage_ConvertTypes -- timer + + return true + + +end + + +-- Search for pattern in 'patternList' which matches 'message', parsed tokens will be stored in table self.info +function lib:FindPattern(message, patternList) + + local pt, timer, found + + for i, v in pairs(patternList) do + +-- timer = GetTime() -- timer + if not self.patternTable[v] then self.patternTable[v] = self:LoadPatternInfo(v) end -- loaded by registering already +-- self.timer.ParseMessage_FindPattern_LoadPatternInfo = GetTime() - timer + self.timer.ParseMessage_FindPattern_LoadPatternInfo -- timer + + pt = self.patternTable[v] + + found = false + + if not pt then return end -- check patternTable + +-- timer = GetTime() -- timer + if self:OptimizerCheck(message, v) then +-- timer = GetTime() -- timer + found, self.info = FindString[pt.tc](message, pt.pattern, self.info) +-- self.timer.ParseMessage_FindPattern_Regexp_FindString = GetTime() - timer + self.timer.ParseMessage_FindPattern_Regexp_FindString -- timer + end +-- self.timer.ParseMessage_FindPattern_Regexp = GetTime() - timer + self.timer.ParseMessage_FindPattern_Regexp -- timer + + if found then +-- self:Print(message.." = " .. v .. ":" .. pt.pattern) -- debug + return v + end + + + end + + +end + + +-- Parses for trailors. +function lib:ParseTrailers(message) + local found, amount, info + + info = self.info + + + if not self.trailers then + self.trailers = { + CRUSHING_TRAILER = self:ConvertPattern(CRUSHING_TRAILER), + GLANCING_TRAILER = self:ConvertPattern(GLANCING_TRAILER), + ABSORB_TRAILER = self:ConvertPattern(ABSORB_TRAILER), + BLOCK_TRAILER = self:ConvertPattern(BLOCK_TRAILER), + RESIST_TRAILER = self:ConvertPattern(RESIST_TRAILER), + VULNERABLE_TRAILER = self:ConvertPattern(VULNERABLE_TRAILER), + } + end + + found = string.find(message, self.trailers.CRUSHING_TRAILER) + if found then + info.isCrushing = true + end + found = string.find(message, self.trailers.GLANCING_TRAILER) + if found then + info.isGlancing = true + end + found, _, amount = string.find(message, self.trailers.ABSORB_TRAILER) + if found then + info.amountAbsorb = amount + end + found, _, amount = string.find(message, self.trailers.BLOCK_TRAILER) + if found then + info.amountBlock = amount + end + found, _, amount = string.find(message, self.trailers.RESIST_TRAILER) + if found then + info.amountResist = amount + end + found, _, amount = string.find(message, self.trailers.VULNERABLE_TRAILER) + if found then + info.amountVulnerable = amount + end + +end + +function lib:ParseInformation(patternName) + + local patternInfo = self.patternTable[patternName] + local info = self.info + + -- Create an info table from pattern table, copies everything except the pattern string. + for i, v in pairs(patternInfo) do + if i == 1 then + info.type = v + elseif type(i) == "number" then + if type(v) == "number" and v < 100 then + info[ self:GetInfoFieldName( self.patternTable[patternName][1], i) ] = info[v] + else + info[ self:GetInfoFieldName( self.patternTable[patternName][1], i) ] = v + end + end + end + + if info.type == "honor" and not info.amount then + info.isDishonor = true + + elseif info.type == "durability" and not info.item then + info.isAllItems = true + end + + for i in ipairs(info) do + info[i] = nil + end + +end + +function lib:ConvertTypes(info) + for i in pairs(info) do + if string.find(i, "^amount") then info[i] = tonumber(info[i]) end + end +end + +function lib:OptimizerCheck(message, patternName) + + --if 1 then return true end + if not ParserLibOptimizer then return true end + + if not ParserLibOptimizer[patternName] then return true end + + if string.find(message, ParserLibOptimizer[patternName], 1, true) then return true end + + return false +end + +-- Most of the parts were learnt from BabbleLib by chknight, so credits goes to him. +function lib:Curry(pattern) + local cp, tt, n, f, o + local DoNothing = function(tok) return tok end + + tt = {} + for tk in string_gmatch(pattern, "%%%d?%$?([sd])") do + table.insert(tt, tk) + end + + cp = { self:ConvertPattern(pattern, true) } + cp.p = cp[1] + + n = table.getn(cp) + for i=1,n-1 do + cp[i] = cp[i+1] + end + table.remove(cp, n) + + f = {} + o = {} + n = table.getn(tt) + for i=1, n do + if tt[i] == "s" then + f[i] = DoNothing + else + f[i] = tonumber + end + end + + if not cp[1] then + if n == 0 then + return function() end + elseif n == 1 then + return function(text) + _, _, o[1] = string.find(text, cp.p) + if o[1] then + return f[1](o[1]) + end + end + elseif n == 2 then + return function(text) + _, _, o[1], o[2]= string.find(text, cp.p) + if o[1] then + return f[1](o[1]), f[2](o[2]) + end + end + elseif n == 3 then + return function(text) + _, _, o[1], o[2], o[3] = string.find(text, cp.p) + if o[1] then + return f[1](o[1]), f[2](o[2]), f[3](o[3]) + end + end + elseif n == 4 then + return function(text) + _, _, o[1], o[2], o[3], o[4] = string.find(text, cp.p) + if o[1] then + return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4]) + end + end + elseif n == 5 then + return function(text) + _, _, o[1], o[2], o[3], o[4], o[5] = string.find(text, cp.p) + if o[1] then + return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4]), f[5](o[5]) + end + end + elseif n == 6 then + return function(text) + _, _, o[1], o[2], o[3], o[4], o[5], o[6] = string.find(text, cp.p) + if o[1] then + return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4]), f[5](o[5]), f[6](o[6]) + end + end + elseif n == 7 then + return function(text) + _, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7] = string.find(text, cp.p) + if o[1] then + return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4]), f[5](o[5]), f[6](o[6]), f[7](o[7]) + end + end + elseif n == 8 then + return function(text) + _, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8] = string.find(text, cp.p) + if o[1] then + return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4]), f[5](o[5]), f[6](o[6]), f[7](o[7]), f[8](o[8]) + end + end + elseif n == 9 then + return function(text) + _, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9] = string.find(text, cp.p) + if o[1] then + return f[1](o[1]), f[2](o[2]), f[3](o[3]), f[4](o[4]), f[5](o[5]), f[6](o[6]), f[7](o[7]), f[8](o[8]), f[9](o[9]) + end + end + end + else + if n == 0 then + return function() end + elseif n == 1 then + return function(text) + _, _, o[1] = string.find(text, cp.p) + if o[1] then + return f[cp[1]](o[cp[1]]) + end + end elseif n == 2 then + return function(text) + _, _, o[1], o[2] = string.find(text, cp.p) + if o[1] then + return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]) + end + end elseif n == 3 then + return function(text) + _, _, o[1], o[2], o[3] = string.find(text, cp.p) + if o[1] then + return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]) + end + end + elseif n == 4 then + return function(text) + _, _, o[1], o[2], o[3], o[4] = string.find(text, cp.p) + if o[1] then + return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]]) + end + end elseif n == 5 then + return function(text) + _, _, o[1], o[2], o[3], o[4], o[5] = string.find(text, cp.p) + if o[1] then + return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]]), f[cp[5]](o[cp[5]]) + end + end elseif n == 6 then + return function(text) + _, _, o[1], o[2], o[3], o[4], o[5], o[6] = string.find(text, cp.p) + if o[1] then + return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]]), f[cp[5]](o[cp[5]]), f[cp[6]](o[cp[6]]) + end + end elseif n == 7 then + return function(text) + _, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7] = string.find(text, cp.p) + if o[1] then + return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]]), f[cp[5]](o[cp[5]]), f[cp[6]](o[cp[6]]), f[cp[7]](o[cp[7]]) + end + end elseif n == 8 then + return function(text) + _, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8] = string.find(text, cp.p) + if o[1] then + return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]]), f[cp[5]](o[cp[5]]), f[cp[6]](o[cp[6]]), f[cp[7]](o[cp[7]]), f[cp[8]](o[cp[8]]) + end + end elseif n == 9 then + return function(text) + _, _, o[1], o[2], o[3], o[4], o[5], o[6], o[7], o[8], o[9] = string.find(text, cp.p) + if o[1] then + return f[cp[1]](o[cp[1]]), f[cp[2]](o[cp[2]]), f[cp[3]](o[cp[3]]), f[cp[4]](o[cp[4]]), f[cp[5]](o[cp[5]]), f[cp[6]](o[cp[6]]), f[cp[7]](o[cp[7]]), f[cp[8]](o[cp[8]]), f[cp[9]](o[cp[9]]) + end + end + end + end +end + + +function lib:PrintTable(args) + for k, v in pairs(args) do + ChatFrame1:AddMessage(tostring(k) .. " = " .. tostring(v)); + end + ChatFrame1:AddMessage(""); +end + +-- Used to test the correcteness of ParserLib on different languages. +function lib:TestPatterns(sendToClients) + + + self:LoadEverything() + + + -- Creating the combat messages. + local testNumber = 123 + local message + local messages = self:GetCompost():Acquire() + for patternName in pairs(self.patternTable) do + messages[patternName] = self:GetCompost():Acquire() + messages[patternName].message = getglobal(patternName) + for i, v in pairs(self.patternTable[patternName]) do + if i ~= "tc" and type(v) == "number" and v < 100 and i~=1 then + messages[patternName][v] = self:GetInfoFieldName(self.patternTable[patternName][1], i) + end + end + for i, v in ipairs(messages[patternName]) do + if string.find(v, "^amount") then + messages[patternName].message = string.gsub(messages[patternName].message, "%%%d?%$?d", testNumber, 1) + else + messages[patternName].message = string.gsub(messages[patternName].message, "%%%d?%$?s", string.upper(v), 1) + end + end + end + + + -- Begin the test. + + local info, msg + local startTime = GetTime() + local startMem = gcinfo() + + for _, event in pairs(self.supportedEvents) do + for _, pattern in pairs(self:LoadPatternList(event)) do + msg = messages[pattern].message + if sendToClients then self:OnEvent(event, msg) end + if self:ParseMessage(msg, event) then + info = self.info + for i, v in ipairs(messages[pattern]) do + if not info[v] + or ( string.find(v, "^amount") and info[v] ~= testNumber ) + or ( not string.find(v, "^amount") and info[v] ~= string.upper(v) ) then + self:Print("Event: " .. event) + self:Print("Pattern: " .. pattern) + self:Print("Message: " .. msg) + self:PrintTable(messages[pattern]) + self:PrintTable(info) + end + end + end + end + end + + self:Print( string.format("Test completed in %.4fs, memory cost %.2fKB.", GetTime() - startTime, gcinfo() - startMem) ) + + self:GetCompost():Reclaim(messages, 1) + +end + +function lib:LoadEverything() + + -- Load all patterns and events. + for _, v in pairs(self.supportedEvents) do + for _, w in pairs(self:LoadPatternList(v)) do + if not self.patternTable[w] then + self.patternTable[w] = self:LoadPatternInfo(w) + end + end + end + +end + +function lib:PrintTimers() + if not self.timer then return end + + local total = 0 + for i in pairs(self.timer) do + total = total + self.timer[i] + end + + if not self.timerIndex then + self.timerIndex = {} + for i in pairs(self.timer) do + table.insert(self.timerIndex, i) + end + table.sort(self.timerIndex) + end + + DEFAULT_CHAT_FRAME:AddMessage("Time\t%\tDescription") + for i, idx in pairs(self.timerIndex) do + DEFAULT_CHAT_FRAME:AddMessage(string.format("%.3f\t%.1f\t%s", self.timer[idx], 100*self.timer[idx]/total, idx) ) + end + + DEFAULT_CHAT_FRAME:AddMessage(total .. "\t100\tTotal") +end +-- Used to load eventTable elements on demand. +function lib:LoadPatternList(eventName) + local list + +--------------- Melee Hits ---------------- + + if eventName == "CHAT_MSG_COMBAT_SELF_HITS" then + + if not self.eventTable["CHAT_MSG_COMBAT_SELF_HITS"] then + + self.eventTable["CHAT_MSG_COMBAT_SELF_HITS"] = + self:LoadPatternCategoryTree( + { + "HitSelf", + "EnvSelf", + } + ) + + end + + list = self.eventTable["CHAT_MSG_COMBAT_SELF_HITS"] + + elseif eventName == "CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_HITS" + or eventName == "CHAT_MSG_COMBAT_CREATURE_VS_PARTY_HITS" + or eventName == "CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS" + or eventName == "CHAT_MSG_COMBAT_PARTY_HITS" + or eventName == "CHAT_MSG_COMBAT_PET_HITS" then + + if not self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS"] then + self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS"] = + self:LoadPatternCategoryTree( { + "HitOtherOther", + "EnvOther", + } ) + end + list = self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_HITS"] + + + elseif eventName == "CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS" + or eventName == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_HITS" then + + if not self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS"] then + self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS"] = + self:LoadPatternCategoryTree( { + { + "HitOtherOther", + "HitOtherSelf", + }, + "EnvOther", + } ) + end + list = self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_HITS"] + + + +--------------- Melee Misses ---------------- + + + + elseif eventName == "CHAT_MSG_COMBAT_SELF_MISSES" then + if not self.eventTable["CHAT_MSG_COMBAT_SELF_MISSES"] then + self.eventTable["CHAT_MSG_COMBAT_SELF_MISSES"] = + self:LoadPatternCategoryTree( { + "MissSelf", + } ) + end + list = self.eventTable["CHAT_MSG_COMBAT_SELF_MISSES"] + + elseif eventName == "CHAT_MSG_COMBAT_CREATURE_VS_CREATURE_MISSES" + or eventName == "CHAT_MSG_COMBAT_CREATURE_VS_PARTY_MISSES" + or eventName == "CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES" + or eventName == "CHAT_MSG_COMBAT_PARTY_MISSES" + or eventName == "CHAT_MSG_COMBAT_PET_MISSES" then + + if not self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES"] then + self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES"] = + self:LoadPatternCategoryTree( { + "MissOtherOther", + } ) + end + + list = self.eventTable["CHAT_MSG_COMBAT_FRIENDLYPLAYER_MISSES"] + + + elseif eventName == "CHAT_MSG_COMBAT_CREATURE_VS_SELF_MISSES" + or eventName == "CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES" then + + if not self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES"] then + self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES"] = + self:LoadPatternCategoryTree( { + { + "MissOtherOther", + "MissOtherSelf", + } + } ) + end + + list = self.eventTable["CHAT_MSG_COMBAT_HOSTILEPLAYER_MISSES"] + +--------------- Spell Buffs ---------------- + elseif eventName == "CHAT_MSG_SPELL_SELF_BUFF" then + if not self.eventTable["CHAT_MSG_SPELL_SELF_BUFF"] then + + if GetLocale() ~= "deDE" then + self.eventTable["CHAT_MSG_SPELL_SELF_BUFF"] = self:LoadPatternCategoryTree( + { + "HealSelf", + "EnchantSelf", + "CastSelf", + "PerformSelf", + "DispelFailSelf", + "SPELLCASTSELFSTART", + "SPELLPERFORMSELFSTART", + { + "DrainSelf", + "PowerGainSelf", + "ExtraAttackSelf", + }, + "SPELLSPLITDAMAGESELFOTHER", + { + "ProcResistSelf", + "SpellMissSelf", + } + } + ) + + else + self.eventTable["CHAT_MSG_SPELL_SELF_BUFF"] = self:LoadPatternCategoryTree( + { + "HealSelf", + "CastSelf", + "PerformSelf", + "DispelFailSelf", + "SPELLCASTSELFSTART", + "SPELLPERFORMSELFSTART", + { + "DrainSelf", + "PowerGainSelf", + "ExtraAttackSelf", + }, + "SPELLSPLITDAMAGESELFOTHER", + { + "ProcResistSelf", + "SpellMissSelf", + } + } + ) + end + + end + + list = self.eventTable["CHAT_MSG_SPELL_SELF_BUFF"] + + + elseif eventName == "CHAT_MSG_SPELL_CREATURE_VS_CREATURE_BUFF" + or eventName == "CHAT_MSG_SPELL_CREATURE_VS_PARTY_BUFF" + or eventName == "CHAT_MSG_SPELL_CREATURE_VS_SELF_BUFF" + or eventName == "CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF" + or eventName == "CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF" + or eventName == "CHAT_MSG_SPELL_PARTY_BUFF" + or eventName == "CHAT_MSG_SPELL_PET_BUFF" then + + if not self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF"] then + + if GetLocale() ~= "deDE" then + self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF"] = self:LoadPatternCategoryTree( + { + { + "HealOther", + "PowerGainOther", + "ExtraAttackOther", + "DrainOther", + }, + "SPELLCASTOTHERSTART", + { + "EnchantOther", + "CastOther", + "PerformOther", + }, + "SPELLPERFORMOTHERSTART", + "SpellMissOther", + "ProcResistOther", + "SplitDamageOther", + "DispelFailOther", + } + ) + + else -- Remove "EnchantOther" from German, since it's 100% ambiguous with SIMPLECASTOTHEROTHER, which is unsolvable. + self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF"] = self:LoadPatternCategoryTree( + { + { + "HealOther", + "PowerGainOther", + "ExtraAttackOther", + "DrainOther", + }, + "SPELLCASTOTHERSTART", + { + "CastOther", + "PerformOther", + }, + "SPELLPERFORMOTHERSTART", + "SpellMissOther", + "ProcResistOther", + "SplitDamageOther", + "DispelFailOther", + } + ) + end + end + + list = self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF"] + + +--------------- Spell Damages ---------------- + + elseif eventName == "CHAT_MSG_SPELL_SELF_DAMAGE" then + if not self.eventTable["CHAT_MSG_SPELL_SELF_DAMAGE"] then + self.eventTable["CHAT_MSG_SPELL_SELF_DAMAGE"] = + self:LoadPatternCategoryTree( { + "SpellHitSelf", + { + "CastSelf", + "DurabilityDamageSelf", + }, + "PerformSelf", + "SpellMissSelf", + "SPELLCASTSELFSTART", + "SPELLPERFORMSELFSTART", + "InterruptSelf", + "DispelFailSelf", + "ExtraAttackSelf", + "DrainSelf", + + } ) + + end + list = self.eventTable["CHAT_MSG_SPELL_SELF_DAMAGE"] + + + elseif eventName == "CHAT_MSG_SPELL_CREATURE_VS_CREATURE_DAMAGE" + or eventName == "CHAT_MSG_SPELL_CREATURE_VS_PARTY_DAMAGE" + or eventName == "CHAT_MSG_SPELL_CREATURE_VS_SELF_DAMAGE" + or eventName == "CHAT_MSG_SPELL_FRIENDLYPLAYER_DAMAGE" + or eventName == "CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE" + or eventName == "CHAT_MSG_SPELL_PARTY_DAMAGE" + or eventName == "CHAT_MSG_SPELL_PET_DAMAGE" then + + if not self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE"] then + self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE"] = + self:LoadPatternCategoryTree( { + "SpellHitOther", + "SPELLCASTOTHERSTART", + "SPELLPERFORMOTHERSTART", + "DrainOther", + "SpellMissOther", + { + "INSTAKILLOTHER", + "INSTAKILLSELF", + }, + { + "PROCRESISTOTHEROTHER", + "PROCRESISTOTHERSELF", + }, + "SplitDamageOther", + { + "CastOther", + "InterruptOther", + "DurabilityDamageOther", + }, + "PerformOther", + "ExtraAttackOther", + { + "DISPELFAILEDOTHEROTHER", + "DISPELFAILEDOTHERSELF", + }, + }) + + end + list = self.eventTable["CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE"] + + +--------------- Periodic Buffs ---------------- + + elseif eventName == "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS" then + + if not self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"] then + + self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"] = + self:LoadPatternCategoryTree( { + { + "HotOther", + "HotSelf", + }, + { + "BuffSelf", + "BuffOther", + "PowerGainOther", + "PowerGainSelf", + }, + "DrainSelf", + "DotSelf", -- Don't think this will hapen but add it anyway. + } ) + end + list = self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"] + + + elseif eventName == "CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS" + or eventName == "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS" + or eventName == "CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS" + or eventName == "CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS" then + + if not self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS"] then + self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS"] = + self:LoadPatternCategoryTree( { + { + "HotOther", +-- "DrainOther", -- Dont think this would happen but add it anyway. + }, + { + "BuffOther", + "PowerGainOther", + "DrainOther", -- When other players use Skull of Impending Doom. + }, + "DotOther", -- Dont think this will happen but add anyway. + "DebuffOther", -- Was fired on older WoW version. + } ) + end + + list = self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS"] + + + +--------------- Periodic Damages ---------------- + + elseif eventName == "CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE" then + if not self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE"] then + + self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE"] = + self:LoadPatternCategoryTree( { + { + "DotSelf", + "DotOther", + }, + { + "DebuffSelf", + "DebuffOther", + }, + { + "SPELLLOGABSORBOTHEROTHER", + "SPELLLOGABSORBOTHERSELF", + "SPELLLOGABSORBSELFSELF", + "SPELLLOGABSORBSELFOTHER", + }, + "DrainSelf", + } ) + end + list = self.eventTable["CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE"] + + + + elseif eventName == "CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE" + or eventName == "CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE" + or eventName == "CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE" + or eventName == "CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE" then + + if not self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE"] then + self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE"] = + self:LoadPatternCategoryTree( { + "DebuffOther", + "DotOther", + { + "SPELLLOGABSORBOTHEROTHER", + "SPELLLOGABSORBSELFOTHER", + }, + "DrainOther", + { + "PowerGainOther", + "BuffOther", -- Was fired on older WoW version. + } + } ) + end + list = self.eventTable["CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE"] + +--------------- Damage Shields ---------------- + + + elseif eventName == "CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF" then + if not self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF"] then + self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF"] = { + "SPELLRESISTOTHEROTHER", + "SPELLRESISTSELFOTHER", + "DAMAGESHIELDOTHEROTHER", + "DAMAGESHIELDSELFOTHER", + } + table.sort(self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF"] , PatternCompare) + end + list = self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_SELF"] + + elseif eventName == "CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS" then + if not self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS"] then + self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS"] = { + "SPELLRESISTOTHEROTHER", + "SPELLRESISTOTHERSELF", + "DAMAGESHIELDOTHEROTHER", + "DAMAGESHIELDOTHERSELF", + } + table.sort(self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS"] , PatternCompare) + end + list = self.eventTable["CHAT_MSG_SPELL_DAMAGESHIELDS_ON_OTHERS"] + + + +--------------- Auras ---------------- + + elseif eventName == "CHAT_MSG_SPELL_AURA_GONE_PARTY" + or eventName == "CHAT_MSG_SPELL_AURA_GONE_OTHER" then + + if not self.eventTable["CHAT_MSG_SPELL_AURA_GONE_OTHER"] then + self.eventTable["CHAT_MSG_SPELL_AURA_GONE_OTHER"] = { + "AURAREMOVEDOTHER", + } + table.sort(self.eventTable["CHAT_MSG_SPELL_AURA_GONE_OTHER"] , PatternCompare) + end + list = self.eventTable["CHAT_MSG_SPELL_AURA_GONE_OTHER"] + + elseif eventName == "CHAT_MSG_SPELL_AURA_GONE_SELF" then + + if not self.eventTable["CHAT_MSG_SPELL_AURA_GONE_SELF"] then + self.eventTable["CHAT_MSG_SPELL_AURA_GONE_SELF"] = { + "AURAREMOVEDOTHER", + "AURAREMOVEDSELF", + } + table.sort(self.eventTable["CHAT_MSG_SPELL_AURA_GONE_SELF"] , PatternCompare) + end + list = self.eventTable["CHAT_MSG_SPELL_AURA_GONE_SELF"] + + elseif eventName == "CHAT_MSG_SPELL_BREAK_AURA" then + + if not self.eventTable["CHAT_MSG_SPELL_BREAK_AURA"] then + self.eventTable["CHAT_MSG_SPELL_BREAK_AURA"] = { + "AURADISPELSELF", + "AURADISPELOTHER", + } + table.sort(self.eventTable["CHAT_MSG_SPELL_BREAK_AURA"] , PatternCompare) + end + list = self.eventTable["CHAT_MSG_SPELL_BREAK_AURA"] + + + + elseif eventName == "CHAT_MSG_SPELL_ITEM_ENCHANTMENTS" then + + if not self.eventTable["CHAT_MSG_SPELL_ITEM_ENCHANTMENTS"] then + self.eventTable["CHAT_MSG_SPELL_ITEM_ENCHANTMENTS"] = { + "ITEMENCHANTMENTADDSELFSELF", + "ITEMENCHANTMENTADDSELFOTHER", + "ITEMENCHANTMENTADDOTHEROTHER", + "ITEMENCHANTMENTADDOTHERSELF", + } + table.sort(self.eventTable["CHAT_MSG_SPELL_ITEM_ENCHANTMENTS"] , PatternCompare) + end + list = self.eventTable["CHAT_MSG_SPELL_ITEM_ENCHANTMENTS"] + +--------------- Trade Skills ---------------- + + + elseif eventName == "CHAT_MSG_SPELL_TRADESKILLS" then + if not self.eventTable["CHAT_MSG_SPELL_TRADESKILLS"] then + self.eventTable["CHAT_MSG_SPELL_TRADESKILLS"] = { + "TRADESKILL_LOG_FIRSTPERSON", + "TRADESKILL_LOG_THIRDPERSON", + "FEEDPET_LOG_FIRSTPERSON", + "FEEDPET_LOG_THIRDPERSON", + } + table.sort(self.eventTable["CHAT_MSG_SPELL_TRADESKILLS"], PatternCompare ) + end + list = self.eventTable["CHAT_MSG_SPELL_TRADESKILLS"] + + elseif eventName == "CHAT_MSG_SPELL_FAILED_LOCALPLAYER" then + + if not self.eventTable["CHAT_MSG_SPELL_FAILED_LOCALPLAYER"] then + self.eventTable["CHAT_MSG_SPELL_FAILED_LOCALPLAYER"] = { + "SPELLFAILPERFORMSELF", + "SPELLFAILCASTSELF", + } + table.sort(self.eventTable["CHAT_MSG_SPELL_FAILED_LOCALPLAYER"], PatternCompare) + end + list = self.eventTable["CHAT_MSG_SPELL_FAILED_LOCALPLAYER"] + + + elseif eventName == "CHAT_MSG_COMBAT_FACTION_CHANGE" then + + if not self.eventTable["CHAT_MSG_COMBAT_FACTION_CHANGE"] then + + self.eventTable["CHAT_MSG_COMBAT_FACTION_CHANGE"] = { + "FACTION_STANDING_CHANGED", + "FACTION_STANDING_DECREASED", + "FACTION_STANDING_INCREASED", + } + table.sort(self.eventTable["CHAT_MSG_COMBAT_FACTION_CHANGE"] , PatternCompare) + end + list = self.eventTable["CHAT_MSG_COMBAT_FACTION_CHANGE"] + + elseif eventName == "CHAT_MSG_COMBAT_HONOR_GAIN" then + + if not self.eventTable["CHAT_MSG_COMBAT_HONOR_GAIN"] then + self.eventTable["CHAT_MSG_COMBAT_HONOR_GAIN"] = { + "COMBATLOG_HONORAWARD", + "COMBATLOG_HONORGAIN", + "COMBATLOG_DISHONORGAIN", + } + table.sort(self.eventTable["CHAT_MSG_COMBAT_HONOR_GAIN"] , PatternCompare) + end + list = self.eventTable["CHAT_MSG_COMBAT_HONOR_GAIN"] + elseif eventName == "CHAT_MSG_COMBAT_XP_GAIN" then + + if not self.eventTable["CHAT_MSG_COMBAT_XP_GAIN"] then + self.eventTable["CHAT_MSG_COMBAT_XP_GAIN"] = { + "COMBATLOG_XPGAIN", + "COMBATLOG_XPGAIN_EXHAUSTION1", + "COMBATLOG_XPGAIN_EXHAUSTION1_GROUP", + "COMBATLOG_XPGAIN_EXHAUSTION1_RAID", + "COMBATLOG_XPGAIN_EXHAUSTION2", + "COMBATLOG_XPGAIN_EXHAUSTION2_GROUP", + "COMBATLOG_XPGAIN_EXHAUSTION2_RAID", + "COMBATLOG_XPGAIN_EXHAUSTION4", + "COMBATLOG_XPGAIN_EXHAUSTION4_GROUP", + "COMBATLOG_XPGAIN_EXHAUSTION4_RAID", + "COMBATLOG_XPGAIN_EXHAUSTION5", + "COMBATLOG_XPGAIN_EXHAUSTION5_GROUP", + "COMBATLOG_XPGAIN_EXHAUSTION5_RAID", + "COMBATLOG_XPGAIN_FIRSTPERSON", + "COMBATLOG_XPGAIN_FIRSTPERSON_GROUP", + "COMBATLOG_XPGAIN_FIRSTPERSON_RAID", + "COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED", + "COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED_GROUP", + "COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED_RAID", + "COMBATLOG_XPLOSS_FIRSTPERSON_UNNAMED", + } + table.sort(self.eventTable["CHAT_MSG_COMBAT_XP_GAIN"] , PatternCompare) + end + list = self.eventTable["CHAT_MSG_COMBAT_XP_GAIN"] + + elseif eventName == "CHAT_MSG_COMBAT_FRIENDLY_DEATH" + or eventName == "CHAT_MSG_COMBAT_HOSTILE_DEATH" then + + if not self.eventTable["CHAT_MSG_COMBAT_HOSTILE_DEATH"] then + self.eventTable["CHAT_MSG_COMBAT_HOSTILE_DEATH"] = { + "SELFKILLOTHER", + "PARTYKILLOTHER", + "UNITDESTROYEDOTHER", + "UNITDIESOTHER", + "UNITDIESSELF", + } + + table.sort(self.eventTable["CHAT_MSG_COMBAT_HOSTILE_DEATH"] , PatternCompare) + end + list = self.eventTable["CHAT_MSG_COMBAT_HOSTILE_DEATH"] + + end + + + if not list then + -- self:Print(string.format("Event '%s' not found.", eventName), 1, 0,0); -- debug + end + + return list + +end + +function lib:LoadPatternCategory(category) + + local list + + if category == "AuraChange" then list = { + "AURACHANGEDOTHER", + "AURACHANGEDSELF", + } + elseif category == "AuraDispel" then list = { + "AURADISPELOTHER", + "AURADISPELSELF", + } + elseif category == "BuffOther" then list = { + "AURAADDEDOTHERHELPFUL", + "AURAAPPLICATIONADDEDOTHERHELPFUL", + } + elseif category == "BuffSelf" then list = { + "AURAADDEDSELFHELPFUL", + "AURAAPPLICATIONADDEDSELFHELPFUL", + } + elseif category == "CastOther" then list = { + "SIMPLECASTOTHEROTHER", + "SIMPLECASTOTHERSELF", + "SPELLTERSE_OTHER", + } + elseif category == "CastSelf" then list = { + "SIMPLECASTSELFOTHER", + "SIMPLECASTSELFSELF", + "SPELLTERSE_SELF", + } + elseif category == "DebuffOther" then list = { + "AURAADDEDOTHERHARMFUL", + "AURAAPPLICATIONADDEDOTHERHARMFUL", + } + elseif category == "DebuffSelf" then list = { + "AURAADDEDSELFHARMFUL", + "AURAAPPLICATIONADDEDSELFHARMFUL", + } + elseif category == "DispelFailOther" then list = { + "DISPELFAILEDOTHEROTHER", + "DISPELFAILEDOTHERSELF", + + } + elseif category == "DispelFailSelf" then list = { + "DISPELFAILEDSELFOTHER", + "DISPELFAILEDSELFSELF", + } + elseif category == "DmgShieldOther" then list = { + "DAMAGESHIELDOTHEROTHER", + "DAMAGESHIELDOTHERSELF", + } + elseif category == "DmgShieldSelf" then list = { + "DAMAGESHIELDSELFOTHER", + } + elseif category == "DurabilityDamageSelf" then list = { + "SPELLDURABILITYDAMAGEALLSELFOTHER", + "SPELLDURABILITYDAMAGESELFOTHER", + } + elseif category == "DurabilityDamageOther" then list = { + "SPELLDURABILITYDAMAGEALLOTHEROTHER", + "SPELLDURABILITYDAMAGEALLOTHERSELF", + "SPELLDURABILITYDAMAGEOTHEROTHER", + "SPELLDURABILITYDAMAGEOTHERSELF", + } + elseif category == "EnchantOther" then list = { + "ITEMENCHANTMENTADDOTHEROTHER", + "ITEMENCHANTMENTADDOTHERSELF", + } + elseif category == "EnchantSelf" then list = { + "ITEMENCHANTMENTADDSELFOTHER", + "ITEMENCHANTMENTADDSELFSELF", + } + elseif category == "ExtraAttackOther" then list = { + "SPELLEXTRAATTACKSOTHER", + "SPELLEXTRAATTACKSOTHER_SINGULAR", + } + elseif category == "ExtraAttackSelf" then list = { + "SPELLEXTRAATTACKSSELF", + "SPELLEXTRAATTACKSSELF_SINGULAR", + } + elseif category == "Fade" then list = { + "AURAREMOVEDOTHER", + "AURAREMOVEDSELF", + } + elseif category == "HealOther" then list = { + "HEALEDCRITOTHEROTHER", + "HEALEDCRITOTHERSELF", + "HEALEDOTHEROTHER", + "HEALEDOTHERSELF", + } + elseif category == "HealSelf" then list = { + "HEALEDCRITSELFOTHER", + "HEALEDCRITSELFSELF", + "HEALEDSELFOTHER", + "HEALEDSELFSELF", + } + elseif category == "HitOtherOther" then list = { + "COMBATHITCRITOTHEROTHER", + "COMBATHITCRITSCHOOLOTHEROTHER", + "COMBATHITOTHEROTHER", + "COMBATHITSCHOOLOTHEROTHER", + } + elseif category == "HitOtherSelf" then list = { + + "COMBATHITCRITOTHERSELF", + "COMBATHITCRITSCHOOLOTHERSELF", + "COMBATHITOTHERSELF", + "COMBATHITSCHOOLOTHERSELF", + } + elseif category == "HitSelf" then list = { + "COMBATHITSCHOOLSELFOTHER", + "COMBATHITSELFOTHER", + "COMBATHITCRITSCHOOLSELFOTHER", + "COMBATHITCRITSELFOTHER", + } + elseif category == "MissOtherOther" then list = { + "MISSEDOTHEROTHER", + "VSABSORBOTHEROTHER", + "VSBLOCKOTHEROTHER", + "VSDEFLECTOTHEROTHER", + "VSDODGEOTHEROTHER", + "VSEVADEOTHEROTHER", + "VSIMMUNEOTHEROTHER", + "VSPARRYOTHEROTHER", + "VSRESISTOTHEROTHER", + "IMMUNEDAMAGECLASSOTHEROTHER", + "IMMUNEOTHEROTHER", + + } + elseif category == "MissOtherSelf" then list = { + "MISSEDOTHERSELF", + "VSABSORBOTHERSELF", + "VSBLOCKOTHERSELF", + "VSDEFLECTOTHERSELF", + "VSDODGEOTHERSELF", + "VSEVADEOTHERSELF", + "VSIMMUNEOTHERSELF", + "VSPARRYOTHERSELF", + "VSRESISTOTHERSELF", + "IMMUNEDAMAGECLASSOTHERSELF", + "IMMUNEOTHERSELF", + } + elseif category == "MissSelf" then list = { + "MISSEDSELFOTHER", + "VSABSORBSELFOTHER", + "VSBLOCKSELFOTHER", + "VSDEFLECTSELFOTHER", + "VSDODGESELFOTHER", + "VSEVADESELFOTHER", + "VSIMMUNESELFOTHER", + "VSPARRYSELFOTHER", + "VSRESISTSELFOTHER", + "IMMUNEDAMAGECLASSSELFOTHER", + "IMMUNESELFOTHER", + "IMMUNESELFSELF", + } + elseif category == "PowerGainOther" then list = { + "POWERGAINOTHEROTHER", + "POWERGAINOTHERSELF", + } + elseif category == "PerformOther" then list = { + "OPEN_LOCK_OTHER", + "SIMPLEPERFORMOTHEROTHER", + "SIMPLEPERFORMOTHERSELF", + "SPELLTERSEPERFORM_OTHER", + } + elseif category == "PerformSelf" then list = { + "OPEN_LOCK_SELF", + "SIMPLEPERFORMSELFOTHER", + "SIMPLEPERFORMSELFSELF", + "SPELLTERSEPERFORM_SELF", + } + elseif category == "ProcResistOther" then list = { + "PROCRESISTOTHEROTHER", + "PROCRESISTOTHERSELF", + } + elseif category == "ProcResistSelf" then list = { + "PROCRESISTSELFOTHER", + "PROCRESISTSELFSELF", + } + elseif category == "EnvOther" then list = { + "VSENVIRONMENTALDAMAGE_DROWNING_OTHER", + "VSENVIRONMENTALDAMAGE_FALLING_OTHER", + "VSENVIRONMENTALDAMAGE_FATIGUE_OTHER", + "VSENVIRONMENTALDAMAGE_FIRE_OTHER", + "VSENVIRONMENTALDAMAGE_LAVA_OTHER", + "VSENVIRONMENTALDAMAGE_SLIME_OTHER", + } + elseif category == "EnvSelf" then list = { + "VSENVIRONMENTALDAMAGE_DROWNING_SELF", + "VSENVIRONMENTALDAMAGE_FALLING_SELF", + "VSENVIRONMENTALDAMAGE_FATIGUE_SELF", + "VSENVIRONMENTALDAMAGE_FIRE_SELF", + "VSENVIRONMENTALDAMAGE_LAVA_SELF", + "VSENVIRONMENTALDAMAGE_SLIME_SELF", + } + -- HoT effects on others. (not casted by others) + elseif category == "HotOther" then list = { + "PERIODICAURAHEALOTHEROTHER", + "PERIODICAURAHEALSELFOTHER", + } + -- HoT effects on you. (not casted by you) + elseif category == "HotSelf" then list = { + "PERIODICAURAHEALSELFSELF", + "PERIODICAURAHEALOTHERSELF", + } + elseif category == "PowerGainSelf" then list = { + "POWERGAINSELFSELF", + "POWERGAINSELFOTHER", + } + elseif category == "BuffOther" then list = { + "AURAAPPLICATIONADDEDOTHERHELPFUL", + "AURAADDEDOTHERHELPFUL", + } + elseif category == "BuffSelf" then list = { + "AURAADDEDSELFHELPFUL", + "AURAAPPLICATIONADDEDSELFHELPFUL", + } + elseif category == "DrainSelf" then list = { + "SPELLPOWERLEECHSELFOTHER", + "SPELLPOWERDRAINSELFOTHER", + "SPELLPOWERDRAINSELFSELF", + } + elseif category == "DrainOther" then list = { + "SPELLPOWERLEECHOTHEROTHER", + "SPELLPOWERLEECHOTHERSELF", + "SPELLPOWERDRAINOTHEROTHER", + "SPELLPOWERDRAINOTHERSELF", + } + -- DoT effects on others (not casted by others) + elseif category == "DotOther" then list = { + "PERIODICAURADAMAGEOTHEROTHER", + "PERIODICAURADAMAGESELFOTHER", + } + -- DoT effects on you (not casted by you) + elseif category == "DotSelf" then list = { + "PERIODICAURADAMAGEOTHERSELF", + "PERIODICAURADAMAGESELFSELF", + } + elseif category == "SpellHitOther" then list = { + "SPELLLOGCRITOTHEROTHER", + "SPELLLOGCRITOTHERSELF", + "SPELLLOGCRITSCHOOLOTHEROTHER", + "SPELLLOGCRITSCHOOLOTHERSELF", + "SPELLLOGOTHEROTHER", + "SPELLLOGOTHERSELF", + "SPELLLOGSCHOOLOTHEROTHER", + "SPELLLOGSCHOOLOTHERSELF", + } + elseif category == "SpellHitSelf" then list = { + "SPELLLOGCRITSELFOTHER", + "SPELLLOGCRITSELFSELF", + "SPELLLOGCRITSCHOOLSELFOTHER", + "SPELLLOGCRITSCHOOLSELFSELF", + "SPELLLOGSELFOTHER", + "SPELLLOGSELFSELF", + "SPELLLOGSCHOOLSELFOTHER", + "SPELLLOGSCHOOLSELFSELF", + } + elseif category == "SpellMissSelf" then list = { + "IMMUNESPELLSELFOTHER", + "IMMUNESPELLSELFSELF", + "SPELLBLOCKEDSELFOTHER", + "SPELLDEFLECTEDSELFOTHER", + "SPELLDEFLECTEDSELFSELF", + "SPELLDODGEDSELFOTHER", + "SPELLDODGEDSELFSELF", + "SPELLEVADEDSELFOTHER", + "SPELLEVADEDSELFSELF", + "SPELLIMMUNESELFOTHER", + "SPELLIMMUNESELFSELF", + "SPELLLOGABSORBSELFOTHER", + "SPELLLOGABSORBSELFSELF", + "SPELLMISSSELFOTHER", + "SPELLMISSSELFSELF", + "SPELLPARRIEDSELFOTHER", + "SPELLPARRIEDSELFSELF", + "SPELLREFLECTSELFOTHER", + "SPELLREFLECTSELFSELF", + "SPELLRESISTSELFOTHER", + "SPELLRESISTSELFSELF", + } + elseif category == "SpellMissOther" then list = { + "IMMUNESPELLOTHEROTHER", + "IMMUNESPELLOTHERSELF", + "SPELLBLOCKEDOTHEROTHER", + "SPELLBLOCKEDOTHERSELF", + "SPELLDODGEDOTHEROTHER", + "SPELLDODGEDOTHERSELF", + "SPELLDEFLECTEDOTHEROTHER", + "SPELLDEFLECTEDOTHERSELF", + "SPELLEVADEDOTHEROTHER", + "SPELLEVADEDOTHERSELF", + "SPELLIMMUNEOTHEROTHER", + "SPELLIMMUNEOTHERSELF", + "SPELLLOGABSORBOTHEROTHER", + "SPELLLOGABSORBOTHERSELF", + "SPELLMISSOTHEROTHER", + "SPELLMISSOTHERSELF", + "SPELLPARRIEDOTHEROTHER", + "SPELLPARRIEDOTHERSELF", + "SPELLREFLECTOTHEROTHER", + "SPELLREFLECTOTHERSELF", + "SPELLRESISTOTHEROTHER", + "SPELLRESISTOTHERSELF", + } + elseif category == "InterruptOther" then list = { + "SPELLINTERRUPTOTHEROTHER", + "SPELLINTERRUPTOTHERSELF", + } + elseif category == "InterruptSelf" then list = { + "SPELLINTERRUPTSELFOTHER", + } + elseif category == "SplitDamageOther" then list = { + "SPELLSPLITDAMAGEOTHEROTHER", + "SPELLSPLITDAMAGEOTHERSELF", + } + else return { category } + end + + return list + +end + +-- Load categories recursively. First layer will not be sorted. +function lib:LoadPatternCategoryTree(catTree, reSort) + if type(catTree) ~= "table" then return end + + local resultList = {} + local list + + for i, v in pairs(catTree) do + + if type(v) == "table" then + list = self:LoadPatternCategoryTree(v, true) + else -- should be string. + list = self:LoadPatternCategory(v) + table.sort(list, PatternCompare) + end + + for j, w in pairs(list) do + table.insert(resultList, w) + end + + end + + if reSort then + table.sort(resultList, PatternCompare) + end + + return resultList + + +end + +-- Used to load patternTable elements on demand. +function lib:LoadPatternInfo(patternName) + + local patternInfo + + if patternName == "AURAADDEDOTHERHELPFUL" then + patternInfo = { "buff", 1, 2, nil, } + elseif patternName == "AURAADDEDSELFHELPFUL" then + patternInfo = { "buff", ParserLib_SELF, 1, nil, } + elseif patternName == "AURAAPPLICATIONADDEDOTHERHELPFUL" then + patternInfo = { "buff", 1, 2, 3, } + elseif patternName == "AURAAPPLICATIONADDEDSELFHELPFUL" then + patternInfo = { "buff", ParserLib_SELF, 1, 2, } + + elseif patternName == "OPEN_LOCK_OTHER" then + patternInfo = { "cast", 1, 2, 3, nil, true, } + elseif patternName == "OPEN_LOCK_SELF" then + patternInfo = { "cast", ParserLib_SELF, 1, 2, nil, true, } + elseif patternName == "SIMPLECASTOTHEROTHER" then + patternInfo = { "cast", 1, 2, 3, nil, nil, } + elseif patternName == "SIMPLECASTOTHERSELF" then + patternInfo = { "cast", 1, 2, ParserLib_SELF, nil, nil, } + elseif patternName == "SIMPLECASTSELFOTHER" then + patternInfo = { "cast", ParserLib_SELF, 1, 2, nil, nil, } + elseif patternName == "SIMPLECASTSELFSELF" then + patternInfo = { "cast", ParserLib_SELF, 1, ParserLib_SELF, nil, nil, } + elseif patternName == "SIMPLEPERFORMOTHEROTHER" then + patternInfo = { "cast", 1, 2, 3, nil, true, } + elseif patternName == "SIMPLEPERFORMOTHERSELF" then + patternInfo = { "cast", 1, 2, ParserLib_SELF, nil, true, } + elseif patternName == "SIMPLEPERFORMSELFOTHER" then + patternInfo = { "cast", ParserLib_SELF, 1, 2, nil, true, } + elseif patternName == "SIMPLEPERFORMSELFSELF" then + patternInfo = { "cast", ParserLib_SELF, 1, ParserLib_SELF, nil, true, } + elseif patternName == "SPELLCASTOTHERSTART" then + patternInfo = { "cast", 1, 2, nil, true, nil, } + elseif patternName == "SPELLCASTSELFSTART" then + patternInfo = { "cast", ParserLib_SELF, 1, nil, true, nil, } + elseif patternName == "SPELLPERFORMOTHERSTART" then + patternInfo = { "cast", 1, 2, nil, true, true, } + elseif patternName == "SPELLPERFORMSELFSTART" then + patternInfo = { "cast", ParserLib_SELF, 1, nil, true, true, } + elseif patternName == "SPELLTERSEPERFORM_OTHER" then + patternInfo = { "cast", 1, 2, nil, nil, true, } + elseif patternName == "SPELLTERSEPERFORM_SELF" then + patternInfo = { "cast", ParserLib_SELF, 1, nil, nil, true, } + elseif patternName == "SPELLTERSE_OTHER" then + patternInfo = { "cast", 1, 2, nil, nil, nil, } + elseif patternName == "SPELLTERSE_SELF" then + patternInfo = { "cast", ParserLib_SELF, 1, nil, nil, nil, } + + elseif patternName == "TRADESKILL_LOG_FIRSTPERSON" then + patternInfo = { "create", ParserLib_SELF, 1, } + elseif patternName == "TRADESKILL_LOG_THIRDPERSON" then + patternInfo = { "create", 1, 2, } + + elseif patternName == "PARTYKILLOTHER" then + patternInfo = { "death", 1, 2, nil, nil, } + elseif patternName == "SELFKILLOTHER" then + patternInfo = { "death", 1, ParserLib_SELF, nil, nil, } + elseif patternName == "UNITDESTROYEDOTHER" then + patternInfo = { "death", 1, nil, nil, true, } + elseif patternName == "UNITDIESOTHER" then + patternInfo = { "death", 1, nil, nil, nil, } + elseif patternName == "UNITDIESSELF" then + patternInfo = { "death", ParserLib_SELF, nil, nil, nil, } + elseif patternName == "INSTAKILLOTHER" then + patternInfo = { "death", 1, nil, 2, nil, } + elseif patternName == "INSTAKILLSELF" then + patternInfo = { "death", ParserLib_SELF, nil, true, nil } + + + elseif patternName == "AURAADDEDOTHERHARMFUL" then + patternInfo = { "debuff", 1, 2, nil, } + elseif patternName == "AURAADDEDSELFHARMFUL" then + patternInfo = { "debuff", ParserLib_SELF, 1, nil, } + elseif patternName == "AURAAPPLICATIONADDEDOTHERHARMFUL" then + patternInfo = { "debuff", 1, 2, 3, } + elseif patternName == "AURAAPPLICATIONADDEDSELFHARMFUL" then + patternInfo = { "debuff", ParserLib_SELF, 1, 2, } + + elseif patternName == "AURADISPELOTHER" then + patternInfo = { "dispel", 1, 2, nil, nil, } + elseif patternName == "AURADISPELSELF" then + patternInfo = { "dispel", ParserLib_SELF, 1, nil, nil, } + elseif patternName == "DISPELFAILEDOTHEROTHER" then + patternInfo = { "dispel", 2, 3, 1, true, } + elseif patternName == "DISPELFAILEDOTHERSELF" then + patternInfo = { "dispel", ParserLib_SELF, 2, 1, true, } + elseif patternName == "DISPELFAILEDSELFOTHER" then + patternInfo = { "dispel", 1, 2, ParserLib_SELF, true, } + elseif patternName == "DISPELFAILEDSELFSELF" then + patternInfo = { "dispel", ParserLib_SELF, 1, ParserLib_SELF, true, } + + elseif patternName == "SPELLPOWERDRAINOTHEROTHER" then + patternInfo = { "drain", 1, 5, 2, 3, 4, } + elseif patternName == "SPELLPOWERDRAINOTHERSELF" then + patternInfo = { "drain", 1, ParserLib_SELF, 2, 3, 4, } + elseif patternName == "SPELLPOWERDRAINSELFOTHER" then + patternInfo = { "drain", ParserLib_SELF, 4, 1, 2, 3, } + elseif patternName == "SPELLPOWERDRAINSELFSELF" then + patternInfo = { "drain", ParserLib_SELF, ParserLib_SELF, 1, 2, 3, } + + elseif patternName == "SPELLDURABILITYDAMAGEALLOTHEROTHER" then + patternInfo = { "durability", 1, 2, 3, nil, } + elseif patternName == "SPELLDURABILITYDAMAGEALLOTHERSELF" then + patternInfo = { "durability", 1, 2, ParserLib_SELF, nil, } + elseif patternName == "SPELLDURABILITYDAMAGEALLSELFOTHER" then + patternInfo = { "durability", ParserLib_SELF, 1, 2, nil, } + elseif patternName == "SPELLDURABILITYDAMAGEOTHEROTHER" then + patternInfo = { "durability", 1, 2, 3, 4, } + elseif patternName == "SPELLDURABILITYDAMAGEOTHERSELF" then + patternInfo = { "durability", 1, 2, ParserLib_SELF, 3, } + elseif patternName == "SPELLDURABILITYDAMAGESELFOTHER" then + patternInfo = { "durability", ParserLib_SELF, 1, 2, 3, } + + elseif patternName == "ITEMENCHANTMENTADDOTHEROTHER" then + patternInfo = { "enchant", 1, 3, 2, 4, } + elseif patternName == "ITEMENCHANTMENTADDOTHERSELF" then + patternInfo = { "enchant", 1, ParserLib_SELF, 2, 3, } + elseif patternName == "ITEMENCHANTMENTADDSELFOTHER" then + patternInfo = { "enchant", ParserLib_SELF, 2, 1, 3, } + elseif patternName == "ITEMENCHANTMENTADDSELFSELF" then + patternInfo = { "enchant", ParserLib_SELF, ParserLib_SELF, 1, 2, } + + elseif patternName == "VSENVIRONMENTALDAMAGE_DROWNING_OTHER" then + patternInfo = { "environment", 1, 2, "drown", } + elseif patternName == "VSENVIRONMENTALDAMAGE_DROWNING_SELF" then + patternInfo = { "environment", ParserLib_SELF, 1, "drown", } + elseif patternName == "VSENVIRONMENTALDAMAGE_FALLING_OTHER" then + patternInfo = { "environment", 1, 2, "fall", } + elseif patternName == "VSENVIRONMENTALDAMAGE_FALLING_SELF" then + patternInfo = { "environment", ParserLib_SELF, 1, "fall", } + elseif patternName == "VSENVIRONMENTALDAMAGE_FATIGUE_OTHER" then + patternInfo = { "environment", 1, 2, "exhaust", } + elseif patternName == "VSENVIRONMENTALDAMAGE_FATIGUE_SELF" then + patternInfo = { "environment", ParserLib_SELF, 1, "exhaust", } + elseif patternName == "VSENVIRONMENTALDAMAGE_FIRE_OTHER" then + patternInfo = { "environment", 1, 2, "fire", } + elseif patternName == "VSENVIRONMENTALDAMAGE_FIRE_SELF" then + patternInfo = { "environment", ParserLib_SELF, 1, "fire", } + elseif patternName == "VSENVIRONMENTALDAMAGE_LAVA_OTHER" then + patternInfo = { "environment", 1, 2, "lava", } + elseif patternName == "VSENVIRONMENTALDAMAGE_LAVA_SELF" then + patternInfo = { "environment", ParserLib_SELF, 1, "lava", } + elseif patternName == "VSENVIRONMENTALDAMAGE_SLIME_OTHER" then + patternInfo = { "environment", 1, 2, "slime", } + elseif patternName == "VSENVIRONMENTALDAMAGE_SLIME_SELF" then + patternInfo = { "environment", ParserLib_SELF, 1, "slime", } + + elseif patternName == "COMBATLOG_XPGAIN" then + patternInfo = { "experience", 2, nil, nil, nil, nil, nil, nil, nil, 1, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION1" then + patternInfo = { "experience", 2, 1, 3, 4, nil, nil, nil, nil, nil, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION1_GROUP" then + patternInfo = { "experience", 2, 1, 3, 4, nil, nil, nil, 5, nil, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION1_RAID" then + patternInfo = { "experience", 2, 1, 3, 4, nil, nil, 5, nil, nil, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION2" then + patternInfo = { "experience", 2, 1, 3, 4, nil, nil, nil, nil, nil, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION2_GROUP" then + patternInfo = { "experience", 2, 1, 3, 4, nil, nil, nil, 5, nil, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION2_RAID" then + patternInfo = { "experience", 2, 1, 3, 4, nil, nil, 5, nil, nil, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION4" then + patternInfo = { "experience", 2, 1, nil, nil, 3, 4, nil, nil, nil, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION4_GROUP" then + patternInfo = { "experience", 2, 1, nil, nil, 3, 4, nil, 5, nil, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION4_RAID" then + patternInfo = { "experience", 2, 1, nil, nil, 3, 4, 5, nil, nil, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION5" then + patternInfo = { "experience", 2, 1, nil, nil, 3, 4, nil, nil, nil, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION5_GROUP" then + patternInfo = { "experience", 2, 1, nil, nil, 3, 4, nil, 5, nil, } + elseif patternName == "COMBATLOG_XPGAIN_EXHAUSTION5_RAID" then + patternInfo = { "experience", 2, 1, nil, nil, 3, 4, 5, nil, nil, } + elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON" then + patternInfo = { "experience", 2, 1, nil, nil, nil, nil, nil, nil, nil, } + elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON_GROUP" then + patternInfo = { "experience", 2, 1, nil, nil, nil, nil, nil, 3, nil, } + elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON_RAID" then + patternInfo = { "experience", 2, 1, nil, nil, nil, nil, 3, nil, nil, } + elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED" then + patternInfo = { "experience", 1, nil, nil, nil, nil, nil, nil, nil, nil, } + elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED_GROUP" then + patternInfo = { "experience", 1, nil, nil, nil, nil, nil, nil, 2, nil, } + elseif patternName == "COMBATLOG_XPGAIN_FIRSTPERSON_UNNAMED_RAID" then + patternInfo = { "experience", 1, nil, nil, nil, nil, nil, 2, nil, nil, } + elseif patternName == "COMBATLOG_XPLOSS_FIRSTPERSON_UNNAMED" then + patternInfo = { "experience", 1, nil, nil, nil, nil, nil, nil, nil, nil, } + + elseif patternName == "SPELLEXTRAATTACKSOTHER" then + patternInfo = { "extraattack", 1, 3, 2, } + elseif patternName == "SPELLEXTRAATTACKSOTHER_SINGULAR" then + patternInfo = { "extraattack", 1, 3, 2, } + elseif patternName == "SPELLEXTRAATTACKSSELF" then + patternInfo = { "extraattack", ParserLib_SELF, 2, 1, } + elseif patternName == "SPELLEXTRAATTACKSSELF_SINGULAR" then + patternInfo = { "extraattack", ParserLib_SELF, 2, 1, } + + elseif patternName == "AURAREMOVEDOTHER" then + patternInfo = { "fade", 2, 1, } + elseif patternName == "AURAREMOVEDSELF" then + patternInfo = { "fade", ParserLib_SELF, 1, } + + elseif patternName == "SPELLFAILCASTSELF" then + patternInfo = { "fail", ParserLib_SELF, 1, 2, } + elseif patternName == "SPELLFAILPERFORMSELF" then + patternInfo = { "fail", ParserLib_SELF, 1, 2, } + + elseif patternName == "FEEDPET_LOG_FIRSTPERSON" then + patternInfo = { "feedpet", ParserLib_SELF, 1, } + elseif patternName == "FEEDPET_LOG_THIRDPERSON" then + patternInfo = { "feedpet", 1, 2, } + + elseif patternName == "POWERGAINOTHEROTHER" then + patternInfo = { "gain", 4, 1, 5, 2, 3, } + elseif patternName == "POWERGAINOTHERSELF" then + patternInfo = { "gain", 3, ParserLib_SELF, 4, 1, 2, } + elseif patternName == "POWERGAINSELFOTHER" then + patternInfo = { "gain", ParserLib_SELF, 1, 4, 2, 3, } + elseif patternName == "POWERGAINSELFSELF" then + patternInfo = { "gain", ParserLib_SELF, ParserLib_SELF, 3, 1, 2, } + + elseif patternName == "HEALEDCRITOTHEROTHER" then + patternInfo = { "heal", 1, 3, 2, 4, true, nil, } + elseif patternName == "HEALEDCRITOTHERSELF" then + patternInfo = { "heal", 1, ParserLib_SELF, 2, 3, true, nil, } + elseif patternName == "HEALEDCRITSELFOTHER" then + patternInfo = { "heal", ParserLib_SELF, 2, 1, 3, true, nil, } + elseif patternName == "HEALEDCRITSELFSELF" then + patternInfo = { "heal", ParserLib_SELF, ParserLib_SELF, 1, 2, true, nil, } + elseif patternName == "HEALEDOTHEROTHER" then + patternInfo = { "heal", 1, 3, 2, 4, nil, nil, } + elseif patternName == "HEALEDOTHERSELF" then + patternInfo = { "heal", 1, ParserLib_SELF, 2, 3, nil, nil, } + elseif patternName == "HEALEDSELFOTHER" then + patternInfo = { "heal", ParserLib_SELF, 2, 1, 3, nil, nil, } + elseif patternName == "HEALEDSELFSELF" then + patternInfo = { "heal", ParserLib_SELF, ParserLib_SELF, 1, 2, nil, nil, } + elseif patternName == "PERIODICAURAHEALOTHEROTHER" then + patternInfo = { "heal", 3, 1, 4, 2, nil, true, } + elseif patternName == "PERIODICAURAHEALOTHERSELF" then + patternInfo = { "heal", 2, ParserLib_SELF, 3, 1, nil, true, } + elseif patternName == "PERIODICAURAHEALSELFOTHER" then + patternInfo = { "heal", ParserLib_SELF, 1, 3, 2, nil, true, } + elseif patternName == "PERIODICAURAHEALSELFSELF" then + patternInfo = { "heal", ParserLib_SELF, ParserLib_SELF, 2, 1, nil, true, } + + elseif patternName == "COMBATHITCRITOTHEROTHER" then + patternInfo = { "hit", 1, 2, ParserLib_MELEE, 3, nil, true, nil, nil, } + elseif patternName == "COMBATHITCRITOTHERSELF" then + patternInfo = { "hit", 1, ParserLib_SELF, ParserLib_MELEE, 2, nil, true, nil, nil, } + elseif patternName == "COMBATHITCRITSCHOOLOTHEROTHER" then + patternInfo = { "hit", 1, 2, ParserLib_MELEE, 3, 4, true, nil, nil, } + elseif patternName == "COMBATHITCRITSCHOOLOTHERSELF" then + patternInfo = { "hit", 1, ParserLib_SELF, ParserLib_MELEE, 2, 3, true, nil, nil, } + elseif patternName == "COMBATHITCRITSCHOOLSELFOTHER" then + patternInfo = { "hit", ParserLib_SELF, 1, ParserLib_MELEE, 2, 3, true, nil, nil, } + elseif patternName == "COMBATHITCRITSELFOTHER" then + patternInfo = { "hit", ParserLib_SELF, 1, ParserLib_MELEE, 2, nil, true, nil, nil, } + elseif patternName == "COMBATHITOTHEROTHER" then + patternInfo = { "hit", 1, 2, ParserLib_MELEE, 3, nil, nil, nil, nil, } + elseif patternName == "COMBATHITOTHERSELF" then + patternInfo = { "hit", 1, ParserLib_SELF, ParserLib_MELEE, 2, nil, nil, nil, nil, } + elseif patternName == "COMBATHITSCHOOLOTHEROTHER" then + patternInfo = { "hit", 1, 2, ParserLib_MELEE, 3, 4, nil, nil, nil, } + elseif patternName == "COMBATHITSCHOOLOTHERSELF" then + patternInfo = { "hit", 1, ParserLib_SELF, ParserLib_MELEE, 2, 3, nil, nil, nil, } + elseif patternName == "COMBATHITSCHOOLSELFOTHER" then + patternInfo = { "hit", ParserLib_SELF, 1, ParserLib_MELEE, 2, 3, nil, nil, nil, } + elseif patternName == "COMBATHITSELFOTHER" then + patternInfo = { "hit", ParserLib_SELF, 1, ParserLib_MELEE, 2, nil, nil, nil, nil, } + elseif patternName == "DAMAGESHIELDOTHEROTHER" then + patternInfo = { "hit", 1, 4, ParserLib_DAMAGESHIELD, 2, 3, nil, nil, nil, } + elseif patternName == "DAMAGESHIELDOTHERSELF" then + patternInfo = { "hit", 1, ParserLib_SELF, ParserLib_DAMAGESHIELD, 2, 3, nil, nil, nil, } + elseif patternName == "DAMAGESHIELDSELFOTHER" then + patternInfo = { "hit", ParserLib_SELF, 3, ParserLib_DAMAGESHIELD, 1, 2, nil, nil, nil, } + elseif patternName == "PERIODICAURADAMAGEOTHEROTHER" then + patternInfo = { "hit", 4, 1, 5, 2, 3, nil, true, nil, } + elseif patternName == "PERIODICAURADAMAGEOTHERSELF" then + patternInfo = { "hit", 3, ParserLib_SELF, 4, 1, 2, nil, true, nil, } + elseif patternName == "PERIODICAURADAMAGESELFOTHER" then + patternInfo = { "hit", ParserLib_SELF, 1, 4, 2, 3, nil, true, nil, } + elseif patternName == "PERIODICAURADAMAGESELFSELF" then + patternInfo = { "hit", ParserLib_SELF, ParserLib_SELF, 3, 1, 2, nil, true, nil, } + elseif patternName == "SPELLLOGCRITOTHEROTHER" then + patternInfo = { "hit", 1, 3, 2, 4, nil, true, nil, nil, } + elseif patternName == "SPELLLOGCRITOTHERSELF" then + patternInfo = { "hit", 1, ParserLib_SELF, 2, 3, nil, true, nil, nil, } + elseif patternName == "SPELLLOGCRITSCHOOLOTHEROTHER" then + patternInfo = { "hit", 1, 3, 2, 4, 5, true, nil, nil, } + elseif patternName == "SPELLLOGCRITSCHOOLOTHERSELF" then + patternInfo = { "hit", 1, ParserLib_SELF, 2, 3, 4, true, nil, nil, } + elseif patternName == "SPELLLOGCRITSCHOOLSELFOTHER" then + patternInfo = { "hit", ParserLib_SELF, 2, 1, 3, 4, true, nil, nil, } + elseif patternName == "SPELLLOGCRITSCHOOLSELFSELF" then + patternInfo = { "hit", ParserLib_SELF, ParserLib_SELF, 1, 2, 3, true, nil, nil, } + elseif patternName == "SPELLLOGCRITSELFOTHER" then + patternInfo = { "hit", ParserLib_SELF, 2, 1, 3, nil, true, nil, nil, } + elseif patternName == "SPELLLOGCRITSELFSELF" then + patternInfo = { "hit", ParserLib_SELF, ParserLib_SELF, 1, 2, nil, true, nil, nil, } + elseif patternName == "SPELLLOGOTHEROTHER" then + patternInfo = { "hit", 1, 3, 2, 4, nil, nil, nil, nil, } + elseif patternName == "SPELLLOGOTHERSELF" then + patternInfo = { "hit", 1, ParserLib_SELF, 2, 3, nil, nil, nil, nil, } + elseif patternName == "SPELLLOGSCHOOLOTHEROTHER" then + patternInfo = { "hit", 1, 3, 2, 4, 5, nil, nil, nil, } + elseif patternName == "SPELLLOGSCHOOLOTHERSELF" then + patternInfo = { "hit", 1, ParserLib_SELF, 2, 3, 4, nil, nil, nil, } + elseif patternName == "SPELLLOGSCHOOLSELFOTHER" then + patternInfo = { "hit", ParserLib_SELF, 2, 1, 3, 4, nil, nil, nil, } + elseif patternName == "SPELLLOGSCHOOLSELFSELF" then + patternInfo = { "hit", ParserLib_SELF, ParserLib_SELF, 1, 2, 3, nil, nil, nil, } + elseif patternName == "SPELLLOGSELFOTHER" then + patternInfo = { "hit", ParserLib_SELF, 2, 1, 3, nil, nil, nil, nil, } + elseif patternName == "SPELLLOGSELFSELF" then + patternInfo = { "hit", ParserLib_SELF, ParserLib_SELF, 1, 2, nil, nil, nil, nil, } + elseif patternName == "SPELLSPLITDAMAGEOTHEROTHER" then + patternInfo = { "hit", 1, 3, 2, 4, nil, nil, nil, true, } + elseif patternName == "SPELLSPLITDAMAGEOTHERSELF" then + patternInfo = { "hit", 1, ParserLib_SELF, 2, 3, nil, nil, nil, true, } + elseif patternName == "SPELLSPLITDAMAGESELFOTHER" then + patternInfo = { "hit", ParserLib_SELF, 2, 1, 3, nil, nil, nil, true, } + + elseif patternName == "COMBATLOG_DISHONORGAIN" then + patternInfo = { "honor", nil, 1, nil, } + elseif patternName == "COMBATLOG_HONORAWARD" then + patternInfo = { "honor", 1, nil, nil, } + elseif patternName == "COMBATLOG_HONORGAIN" then + patternInfo = { "honor", 3, 1, 2, } + + elseif patternName == "SPELLINTERRUPTOTHEROTHER" then + patternInfo = { "interrupt", 1, 2, 3, } + elseif patternName == "SPELLINTERRUPTOTHERSELF" then + patternInfo = { "interrupt", 1, ParserLib_SELF, 2, } + elseif patternName == "SPELLINTERRUPTSELFOTHER" then + patternInfo = { "interrupt", ParserLib_SELF, 1, 2, } + + elseif patternName == "SPELLPOWERLEECHOTHEROTHER" then + patternInfo = { "leech", 1, 5, 2, 3, 4, 6, 7, 8, } + elseif patternName == "SPELLPOWERLEECHOTHERSELF" then + patternInfo = { "leech", 1, ParserLib_SELF, 2, 3, 4, 5, 6, 7, } + elseif patternName == "SPELLPOWERLEECHSELFOTHER" then + patternInfo = { "leech", ParserLib_SELF, 4, 1, 2, 3, ParserLib_SELF, 5, 6, } + + elseif patternName == "IMMUNEDAMAGECLASSOTHEROTHER" then + patternInfo = { "miss", 2, 1, ParserLib_MELEE, "immune", } + elseif patternName == "IMMUNEDAMAGECLASSOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "immune", } + elseif patternName == "IMMUNEDAMAGECLASSSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "immune", } + elseif patternName == "IMMUNEOTHEROTHER" then + patternInfo = { "miss", 1, 2, ParserLib_MELEE, "immune", } + elseif patternName == "IMMUNEOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "immune", } + elseif patternName == "IMMUNESELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "immune", } + elseif patternName == "IMMUNESELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, ParserLib_MELEE, "immune", } + elseif patternName == "IMMUNESPELLOTHEROTHER" then + patternInfo = { "miss", 2, 1, 3, "immune", } + elseif patternName == "IMMUNESPELLOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "immune", } + elseif patternName == "IMMUNESPELLSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, 2, "immune", } + elseif patternName == "IMMUNESPELLSELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "immune", } + elseif patternName == "MISSEDOTHEROTHER" then + patternInfo = { "miss", 1, 2, ParserLib_MELEE, "miss", } + elseif patternName == "MISSEDOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "miss", } + elseif patternName == "MISSEDSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "miss", } + elseif patternName == "PROCRESISTOTHEROTHER" then + patternInfo = { "miss", 2, 1, 3, "resist", } + elseif patternName == "PROCRESISTOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "resist", } + elseif patternName == "PROCRESISTSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, 2, "resist", } + elseif patternName == "PROCRESISTSELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "resist", } + elseif patternName == "SPELLBLOCKEDOTHEROTHER" then + patternInfo = { "miss", 1, 3, 2, "block", } + elseif patternName == "SPELLBLOCKEDOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "block", } + elseif patternName == "SPELLBLOCKEDSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 2, 1, "block", } + elseif patternName == "SPELLDEFLECTEDOTHEROTHER" then + patternInfo = { "miss", 1, 3, 2, "deflect", } + elseif patternName == "SPELLDEFLECTEDOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "deflect", } + elseif patternName == "SPELLDEFLECTEDSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 2, 1, "deflect", } + elseif patternName == "SPELLDEFLECTEDSELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "deflect", } + elseif patternName == "SPELLDODGEDOTHEROTHER" then + patternInfo = { "miss", 1, 3, 2, "dodge", } + elseif patternName == "SPELLDODGEDOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "dodge", } + elseif patternName == "SPELLDODGEDSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 2, 1, "dodge", } + elseif patternName == "SPELLDODGEDSELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "dodge", } + elseif patternName == "SPELLEVADEDOTHEROTHER" then + patternInfo = { "miss", 1, 3, 2, "evade", } + elseif patternName == "SPELLEVADEDOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "evade", } + elseif patternName == "SPELLEVADEDSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 2, 1, "evade", } + elseif patternName == "SPELLEVADEDSELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "evade", } + elseif patternName == "SPELLIMMUNEOTHEROTHER" then + patternInfo = { "miss", 1, 3, 2, "immune", } + elseif patternName == "SPELLIMMUNEOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "immune", } + elseif patternName == "SPELLIMMUNESELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 2, 1, "immune", } + elseif patternName == "SPELLIMMUNESELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "immune", } + elseif patternName == "SPELLLOGABSORBOTHEROTHER" then + patternInfo = { "miss", 1, 3, 2, "absorb", } + elseif patternName == "SPELLLOGABSORBOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "absorb", } + elseif patternName == "SPELLLOGABSORBSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 2, 1, "absorb", } + elseif patternName == "SPELLLOGABSORBSELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "absorb", } + elseif patternName == "SPELLMISSOTHEROTHER" then + patternInfo = { "miss", 1, 3, 2, "miss", } + elseif patternName == "SPELLMISSOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "miss", } + elseif patternName == "SPELLMISSSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 2, 1, "miss", } + elseif patternName == "SPELLMISSSELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "miss", } + elseif patternName == "SPELLPARRIEDOTHEROTHER" then + patternInfo = { "miss", 1, 3, 2, "parry", } + elseif patternName == "SPELLPARRIEDOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "parry", } + elseif patternName == "SPELLPARRIEDSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 2, 1, "parry", } + elseif patternName == "SPELLPARRIEDSELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "parry", } + elseif patternName == "SPELLREFLECTOTHEROTHER" then + patternInfo = { "miss", 1, 3, 2, "reflect", } + elseif patternName == "SPELLREFLECTOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "reflect", } + elseif patternName == "SPELLREFLECTSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 2, 1, "reflect", } + elseif patternName == "SPELLREFLECTSELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "reflect", } + elseif patternName == "SPELLRESISTOTHEROTHER" then + patternInfo = { "miss", 1, 3, 2, "resist", } + elseif patternName == "SPELLRESISTOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, 2, "resist", } + elseif patternName == "SPELLRESISTSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 2, 1, "resist", } + elseif patternName == "SPELLRESISTSELFSELF" then + patternInfo = { "miss", ParserLib_SELF, ParserLib_SELF, 1, "resist", } + elseif patternName == "VSABSORBOTHEROTHER" then + patternInfo = { "miss", 1, 2, ParserLib_MELEE, "absorb", } + elseif patternName == "VSABSORBOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "absorb", } + elseif patternName == "VSABSORBSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "absorb", } + elseif patternName == "VSBLOCKOTHEROTHER" then + patternInfo = { "miss", 1, 2, ParserLib_MELEE, "block", } + elseif patternName == "VSBLOCKOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "block", } + elseif patternName == "VSBLOCKSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "block", } + elseif patternName == "VSDEFLECTOTHEROTHER" then + patternInfo = { "miss", 1, 2, ParserLib_MELEE, "deflect", } + elseif patternName == "VSDEFLECTOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "deflect", } + elseif patternName == "VSDEFLECTSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "deflect", } + elseif patternName == "VSDODGEOTHEROTHER" then + patternInfo = { "miss", 1, 2, ParserLib_MELEE, "dodge", } + elseif patternName == "VSDODGEOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "dodge", } + elseif patternName == "VSDODGESELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "dodge", } + elseif patternName == "VSEVADEOTHEROTHER" then + patternInfo = { "miss", 1, 2, ParserLib_MELEE, "evade", } + elseif patternName == "VSEVADEOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "evade", } + elseif patternName == "VSEVADESELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "evade", } + elseif patternName == "VSIMMUNEOTHEROTHER" then + patternInfo = { "miss", 1, 2, ParserLib_MELEE, "immune", } + elseif patternName == "VSIMMUNEOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "immune", } + elseif patternName == "VSIMMUNESELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "immune", } + elseif patternName == "VSPARRYOTHEROTHER" then + patternInfo = { "miss", 1, 2, ParserLib_MELEE, "parry", } + elseif patternName == "VSPARRYOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "parry", } + elseif patternName == "VSPARRYSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "parry", } + elseif patternName == "VSRESISTOTHEROTHER" then + patternInfo = { "miss", 1, 2, ParserLib_MELEE, "resist", } + elseif patternName == "VSRESISTOTHERSELF" then + patternInfo = { "miss", 1, ParserLib_SELF, ParserLib_MELEE, "resist", } + elseif patternName == "VSRESISTSELFOTHER" then + patternInfo = { "miss", ParserLib_SELF, 1, ParserLib_MELEE, "resist", } + + elseif patternName == "FACTION_STANDING_CHANGED" then + patternInfo = { "reputation", 2, nil, 1, nil, } + elseif patternName == "FACTION_STANDING_DECREASED" then + patternInfo = { "reputation", 1, 2, nil, true, } + elseif patternName == "FACTION_STANDING_INCREASED" then + patternInfo = { "reputation", 1, 2, nil, nil, } + end + + if not patternInfo then + -- self:Print("LoadPatternInfo(): Cannot find " .. patternName ); -- debug + return + end + + local pattern = getglobal(patternName); -- Get the pattern from GlobalStrings.lua + + -- Add a function to fix some global strings. + if FixGlobalString then + local fixPattern = FixGlobalString(patternName, pattern); + if pattern ~= fixPattern then + --self:Print("PP : "..pattern) -- debug + --self:Print("FP : "..fixPattern) -- debug + setglobal(patternName, fixPattern); + pattern = fixPattern + end + end + + -- How many regexp tokens in this pattern? + local tc = 0 + for _ in string_gmatch(pattern, "%%%d?%$?([sd])") do tc = tc + 1 end + + -- Convert string.format tokens into LUA regexp tokens. + pattern = { self:ConvertPattern(pattern, true) } + + local n = table.getn(pattern) + if n > 1 then -- Extra return values are the remapped token sequences. + + for j in pairs(patternInfo) do + if type(patternInfo[j]) == "number" and patternInfo[j] < 100 then + patternInfo[j] = pattern[patternInfo[j]+1] -- Remap to correct token sequence. + end + end + + end + + patternInfo.tc = tc + patternInfo.pattern = pattern[1] + + return patternInfo + +end + +-- Fields of the patternTable. +lib.infoMap = { + hit = { "source", "victim", "skill", "amount", "element", "isCrit", "isDOT", "isSplit" }, + heal = { "source", "victim", "skill", "amount", "isCrit", "isDOT" }, + miss = { "source", "victim", "skill", "missType" }, + death = { "victim", "source", "skill", "isItem" }, + debuff = { "victim", "skill", "amountRank" }, + buff = { "victim", "skill", "amountRank" }, + fade = { "victim", "skill" }, + cast = { "source", "skill", "victim", "isBegin", "isPerform" }, + gain = { "source", "victim", "skill", "amount", "attribute" }, + drain = { "source", "victim", "skill", "amount", "attribute" }, + leech = { "source", "victim", "skill", "amount", "attribute", "sourceGained", "amountGained", "attributeGained" }, + dispel = { "victim", "skill", "source", "isFailed" }, + extraattack = { "victim", "skill", "amount" }, + environment = { "victim", "amount", "damageType" }, + experience = { "amount", "source", "bonusAmount", "bonusType", "penaltyAmount", "penaltyType", "amountRaidPenalty", "amountGroupBonus", "victim" }, + reputation = { "faction", "amount", "rank", "isNegative" }, + feedpet = { "victim", "item" }, + enchant = { "source", "victim", "skill", "item" }, + fail = { "source", "skill", "reason" }, + interrupt = { "source", "victim", "skill" }, + create = { "source", "item" }, + honor = { "amount", "source", "sourceRank" }, -- if amount == nil then isDishonor = true. + durability = { "source", "skill", "victim", "item" }, -- is not item then isAllItems = true + unknown = { "message" }, +} + +function lib:GetInfoFieldName(infoType, fieldIndex) + if self.infoMap[infoType] then + return self.infoMap[infoType][fieldIndex-1] -- Skip the first field in patternTable which is 'type'. + end +end + +-------------------------------- +-- Load this bitch! -- +-------------------------------- +libobj:Register(lib) diff --git a/modules/chronometer/libs/ParserLib/optimizer-enUS.lua b/modules/chronometer/libs/ParserLib/optimizer-enUS.lua new file mode 100644 index 0000000..3bcda06 --- /dev/null +++ b/modules/chronometer/libs/ParserLib/optimizer-enUS.lua @@ -0,0 +1,215 @@ + +if GetLocale() == "enUS" then + + if not ParserLibOptimizer then + ParserLibOptimizer = { + AURAADDEDOTHERHARMFUL = "afflict", + AURAADDEDOTHERHELPFUL = "gain", + AURAADDEDSELFHARMFUL = "afflict", + AURAADDEDSELFHELPFUL = "gain", + AURAAPPLICATIONADDEDOTHERHARMFUL = "afflict", + AURAAPPLICATIONADDEDOTHERHELPFUL = "gain", + AURAAPPLICATIONADDEDSELFHARMFUL = "afflict", + AURAAPPLICATIONADDEDSELFHELPFUL = "gain", + AURADISPELOTHER = "remove", + AURADISPELSELF = "remove", + AURAREMOVEDOTHER = "fade", + AURAREMOVEDSELF = "fade", + COMBATHITCRITOTHEROTHER = "crit", + COMBATHITCRITOTHERSELF = "crit", + COMBATHITCRITSELFOTHER = "crit", + COMBATHITCRITSELFSELF = "crit", + COMBATHITCRITSCHOOLOTHEROTHER = "crit", + COMBATHITCRITSCHOOLOTHERSELF = "crit", + COMBATHITCRITSCHOOLSELFOTHER = "crit", + COMBATHITCRITSCHOOLSELFSELF = "crit", + COMBATHITOTHEROTHER = "hit", + COMBATHITOTHERSELF = "hit", + COMBATHITSELFOTHER = "hit", + COMBATHITSELFSELF = "hit", + COMBATHITSCHOOLOTHEROTHER = "hit", + COMBATHITSCHOOLOTHERSELF = "hit", + COMBATHITSCHOOLSELFOTHER = "hit", + COMBATHITSCHOOLSELFSELF = "hit", + DAMAGESHIELDOTHEROTHER = "reflect", + DAMAGESHIELDOTHERSELF = "reflect", + DAMAGESHIELDSELFOTHER = "reflect", + DISPELFAILEDOTHEROTHER = "fail", + DISPELFAILEDOTHERSELF = "fail", + DISPELFAILEDSELFOTHER = "fail", + DISPELFAILEDSELFSELF = "fail", + HEALEDCRITOTHEROTHER = "crit", + HEALEDCRITOTHERSELF = "crit", + HEALEDCRITSELFOTHER = "crit", + HEALEDCRITSELFSELF = "crit", + HEALEDOTHEROTHER = "heal", + HEALEDOTHERSELF = "heal", + HEALEDSELFOTHER = "heal", + HEALEDSELFSELF = "heal", + IMMUNESPELLOTHEROTHER = "immune", + IMMUNESPELLSELFOTHER = "immune", + IMMUNESPELLOTHERSELF = "immune", + IMMUNESPELLSELFSELF = "immune", + ITEMENCHANTMENTADDOTHEROTHER = "cast", + ITEMENCHANTMENTADDOTHERSELF = "cast", + ITEMENCHANTMENTADDSELFOTHER = "cast", + ITEMENCHANTMENTADDSELFSELF = "cast", + MISSEDOTHEROTHER = "miss", + MISSEDOTHERSELF = "miss", + MISSEDSELFOTHER = "miss", + MISSEDSELFSELF = "miss", + OPEN_LOCK_OTHER = "perform", + OPEN_LOCK_SELF = "perform", + PARTYKILLOTHER = "slain", + PERIODICAURADAMAGEOTHEROTHER = "suffer", + PERIODICAURADAMAGEOTHERSELF = "suffer", + PERIODICAURADAMAGESELFOTHER = "suffer", + PERIODICAURADAMAGESELFSELF = "suffer", + PERIODICAURAHEALOTHEROTHER = "gain", + PERIODICAURAHEALOTHERSELF = "gain", + PERIODICAURAHEALSELFOTHER = "gain", + PERIODICAURAHEALSELFSELF = "gain", + POWERGAINOTHEROTHER = "gain", + POWERGAINOTHERSELF = "gain", + POWERGAINSELFSELF = "gain", + POWERGAINSELFOTHER = "gain", + PROCRESISTOTHEROTHER = "resist", + PROCRESISTOTHERSELF = "resist", + PROCRESISTSELFOTHER = "resist", + PROCRESISTSELFSELF = "resist", + SIMPLECASTOTHEROTHER = "cast", + SIMPLECASTOTHERSELF = "cast", + SIMPLECASTSELFOTHER = "cast", + SIMPLECASTSELFSELF = "cast", + SIMPLEPERFORMOTHEROTHER = "perform", + SIMPLEPERFORMOTHERSELF = "perform", + SIMPLEPERFORMSELFOTHER = "perform", + SIMPLEPERFORMSELFSELF = "perform", + SPELLBLOCKEDOTHEROTHER = "block", + SPELLBLOCKEDOTHERSELF = "block", + SPELLBLOCKEDSELFOTHER = "block", + SPELLBLOCKEDSELFSELF = "block", + SPELLCASTOTHERSTART = "begin", + SPELLCASTSELFSTART = "begin", + SPELLDEFLECTEDOTHEROTHER = "deflect", + SPELLDEFLECTEDOTHERSELF = "deflect", + SPELLDEFLECTEDSELFOTHER = "deflect", + SPELLDEFLECTEDSELFSELF = "deflect", + SPELLDODGEDOTHEROTHER = "dodge", + SPELLDODGEDOTHERSELF = "dodge", + SPELLDODGEDSELFOTHER = "dodge", + SPELLEVADEDOTHEROTHER = "evade", + SPELLEVADEDOTHERSELF = "evade", + SPELLEVADEDSELFOTHER = "evade", + SPELLEVADEDSELFSELF = "evade", + SPELLEXTRAATTACKSOTHER = "extra", + SPELLEXTRAATTACKSOTHER_SINGULAR = "extra", + SPELLEXTRAATTACKSSELF = "extra", + SPELLEXTRAATTACKSSELF_SINGULAR = "extra", + SPELLFAILCASTSELF = "fail", + SPELLFAILPERFORMSELF = "fail", + SPELLIMMUNEOTHEROTHER = "immune", + SPELLIMMUNEOTHERSELF = "immune", + SPELLIMMUNESELFOTHER = "immune", + SPELLIMMUNESELFSELF = "immune", + SPELLINTERRUPTOTHEROTHER = "interrupt", + SPELLINTERRUPTOTHERSELF = "interrupt", + SPELLINTERRUPTSELFOTHER = "interrupt", + SPELLLOGABSORBOTHEROTHER = "absorb", + SPELLLOGABSORBOTHERSELF = "absorb", + SPELLLOGABSORBSELFOTHER = "absorb", + SPELLLOGABSORBSELFSELF = "absorb", + SPELLLOGCRITOTHEROTHER = "crit", + SPELLLOGCRITOTHERSELF = "crit", + SPELLLOGCRITSCHOOLOTHEROTHER = "crit", + SPELLLOGCRITSCHOOLOTHERSELF = "crit", + SPELLLOGCRITSCHOOLSELFOTHER = "crit", + SPELLLOGCRITSCHOOLSELFSELF = "crit", + SPELLLOGCRITSELFOTHER = "crit", + SPELLLOGOTHEROTHER = "hit", + SPELLLOGOTHERSELF = "hit", + SPELLLOGOTHERSELF = "hit", + SPELLLOGSCHOOLOTHEROTHER = "hit", + SPELLLOGSCHOOLOTHERSELF = "hit", + SPELLLOGSCHOOLSELFOTHER = "hit", + SPELLLOGSCHOOLSELFSELF = "hit", + SPELLLOGSELFOTHER = "hit", + SPELLMISSOTHEROTHER = "miss", + SPELLMISSOTHERSELF = "miss", + SPELLMISSSELFOTHER = "miss", + SPELLPARRIEDOTHEROTHER = "parr", + SPELLPARRIEDOTHERSELF = "parr", + SPELLPARRIEDSELFOTHER = "parr", + SPELLPERFORMOTHERSTART = "begin", + SPELLPERFORMSELFSTART = "begin", + SPELLPOWERDRAINOTHEROTHER = "drain", + SPELLPOWERDRAINOTHERSELF = "drain", + SPELLPOWERDRAINSELFOTHER = "drain", + SPELLPOWERLEECHOTHEROTHER = "drain", + SPELLPOWERLEECHOTHERSELF = "drain", + SPELLPOWERLEECHSELFOTHER = "drain", + SPELLREFLECTOTHEROTHER = "reflect", + SPELLREFLECTOTHERSELF = "reflect", + SPELLREFLECTSELFOTHER = "reflect", + SPELLREFLECTSELFSELF = "reflect", + SPELLRESISTOTHEROTHER = "resist", + SPELLRESISTOTHERSELF = "resist", + SPELLRESISTSELFOTHER = "resist", + SPELLRESISTSELFSELF = "resist", + SPELLSPLITDAMAGESELFOTHER = "cause", + SPELLSPLITDAMAGEOTHEROTHER = "cause", + SPELLSPLITDAMAGEOTHERSELF = "cause", + SPELLTERSEPERFORM_OTHER = "perform", + SPELLTERSEPERFORM_SELF = "perform", + SPELLTERSE_OTHER = "cast", + SPELLTERSE_SELF = "cast", + VSABSORBOTHEROTHER = "absorb", + VSABSORBOTHERSELF = "absorb", + VSABSORBSELFOTHER = "absorb", + VSBLOCKOTHEROTHER = "block", + VSBLOCKOTHERSELF = "block", + VSBLOCKSELFOTHER = "block", + VSBLOCKSELFSELF = "block", + VSDEFLECTOTHEROTHER = "deflect", + VSDEFLECTOTHERSELF = "deflect", + VSDEFLECTSELFOTHER = "deflect", + VSDEFLECTSELFSELF = "deflect", + VSDODGEOTHEROTHER = "dodge", + VSDODGEOTHERSELF = "dodge", + VSDODGESELFOTHER = "dodge", + VSDODGESELFSELF = "dodge", + VSENVIRONMENTALDAMAGE_FALLING_OTHER = "fall", + VSENVIRONMENTALDAMAGE_FALLING_SELF = "fall", + VSENVIRONMENTALDAMAGE_FIRE_OTHER = "fire", + VSENVIRONMENTALDAMAGE_FIRE_SELF = "fire", + VSENVIRONMENTALDAMAGE_LAVA_OTHER = "lava", + VSENVIRONMENTALDAMAGE_LAVA_SELF = "lava", + VSEVADEOTHEROTHER = "evade", + VSEVADEOTHERSELF = "evade", + VSEVADESELFOTHER = "evade", + VSEVADESELFSELF = "evade", + VSIMMUNEOTHEROTHER = "immune", + VSIMMUNEOTHERSELF = "immune", + VSIMMUNESELFOTHER = "immune", + VSPARRYOTHEROTHER = "parr", + VSPARRYOTHERSELF = "parr", + VSPARRYSELFOTHER = "parr", + VSRESISTOTHEROTHER = "resist", + VSRESISTOTHERSELF = "resist", + VSRESISTSELFOTHER = "resist", + VSRESISTSELFSELF = "resist", + VSENVIRONMENTALDAMAGE_FATIGUE_OTHER = "exhaust", + VSENVIRONMENTALDAMAGE_FIRE_OTHER = "fire", + VSENVIRONMENTALDAMAGE_SLIME_OTHER = "slime", + VSENVIRONMENTALDAMAGE_SLIME_SELF = "slime", + VSENVIRONMENTALDAMAGE_DROWNING_OTHER = "drown", + UNITDIESSELF = "die", + UNITDIESOTHER = "die", + UNITDESTROYEDOTHER = "destroy", + + } + + end + +end + diff --git a/modules/chronometer/libs/ParserLib/readme.txt b/modules/chronometer/libs/ParserLib/readme.txt new file mode 100644 index 0000000..1c230f7 --- /dev/null +++ b/modules/chronometer/libs/ParserLib/readme.txt @@ -0,0 +1 @@ +The newest documentation can be found at http://www.wowace.com/index.php/ParserLib \ No newline at end of file diff --git a/modules/chronometer/loader.xml b/modules/chronometer/loader.xml new file mode 100644 index 0000000..cc0858c --- /dev/null +++ b/modules/chronometer/loader.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/modules/chronometer/module.lua b/modules/chronometer/module.lua new file mode 100644 index 0000000..7bf5d0f --- /dev/null +++ b/modules/chronometer/module.lua @@ -0,0 +1,45 @@ +local MTH_ChronometerModule = { + name = "chronometer", + enabled = true, + version = "1.0.3", + events = {}, + initialized = false, +} + +local function MTH_CHRON_ApplyRuntimeEnabledState(enabled) + if not MTH_ChronometerHunter then + return + end + if enabled then + if MTH_ChronometerHunter.MTH_Enable then + MTH_ChronometerHunter:MTH_Enable() + end + else + if MTH_ChronometerHunter.MTH_Disable then + MTH_ChronometerHunter:MTH_Disable() + end + end +end + +function MTH_ChronometerModule:init() + if MTH_ChronometerHunter and MTH_ChronometerHunter.MTH_Initialize then + MTH_ChronometerHunter:MTH_Initialize() + end + MTH_CHRON_ApplyRuntimeEnabledState(self.enabled and true or false) + self.initialized = true +end + +function MTH_ChronometerModule:setEnabled(enabled) + if not self.initialized then + self:init() + end + MTH_CHRON_ApplyRuntimeEnabledState(enabled and true or false) +end + +function MTH_ChronometerModule:cleanup() + if MTH_ChronometerHunter and MTH_ChronometerHunter.MTH_Disable then + MTH_ChronometerHunter:MTH_Disable() + end +end + +MTH:RegisterModule("chronometer", MTH_ChronometerModule) diff --git a/modules/feedomatic/Bindings.xml b/modules/feedomatic/Bindings.xml new file mode 100644 index 0000000..13f45b3 --- /dev/null +++ b/modules/feedomatic/Bindings.xml @@ -0,0 +1,5 @@ + + + FOM_Feed(); + + \ No newline at end of file diff --git a/modules/feedomatic/FeedOMatic.lua b/modules/feedomatic/FeedOMatic.lua new file mode 100644 index 0000000..efd3e7f --- /dev/null +++ b/modules/feedomatic/FeedOMatic.lua @@ -0,0 +1,2221 @@ +------------------------------------------------------ +-- FeedOMatic.lua +------------------------------------------------------ +FOM_VERSION = "11200.2"; +------------------------------------------------------ + +-- TODO: if you don't auto-feed, have an option to make the need to feed more noticeable (e.g., pulsing halo around the pet-happiness icon). + +-- constants +FOM_WARNING_INTERVAL = 10; -- don't warn more than once per this many seconds +MAX_QUALITY = 35 * 60 + 1; -- We store a notion of a food's "quality": its best happiness-per-tick multiplied by the pet's level as of when that tick occurred. We use "best" because a pet that's closer to "sated" (maximum happiness) will receive less happiness per tick than he would from the same food if he were hungrier. (So, a food that gives 35 happiness per tick to a level 60 pet is "better" than a food that's worth 35 happiness per tick to a level 30 pet.) Foods whose quality hasn't been observed yet are given this value when sorting, so we can prioritize the discovery of new foods' quality ratings. +MAX_KEEPOPEN_SLOTS = 150; + +-- Configuration +FOM_Config_Default = { + Enabled = false; + Alert = "emote"; + Level = "content"; + KeepOpenSlots = 8; + AvoidUsefulFood = true; + AvoidQuestFood = true; + AvoidBonusFood = true; + Fallback = false; + SaveForCookingLevel = 1; + PreferHigherQuality = true; +}; +FOM_Config = FOM_Config_Default; + +-- FOM_Cooking = { }; +-- Has the following internal structure: +-- REALM_PLAYER = { +-- FOODNAME = SKILL_DIFFICULTY, +-- } + +-- FOM_QuestFood = { }; +-- Has the following internal structure: +-- REALM_PLAYER = { +-- FOODNAME = QUANTITY_REQUIRED, +-- } + +-- FOM_FoodQuality = { }; +-- Has the following internal structure: +-- REALM_PLAYER = { +-- PETNAME = { +-- FOODNAME = HAPPINESS, +-- } +-- } + +-- Variables +FOM_State = { }; +FOM_State.InCombat = false; +FOM_State.IsAFK = false; +FOM_State.ShouldFeed = false; +FOM_LastWarning = 0; + +FOM_LastFood = nil; +FOM_LastChoiceReason = nil; +FOM_RealmPlayer = nil; +FOM_LastPetName = nil; +FOM_LastFeedAttempt = nil; + +-- Anti-freeze code borrowed from ReagentInfo (in turn, from Quest-I-On): +-- keeps WoW from locking up if we try to scan the tradeskill window too fast. +FOM_TradeSkillLock = { }; +FOM_TradeSkillLock.Locked = false; +FOM_TradeSkillLock.EventTimer = 0; +FOM_TradeSkillLock.EventCooldown = 0; +FOM_TradeSkillLock.EventCooldownTime = 1; + + +-- State variable used to track required quantities of quest food when it's in more than one stack +FOM_Quantity = { }; + +-- Remember how item IDs map to food names at runtime, but don't bloat long-term memory with it... +FOM_FoodIDsToNames = {}; + +FOM_FamilyAliasMap = nil; +FOM_DietByItemID = nil; +FOM_FamilyToLegacyMap = { + ["Bats"] = BAT, + ["Bears"] = BEAR, + ["Boars"] = BOAR, + ["Carrion Birds"] = CARRION_BIRD, + ["Cats"] = CAT, + ["Crabs"] = CRAB, + ["Crocolisks"] = CROCOLISK, + ["Gorillas"] = GORILLA, + ["Hyenas"] = HYENA, + ["Owls"] = OWL, + ["Raptors"] = RAPTOR, + ["Scorpids"] = SCORPID, + ["Spiders"] = SPIDER, + ["Tallstriders"] = TALLSTRIDER, + ["Turtles"] = TURTLE, + ["Wind Serpents"] = WIND_SERPENT, + ["Wolves"] = WOLF, +}; + +local function FOM_Trace(message) + return; +end + +local function FOM_CoreFeedReady() + return type(MTH_FEED_BeginAttempt) == "function" and type(MTH_FEED_FinalizeAttempt) == "function"; +end + +local function FOM_CoreBeginAttempt(payload) + if (not FOM_CoreFeedReady()) then + return nil; + end + local ok, attemptId = pcall(MTH_FEED_BeginAttempt, payload); + if (ok) then + return attemptId; + end + return nil; +end + +local function FOM_CoreRecordReject(attemptId, reason, rawMessage) + if (attemptId == nil or type(MTH_FEED_RecordRejectEvent) ~= "function") then + return; + end + pcall(MTH_FEED_RecordRejectEvent, attemptId, { + reason = reason, + rawMessage = rawMessage, + }); +end + +local function FOM_CoreFinalizeAttempt(attemptId, outcome, reason) + if (attemptId == nil or type(MTH_FEED_FinalizeAttempt) ~= "function") then + return; + end + pcall(MTH_FEED_FinalizeAttempt, attemptId, { + outcome = outcome, + reason = reason, + }); +end + +function FOM_NormalizeFamilyName(name) + if (name == nil or name == "") then + return nil; + end + local normalized = string.lower(name); + normalized = string.gsub(normalized, "%b()", ""); + normalized = string.gsub(normalized, "[^%a]", ""); + if (normalized == "") then + return nil; + end + return normalized; +end + +function FOM_AddFamilyAlias(aliasName, canonicalName) + local aliasKey = FOM_NormalizeFamilyName(aliasName); + if (aliasKey ~= nil and canonicalName ~= nil and canonicalName ~= "") then + FOM_FamilyAliasMap[aliasKey] = canonicalName; + end +end + +function FOM_BuildFamilyAliasMap() + if (FOM_FamilyAliasMap ~= nil) then + return; + end + FOM_FamilyAliasMap = {}; + if (MTH_DS_Families == nil) then + return; + end + + for canonicalName, _ in MTH_DS_Families do + if (type(canonicalName) == "string") then + local singularName = string.gsub(canonicalName, "%s*%b()", ""); + if (string.sub(singularName, -3) == "ies") then + singularName = string.sub(singularName, 1, string.len(singularName) - 3) .. "y"; + elseif (string.sub(singularName, -2) == "es") then + singularName = string.sub(singularName, 1, string.len(singularName) - 2); + elseif (string.sub(singularName, -1) == "s") then + singularName = string.sub(singularName, 1, string.len(singularName) - 1); + end + FOM_AddFamilyAlias(canonicalName, canonicalName); + FOM_AddFamilyAlias(singularName, canonicalName); + end + end + + FOM_AddFamilyAlias("Carrion Bird", "Carrion Birds"); + FOM_AddFamilyAlias("Wind Serpent", "Wind Serpents"); + FOM_AddFamilyAlias("Serpent", "Serpents (Cobra)"); +end + +function FOM_GetCanonicalPetFamily(rawFamily) + if (rawFamily == nil or rawFamily == "") then + rawFamily = UnitCreatureFamily("pet"); + end + if (rawFamily == nil or rawFamily == "") then + return nil; + end + if (MTH_DS_Families and MTH_DS_Families[rawFamily]) then + return rawFamily; + end + + FOM_BuildFamilyAliasMap(); + if (FOM_FamilyAliasMap ~= nil) then + local aliasKey = FOM_NormalizeFamilyName(rawFamily); + if (aliasKey and FOM_FamilyAliasMap[aliasKey]) then + return FOM_FamilyAliasMap[aliasKey]; + end + end + + return rawFamily; +end + +function FOM_GetLegacyFamilyKey(rawFamily) + local canonicalFamily = FOM_GetCanonicalPetFamily(rawFamily); + if (canonicalFamily == nil) then + return rawFamily; + end + if (FOM_FamilyToLegacyMap[canonicalFamily] ~= nil) then + return FOM_FamilyToLegacyMap[canonicalFamily]; + end + return rawFamily; +end + +function FOM_NormalizeDietToken(diet) + if (diet == nil or diet == "") then + return nil; + end + local lowerDiet = string.lower(diet); + if (lowerDiet == "meat" or lowerDiet == string.lower(FOM_DIET_MEAT)) then return FOM_DIET_MEAT; end + if (lowerDiet == "fish" or lowerDiet == string.lower(FOM_DIET_FISH)) then return FOM_DIET_FISH; end + if (lowerDiet == "bread" or lowerDiet == string.lower(FOM_DIET_BREAD)) then return FOM_DIET_BREAD; end + if (lowerDiet == "cheese" or lowerDiet == string.lower(FOM_DIET_CHEESE)) then return FOM_DIET_CHEESE; end + if (lowerDiet == "fruit" or lowerDiet == string.lower(FOM_DIET_FRUIT)) then return FOM_DIET_FRUIT; end + if (lowerDiet == "fungus" or lowerDiet == string.lower(FOM_DIET_FUNGUS)) then return FOM_DIET_FUNGUS; end + if (lowerDiet == string.lower(FOM_DIET_BONUS)) then return FOM_DIET_BONUS; end + if (lowerDiet == string.lower(FOM_DIET_ALL)) then return FOM_DIET_ALL; end + return lowerDiet; +end + +function FOM_GetPetDietList(rawFamily) + local canonicalFamily = FOM_GetCanonicalPetFamily(rawFamily); + if (canonicalFamily and MTH_DS_Families and MTH_DS_Families[canonicalFamily]) then + local familyRow = MTH_DS_Families[canonicalFamily]; + if (familyRow and type(familyRow.food) == "table" and table.getn(familyRow.food) > 0) then + local dbDiets = {}; + for _, dietName in familyRow.food do + local normalizedDiet = FOM_NormalizeDietToken(dietName); + if (normalizedDiet ~= nil) then + table.insert(dbDiets, normalizedDiet); + end + end + if (table.getn(dbDiets) > 0) then + return dbDiets; + end + end + end + + -- Keep live API fallback: some edge cases can fail family mapping at runtime, + -- and returning nil here causes false "no feedable pet" behavior on keybind use. + local liveDiets = {GetPetFoodTypes()}; + if (liveDiets == nil) then + return nil; + end + for index, dietName in liveDiets do + liveDiets[index] = FOM_NormalizeDietToken(dietName); + end + return liveDiets; +end + +local function FOM_BuildDietByItemIDMap() + if (FOM_DietByItemID ~= nil) then + return FOM_DietByItemID; + end + + FOM_DietByItemID = {}; + if (type(FOM_Foods) ~= "table") then + return FOM_DietByItemID; + end + + for dietName, dietItems in FOM_Foods do + if (type(dietItems) == "table") then + for _, itemID in dietItems do + local numericItemID = tonumber(itemID); + if (numericItemID ~= nil) then + FOM_DietByItemID[numericItemID] = dietName; + end + end + end + end + + return FOM_DietByItemID; +end + +local function FOM_GetItemIDFromLink(itemLink) + if (itemLink == nil or itemLink == "") then + return nil; + end + + local _, _, itemID = string.find(itemLink, "Hitem:(%d+)"); + if (itemID ~= nil) then + return tonumber(itemID); + end + + local _, _, legacyItemID = string.find(itemLink, "Hitem:((%d+).-)"); + if (legacyItemID ~= nil) then + return tonumber(legacyItemID); + end + + return nil; +end + +function MTH_GetMerchantFoodsByDiet() + local result = { + byDiet = {}, + items = {}, + unknown = {}, + }; + + if (type(GetMerchantNumItems) ~= "function") then + return result; + end + + local itemCount = GetMerchantNumItems() or 0; + if (itemCount <= 0) then + return result; + end + + local dietByItemID = FOM_BuildDietByItemIDMap(); + for index = 1, itemCount do + local itemName, _, itemPrice, itemStackCount, itemNumAvailable = GetMerchantItemInfo(index); + local itemLink = GetMerchantItemLink(index); + local itemID = FOM_GetItemIDFromLink(itemLink); + if (itemID ~= nil) then + local dietName = dietByItemID[itemID]; + local foodRow = { + index = index, + id = itemID, + name = itemName, + link = itemLink, + diet = dietName, + price = itemPrice, + stack = itemStackCount, + available = itemNumAvailable, + }; + + if (dietName ~= nil) then + if (result.byDiet[dietName] == nil) then + result.byDiet[dietName] = {}; + end + table.insert(result.byDiet[dietName], foodRow); + table.insert(result.items, foodRow); + else + table.insert(result.unknown, foodRow); + if (type(MTH_FEED_RecordUnknownFoodCandidate) == "function") then + pcall(MTH_FEED_RecordUnknownFoodCandidate, { + itemId = itemID, + itemName = itemName, + itemLink = itemLink, + source = "vendor-scan", + observedFoodLevel = (type(MTH_FEED_GetFoodLevel) == "function") and MTH_FEED_GetFoodLevel(itemID) or nil, + observedDietHint = nil, + evidenceType = "vendor-scan", + }); + end + end + end + end + + return result; +end + +local function FOM_DebugLog(message) + if (type(MTH_Log) == "function") then + MTH_Log("[FeedOMatic] " .. tostring(message or ""), "debug"); + end +end + +local function FOM_DebugLogMerchantFoods(sourceEvent) + local merchantFoods = MTH_GetMerchantFoodsByDiet(); + local eventLabel = tostring(sourceEvent or "MERCHANT"); + if (merchantFoods == nil) then + FOM_DebugLog(eventLabel .. ": no merchant data available."); + return; + end + + local knownCount = table.getn(merchantFoods.items or {}); + local unknownCount = table.getn(merchantFoods.unknown or {}); + local merchantName = UnitName("npc") or UnitName("target") or "unknown"; + + FOM_DebugLog(eventLabel .. ": " .. tostring(merchantName) .. " | known foods=" .. tostring(knownCount) .. " | unknown items=" .. tostring(unknownCount)); + + if (type(merchantFoods.byDiet) == "table") then + for dietName, foodRows in merchantFoods.byDiet do + local dietCount = table.getn(foodRows or {}); + FOM_DebugLog("diet " .. tostring(dietName) .. ": " .. tostring(dietCount) .. " item(s)"); + for _, row in foodRows do + FOM_DebugLog(" - [" .. tostring(row.id) .. "] " .. tostring(row.name) .. " (merchantIndex=" .. tostring(row.index) .. ")"); + end + end + end + + if (unknownCount > 0) then + for _, row in merchantFoods.unknown do + FOM_DebugLog("unknown food map: [" .. tostring(row.id) .. "] " .. tostring(row.name) .. " (merchantIndex=" .. tostring(row.index) .. ")"); + end + end +end + +function FOM_FeedButton_OnClick() + if (arg1 == "RightButton") then + if (MTH_OpenOptions) then + MTH_OpenOptions("FeedOMatic"); + end + else + FOM_Feed(); + end +end + +function FOM_FeedButton_OnEnter() + if ( PetFrameHappiness.tooltip ) then + GameTooltip:SetOwner(PetFrameHappiness, "ANCHOR_RIGHT"); + GameTooltip:SetText(PetFrameHappiness.tooltip); + if ( PetFrameHappiness.tooltipDamage ) then + GameTooltip:AddLine(PetFrameHappiness.tooltipDamage, "", 1, 1, 1); + end + if ( PetFrameHappiness.tooltipLoyalty ) then + GameTooltip:AddLine(PetFrameHappiness.tooltipLoyalty, "", 1, 1, 1); + end + if (FOM_LastChoiceReason ~= nil and FOM_LastChoiceReason ~= "") then + GameTooltip:AddLine("Feed-O-Matic: " .. FOM_LastChoiceReason, 0.6, 0.9, 1.0); + end + GameTooltip:Show(); + end +end + +function FOM_FeedButton_OnLeave() + GameTooltip:Hide(); +end + +function FOM_OnLoad() + + -- Register for Events + if (not MTH_FEED_WRAPPER_MODE) then + this:RegisterEvent("VARIABLES_LOADED"); + end + + -- Register Slash Commands + SLASH_FEEDOMATIC1 = "/feedomatic"; + SLASH_FEEDOMATIC2 = "/fom"; + SLASH_FEEDOMATIC3 = "/feed"; + SLASH_FEEDOMATIC4 = "/petfeed"; -- Rauen's PetFeed compatibility + SLASH_FEEDOMATIC5 = "/pf"; + SlashCmdList["FEEDOMATIC"] = function(msg) + if MTH_IsModuleEnabled and not MTH_IsModuleEnabled("feedomatic") then + if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + MTH:Print("Feed-O-Matic is disabled while module 'feedomatic' is disabled.") + end + return + end + FOM_ChatCommandHandler(msg); + end + + -- hook functions so we can manage per-pet saved food quality data + if (MTH_PETL_Original_PetAbandon == nil) then + MTH_PETL_Original_PetAbandon = PetAbandon; + end + FOM_Original_PetAbandon = MTH_PETL_Original_PetAbandon; + + if (not MTH_FEED_WRAPPER_MODE) then + PetAbandon = MTH_PetAbandonHook; + end + + --GFWUtils.Debug = true; + +end + +function MTH_FOM_GetCorePetInfo() + if (type(MTH_GetCurrentPetInfo) == "function") then + local info = MTH_GetCurrentPetInfo(); + if (type(info) == "table") then + return info; + end + end + + local info = { + exists = false, + liveExists = false, + dead = false, + name = nil, + level = nil, + happiness = nil, + loyalty = nil, + health = nil, + healthMax = nil, + }; + + info.liveExists = (type(UnitExists) == "function" and UnitExists("pet")) and true or false; + info.exists = info.liveExists; + if (type(UnitName) == "function") then + info.name = UnitName("pet"); + end + if (type(UnitLevel) == "function") then + info.level = tonumber(UnitLevel("pet")); + end + if (type(UnitHealth) == "function") then + info.health = tonumber(UnitHealth("pet")); + end + if (type(UnitHealthMax) == "function") then + info.healthMax = tonumber(UnitHealthMax("pet")); + end + if (type(UnitIsDead) == "function") then + info.dead = UnitIsDead("pet") and true or false; + end + if (type(GetPetHappiness) == "function") then + local happiness, _, loyalty = GetPetHappiness(); + info.happiness = happiness; + info.loyalty = loyalty; + end + + return info; +end + +function MTH_FOM_IsValidPetName(name) + return (name and name ~= "" and name ~= UNKNOWNOBJECT) and true or false; +end + +function MTH_FOM_GetLivePetName() + local info = MTH_FOM_GetCorePetInfo(); + if (info and info.liveExists and MTH_FOM_IsValidPetName(info.name)) then + return info.name; + end + return nil; +end + +function MTH_FOM_GetTrackedPetName() + local info = MTH_FOM_GetCorePetInfo(); + if (info and MTH_FOM_IsValidPetName(info.name)) then + return info.name; + end + return nil; +end + +function FOM_CheckSetup() + + _, realClass = UnitClass("player"); + if (realClass ~= "HUNTER") then return; end + + if (FOM_RealmPlayer == nil) then + FOM_RealmPlayer = GetCVar("realmName") .. "." .. UnitName("player"); + end + local currentPetName = MTH_FOM_GetTrackedPetName(); + if (currentPetName) then + FOM_LastPetName = currentPetName; + end + + if (FOM_FoodQuality == nil) then + FOM_FoodQuality = { }; + end + if (FOM_FoodQuality[FOM_RealmPlayer] == nil) then + FOM_FoodQuality[FOM_RealmPlayer] = { }; + end + if (FOM_LastPetName) then + if (FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName] == nil) then + FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName] = { }; + end + end + +end + +function FOM_OnEvent(event, arg1) + + -- Save Variables + if ( event == "VARIABLES_LOADED" ) then + + _, realClass = UnitClass("player"); + if (realClass == "HUNTER") then + -- monitor status for whether we're able to feed + if (not MTH_FEED_WRAPPER_MODE) then + this:RegisterEvent("PET_ATTACK_START"); + this:RegisterEvent("PET_ATTACK_STOP"); +-- this:RegisterEvent("CHAT_MSG_SYSTEM"); + + this:RegisterEvent("PET_STABLE_SHOW"); + this:RegisterEvent("PET_STABLE_UPDATE"); + + -- track whether foods are useful for Cooking + this:RegisterEvent("TRADE_SKILL_SHOW"); + this:RegisterEvent("TRADE_SKILL_UPDATE"); + + -- figure out what happens when we try to feed pet (gain happiness, didn't like, etc) + this:RegisterEvent("CHAT_MSG_SPELL_TRADESKILLS"); + this:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"); + this:RegisterEvent("UI_ERROR_MESSAGE"); + + -- Events for trying to catch when the pet needs feeding + this:RegisterEvent("PET_BAR_SHOWGRID"); + this:RegisterEvent("PET_BAR_UPDATE"); + this:RegisterEvent("PET_UI_UPDATE"); + this:RegisterEvent("UNIT_HAPPINESS"); + this:RegisterEvent("PLAYER_REGEN_ENABLED"); + end + + if (FOM_FeedButton == nil) then + FOM_FeedButton = CreateFrame("Button", "FOM_FeedButton", PetFrameHappiness); + FOM_FeedButton:SetAllPoints(PetFrameHappiness); + FOM_FeedButton:RegisterForClicks("LeftButtonUp", "RightButtonUp"); + FOM_FeedButton:SetScript("OnClick", FOM_FeedButton_OnClick); + FOM_FeedButton:SetScript("OnEnter", FOM_FeedButton_OnEnter); + FOM_FeedButton:SetScript("OnLeave", FOM_FeedButton_OnLeave); + end + + if (FOM_Config.Level == "happy") then + -- we've redefined the Level option and this setting is no loger available + FOM_Config.Level = "content"; + end + + end + return; + + elseif ( event == "MERCHANT_SHOW" or event == "MERCHANT_UPDATE" ) then + FOM_DebugLogMerchantFoods(event); + return; + + elseif ( event == "PET_ATTACK_START" ) then + + -- Set Flag + FOM_State.InCombat = true; + return; + + elseif ( event == "PET_ATTACK_STOP" ) then + + -- Remove Flag + FOM_State.InCombat = false; + + elseif ( event == "CHAT_MSG_SPELL_TRADESKILLS" ) then + + if (FOM_FEEDPET_LOG_FIRSTPERSON == nil) then + FOM_FEEDPET_LOG_FIRSTPERSON = GFWUtils.FormatToPattern(FEEDPET_LOG_FIRSTPERSON); + end + _, _, foodName = string.find(arg1, FOM_FEEDPET_LOG_FIRSTPERSON); + if (foodName and foodName ~= "") then + local foodID = GFWTable.KeyOf(FOM_FoodIDsToNames, foodName); + if (foodID == nil) then + local bag, slot = FOM_FindSpecificFood(foodName); + local foodLink = GetContainerItemLink(bag, slot); + foodID = FOM_IDFromLink(foodLink); + end + if (foodID) then + FOM_LastFood = GFWUtils.ItemLink(foodID); + GFWUtils.DebugLog("Manually fed "..FOM_LastFood); + end + end + return; + + elseif ( event == "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS" ) then + + if (arg1 and FOM_HasFeedEffect()) then + if (FOM_POWERGAIN_OTHER == nil and POWERGAINSELFOTHER) then + FOM_POWERGAIN_OTHER = GFWUtils.FormatToPattern(POWERGAINSELFOTHER); + end + if (FOM_POWERGAIN_OTHER == nil and POWERGAIN_OTHER) then + FOM_POWERGAIN_OTHER = GFWUtils.FormatToPattern(POWERGAIN_OTHER); + end + if (FOM_POWERGAIN_OTHER == nil) then + GFWUtils.PrintOnce(GFWUtils.Red("Feed-O-Matic Error: ").. "Can't find parse pattern for pet happiness."); + return; + end + _, _, name, amount, powerType = string.find(arg1, FOM_POWERGAIN_OTHER); + local happiness; + local petInfo = MTH_FOM_GetCorePetInfo(); + local livePetName = MTH_FOM_GetLivePetName(); + if (name == livePetName and powerType == HAPPINESS_POINTS) then + happiness = tonumber(amount); + else + return; + end + + if (type(FOM_LastFeedAttempt) == "table" and FOM_LastFeedAttempt.coreAttemptId and happiness and happiness > 0 and type(MTH_FEED_RecordHappinessTick) == "function") then + pcall(MTH_FEED_RecordHappinessTick, FOM_LastFeedAttempt.coreAttemptId, { + happinessTick = happiness, + petLevel = tonumber(petInfo and petInfo.level) or nil, + }); + end + end + return; + + elseif ( event == "UI_ERROR_MESSAGE" ) then + local lowerError = string.lower(tostring(arg1 or "")); + if (FOM_LastFood) then + FOM_Trace("ui error while feeding message='" .. tostring(arg1 or "") + .. "' lower='" .. tostring(lowerError) .. "'") + end + local isLowLevelError = (arg1 and SPELL_FAILED_FOOD_LOWLEVEL and string.find(arg1, SPELL_FAILED_FOOD_LOWLEVEL)) + or string.find(lowerError, "low level") + or string.find(lowerError, "level too low") + or string.find(lowerError, "too low level") + local isWrongFoodError = (arg1 and SPELL_FAILED_WRONG_PET_FOOD and string.find(arg1, SPELL_FAILED_WRONG_PET_FOOD)) + or string.find(lowerError, "wrong pet food") + or string.find(lowerError, "doesn't like") + or string.find(lowerError, "does not like") + + if (isLowLevelError) then + if (type(FOM_LastFeedAttempt) == "table") then + FOM_CoreRecordReject(FOM_LastFeedAttempt.coreAttemptId, "low-level", arg1); + FOM_CoreFinalizeAttempt(FOM_LastFeedAttempt.coreAttemptId, "rejected", "low-level"); + FOM_LastFeedAttempt = nil; + end + if not (FOM_LastFood == nil) then + FOM_LastFood = nil; + local alertPetName = MTH_FOM_GetTrackedPetName() or "Your pet"; + if ( FOM_Config.Alert == "chat") then + GFWUtils.Print(string.format(FOM_FEEDING_EAT_ANOTHER, alertPetName)); + elseif ( FOM_Config.Alert == "emote") then + SendChatMessage(string.format(FOM_FEEDING_FEED_ANOTHER, alertPetName), "EMOTE"); + end + return; + end + + elseif (isWrongFoodError) then + if (type(FOM_LastFeedAttempt) == "table") then + FOM_CoreRecordReject(FOM_LastFeedAttempt.coreAttemptId, "wrong-food", arg1); + FOM_CoreFinalizeAttempt(FOM_LastFeedAttempt.coreAttemptId, "rejected", "wrong-food"); + FOM_LastFeedAttempt = nil; + end + if (FOM_LastFood) then + local alertPetName = MTH_FOM_GetTrackedPetName() or "Your pet"; + local itemID = FOM_IDFromLink(FOM_LastFood); + FOM_Trace("learn reject wrongfood pet='" .. tostring(FOM_LastPetName or alertPetName) + .. "' itemId=" .. tostring(itemID) + .. " link='" .. tostring(FOM_LastFood) .. "'") + + if ( FOM_Config.Alert == "chat") then + GFWUtils.Print(string.format(FOM_FEEDING_EAT_ANOTHER, alertPetName)); + elseif ( FOM_Config.Alert == "emote") then + SendChatMessage(string.format(FOM_FEEDING_FEED_ANOTHER, alertPetName), "EMOTE"); + end + -- remove from diet + local dietList = FOM_GetPetDietList(); + if (dietList ~= nil) then + for _, diet in dietList do + diet = FOM_NormalizeDietToken(diet); + if (diet ~= nil and FOM_RemoveFood(diet, itemID) ) then + local capDiet = string.upper(string.sub(diet, 1, 1)) .. string.sub(diet, 2); -- print a nicely capitalized version + GFWUtils.Print("Removed "..FOM_LastFood.." from "..GFWUtils.Hilite(capDiet).." list."); + end + end + end + FOM_LastFood = nil; + return; + end + end + return; + + elseif (event == "TRADE_SKILL_SHOW" or event == "TRADE_SKILL_UPDATE") then + if (GetTradeSkillLine() ~= nil and GetTradeSkillLine() == FOM_CookingSpellName()) then + if (FOM_Config.SaveForCookingLevel >= 0 and FOM_Config.SaveForCookingLevel <= 3) then + -- Update Cooking reagents list so we can avoid consuming food we could skillup from. + if (FOM_RealmPlayer == nil) then + FOM_RealmPlayer = GetCVar("realmName") .. "." .. UnitName("player"); + end + if (FOM_Cooking == nil) then + FOM_Cooking = { }; + end + if (FOM_Cooking[FOM_RealmPlayer] == nil) then + FOM_Cooking[FOM_RealmPlayer] = { }; + end + if (FOM_Cooking ~= nil and FOM_Cooking[FOM_RealmPlayer] ~= nil and TradeSkillFrame and TradeSkillFrame:IsVisible() and not FOM_TradeSkillLock.Locked) then + -- This prevents further update events from being handled if we're already processing one. + -- This is done to prevent the game from freezing under certain conditions. + FOM_TradeSkillLock.Locked = true; + + for i=1, GetNumTradeSkills() do + local itemName, type, _, _ = GetTradeSkillInfo(i); + if (type ~= "header") then + for j=1, GetTradeSkillNumReagents(i) do + local reagentLink = GetTradeSkillReagentItemLink(i, j); + local itemID = FOM_IDFromLink(reagentLink); + + if (itemID and FOM_IsKnownFood(itemID)) then + if (FOM_Cooking[FOM_RealmPlayer][itemID] == nil) then + FOM_Cooking[FOM_RealmPlayer][itemID] = FOM_DifficultyToNum(type); + else + FOM_Cooking[FOM_RealmPlayer][itemID] = max(FOM_Cooking[FOM_RealmPlayer][itemID], FOM_DifficultyToNum(type)); + end + end + end + end + end + end + end + end + return; + + elseif (event == "PET_STABLE_SHOW" or event == "PET_STABLE_UPDATE") then + + -- clean up the FOM_FoodQuality sub-tables in case we missed you abandoning a pet + FOM_CheckSetup(); + local stabledPetNames = nil; + if (type(MTH_GetStablePetNames) == "function") then + stabledPetNames = MTH_GetStablePetNames(); + end + if (stabledPetNames == nil) then + stabledPetNames = {}; + end + local orphanedPetNames = {}; + for savedPetName in FOM_FoodQuality[FOM_RealmPlayer] do + if (stabledPetNames == nil) then + GFWUtils.DebugLog("stabledPetNames == nil"); + end + if (stabledPetNames ~= nil and GFWTable.IndexOf(stabledPetNames, savedPetName) == 0) then + table.insert(orphanedPetNames, savedPetName); + end + end + for _, orphanedPet in orphanedPetNames do + FOM_FoodQuality[FOM_RealmPlayer][orphanedPet] = nil; + end + return; + + elseif (FOM_Config.Level) then + FOM_CheckHappiness(); + end + +end + +-- Update our list of quest objectives so we can avoid consuming food we want to accumulate for a quest. +function FOM_ScanQuests() + FOM_QuestFood = nil; + for questNum=1, GetNumQuestLogEntries() do + local QText, level, questTag, isHeader, isCollapsed, isComplete = GetQuestLogTitle(questNum); + if (not isHeader) then + for objectiveNum=1, GetNumQuestLeaderBoards(questNum) do + local text, type, finished = GetQuestLogLeaderBoard(objectiveNum, questNum); + if (text ~= nil and strlen(text) > 0) then + local _, _, objectiveName, numCurrent, numRequired = string.find(text, "(.*): (%d+)/(%d+)"); + if (FOM_IsKnownFood(objectiveName)) then + if (FOM_QuestFood == nil) then + FOM_QuestFood = { }; + end + if (FOM_QuestFood[FOM_RealmPlayer] == nil) then + FOM_QuestFood[FOM_RealmPlayer] = { }; + end + + if (FOM_QuestFood[FOM_RealmPlayer][objectiveName] == nil) then + FOM_QuestFood[FOM_RealmPlayer][objectiveName] = tonumber(numRequired); + else + FOM_QuestFood[FOM_RealmPlayer][objectiveName] = max(FOM_QuestFood[FOM_RealmPlayer][objectiveName], tonumber(numRequired)); + end + end + end + end + end + end +end + +function FOM_DifficultyToNum(level) + if (level == "optimal" or level == "orange") then + return 3; + elseif (level == "medium" or level == "yellow") then + return 2; + elseif (level == "easy" or level == "green") then + return 1; + elseif (level == "trivial" or level == "gray" or level == "grey") then + return 1; + else -- bad input + return nil; + end +end + +function FOM_OnUpdate(elapsed) + + _, realClass = UnitClass("player"); + if (realClass ~= "HUNTER") then return; end + + -- If it's been more than a second since our last tradeskill update, + -- we can allow the event to process again. + FOM_TradeSkillLock.EventTimer = FOM_TradeSkillLock.EventTimer + elapsed; + if (FOM_TradeSkillLock.Locked) then + FOM_TradeSkillLock.EventCooldown = FOM_TradeSkillLock.EventCooldown + elapsed; + if (FOM_TradeSkillLock.EventCooldown > FOM_TradeSkillLock.EventCooldownTime) then + + FOM_TradeSkillLock.EventCooldown = 0; + FOM_TradeSkillLock.Locked = false; + end + end + + --GFWUtils.Debug = true; + + if (FOM_State.ShouldFeed and FOM_Config.IconWarning and PetFrameHappiness) then + if (PetFrameHappiness:IsVisible() and PetFrameHappiness:GetAlpha() == 1) then + FOM_FadeOut(); + end + end + + if (type(FOM_LastFeedAttempt) == "table") then + local now = GetTime() or 0; + local startedAt = tonumber(FOM_LastFeedAttempt.startedAt) or 0; + local elapsedSinceFeed = now - startedAt; + if (elapsedSinceFeed >= 0.10 and not FOM_LastFeedAttempt.loggedFirstCheck) then + FOM_LastFeedAttempt.loggedFirstCheck = true; + FOM_Trace("feed outcome check itemId=" .. tostring(FOM_LastFeedAttempt.itemId) + .. " pet='" .. tostring(FOM_LastFeedAttempt.petName or "") + .. "' hasFeedBuff=" .. tostring(FOM_HasFeedEffect() and true or false) + .. " elapsed=" .. string.format("%.2f", elapsedSinceFeed)); + end + + if (FOM_HasFeedEffect()) then + if (FOM_LastFeedAttempt.coreAttemptId and type(MTH_FEED_RecordBuffOutcome) == "function") then + pcall(MTH_FEED_RecordBuffOutcome, FOM_LastFeedAttempt.coreAttemptId, { hasFeedBuff = true, elapsed = elapsedSinceFeed }); + end + FOM_CoreFinalizeAttempt(FOM_LastFeedAttempt.coreAttemptId, "accepted", "accepted"); + FOM_Trace("feed outcome accepted-with-buff itemId=" .. tostring(FOM_LastFeedAttempt.itemId) + .. " pet='" .. tostring(FOM_LastFeedAttempt.petName or "") + .. "' elapsed=" .. string.format("%.2f", elapsedSinceFeed)); + FOM_LastFeedAttempt = nil; + elseif (elapsedSinceFeed >= 2.50) then + if (FOM_LastFeedAttempt.coreAttemptId and type(MTH_FEED_RecordBuffOutcome) == "function") then + pcall(MTH_FEED_RecordBuffOutcome, FOM_LastFeedAttempt.coreAttemptId, { hasFeedBuff = false, elapsed = elapsedSinceFeed }); + end + FOM_CoreRecordReject(FOM_LastFeedAttempt.coreAttemptId, "no-buff", nil); + FOM_CoreFinalizeAttempt(FOM_LastFeedAttempt.coreAttemptId, "rejected", "no-buff"); + FOM_Trace("feed outcome no-eating-buff itemId=" .. tostring(FOM_LastFeedAttempt.itemId) + .. " pet='" .. tostring(FOM_LastFeedAttempt.petName or "") + .. "' elapsed=" .. string.format("%.2f", elapsedSinceFeed) + .. " (likely not accepted by pet)"); + FOM_LastFood = nil; + FOM_LastFeedAttempt = nil; + end + end +end + +function FOM_FadeOut() + local fadeInfo = {}; + fadeInfo.mode = "OUT"; + fadeInfo.timeToFade = 0.5; + fadeInfo.finishedFunc = FOM_FadeIn; + UIFrameFade(PetFrameHappiness, fadeInfo); +end + +--hack since a frame can't have a reference to itself in it +function FOM_FadeIn() + UIFrameFadeIn(PetFrameHappiness, 0.5); +end + +function FOM_CanFeed() + local petInfo = MTH_FOM_GetCorePetInfo(); + if ( not (petInfo and petInfo.liveExists) ) then + GFWUtils.DebugLog("Can't feed; pet doesn't exist."); + return false; + end + if ( tonumber(petInfo.health) and tonumber(petInfo.health) <= 0 ) then + GFWUtils.DebugLog("Can't feed; pet is dead."); + return false; + end + if ( UnitHealth("player") <= 0 ) then + GFWUtils.DebugLog("Can't feed; I'm dead."); + return false; + end + if ( CastingBarFrameStatusBar:IsVisible() ) then + GFWUtils.DebugLog("Can't feed; casting a spell / tradeksill."); + return false; + end + if ( UnitOnTaxi("player") ) then + GFWUtils.DebugLog("Can't feed; flying."); + return false; + end + if ( FOM_State.InCombat ) or ( PlayerFrame.inCombat ) then + GFWUtils.DebugLog("Can't feed; in combat."); + return false; + end + if ( LootFrame:IsVisible() ) then + GFWUtils.DebugLog("Shouldn't feed; loot window is open."); + return false; + end + + + local buff, buffIndex; + local dontFeedBuffTextures = { + "Interface\\Icons\\Ability_Ambush", -- NE Shadowmeld (maybe not unique buff icon?) + "Interface\\Icons\\Ability_Rogue_FeignDeath", -- Feign Death + "Interface\\Icons\\INV_Drink_07", -- drinking + "Interface\\Icons\\INV_Misc_Fork&Knife", -- eating + }; + local mountTextureSubStrings = { + "Ability_Mount", + "INV_Misc_Foot_Kodo", + }; + for buffIndex=0, 15 do + local buff = GetPlayerBuffTexture(buffIndex); + if ( buff ~= nil) then + for _, buffTexture in dontFeedBuffTextures do + if ( buff == buffTexture ) then + GFWUtils.DebugLog("Can't feed; currently, eating, drinking, or feigning death."); + return false; + end + end + if ( UnitLevel("player") >= 40 ) then + for _, buffTexture in mountTextureSubStrings do + if ( string.find(buff, buffTexture) ) then + FOMTooltip:SetUnitBuff("player", buffIndex+1); + local msg = FOMTooltipTextLeft1:GetText(); + if (msg ~= nil) then + msg = string.lower(msg); + for _, mountName in FOM_MOUNT_NAME_SUBSTRINGS do + if (string.find(msg, mountName)) then + GFWUtils.DebugLog("Can't feed; mounted."); + return false; + end + end + end + end + end + end + end + end + + return true; +end + +function FOM_ChatCommandHandler(msg) + + if ( msg == "" ) then + if (MTH_OpenOptions) then + MTH_OpenOptions("FeedOMatic"); + end + return; + end + + -- Check for Pet (we don't really need one for most of our chat commands, but we conveniently use its name.) + local commandPetInfo = MTH_FOM_GetCorePetInfo(); + if (commandPetInfo and commandPetInfo.liveExists and MTH_FOM_IsValidPetName(commandPetInfo.name)) then + petName = commandPetInfo.name; + if (GetLocale() ~= "enUS") then + if (FOM_LocaleInfo == nil) then + FOM_LocaleInfo = {}; + end + local localeFamilyKey = FOM_GetLegacyFamilyKey(); + if (localeFamilyKey ~= nil and localeFamilyKey ~= "") then + FOM_LocaleInfo[localeFamilyKey] = FOM_GetPetDietList(); + end + end + else + petName = "Your pet"; + end + + -- Print Help + if ( msg == "help" ) or ( msg == "" ) then + GFWUtils.Print("Fizzwidget Feed-O-Matic "..FOM_VERSION..":"); + GFWUtils.Print("/feedomatic /fom "); + GFWUtils.Print("- "..GFWUtils.Hilite("help").." - Print this helplist."); + GFWUtils.Print("- "..GFWUtils.Hilite("status").." - Check current settings."); + GFWUtils.Print("- "..GFWUtils.Hilite("reset").." - Reset to default settings."); + GFWUtils.Print("- "..GFWUtils.Hilite("alert chat").." | "..GFWUtils.Hilite("emote").." | "..GFWUtils.Hilite("off").." - Alert via chat window or emote channel when feeding."); + GFWUtils.Print("- "..GFWUtils.Hilite("level content").." | "..GFWUtils.Hilite("happy").." | "..GFWUtils.Hilite("off").." - Provide an extra reminder to feed your pet when happiness is below this level."); + GFWUtils.Print("- "..GFWUtils.Hilite("saveforcook orange").." | "..GFWUtils.Hilite("yellow").." | "..GFWUtils.Hilite("green").." | "..GFWUtils.Hilite("gray").." | "..GFWUtils.Hilite("off").." - Avoid foods used in cooking recipes (based on their difficulty)."); + GFWUtils.Print("- "..GFWUtils.Hilite("savequest on").." | "..GFWUtils.Hilite("off").." - Avoid foods you need to collect for a quest."); + GFWUtils.Print("- "..GFWUtils.Hilite("savebonus on").." | "..GFWUtils.Hilite("off").." - Avoid foods which have bonus effects."); + GFWUtils.Print("- "..GFWUtils.Hilite("fallback on").." | "..GFWUtils.Hilite("off").." - Fall back to foods we'd normally avoid if no other food is available."); + GFWUtils.Print("- "..GFWUtils.Hilite("keepopen ").." - Set when to prefer smaller stacks of food versus evaluating food based on quality. Specify "..GFWUtils.Hilite("off").." instead of a number to always select foods by quality, or "..GFWUtils.Hilite("max").." to always prefer smaller stacks."); + GFWUtils.Print("- "..GFWUtils.Hilite("quality high").." | "..GFWUtils.Hilite("low").." - Set whether to prefer foods that give your pet more happiness faster or less happiness more slowly."); + GFWUtils.Print("- "..GFWUtils.Hilite("feed").." - Feed your pet (automatically finds an appropriate food)."); + GFWUtils.Print("- "..GFWUtils.Hilite("feed ").." - Feed your pet a specific food."); + GFWUtils.Print("- "..GFWUtils.Hilite("add ").." - Add food to list."); + GFWUtils.Print("- "..GFWUtils.Hilite("remove ").." - Remove food from list."); + GFWUtils.Print("- "..GFWUtils.Hilite("show ").." - Show food list."); + return; + end + + if ( msg == "version" ) then + GFWUtils.Print("Fizzwidget Feed-O-Matic "..FOM_VERSION..":"); + return; + end + + -- Check Status + if ( msg == "status" ) then + if (FOM_Config.Level) then + GFWUtils.Print("Feed-O-Matic will help remind you to feed your pet when he's "..GFWUtils.Hilite(FOM_Config.Level).."."); + else + GFWUtils.Print("Feed-O-Matic will "..GFWUtils.Hilite("not").." help remind you when to feed your pet."); + end + + if (FOM_Config.KeepOpenSlots < MAX_KEEPOPEN_SLOTS) then + + if (FOM_Config.PreferHigherQuality) then + GFWUtils.Print("Feed-O-Matic will prefer to use higher quality foods first."); + else + GFWUtils.Print("Feed-O-Matic will prefer to use lower quality foods first."); + end + + if (FOM_Config.KeepOpenSlots == 0) then + GFWUtils.Print("Feed-O-Matic will look first at food quality when determining what to feed to your pet."); + else + GFWUtils.Print("If fewer than "..GFWUtils.Hilite(FOM_Config.KeepOpenSlots).." spaces are open in your inventory, Feed-O-Matic will prefer smaller stacks of food regardless of quality."); + end + + else + GFWUtils.Print("Feed-O-Matic will always prefer smaller stacks of food regardless of quality."); + end + + if (FOM_Config.Alert == "emote") then + GFWUtils.Print("You will automatically emote when feeding "..petName.."."); + elseif (FOM_Config.Alert == "chat") then + GFWUtils.Print("Feed-O-Matic will notify you in chat when feeding "..petName.."."); + else + GFWUtils.Print("There will be no alert when feeding "..petName.."."); + end + + if (FOM_Config.SaveForCookingLevel >= 0 and FOM_Config.SaveForCookingLevel <= 3) then + if (FOM_Config.SaveForCookingLevel == 3) then + level = "orange"; + elseif (FOM_Config.SaveForCookingLevel == 2) then + level = "yellow"; + elseif (FOM_Config.SaveForCookingLevel == 1) then + level = "green"; + elseif (FOM_Config.SaveForCookingLevel == 0) then + level = "gray"; + end + GFWUtils.Print("Feed-O-Matic will avoid foods used in "..GFWUtils.Hilite(level).." or higher Cooking recipes."); + else + GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they're used in Cooking."); + end + + if (FOM_Config.AvoidQuestFood) then + GFWUtils.Print("Feed-O-Matic will avoid foods you need to collect for quests."); + else + GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they're needed for quests."); + end + if (FOM_Config.AvoidBonusFood) then + GFWUtils.Print("Feed-O-Matic will avoid foods that have an additional bonus effect when eaten by a player."); + else + GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they have bonus effects."); + end + if (FOM_Config.Fallback) then + GFWUtils.Print("Feed-O-Matic will fall back to food it would otherwise avoid if no other food is available."); + else + GFWUtils.Print("Feed-O-Matic will not feed your pet if the only foods available are foods you'd prefer to avoid feeding."); + end + return; + end + + -- Reset Variables + if ( msg == "reset" ) then + FOM_Config = FOM_Config_Default; + FOM_Cooking = nil; + FOM_FoodQuality = nil; + FOM_AddedFoods = nil; + FOM_RemovedFoods = nil; + FOM_QuestFood = nil; + GFWUtils.Print("Feed-O-Matic configuration reset."); + FOM_ChatCommandHandler("status"); + return; + end + + -- Turn automatic feeding On + if ( msg == "on" ) then + GFWUtils.Print("Automatic feeding is no longer available due to changes in the WoW client as of Patch 1.10."); + return; + end + + local _, _, cmd, option = string.find(msg, "(%w+) (%w+)"); + + -- Toggle Alert + if ( cmd == "alert" ) then + + if (option == "emote") then + FOM_Config.Alert = "emote"; + GFWUtils.Print("You will automatically emote when feeding "..petName.."."); + elseif (option == "chat") then + FOM_Config.Alert = "chat"; + GFWUtils.Print("Feed-O-Matic will notify you in chat when feeding "..petName.."."); + elseif (option == "off") then + FOM_Config.Alert = nil; + GFWUtils.Print("There will be no alert when feeding "..petName.."."); + else + GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic alert chat").." | "..GFWUtils.Hilite("emote").." | "..GFWUtils.Hilite("off")); + end + return; + end + + -- Set Happiness Level + if ( cmd == "level" ) then + if ( option == "content" ) then + FOM_Config.Level = "content"; + elseif ( option == "happy" ) then + FOM_Config.Level = "happy"; + elseif ( option == "debug" ) then + FOM_Config.Level = "debug"; + else + FOM_Config.Level = nil; + end + if (FOM_Config.Level) then + GFWUtils.Print("Feed-O-Matic will help remind you to feed your pet when he's less than "..GFWUtils.Hilite(FOM_Config.Level).."."); + FOM_CheckHappiness(); + else + GFWUtils.Print("Feed-O-Matic will "..GFWUtils.Hilite("not").." help remind you when to feed your pet."); + FOM_Status.ShouldFeed = nil; + end + return; + end + + -- Set Cooking recipe level + if ( cmd == "saveforcook" ) then + local level = option; + if (level ~= nil) then + local levelNum = FOM_DifficultyToNum(level); + if (levelNum ~= nil) then + FOM_Config.SaveForCookingLevel = levelNum; + FOM_Config.AvoidUsefulFood = true; + GFWUtils.Print("Feed-O-Matic will avoid foods used in "..GFWUtils.Hilite(level).." or higher Cooking recipes. You'll need to open your Cooking window for Feed-O-Matic to cache information about what recipes you know."); + return; + elseif (level == "off") then + FOM_Config.SaveForCookingLevel = 4; + if (not FOM_Config.AvoidQuestFood and not FOM_Config.Avoid9) then + FOM_Config.AvoidUsefulFood = false; + end + GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they're used in Cooking."); + return; + end + end + GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic saveforcook orange").." | "..GFWUtils.Hilite("yellow").." | "..GFWUtils.Hilite("green").." | "..GFWUtils.Hilite("gray").." | "..GFWUtils.Hilite("off")); + return; + end + + -- Set avoiding food with bonuses + if ( cmd == "savequest" ) then + if (option == "on") then + FOM_Config.AvoidQuestFood = true; + FOM_Config.AvoidUsefulFood = true; + FOM_ScanQuests(); + GFWUtils.Print("Feed-O-Matic will avoid foods you need to collect for quests."); + elseif (option == "off") then + FOM_Config.AvoidQuestFood = true; + if not (FOM_Config.SaveForCookingLevel >= 0 and FOM_Config.SaveForCookingLevel <= 3 and not FOM_Config.AvoidBonusFood) then + FOM_Config.AvoidUsefulFood = false; + end + GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they're needed for quests."); + else + GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic savequest on").." | "..GFWUtils.Hilite("off")); + end + return; + end + + -- Set avoiding quest-objective food + if ( cmd == "savebonus" ) then + if (option == "on") then + FOM_Config.AvoidBonusFood = true; + FOM_Config.AvoidUsefulFood = true; + GFWUtils.Print("Feed-O-Matic will avoid foods that have an additional bonus effect when eaten by a player."); + elseif (option == "off") then + FOM_Config.AvoidBonusFood = true; + if not (FOM_Config.SaveForCookingLevel >= 0 and FOM_Config.SaveForCookingLevel <= 3 and not FOM_Config.AvoidQuestFood) then + FOM_Config.AvoidUsefulFood = false; + end + GFWUtils.Print("Feed-O-Matic will choose foods without regard to whether they have bonus effects."); + else + GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic savebonus on").." | "..GFWUtils.Hilite("off")); + end + return; + end + + if ( cmd == "fallback" ) then + if (option == "on") then + FOM_Config.Fallback = true; + GFWUtils.Print("Feed-O-Matic will fall back to food it would otherwise avoid if no other food is available."); + elseif (option == "off") then + FOM_Config.Fallback = false; + GFWUtils.Print("Feed-O-Matic will not feed your pet if the only foods available are foods you'd prefer to avoid feeding."); + else + GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic fallback on").." | "..GFWUtils.Hilite("off")); + end + return; + end + + -- Set quality sorting direction + if ( cmd == "quality" ) then + if (option == "high") then + FOM_Config.PreferHigherQuality = true; + GFWUtils.Print("Feed-O-Matic will prefer to use higher quality foods first."); + elseif (option == "low") then + FOM_Config.PreferHigherQuality = false; + GFWUtils.Print("Feed-O-Matic will prefer to use lower quality foods first."); + else + GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic quality high").." | "..GFWUtils.Hilite("low")); + end + return; + end + + -- Set inventory management threshold + if ( cmd == "keepopen" ) then + if (option == "off" or option == "none") then + newNum = 0; + elseif (option == "max") then + newNum = MAX_KEEPOPEN_SLOTS; + else + newNum = tonumber(option); + end + if (newNum == nil) then + GFWUtils.Print("Usage: "..GFWUtils.Hilite("/feedomatic keepopen ")); + return; + end + FOM_Config.KeepOpenSlots = newNum; + GFWUtils.Print("Feed-O-Matic will try to keep at least "..GFWUtils.Hilite(FOM_Config.KeepOpenSlots).." spaces open in your inventory when looking for food."); + return; + end + + -- Feed Pet + local _, _, cmd, foodString = string.find(msg, "(%w+) *(.*)"); + if ( cmd == "feed" ) then + if (foodString == "") then + FOM_Feed(nil); -- automatically find a food and feed it + else + local inputFoods = { }; + for itemLink in string.gfind(foodString, "%[[%w%s:()\"'-]+%]") do + local _, _, foodName = string.find(itemLink, "^%[([%w%s:()\"'-]+)%]$"); + table.insert(inputFoods, foodName); + end + if (table.getn(inputFoods) == 0) then + table.insert(inputFoods, foodString); -- if no item links, treat whole input line as one food's name + end + + for _, food in inputFoods do + FOM_Feed(food); + end + end + return; + end + + local _, _, cmd, diet, foodString = string.find(msg, "(%w+) (%w+) *(.*)"); + if ( cmd == "add" or cmd == "remove" or cmd == "show" or cmd == "list" ) then + + diet = string.lower(diet); -- let's be case insensitive + if ( FOM_Foods[diet] == nil and diet ~= FOM_DIET_ALL) then + local usageString = "Usage: "..GFWUtils.Hilite("/feedomatic "..cmd..FOM_DIET_MEAT).." | "..GFWUtils.Hilite(FOM_DIET_FISH).." | "..GFWUtils.Hilite(FOM_DIET_BREAD).." | "..GFWUtils.Hilite(FOM_DIET_CHEESE).." | "..GFWUtils.Hilite(FOM_DIET_FRUIT).." | "..GFWUtils.Hilite(FOM_DIET_FUNGUS).." | "..GFWUtils.Hilite(FOM_DIET_BONUS) + if (cmd ~= "show" and cmd ~= "list") then + usageString = usageString.." ."; + end + GFWUtils.Print(usageString); + return; + end + + if (cmd == "show" or cmd == "list") then + if ( diet == FOM_DIET_ALL ) then + diets = {FOM_DIET_MEAT, FOM_DIET_FISH, FOM_DIET_BREAD, FOM_DIET_CHEESE, FOM_DIET_FRUIT, FOM_DIET_FUNGUS, FOM_DIET_BONUS}; + else + diets = {diet}; + end + + for _, aDiet in diets do + local capDiet = string.upper(string.sub(aDiet, 1, 1)) .. string.sub(aDiet, 2); -- print a nicely capitalized version + GFWUtils.Print("Feed-O-Matic "..GFWUtils.Hilite(capDiet).." List:"); + local dietFoods = FOM_Foods[aDiet]; + if (FOM_AddedFoods ~= nil and FOM_AddedFoods[aDiet] ~= nil) then + dietFoods = GFWTable.Merge(dietFoods, FOM_AddedFoods[aDiet]); + end + if (FOM_RemovedFoods ~= nil and FOM_RemovedFoods[aDiet] ~= nil) then + dietFoods = GFWTable.Subtract(dietFoods, FOM_RemovedFoods[aDiet]); + end + table.sort(dietFoods); + for _, food in dietFoods do + local foodName = GetItemInfo(food); + if (foodName) then + if (FOM_FoodIDsToNames == nil) then + FOM_FoodIDsToNames = {}; + end + FOM_FoodIDsToNames[food] = foodName; + GFWUtils.Print(GFWUtils.Hilite(" - ")..foodName); + else + GFWUtils.Print(GFWUtils.Hilite(" - ").."item id "..food.." (name not available)"); + end + end + end + return; + else + + local inputFoods = { }; + for itemLink in string.gfind(foodString, "|c%x+|Hitem:%d+:%d+:%d+:%d+|h%[.-%]|h|r") do + table.insert(inputFoods, itemLink); + local foodID = FOM_IDFromLink(itemLink); + if (foodID) then + local foodName = FOM_NameFromLink(itemLink); + if (FOM_FoodIDsToNames == nil) then + FOM_FoodIDsToNames = {}; + end + FOM_FoodIDsToNames[foodID] = foodName; + end + end + if (table.getn(inputFoods) == 0) then + GFWUtils.Print("The "..GFWUtils.Hilite("/fom "..cmd).." command requires an item link; shift-click an item to insert a link."); + return; + end + + local capDiet = string.upper(string.sub(diet, 1, 1)) .. string.sub(diet, 2); -- print a nicely capitalized version + if ( cmd == "add" ) then + for _, food in inputFoods do + local foodID = FOM_IDFromLink(food); + if ( FOM_AddFood(diet, tonumber(foodID)) ) then + GFWUtils.Print("Added "..food.." to "..GFWUtils.Hilite(capDiet).." list."); + else + GFWUtils.Print(food.." already in "..GFWUtils.Hilite(capDiet).." list."); + end + end + if (FOM_Config.AvoidQuestFood) then + FOM_ScanQuests(); -- in case any of the newly added foods are quest objectives + end + return; + elseif (cmd == "remove" ) then + for _, food in inputFoods do + local foodID = FOM_IDFromLink(food); + if ( FOM_RemoveFood(diet, tonumber(foodID)) ) then + GFWUtils.Print("Removed "..food.." from "..GFWUtils.Hilite(capDiet).." list."); + else + GFWUtils.Print("Could not find "..food.." in "..GFWUtils.Hilite(capDiet).." list."); + end + end + return; + end + end + end + + -- if we got down to here, we got bad input + FOM_ChatCommandHandler("help"); +end + +-- Add a food to a list +function FOM_AddFood(diet, food) + + if (FOM_Foods[diet] == nil) then + GFWUtils.DebugLog("FOM_Foods[diet] == nil"); + end + if (FOM_AddedFoods == nil or FOM_AddedFoods[diet] == nil) then + GFWUtils.DebugLog("FOM_AddedFoods == nil or FOM_AddedFoods[diet] == nil"); + end + if (FOM_RemovedFoods == nil or FOM_RemovedFoods[diet] == nil) then + GFWUtils.DebugLog("FOM_RemovedFoods == nil or FOM_RemovedFoods[diet] == nil"); + end + if ( GFWTable.IndexOf(FOM_Foods[diet], food) == 0 ) then + if (FOM_AddedFoods == nil) then + FOM_AddedFoods = {}; + end + if (FOM_AddedFoods[diet] == nil) then + FOM_AddedFoods[diet] = {}; + end + if ( GFWTable.IndexOf(FOM_AddedFoods[diet], food) == 0 ) then + table.insert( FOM_AddedFoods[diet], food ); + table.sort( FOM_AddedFoods[diet] ); + if (FOM_RemovedFoods and FOM_RemovedFoods[diet] and GFWTable.IndexOf(FOM_RemovedFoods[diet], food) ~= 0) then + table.remove( FOM_RemovedFoods[diet], GFWTable.IndexOf(FOM_RemovedFoods[diet], food) ); + table.sort( FOM_RemovedFoods[diet] ); + end + return true; + else + return false; + end + else + return false; + end + +end + +-- Remove a food from a list +function FOM_RemoveFood(diet, food) + + if (FOM_Foods[diet] == nil) then + GFWUtils.DebugLog("FOM_Foods[diet] == nil"); + end + if (FOM_AddedFoods == nil or FOM_AddedFoods[diet] == nil) then + GFWUtils.DebugLog("FOM_AddedFoods == nil or FOM_AddedFoods[diet] == nil"); + end + if (FOM_RemovedFoods == nil or FOM_RemovedFoods[diet] == nil) then + GFWUtils.DebugLog("FOM_RemovedFoods == nil or FOM_RemovedFoods[diet] == nil"); + end + if ( GFWTable.IndexOf(FOM_Foods[diet], food) ~= 0 ) then + if (FOM_RemovedFoods == nil) then + FOM_RemovedFoods = {}; + end + if (FOM_RemovedFoods[diet] == nil) then + FOM_RemovedFoods[diet] = {}; + end + if ( GFWTable.IndexOf(FOM_RemovedFoods[diet], food) == 0 ) then + table.insert( FOM_RemovedFoods[diet], food ); + table.sort( FOM_RemovedFoods[diet] ); + if (FOM_AddedFoods and FOM_AddedFoods[diet] and GFWTable.IndexOf(FOM_AddedFoods[diet], food) ~= 0) then + table.remove( FOM_AddedFoods[diet], GFWTable.IndexOf(FOM_AddedFoods[diet], food) ); + table.sort( FOM_AddedFoods[diet] ); + end + return true; + else + return false; + end + else + if (FOM_AddedFoods and FOM_AddedFoods[diet] and GFWTable.IndexOf(FOM_AddedFoods[diet], food) ~= 0) then + table.remove( FOM_AddedFoods[diet], GFWTable.IndexOf(FOM_AddedFoods[diet], food) ); + table.sort( FOM_AddedFoods[diet] ); + return true; + end + return false; + end + +end + +function FOM_IsBGActive() + local bgNum = 1; + local status; + repeat + status = GetBattlefieldStatus(bgNum); + if (status == "active") then + return true; + end + bgNum = bgNum + 1; + until (status == nil) + return false; +end +-- Check Happiness +function FOM_CheckHappiness() + local petInfo = MTH_FOM_GetCorePetInfo(); + + -- Check for pet + if not ( petInfo and petInfo.liveExists ) then + FOM_State.ShouldFeed = nil; + return; + end + + -- Get Pet Info + local pet = (MTH_FOM_IsValidPetName(petInfo.name) and petInfo.name) or "Your pet"; + local happiness = petInfo.happiness; + + -- Check No Happiness + if ( happiness == 0 ) or ( happiness == nil ) then return; end + + local level; + if ( FOM_Config.Level == "unhappy" ) then + level = 1; + elseif ( FOM_Config.Level == "content" ) then + level = 2; + elseif ( FOM_Config.Level == "happy" ) then + level = 3; + elseif ( FOM_Config.Level == "debug" ) then + level = 4; + else + level = 0; + end + + -- Check if Need Feeding + if ( happiness < level + 1 ) then + + if (UnitIsDead("pet")) then return; end + if (UnitAffectingCombat("pet")) then return; end + if (UnitAffectingCombat("player")) then return; end + + FOM_State.ShouldFeed = true; + if (not FOM_HasFeedEffect() and GetTime() - FOM_LastWarning > FOM_WARNING_INTERVAL) then + if (FOM_Config.TextWarning) then + local msg; + if (level - happiness == 0) then + msg = FOM_PET_HUNGRY; + else + msg = FOM_PET_VERY_HUNGRY; + end + GFWUtils.Print(string.format(msg, pet)); + GFWUtils.Note(string.format(msg, pet)); + end + FOM_PlayHungrySound(); + FOM_LastWarning = GetTime(); + end + else + FOM_State.ShouldFeed = nil; + end + +end + +FOM_HungrySounds = { + [BAT] = "Sound\\Creature\\FelBat\\FelBatDeath.wav", + [BEAR] = "Sound\\Creature\\Bear\\mBearDeathA.wav", + [BOAR] = "Sound\\Creature\\Boar\\mWildBoarAggro2.wav", + [CAT] = "Sound\\Creature\\Tiger\\mTigerStand2A.wav", + [CARRION_BIRD] = "Sound\\Creature\\Carrion\\mCarrionWoundCriticalA.wav", + [CRAB] = "Sound\\Creature\\Crab\\CrabDeathA.wav", + [CROCOLISK] = "Sound\\Creature\\Basilisk\\mBasiliskSpellCastA.wav", + [GORILLA] = "Sound\\Creature\\Gorilla\\GorillaDeathA.wav", + [HYENA] = "Sound\\Creature\\Hyena\\HyenaPreAggroA.wav", + [OWL] = "Sound\\Creature\\OWl\\OwlPreAggro.wav", + [RAPTOR] = "Sound\\Creature\\Raptor\\mRaptorWoundCriticalA.wav", + [SCORPID] = "Sound\\Creature\\SilithidWasp\\mSilithidWaspStand2A.wav", + [SPIDER] = "Sound\\Creature\\Tarantula\\mTarantulaFidget2a.wav", + [TALLSTRIDER] = "Sound\\Creature\\TallStrider\\tallStriderPreAggroA.wav", + [TURTLE] = "Sound\\Creature\\SeaTurtle\\SeaTurtleWoundCritA.wav", + [WIND_SERPENT] = "Sound\\Creature\\WindSerpant\\mWindSerpantDeathA.wav", + [WOLF] = "Sound\\Creature\\Wolf\\mWolfFidget2c.wav", +}; +function FOM_PlayHungrySound() + if (FOM_Config.AudioWarning) then + local type = FOM_GetLegacyFamilyKey(); + local sound = FOM_HungrySounds[type]; + if (sound == nil or FOM_Config.AudioWarning == "bell") then + PlaySoundFile("Sound\\Doodad\\BellTollNightElf.wav"); + else + PlaySoundFile(sound); + end + end +end + +-- Check Feed Effect +function FOM_HasFeedEffect() + + local i = 1; + local buff; + buff = UnitBuff("pet", i); + while buff do + if ( string.find(buff, "Ability_Hunter_BeastTraining") ) then + return true; + end + i = i + 1; + buff = UnitBuff("pet", i); + end + return false; + +end + +-- Feed Pet +function FOM_Feed(aFood) + if MTH_IsModuleEnabled and not MTH_IsModuleEnabled("feedomatic") then + if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + MTH:Print("[FOM WRAP] FOM_Feed blocked: module disabled", "debug") + end + return; + end + + -- Make sure we have a feedable pet + local petInfo = MTH_FOM_GetCorePetInfo(); + if not (petInfo and petInfo.liveExists) then + GFWUtils.Note(FOM_ERROR_NO_PET); + return; + end + if (petInfo.dead or (tonumber(petInfo.health) and tonumber(petInfo.health) <= 0)) then + GFWUtils.Note(FOM_ERROR_PET_DEAD); + return; + end + if (FOM_GetPetDietList() == nil) then + GFWUtils.Note(FOM_ERROR_NO_FEEDABLE_PET); + return; + end + if (UnitAffectingCombat("player") or UnitAffectingCombat("pet")) then + GFWUtils.Note(FOM_ERROR_IN_COMBAT); + return; + end + + -- Assign Variable + local pet = (MTH_FOM_IsValidPetName(petInfo.name) and petInfo.name) or "Your pet"; + FOM_Trace("feed request pet='" .. tostring(pet) + .. "' level=" .. tostring(petInfo.level) + .. " happiness=" .. tostring(petInfo.happiness) + .. " manualFood='" .. tostring(aFood or "") .. "'") + + FOM_CheckSetup(); + if (FOM_LastPetName == nil or FOM_LastPetName == "") then + GFWUtils.DebugLog("Can't get pet info."); + return; + end + + if (GetLocale() ~= "enUS") then + if (FOM_LocaleInfo == nil) then + FOM_LocaleInfo = {}; + end + local localeFamilyKey = FOM_GetLegacyFamilyKey(); + if (localeFamilyKey ~= nil and localeFamilyKey ~= "") then + FOM_LocaleInfo[localeFamilyKey] = FOM_GetPetDietList(); + end + end + + -- Look for Food + local foodBag, foodItem; + if (aFood ~= nil) then + -- if told to feed a specific food, do so + foodBag, foodItem = FOM_FindSpecificFood(aFood); + if ( foodBag == nil) then + -- No Food Could be Found + GFWUtils.Print(string.format(FOM_ERROR_FOOD_NOT_FOUND, pet, aFood)); + return; + end + FOM_LastChoiceReason = "manual selection"; + else + foodBag, foodItem = FOM_NewFindFood(); + end + + local fallbackBag, fallbackItem = FOM_NewFindFood(1); + + if ( foodBag == nil) then + if (not FOM_Config.Fallback and fallbackBag) then + local pronoun = FOM_PRONOUN_MALE; + if (UnitSex("pet") == 3) then pronoun = FOM_PRONOUN_FEMALE; end + GFWUtils.Print(string.format(FOM_ERROR_NO_FOOD_NO_FALLBACK, pet, pronoun)); + return; + elseif (fallbackBag) then + foodBag, foodItem = fallbackBag, fallbackItem; + if (FOM_LastChoiceReason == nil or FOM_LastChoiceReason == "") then + FOM_LastChoiceReason = "fallback to useful food"; + else + FOM_LastChoiceReason = FOM_LastChoiceReason .. ", fallback to useful food"; + end + else + -- No Food Could be Found + GFWUtils.Print(string.format(FOM_ERROR_NO_FOOD, pet)); + FOM_LastChoiceReason = nil; + return; + end + end + + FOM_LastFood = GetContainerItemLink(foodBag, foodItem); + local selectedId = FOM_IDFromLink(FOM_LastFood) + local selectedFoodLevel = (type(MTH_FEED_GetFoodLevel) == "function") and MTH_FEED_GetFoodLevel(selectedId) or nil + local coreAttemptId = FOM_CoreBeginAttempt({ + itemId = selectedId, + itemName = FOM_NameFromLink(FOM_LastFood), + itemLink = tostring(FOM_LastFood or ""), + source = "feedomatic:auto", + petId = tostring(petInfo and petInfo.id or ""), + petName = tostring(pet or ""), + family = tostring(petInfo and petInfo.family or ""), + petLevel = tonumber(petInfo and petInfo.level) or nil, + foodLevel = selectedFoodLevel, + }) + FOM_Trace("selected food link='" .. tostring(FOM_LastFood) + .. "' itemId=" .. tostring(selectedId) + .. " bag=" .. tostring(foodBag) + .. " slot=" .. tostring(foodItem) + .. " reason='" .. tostring(FOM_LastChoiceReason or "") .. "'") + + GFWUtils.DebugLog("Picked "..FOM_LastFood.." (bag "..foodBag..", slot "..foodItem..") for feeding."); + if (FOM_Config.Debug) then + -- don't actually feed anything, just show what we would choose + return; + end + + -- Actually feed the item to the pet + PickupContainerItem(foodBag, foodItem); + FOM_Trace("feed execute pickup bag=" .. tostring(foodBag) + .. " slot=" .. tostring(foodItem) + .. " cursorHasItem=" .. tostring(CursorHasItem() and true or false)) + if ( CursorHasItem() ) then + MTH_FEED_SuppressChatUntil = (GetTime() or 0) + 2.0; + MTH_FEED_SuppressChatFoodName = FOM_NameFromLink(FOM_LastFood); + MTH_FEED_SuppressCoreDropHook = true; + local ok = pcall(DropItemOnUnit, "pet"); + MTH_FEED_SuppressCoreDropHook = nil; + if (not ok) then + FOM_Trace("feed execute drop on pet failed during protected call"); + end + FOM_Trace("feed execute drop on pet cursorHasItemAfterDrop=" .. tostring(CursorHasItem() and true or false)) + end + if (coreAttemptId and type(MTH_FEED_RecordClientDropResult) == "function") then + pcall(MTH_FEED_RecordClientDropResult, coreAttemptId, { + consumedFromCursor = (CursorHasItem() and false or true), + }) + end + if ( CursorHasItem() ) then + FOM_Trace("feed execute immediate-fail item stayed on cursor; likely rejected before combat log/error parse") + PickupContainerItem(foodBag, foodItem); + FOM_CoreRecordReject(coreAttemptId, "client-drop-fail", nil); + FOM_CoreFinalizeAttempt(coreAttemptId, "rejected", "client-drop-fail"); + FOM_Trace("feed execute returned item to bag") + else + FOM_Trace("feed execute accepted by client (item consumed from cursor)") + FOM_LastFeedAttempt = { + startedAt = GetTime() or 0, + petName = tostring(pet or ""), + itemId = selectedId, + itemLink = tostring(FOM_LastFood or ""), + coreAttemptId = coreAttemptId, + loggedFirstCheck = false, + } + FOM_State.ShouldFeed = nil; + -- Alert + if ( FOM_Config.Alert == "chat") then + GFWUtils.Print(string.format(FOM_FEEDING_EAT, pet, GFWUtils.Hilite(FOM_LastFood))); + elseif ( FOM_Config.Alert == "emote") then + SendChatMessage(string.format(FOM_FEEDING_FEED, pet, FOM_LastFood).. FOM_RandomEmote(), "EMOTE"); + end + end + +end + +function FOM_RandomEmote() + + local randomEmotes = {}; + if (UnitSex("pet") == 2) then + randomEmotes = GFWTable.Merge(randomEmotes, FOM_Emotes["male"]); + elseif (UnitSex("pet") == 3) then + randomEmotes = GFWTable.Merge(randomEmotes, FOM_Emotes["female"]); + end + + randomEmotes = GFWTable.Merge(randomEmotes, FOM_Emotes[FOM_GetLegacyFamilyKey()]); + randomEmotes = GFWTable.Merge(randomEmotes, FOM_Emotes[FOM_NameFromLink(FOM_LastFood)]); + randomEmotes = GFWTable.Merge(randomEmotes, FOM_Emotes["any"]); + + return randomEmotes[math.random(table.getn(randomEmotes))]; + +end + +function FOM_FindSpecificFood(foodName) + for bagNum = 0, 4 do + if (not FOM_BagIsQuiver(bagNum) ) then + -- skip bags that can't contain food + + local bagSize = GetContainerNumSlots(bagNum); + for itemNum = 1, bagSize do + + itemName = FOM_GetItemName(bagNum, itemNum); + if ( itemName == foodName ) then + return bagNum, itemNum; + end + + end + end + end + return nil; +end + +function FOM_IsTemporaryFood(itemLink) + + local _, _, link = string.find(itemLink, "(item:%d+:%d+:%d+:%d+)"); + if (link == nil or link == "") then + return false; + end + FOMTooltip:ClearLines(); + FOMTooltip:SetHyperlink(link); + if (FOMTooltipTextLeft2:GetText() == ITEM_CONJURED) then + return true; + else + return false; + end + +end + +function FOM_FlatFoodList() + local foodList = {}; + local overflowFoodList = {}; + local scanned = 0; + local inDiet = 0; + local compatibleCount = 0; + local overflowCompatibleCount = 0; + local rejectedByException = 0; + FOM_Quantity = { }; + local petInfo = MTH_FOM_GetCorePetInfo(); + local petLevel = tonumber(petInfo and petInfo.level) or 0; + for bagNum = 0, 4 do + if (not FOM_BagIsQuiver(bagNum) ) then + -- skip bags that can't contain food + for itemNum = 1, GetContainerNumSlots(bagNum) do + local itemLink = GetContainerItemLink(bagNum, itemNum); + if (itemLink) then + scanned = scanned + 1; + local itemID = FOM_IDFromLink(itemLink); + local _, itemCount = GetContainerItemInfo(bagNum, itemNum); + if ( FOM_IsInDiet(itemID) ) then + inDiet = inDiet + 1; + if (FOM_FoodIDsToNames == nil) then + FOM_FoodIDsToNames = {}; + end + local name = FOM_NameFromLink(itemLink); + FOM_FoodIDsToNames[itemID] = name; + local isUseful = FOM_IsUsefulFood(itemID, itemCount); + local isKnownFood = (type(MTH_FEED_IsKnownFoodItem) == "function") and MTH_FEED_IsKnownFoodItem(itemID) or FOM_IsKnownFood(itemID); + local foodLevel = (type(MTH_FEED_GetFoodLevel) == "function") and MTH_FEED_GetFoodLevel(itemID) or nil; + if (not isKnownFood and type(MTH_FEED_RecordUnknownFoodCandidate) == "function") then + pcall(MTH_FEED_RecordUnknownFoodCandidate, { + itemId = itemID, + itemName = name, + itemLink = tostring(itemLink or ""), + source = "bag-scan", + observedFoodLevel = foodLevel, + observedDietHint = nil, + evidenceType = "bag-scan", + }); + end + + local isExceptionReject = (type(MTH_FEED_IsExceptionReject) == "function") and MTH_FEED_IsExceptionReject(itemID) or false; + if (isExceptionReject) then + rejectedByException = rejectedByException + 1; + end + + local isCompatible = true; + local isAbovePreferredLevel = false; + if (petLevel > 0 and foodLevel ~= nil) then + if (type(MTH_FEED_GetFoodCompatibilityWindow) == "function") then + local minAllowed, maxAllowed = MTH_FEED_GetFoodCompatibilityWindow(petLevel); + local numericFood = tonumber(foodLevel) or 0; + if (minAllowed ~= nil and numericFood < tonumber(minAllowed)) then + isCompatible = false; + end + if (maxAllowed ~= nil and numericFood > tonumber(maxAllowed)) then + isCompatible = false; + isAbovePreferredLevel = true; + end + elseif (type(MTH_FEED_IsFoodCompatible) == "function") then + isCompatible = MTH_FEED_IsFoodCompatible(petLevel, foodLevel); + end + end + + if (isCompatible and not isExceptionReject) then + compatibleCount = compatibleCount + 1; + local foodQuality = tonumber(foodLevel) or MAX_QUALITY; + table.insert(foodList, {bag=bagNum, slot=itemNum, link=itemLink, count=itemCount, quality=foodQuality, useful=isUseful, temp=FOM_IsTemporaryFood(itemLink)}); + elseif (isAbovePreferredLevel and not isExceptionReject) then + overflowCompatibleCount = overflowCompatibleCount + 1; + local overflowQuality = tonumber(foodLevel) or MAX_QUALITY; + table.insert(overflowFoodList, {bag=bagNum, slot=itemNum, link=itemLink, count=itemCount, quality=overflowQuality, useful=isUseful, temp=FOM_IsTemporaryFood(itemLink), overflow=true}); + end + end + end + end + end + end + if (table.getn(foodList) == 0 and table.getn(overflowFoodList) > 0) then + foodList = overflowFoodList; + end + FOM_Trace("flat food scan scanned=" .. tostring(scanned) + .. " inDiet=" .. tostring(inDiet) + .. " compatible=" .. tostring(compatibleCount) + .. " overflowCompatible=" .. tostring(overflowCompatibleCount) + .. " exceptionRejects=" .. tostring(rejectedByException) + .. " candidates=" .. tostring(table.getn(foodList))) + return foodList; +end + +function FOM_NewFindFood(fallback) + local FlatFoodList = FOM_FlatFoodList(); + local initialCount = table.getn(FlatFoodList) + local reasonParts = {}; + + -- if there are any conjured foods, drop everything else from the list + local tempFoodsOnly = {}; + for _, foodInfo in FlatFoodList do + if (foodInfo.temp) then + table.insert(tempFoodsOnly, foodInfo); + end + end + if (table.getn(tempFoodsOnly) > 0) then + FlatFoodList = tempFoodsOnly; + table.insert(reasonParts, "conjured food first"); + end + local postConjuredCount = table.getn(FlatFoodList) + + + table.sort(FlatFoodList, FOM_SortCount); -- small stacks first + if (FOM_NumOpenBagSlots() > FOM_Config.KeepOpenSlots) then + if (FOM_Config.PreferHigherQuality) then + table.sort(FlatFoodList, FOM_SortQualityDescending); -- higher quality first + table.insert(reasonParts, "higher quality preferred"); + else + table.sort(FlatFoodList, FOM_SortQualityAscending); -- lower quality first + table.insert(reasonParts, "lower quality preferred"); + end + else + table.insert(reasonParts, "small stack to free bag space"); + end + if (FOM_Config.AvoidUsefulFood and not fallback) then + local nonUsefulFoodsOnly = {}; + for _, foodInfo in FlatFoodList do + if (not foodInfo.useful) then + table.insert(nonUsefulFoodsOnly, foodInfo); + end + end + FlatFoodList = nonUsefulFoodsOnly; + table.insert(reasonParts, "avoiding useful food"); + else + table.sort(FlatFoodList, FOM_SortUseful); -- non-useful first + if (fallback) then + table.insert(reasonParts, "fallback allows useful food"); + end + end + FOM_Trace("find food fallback=" .. tostring(fallback and true or false) + .. " initial=" .. tostring(initialCount) + .. " postConjured=" .. tostring(postConjuredCount) + .. " postFilters=" .. tostring(table.getn(FlatFoodList)) + .. " reasons='" .. tostring(table.concat(reasonParts, ", ")) .. "'") + + for _, foodInfo in FlatFoodList do + if (foodInfo.overflow) then + if (table.getn(reasonParts) > 0) then + FOM_LastChoiceReason = table.concat(reasonParts, ", ") .. ", overflow level fallback"; + else + FOM_LastChoiceReason = "overflow level fallback"; + end + elseif (table.getn(reasonParts) > 0) then + FOM_LastChoiceReason = table.concat(reasonParts, ", "); + else + FOM_LastChoiceReason = nil; + end + FOM_Trace("find food result bag=" .. tostring(foodInfo.bag) + .. " slot=" .. tostring(foodInfo.slot) + .. " quality=" .. tostring(foodInfo.quality) + .. " useful=" .. tostring(foodInfo.useful and true or false) + .. " temp=" .. tostring(foodInfo.temp and true or false)) + return foodInfo.bag, foodInfo.slot; + end + FOM_LastChoiceReason = nil; + FOM_Trace("find food result: none") + + return nil; +end + +function FOM_SortCount(a, b) + return a.count < b.count; +end + +function FOM_SortQualityDescending(a, b) + return a.quality > b.quality; +end + +function FOM_SortQualityAscending(a, b) + return a.quality < b.quality; +end + +function FOM_SortUseful(a, b) + if (a.useful) then + aUseful = 1; + else + aUseful = 0; + end + if (b.useful) then + bUseful = 1; + else + bUseful = 0; + end + return aUseful < bUseful; +end + +function FOM_IsUsefulFood(itemID, quantity) + local foodName = GetItemInfo(itemID); + if (foodName == nil) then + GFWUtils.DebugLog("Can't get info for item ID "..itemID..", assuming it's OK to eat."); + return false; + end + if (FOM_Cooking and FOM_Cooking[FOM_RealmPlayer] and FOM_Cooking[FOM_RealmPlayer][itemID]) then + if (FOM_Cooking[FOM_RealmPlayer][itemID] >= FOM_Config.SaveForCookingLevel) then + GFWUtils.DebugLog("Skipping "..quantity.."x "..foodName.."; is good for cooking."); + return true; + end + end + if (FOM_Config.AvoidQuestFood) then + FOM_ScanQuests(); + if (FOM_QuestFood ~= nil and FOM_QuestFood[FOM_RealmPlayer] ~= nil and FOM_QuestFood[FOM_RealmPlayer][foodName]) then + if (FOM_Quantity[foodName] == nil) then + FOM_Quantity[foodName] = quantity; + else + FOM_Quantity[foodName] = FOM_Quantity[foodName] + quantity; + end + if (FOM_Quantity[foodName] > FOM_QuestFood[FOM_RealmPlayer][foodName]) then + GFWUtils.DebugLog("Not skipping "..quantity.."x "..foodName.."; is needed for quest, but we have more than enough."); + return false; + else + GFWUtils.DebugLog("Skipping "..quantity.."x "..foodName.."; is needed for quest."); + return true; + end + end + end + if (FOM_Config.AvoidBonusFood and FOM_IsInDiet(itemID, FOM_DIET_BONUS)) then + GFWUtils.DebugLog("Skipping "..quantity.."x "..foodName.."; has bonus effect when eaten by player."); + return true; + end + --GFWUtils.DebugLog("Not skipping "..quantity.."x "..foodName.."; doesn't have other uses."); + return false; +end + +function FOM_NumOpenBagSlots() + local openSlots = 0; + for bagNum = 0, 4 do + if (not FOM_BagIsQuiver(bagNum) ) then + -- skip bags that can't contain food + + local bagSize = GetContainerNumSlots(bagNum); + for itemNum = 1, bagSize do + if (GetContainerItemInfo(bagNum, itemNum) == nil) then + openSlots = openSlots + 1; + end + end + end + end + return openSlots; +end + +function FOM_IsInDiet(food, dietList) + + if ( dietList == nil ) then + dietList = FOM_GetPetDietList(); + end + if ( dietList == nil ) then + return false; + end + if (type(dietList) ~= "table") then + dietList = {dietList}; + end + for _, diet in dietList do + diet = FOM_NormalizeDietToken(diet); + if (diet == nil) then + diet = ""; + end + if (FOM_Foods[diet] == nil) then + GFWUtils.DebugLog("FOM_Foods[diet] == nil"); + end + if (FOM_RemovedFoods ~= nil and FOM_RemovedFoods[diet] ~= nil and GFWTable.IndexOf(FOM_RemovedFoods[diet], food) ~= 0) then + return false; + end + if (FOM_AddedFoods ~= nil and FOM_AddedFoods[diet] ~= nil and GFWTable.IndexOf(FOM_AddedFoods[diet], food) ~= 0) then + return true; + end + if (GFWTable.IndexOf(FOM_Foods[diet], food) ~= 0) then + return true; + end + end + + return false; + +end + +function FOM_IsKnownFood(food) + return FOM_IsInDiet(food, {FOM_DIET_MEAT, FOM_DIET_FISH, FOM_DIET_BREAD, FOM_DIET_CHEESE, FOM_DIET_FUNGUS, FOM_DIET_FRUIT}); +end + +-- Get Item Name +function FOM_GetItemName(bag, slot) + + local itemLink = GetContainerItemLink(bag, slot); + if (itemLink) then + return FOM_NameFromLink(itemLink); + else + return ""; + end +end + +function MTH_PetRenameHook(newName) + + FOM_CheckSetup(); + local oldName = FOM_LastPetName; + + -- move our saved food quality data to be indexed under the new name + if (type(FOM_FoodQuality) == "table" and type(FOM_RealmPlayer) == "string" and FOM_RealmPlayer ~= "" and type(FOM_FoodQuality[FOM_RealmPlayer]) == "table") then + FOM_FoodQuality[FOM_RealmPlayer][newName] = FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName]; + FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName] = nil; + end + if (type(MTH_PETS_RecordPetRename) == "function") then + MTH_PETS_RecordPetRename(oldName, newName, "pet-lifecycle:rename"); + end + FOM_LastPetName = newName; + + local renameDelegate = (_G and _G["MTH_PETL_Original_PetRename"]) or (_G and _G["MTH_PETS_CoreOriginal_PetRename"]); + if (type(renameDelegate) == "function" and renameDelegate ~= MTH_PetRenameHook) then + renameDelegate(newName); + end + +end + +function MTH_PetAbandonHook() + + FOM_CheckSetup(); + local abandonedPetName = FOM_LastPetName; + if (type(MTH_PETS_RecordPetAbandon) == "function") then + MTH_PETS_RecordPetAbandon("pet-lifecycle:abandon", abandonedPetName); + end + + -- delete saved food-quality data for this pet so we don't bloat SavedVariables + FOM_FoodQuality[FOM_RealmPlayer][FOM_LastPetName] = nil; + FOM_LastPetName = nil; + + MTH_PETL_Original_PetAbandon(); + +end + +FOM_PetRename = MTH_PetRenameHook; +FOM_PetAbandon = MTH_PetAbandonHook; + +-- The icon for the cooking spell is unique and the same in all languages; use that to determine the localized name. +function FOM_CookingSpellName() + FOM_COOKING_ICON = "Interface\\Icons\\INV_Misc_Food_15"; + if (FOM_COOKING_NAME == nil) then + local spellName; + local i = 0; + repeat + i = i + 1; + spellName = GetSpellName(i, BOOKTYPE_SPELL); + if (spellName ~= nil and GetSpellTexture(i, BOOKTYPE_SPELL) == FOM_COOKING_ICON) then + FOM_COOKING_NAME = spellName; + return FOM_COOKING_NAME; + end + until (spellName == nil); + end + return FOM_COOKING_NAME; +end + +function FOM_BagIsQuiver(bagNum) + local invSlotID = ContainerIDToInventoryID(bagNum); + local bagLink = GetInventoryItemLink("player", invSlotID); + if (bagLink == nil) then + return false; + end + local _, _, itemID = string.find(bagLink, "item:(%d+)"); + if (tonumber(itemID)) then + itemID = tonumber(itemID); + local name, link, rarity, minLevel, type, subType, stackCount, equipLoc = GetItemInfo(itemID); + if (type == "Ammo Pouch" or type == "Quiver" or subType == "Ammo Pouch" or subType == "Quiver") then + return true; + end + if (type == FOM_AMMO_POUCH or type == FOM_QUIVER or subType == FOM_AMMO_POUCH or subType == FOM_QUIVER) then + return true; + end + end + return false; +end + +function FOM_IDFromLink(itemLink) + if (itemLink == nil) then return nil; end + local _, _, itemID = string.find(itemLink, "item:(%d+)"); + if (tonumber(itemID)) then + return tonumber(itemID); + else + return nil; + end +end + +function FOM_NameFromLink(itemLink) + if (itemLink == nil) then return nil; end + local _, _, name = string.find(itemLink, "%[(.-)%]"); + return name; +end + + diff --git a/modules/feedomatic/FeedOMatic_Emotes.lua b/modules/feedomatic/FeedOMatic_Emotes.lua new file mode 100644 index 0000000..547bda9 --- /dev/null +++ b/modules/feedomatic/FeedOMatic_Emotes.lua @@ -0,0 +1,86 @@ +FOM_Emotes = { + + ["any"] = { + "Yum!", + "Mmm, good stuff.", + }, + + ["male"] = { + "Good boy!", + }, + ["female"] = { + "Good girl!", + }, + + ["Zesty Clam Meat"] = { + "Mmm, zesty!", + }, + ["Heaven Peach"] = { + "Movin' to the country...", + }, + ["Mystery Meat"] = { + "Tastes like chicken.", + }, + ["Dragonbreath Chili"] = { + "Yow, spicy!", + }, + + ["Bat"] = { + "Squeak-squeak snack attack!", + }, + ["Bear"] = { + "One paw for me, one paw for the bear.", + }, + ["Boar"] = { + "Good piggy!" + }, + ["Carrion Bird"] = { + "A gourmet meal... by scavenger standards.", + }, + ["Cat"] = { + "Nice kitty!", + }, + ["Crab"] = { + "Crabby no more!", + }, + ["Crocolisk"] = { + "Easy there, chompy friend.", + }, + ["Gorilla"] = { + "Big buddy deserves a big bite.", + }, + ["Hyena"] = { + "Good dog!", + }, + ["Owl"] = { + "Whoo's a hungry hunter helper?", + }, + ["Raptor"] = { + "Tiny arms, huge appetite.", + }, + ["Scorpid"] = { + "No stinging the cook, deal?", + }, + ["Spider"] = { + "Eight legs, zero table manners.", + }, + ["Tallstrider"] = { + "Long neck, longer lunch break.", + }, + ["Turtle"] = { + "Slow and steady wins the snacks.", + }, + ["Wind Serpent"] = { + "Lightning in the sky, snacks on the ground.", + }, + ["Wolf"] = { + "Good dog!", + }, + + ["Foxes"] = { + "Clever fox, clever snack choice.", + }, + ["Serpents (Cobra)"] = { + "No hissing with your mouth full.", + }, +}; diff --git a/modules/feedomatic/FeedOMatic_Foods.lua b/modules/feedomatic/FeedOMatic_Foods.lua new file mode 100644 index 0000000..e1a77d8 --- /dev/null +++ b/modules/feedomatic/FeedOMatic_Foods.lua @@ -0,0 +1,238 @@ +FOM_Foods = { + [FOM_DIET_FUNGUS] = { + 4607, -- Delicious Cave Mold + 8948, -- Dried King Bolete + 4604, -- Forest Mushroom Cap + 4608, -- Raw Black Truffle + 4605, -- Red-speckled Mushroom + 3448, -- Senggin Root + 4606, -- Spongy Morel + }, + [FOM_DIET_FISH] = { + 13889, -- Raw Whitescale Salmon + 19996, -- Harvest Fish + 13933, -- Lobster Stew + 13888, -- Darkclaw Lobster + 13935, -- Baked Salmon + 13546, -- Bloodbelly Fish + 6290, -- Brilliant Smallfish + 4593, -- Bristle Whisker Catfish + 5503, -- Clam Meat + 16971, -- Clamlette Surprise + 2682, -- Cooked Crab Claw + 13927, -- Cooked Glossy Mightfish + 2675, -- Crawler Claw + 13930, -- Filet of Redgill + 5476, -- Fillet of Frenzy + 4655, -- Giant Clam Meat + 6038, -- Giant Clam Scorcho + 13928, -- Grilled Squid + 13929, -- Hot Smoked Bass + 13893, -- Large Raw Mightfish + 4592, -- Longjaw Mud Snapper + 8364, -- Mithril Head Trout + 13932, -- Poached Sunscale Salmon + 5095, -- Rainbow Fin Albacore + 6291, -- Raw Brilliant Smallfish + 6308, -- Raw Bristle Whisker Catfish + 13754, -- Raw Glossy Mightfish + 6317, -- Raw Loch Frenzy + 6289, -- Raw Longjaw Mud Snapper + 8365, -- Raw Mithril Head Trout + 6361, -- Raw Rainbow Fin Albacore + 13758, -- Raw Redgill + 6362, -- Raw Rockscale Cod + 6303, -- Raw Slitherskin Mackerel + 8959, -- Raw Spinefin Halibut + 4603, -- Raw Spotted Yellowtail + 13756, -- Raw Summer Bass + 13760, -- Raw Sunscale Salmon + 4594, -- Rockscale Cod + 787, -- Slitherskin Mackerel + 5468, -- Soft Frenzy Flesh + 15924, -- Soft-shelled Clam Meat + 12216, -- Spiced Chili Crab + 8957, -- Spinefin Halibut + 6887, -- Spotted Yellowtail + 5504, -- Tangy Clam Meat + 12206, -- Tender Crab Meat + 13755, -- Winter Squid + 7974, -- Zesty Clam Meat + }, + [FOM_DIET_MEAT] = { + 21235, -- Winter Veil Roast + 19995, -- Harvest Boar + 4457, -- Barbecued Buzzard Wing + 3173, -- Bear Meat + 2888, -- Beer Basted Boar Ribs + 3730, -- Big Bear Meat + 3726, -- Big Bear Steak + 3220, -- Blood Sausage + 2677, -- Boar Ribs + 3404, -- Buzzard Wing + 12213, -- Carrion Surprise + 2679, -- Charred Wolf Meat + 769, -- Chunk of Boar Meat + 2673, -- Coyote Meat + 2684, -- Coyote Steak + 1081, -- Crisp Spider Meat + 12224, -- Crispy Bat Wing + 5479, -- Crispy Lizard Tail + 2924, -- Crocolisk Meat + 3662, -- Crocolisk Steak + 4599, -- Cured Ham Steak + 17119, -- Deeprun Rat Kabob + 5478, -- Dig Rat Stew + 5051, -- Dig Rat + 12217, -- Dragonbreath Chili + 2687, -- Dry Pork Ribs + 9681, -- Grilled King Crawler Legs + 2287, -- Haunch of Meat + 12204, -- Heavy Kodo Meat + 3727, -- Hot Lion Chops + 13851, -- Hot Wolf Ribs + 12212, -- Jungle Stew + 5472, -- Kaldorei Spider Kabob + 5467, -- Kodo Meat + 5480, -- Lean Venison + 1015, -- Lean Wolf Flank + 12209, -- Lean Wolf Steak + 3731, -- Lion Meat + 12223, -- Meaty Bat Wing + 3770, -- Mutton Chop + 12037, -- Mystery Meat + 4739, -- Plainstrider Meat + 12184, -- Raptor Flesh + 13759, -- Raw Nightfin Snapper + 12203, -- Red Wolf Meat + 12210, -- Roast Raptor + 2681, -- Roasted Boar Meat + 5474, -- Roasted Kodo Meat + 8952, -- Roasted Quail + 1017, -- Seasoned Wolf Kabob + 5465, -- Small Spider Leg + 6890, -- Smoked Bear Meat + 3729, -- Soothing Turtle Bisque + 2680, -- Spiced Wolf Meat + 17222, -- Spider Sausage + 5471, -- Stag Meat + 5469, -- Strider Meat + 5477, -- Strider Stew + 2672, -- Stringy Wolf Meat + 2685, -- Succulent Pork Ribs + 3728, -- Tasty Lion Steak + 3667, -- Tender Crocolisk Meat + 12208, -- Tender Wolf Meat + 18045, -- Tender Wolf Steak + 5470, -- Thunder Lizard Tail + 12202, -- Tiger Meat + 117, -- Tough Jerky + 3712, -- Turtle Meat + 12205, -- White Spider Meat + 3771, -- Wild Hog Shank + }, + [FOM_DIET_BREAD] = { + 1113, -- Conjured Bread + 5349, -- Conjured Muffin + 1487, -- Conjured Pumpernickel + 1114, -- Conjured Rye + 8075, -- Conjured Sourdough + 8076, -- Conjured Sweet Roll + 22895, -- Conjured Cinnamon Roll + 83004, -- Conjured Mana Orange + 19696, -- Harvest Bread + 21254, -- Winter Veil Cookie + 2683, -- Crab Cake + 4541, -- Freshly Baked Bread + 17197, -- Gingerbread Cookie + 3666, -- Gooey Spider Cake + 8950, -- Homemade Cherry Pie + 4542, -- Moist Cornbread + 4544, -- Mulgore Spice Bread + 4601, -- Soft Banana Bread + 4540, -- Tough Hunk of Bread + 16169, -- Wild Ricecake + }, + [FOM_DIET_CHEESE] = { + 8932, -- Alterac Swiss + 414, -- Dalaran Sharp + 2070, -- Darnassian Bleu + 422, -- Dwarven Mild + 3927, -- Fine Aged Cheddar + 1707, -- Stormwind Brie + }, + [FOM_DIET_FRUIT] = { + 19994, -- Harvest Fruit + 8953, -- Deep Fried Plantains + 4539, -- Goldenbark Apple + 16168, -- Heaven Peach + 4602, -- Moon Harvest Pumpkin + 4536, -- Shiny Red Apple + 4538, -- Snapvine Watermelon + 4537, -- Tel'Abim Banana + 11950, -- Windblossom Berries + }, + [FOM_DIET_BONUS] = { + -- eat for 10 sec to get "well fed" sta/spi bonus + 2888, -- Beer Basted Boar Ribs + 11584, -- Cactus Apple Surprise + 7807, -- Candy Bar + 7808, -- Chocolate Square + 12224, -- Crispy Bat Wing + 17197, -- Gingerbread Cookie + 6888, -- Herb Baked Egg + 5472, -- Kaldorei Spider Kabob + 7806, -- Lollipop + 5474, -- Roasted Kodo Meat + 2680, -- Spiced Wolf Meat + 3220, -- Blood Sausage + 5525, -- Boiled Clams + 2684, -- Coyote Steak + 2683, -- Crab Cake + 3662, -- Crocolisk Steak + 2687, -- Dry Pork Ribs + 5476, -- Fillet of Frenzy + 724, -- Goretusk Liver Pie + 5477, -- Strider Stew + 3726, -- Big Bear Steak + 5479, -- Crispy Lizard Tail + 3664, -- Crocolisk Gumbo + 3665, -- Curiously Tasty Omelet + 5527, -- Goblin Deviled Clams + 3666, -- Gooey Spider Cake + 3727, -- Hot Lion Chops + 5480, -- Lean Venison + 12209, -- Lean Wolf Steak + 3663, -- Murloc Fin Soup + 1082, -- Redridge Goulash + 1017, -- Seasoned Wolf Kabob + 4457, -- Barbecued Buzzard Wing + 12213, -- Carrion Surprise + 6038, -- Giant Clam Scorcho + 13851, -- Hot Wolf Ribs + 12212, -- Jungle Stew + 12214, -- Mystery Stew + 12210, -- Roast Raptor + 3729, -- Soothing Turtle Bisque + 3728, -- Tasty Lion Steak + 16971, -- Clamlette Surprise + 12215, -- Heavy Kodo Stew + 12218, -- Monster Omelet + 12216, -- Spiced Chili Crab + 17222, -- Spider Sausage + 18045, -- Tender Wolf Steak + -- restores both health and mana + 2682, -- Cooked Crab Claw + 3448, -- Senggin Root + -- bonus effect with no 10 sec requirement + 12217, -- Dragonbreath Chili + 13927, -- Cooked Glossy Mightfish + 13928, -- Grilled Squid + 13929, -- Hot Smoked Bass + 13932, -- Poached Sunscale Salmon + 13931, -- Nightfin Soup + 13934, -- Mightfish Steak + 11950, -- Windblossom Berries + 18254, -- Runn Tum Tuber Surprise + }, +}; diff --git a/modules/feedomatic/GFWTable.lua b/modules/feedomatic/GFWTable.lua new file mode 100644 index 0000000..2805041 --- /dev/null +++ b/modules/feedomatic/GFWTable.lua @@ -0,0 +1,174 @@ +------------------------------------------------------ +-- GFWTable.lua +-- Utilities for manipulating tables +------------------------------------------------------ + +GFWTABLE_THIS_VERSION = 6; + +------------------------------------------------------ + +-- Mean: returns the mean value of a table of numbers +function GFWTable_temp_Mean(aTable) + if (aTable == nil or table.getn(aTable) == 0) then + return nil; + end + return GFWTable.Sum(aTable) / table.getn(aTable); +end + +-- Sum: returns the sum of a table of numbers +function GFWTable_temp_Sum(aTable) + if (aTable == nil or table.getn(aTable) == 0) then + return nil; + end + local sum = 0; + for i=1, table.getn(aTable) do + if (tonumber(aTable[i])) then + sum = sum + aTable[i]; + end + end + return sum; +end + +-- Median: returns the median value (most useful in a table of numbers, but usable in any sorted table) +function GFWTable_temp_Median(aTable) + if (aTable == nil or table.getn(aTable) == 0) then + return nil; + end + if (table.getn(aTable) == 1) then + return aTable[1]; + end + local sortedTable = GFWTable.Copy(aTable); -- leave the original table in whatever order it's in + table.sort(sortedTable); + local count = table.getn(sortedTable); + local median; + if (math.mod(count, 2) == 0) then -- even table size + local middleIndex1 = count / 2; + local middleIndex2 = middleIndex1 + 1; + median = (sortedTable[middleIndex1] + sortedTable[middleIndex2]) / 2; --average the two middle values + else -- the table size is odd + local trueMiddleIndex = (count + 1) / 2; -- calculate the middle index + median = sortedTable[trueMiddleIndex]; + end + return median; +end + +-- Merge: returns the union of two tables (without repeated elements) +function GFWTable_temp_Merge(table1, table2) + local newTable = { }; + if (table1) then + for i=1, table.getn(table1) do + table.insert(newTable, table1[i]); + end + end + if (table2) then + for i=1, table.getn(table2) do + if (GFWTable.IndexOf(newTable, table2[i]) == 0) then + table.insert(newTable, table2[i]); + end + end + end + return newTable; +end + +-- Diff: returns the difference of two tables (those elements which occur in either but not both) +function GFWTable_temp_Diff(table1, table2) + local newTable = { }; + if (table1 == nil) then + table1 = {}; + end + if (table2 == nil) then + table2 = {}; + end + for i=1, table.getn(table1) do + if (GFWTable.IndexOf(table2, table1[i]) == 0) then + table.insert(newTable, table1[i]); + end + end + for i=1, table.getn(table2) do + if (GFWTable.IndexOf(table1, table2[i]) == 0) then + table.insert(newTable, table2[i]); + end + end + return newTable; +end + +-- Subtract: returns a table containing those items in table1 not present in table2 +function GFWTable_temp_Subtract(table1, table2) + local newTable = { }; + if (table1 == nil or table.getn(table1) == 0) then return newTable; end + if (table2 == nil or table.getn(table2) == 0) then return table1; end + for i=1, table.getn(table1) do + if (GFWTable.IndexOf(table2, table1[i]) == 0) then + table.insert(newTable, table1[i]); + end + end + return newTable; +end + +-- IndexOf: returns the index (1-based) of an item in a table +function GFWTable_temp_IndexOf(aTable, item) + return GFWTable.KeyOf(aTable, item) or 0; +end + +-- KeyOf: returns the key to an item in a table with numeric or non-numeric keys, or nil if not found +function GFWTable_temp_KeyOf(aTable, item) + if (aTable == nil or type(aTable) ~= "table") then + return nil; -- caller probably won't expect this, causing traceable error in their code + end + for key, value in pairs(aTable) do + if (item == value) then + return key; + end + end + return nil; +end + +-- Copy: copies one table's elements into a new table (useful if you want to change them while preserving the first table). +-- Not a deep copy. +function GFWTable_temp_Copy(aTable) + local newTable = { }; + for i=1, table.getn(aTable) do + newTable[i] = aTable[i]; + end + return newTable; +end + +-- Count: returns number of items in a table regardless of whether it has numeric indices. +function GFWTable_temp_Count(aTable) + if (aTable == nil or type(aTable) ~= "table") then + return nil; -- caller probably won't expect this, causing traceable error in their code + end + local count = 0; + for key, value in pairs(aTable) do + count = count + 1; + end + return count; +end + +------------------------------------------------------ +-- load only if not already loaded +------------------------------------------------------ + +if (GFWTable == nil) then + GFWTable = {}; +end +local G = GFWTable; +if (G.Version == nil or (tonumber(G.Version) ~= nil and G.Version < GFWTABLE_THIS_VERSION)) then + + -- Functions + G.Mean = GFWTable_temp_Mean; + G.Sum = GFWTable_temp_Sum; + G.Median = GFWTable_temp_Median; + G.Merge = GFWTable_temp_Merge; + G.Diff = GFWTable_temp_Diff; + G.Subtract = GFWTable_temp_Subtract; + G.IndexOf = GFWTable_temp_IndexOf; + G.KeyOf = GFWTable_temp_KeyOf; + G.Copy = GFWTable_temp_Copy; + G.Count = GFWTable_temp_Count; + + -- Set version number + G.Version = GFWTABLE_THIS_VERSION; +end + + diff --git a/modules/feedomatic/GFWUtils.lua b/modules/feedomatic/GFWUtils.lua new file mode 100644 index 0000000..dd966ad --- /dev/null +++ b/modules/feedomatic/GFWUtils.lua @@ -0,0 +1,202 @@ +------------------------------------------------------ +-- GFWUtils.lua +-- Useful utility / debug functions +------------------------------------------------------ + +GFWUTILS_THIS_VERSION = 8; + +------------------------------------------------------ + +-- Shortcuts for common text color codes +function GFWUtils_temp_HiliteText(text) + if (text == nil) then return nil; end + return HIGHLIGHT_FONT_COLOR_CODE..text..FONT_COLOR_CODE_CLOSE; +end +function GFWUtils_temp_RedText(text) + if (text == nil) then return nil; end + return RED_FONT_COLOR_CODE..text..FONT_COLOR_CODE_CLOSE; +end +function GFWUtils_temp_GrayText(text) + if (text == nil) then return nil; end + return GRAY_FONT_COLOR_CODE..text..FONT_COLOR_CODE_CLOSE; +end +function GFWUtils_temp_LightYellowText(text) + if (text == nil) then return nil; end + return LIGHTYELLOW_FONT_COLOR_CODE..text..FONT_COLOR_CODE_CLOSE; +end + +-- Prints a message to the chat frame +function GFWUtils_temp_Print(message, r, g, b) + if MTH and MTH.Print then + MTH:Print(message) + else + DEFAULT_CHAT_FRAME:AddMessage(message, (r or GFW_FONT_COLOR.r), (g or GFW_FONT_COLOR.g), (b or GFW_FONT_COLOR.b)); + end +end + +-- Prints a message to the chat frame once per interval (seconds) or once per session if interval is nil +function GFWUtils_temp_PrintOnce(message, interval, r, g, b) + if (GFWUtils.PrintOnceCache == nil) then + GFWUtils.PrintOnceCache = {}; + end + if (interval == nil and GFWUtils.PrintOnceCache[message]) then + return; + end + if (interval and GFWUtils.PrintOnceCache[message] and GetTime() - GFWUtils.PrintOnceCache[message] < interval) then + return; + end + if MTH and MTH.Print then + MTH:Print(message) + else + DEFAULT_CHAT_FRAME:AddMessage(message, (r or GFW_FONT_COLOR.r), (g or GFW_FONT_COLOR.g), (b or GFW_FONT_COLOR.b)); + end + GFWUtils.PrintOnceCache[message] = GetTime(); +end + +-- Prints a message to the chat frame only if Debug is set +function GFWUtils_temp_DebugLog(message) + if (GFWUtils.Debug) then + if MTH and MTH.Print then + MTH:Print(message, "debug") + else + DEFAULT_CHAT_FRAME:AddMessage(message, GFW_DEBUG_COLOR.r, GFW_DEBUG_COLOR.g, GFW_DEBUG_COLOR.b); + end + end +end + +-- Prints a message in yellow to the floating messages frame +function GFWUtils_temp_Note(message) + UIErrorsFrame:AddMessage(message, 1.0, 1.0, 0.0, 1.0, UIERRORS_HOLD_TIME); +end + +-- Converts string.format to a string.find pattern: "%s hits %s for %d." to "(.+) hits (.+) for (%d+)" +-- based on Recap by Gello +function GFWUtils_temp_FormatToPattern(formatString) + + local patternString = formatString; + + patternString = string.gsub(patternString, "%%%d+%$([diouXxfgbcsq])", "%%%1"); -- reordering specifiers (e.g. %2$s) stripped + patternString = string.gsub(patternString, "([%$%(%)%.%[%]%*%+%-%?%^])", "%%%1"); -- convert regex special characters + + patternString = string.gsub(patternString, "%%c", "(.)"); -- %c to (.) + patternString = string.gsub(patternString, "%%s", "(.+)"); -- %s to (.+) + patternString = string.gsub(patternString, "%%[du]", "(%%d+)"); -- %d to (%d+) + patternString = string.gsub(patternString, "%%([gf])", "(%%d+%%.*%%d*)"); -- %g or %f to (%d+%.*%d*) + + return patternString; + +end + +-- Splits a string into a table of strings separated by 'separator'. +-- e.g. Split(aString, ", ") is the reverse of table.concat(aTable, ", ") +function GFWUtils_temp_Split(aString, separator) + if (aString == nil) then return nil; end + + local t = {}; + local function helper(segment) + table.insert(t, segment); + end + helper((string.gsub(aString, "(.-)"..separator, helper))); + return t; +end + +-- Capitalizes the first letter of each word in aString. +function GFWUtils_temp_TitleCase(aString) + if (aString == nil) then return nil; end + local function capWords(first, rest) + return string.upper(first)..string.lower(rest); + end + return string.gsub(aString, "(%w)([%w_']*)", capWords); +end + +-- Splits a cash amount into gold, siver, and copper +function GFWUtils_temp_GSC(money) + if (money == nil) then money = 0; end + local g = math.floor(money / 10000); + local s = math.floor((money - (g*10000)) / 100); + local c = math.floor(money - (g*10000) - (s*100)); + return g,s,c; +end + +-- Formats money text by color for gold, silver, copper +-- discards copper for amounts greater than 1g unless second arg evaluates true +-- based on Auctioneer +function GFWUtils_temp_TextGSC(money, noRound) + local GSC_GOLD="ffd100"; + local GSC_SILVER="e6e6e6"; + local GSC_COPPER="c8602c"; + local GSC_START="|cff%s%d%s|r"; + local GSC_PART=" |cff%s%02d%s|r"; + local GSC_NONE="|cffa0a0a0none|r"; + + local g, s, c = GFWUtils.GSC(money); + + local gsc = ""; + if (g > 0) then + gsc = format(GSC_START, GSC_GOLD, g, "g"); + if (s > 0) then + gsc = gsc..format(GSC_PART, GSC_SILVER, s, "s"); + if (noRound and c > 0) then + gsc = gsc..format(GSC_PART, GSC_COPPER, c, "c"); + end + end + elseif (s > 0) then + gsc = format(GSC_START, GSC_SILVER, s, "s"); + if (c > 0) then + gsc = gsc..format(GSC_PART, GSC_COPPER, c, "c"); + end + elseif (c > 0) then + gsc = gsc..format(GSC_START, GSC_COPPER, c, "c"); + else + gsc = GSC_NONE; + end + + return gsc; +end + +function GFWUtils_temp_ItemLink(linkInfo) + local sName, sLink, iQuality, iLevel, sType, sSubType, iCount = GetItemInfo(linkInfo); + if (sName) then + local _, _, _, color = GetItemQualityColor(iQuality); + local linkFormat = "%s|H%s|h[%s]|h|r"; + return string.format(linkFormat, color, sLink, sName); + else + return nil; + end +end + +------------------------------------------------------ +-- load only if not already loaded +------------------------------------------------------ + +if (GFWUtils == nil) then + GFWUtils = {}; +end +local G = GFWUtils; +if (G.Version == nil or (tonumber(G.Version) ~= nil and G.Version < GFWUTILS_THIS_VERSION)) then + + -- Constants + GFW_FONT_COLOR = {r=0.25, g=1.0, b=1.0}; + GFW_DEBUG_COLOR = {r=1.0, g=0.75, b=0.25}; + G.Debug = false; + + -- Functions + G.Hilite = GFWUtils_temp_HiliteText; + G.Red = GFWUtils_temp_RedText; + G.Gray = GFWUtils_temp_GrayText; + G.LtY = GFWUtils_temp_LightYellowText; + G.Print = GFWUtils_temp_Print; + G.PrintOnce = GFWUtils_temp_PrintOnce; + G.DebugLog = GFWUtils_temp_DebugLog; + G.Note = GFWUtils_temp_Note; + G.FormatToPattern = GFWUtils_temp_FormatToPattern; + G.Split = GFWUtils_temp_Split; + G.TitleCase = GFWUtils_temp_TitleCase; + G.GSC = GFWUtils_temp_GSC; + G.TextGSC = GFWUtils_temp_TextGSC; + G.ItemLink = GFWUtils_temp_ItemLink; + + -- Set version number + G.Version = GFWUTILS_THIS_VERSION; +end + diff --git a/modules/feedomatic/GFW_FeedOMatic.toc b/modules/feedomatic/GFW_FeedOMatic.toc new file mode 100644 index 0000000..b311fe0 --- /dev/null +++ b/modules/feedomatic/GFW_FeedOMatic.toc @@ -0,0 +1,12 @@ +## Interface: 11200 +## Version: 11200.2 +## Title: Fizzwidget Feed-O-Matic +## Author: Gazmik Fizzwidget +## Notes: Helps a Hunter keep his pets well fed (and the food in his inventory under control) +## SavedVariables: FOM_Config, FOM_FoodQuality, FOM_AddedFoods, FOM_RemovedFoods, FOM_Cooking, FOM_QuestFood, FOM_LocaleInfo +GFWUtils.lua +GFWTable.lua +localization.lua +FeedOMatic_Emotes.lua +FeedOMatic_Foods.lua +FeedOMatic.lua diff --git a/modules/feedomatic/UpdateUI.xml b/modules/feedomatic/UpdateUI.xml new file mode 100644 index 0000000..48f2d9a --- /dev/null +++ b/modules/feedomatic/UpdateUI.xml @@ -0,0 +1,5 @@ + + Helps a Hunter keep his pets well fed (and the food in his inventory under control). + + + diff --git a/modules/feedomatic/loader.xml b/modules/feedomatic/loader.xml new file mode 100644 index 0000000..95b2a1e --- /dev/null +++ b/modules/feedomatic/loader.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/feedomatic/localization.lua b/modules/feedomatic/localization.lua new file mode 100644 index 0000000..24d589f --- /dev/null +++ b/modules/feedomatic/localization.lua @@ -0,0 +1,304 @@ +----------------------------------------------------- +-- localization.lua +-- English strings by default, localizations override with their own. +------------------------------------------------------ + +-- Key bindings +BINDING_HEADER_FEEDOMATIC = "Fizzwidget Feed-O-Matic"; +BINDING_NAME_FEEDOMATIC = "Feed Pet"; + +-- Diet names. These should be the all-lowercase versions of the six diets returned from GetPetFoodTypes(). (Want to get them all nice and quick for your localization? Go tame a bear... they eat anything.) +-- THESE STRINGS MUST BE LOCALIZED for Feed-O-Matic to work properly in other locales. +FOM_DIET_MEAT = "meat"; +FOM_DIET_FISH = "fish"; +FOM_DIET_BREAD = "bread"; +FOM_DIET_CHEESE = "cheese"; +FOM_DIET_FRUIT = "fruit"; +FOM_DIET_FUNGUS = "fungus"; + +-- If the player has a buff whose icon name contains "Ability_Mount" and whose name contains one of these substrings, we assume the player is mounted. We have to use this rather oblique way to check for mounted because some other buffs use the Ability_Mount_* icons. If these aren't localized, we may not recognize when the player is mounted and still try to feed the pet. +FOM_MOUNT_NAME_SUBSTRINGS = { "kodo", "wolf", "raptor", "steed", "horse", "ram", "saber", "mechanostrider" }; + +-- It's not clear whether the 5th and 6th returns from GetItemInfo() are supposed to be localized... so we have localized keys for them just in case. If these aren't localized, we'll just needlessly search your ammo bags for food. +FOM_AMMO_POUCH = "Ammo Pouch"; +FOM_QUIVER = "Quiver"; + +-- From here on down, the localized strings are just for readability... they don't affect whether Feed-O-Matic works. + +-- Two special diets; one is used for tracking food that provides a bonus (or at least, that you don't want to have fed to your pet when the 'savebonus' option is on), and another that's used in the chat-line interface for listing all diets. +FOM_DIET_BONUS = "bonus"; +FOM_DIET_ALL = "all"; + +-- Used in tooltips to indicate food quality. +FOM_QUALITY_UNKNOWN = "%s can eat this, but hasn't tried it yet."; +FOM_QUALITY_UNDER = "%s doesn't like this anymore."; +FOM_QUALITY_MIGHT = "%s might eat this."; +FOM_QUALITY_WILL = "%s will eat this."; +FOM_QUALITY_LIKE = "%s likes to eat this."; +FOM_QUALITY_LOVE = "%s loves to eat this."; + +-- User-visible errors +FOM_ERROR_NO_PET = "You don't have a pet to feed."; +FOM_ERROR_PET_DEAD = "Your pet is dead."; +FOM_ERROR_NO_FEEDABLE_PET = "You don't have a feedable pet."; +FOM_ERROR_IN_COMBAT = "You can't feed your pet during combat."; +FOM_ERROR_FOOD_NOT_FOUND = "%s could not find %s in your pack."; +FOM_ERROR_NO_FOOD = "%s could not find any food in your pack."; +FOM_ERROR_NO_FOOD_NO_FALLBACK = "%s could not find any food in your pack that you haven't forbidden %s to eat."; + +FOM_PRONOUN_MALE = "him"; +FOM_PRONOUN_FEMALE = "her"; + +-- Feeding status messages +FOM_FEEDING_EAT_ANOTHER = "%s doesn't like that food and will try something else..."; +FOM_FEEDING_FEED_ANOTHER = "needs to feed %s something else..."; +FOM_FEEDING_EAT = "%s eats a %s from your pack."; +FOM_FEEDING_FEED = "feeds %s a %s. "; + +FOM_PET_HUNGRY = "%s is hungry!"; +FOM_PET_VERY_HUNGRY = "%s is very hungry!"; + +-- Beast family names +BAT = "Bat"; +BEAR = "Bear"; +BOAR = "Boar"; +CARRION_BIRD = "Carrion Bird"; +CAT = "Cat"; +CRAB = "Crab"; +CROCOLISK = "Crocolisk"; +GORILLA = "Gorilla"; +HYENA = "Hyena"; +OWL = "Owl"; +RAPTOR = "Raptor"; +SCORPID = "Scorpid"; +SPIDER = "Spider"; +TALLSTRIDER = "Tallstrider"; +TURTLE = "Turtle"; +WIND_SERPENT = "Wind Serpent"; +WOLF = "Wolf"; + +FOM_OPTIONS_HEADER = "Feed-O-Matic Options"; +FOM_OPTIONS_GENERAL = "General options:"; +FOM_OPTIONS_FOOD_CHOICE = "Avoid foods used in Cooking:"; +FOM_OPTIONS_FEED_NOTIFY = "Notify when feeding:"; +FOM_OPTIONS_WARNING = "Warn that your pet needs feeding:"; + +FOM_KEEPOPEN = "Try to keep at least"; +FOM_KEEPOPEN_SLOTS = "slots open in your bags."; +FOM_KEEPOPEN_INFO = "(Will choose foods from smaller stacks when low on space, regardless of how well your pet likes them.)"; + +FOM_OptionsButtonText = { + ["Tooltip"] = "Show your pet's opinion of foods in their tooltips", + ["AvoidQuestFood"] = "Avoid foods needed for quests", + ["AvoidBonusFood"] = "Avoid foods that have a bonus effect when eaten", + ["PreferHigherQuality"] = "Prefer foods your pet likes more", + ["Fallback"] = "Fall back to otherwise-avoided foods if out of others", + ["SaveForCook_All"] = "all foods used in cooking", + ["SaveForCook_Green"] = "only foods used in |cff3fbf3f\"easy\"|r or better recipes", + ["SaveForCook_Yellow"] = "only foods used in |cffffff00\"medium\"|r or better recipes", + ["SaveForCook_Orange"] = "only foods used in |cffff7f3f\"difficult\"|r recipes", + ["SaveForCook_None"] = "Don't save foods used in cooking", + ["IconWarning"] = "Flash icon", + ["TextWarning"] = "Show text", + ["AudioWarning"] = "Play sound", + ["AudioWarningBell"] = "Generic bell sound instead of pet-specific", + ["AlertEmote"] = "Via emote (seen by others)", + ["AlertChat"] = "In the chat window (seen by you only)", + ["AlertNone"] = "Don't notify", + ["LevelContent"] = "when \"content\"", + ["LevelUnhappy"] = "when \"unhappy\"", + ["LevelOff"] = "Don't warn", +}; +------------------------------------------------------ + +if (GetLocale() == "frFR") then + +-- Diet names. These should be the all-lowercase versions of the six diets returned from GetPetFoodTypes(). (Want to get them all nice and quick for your localization? Go tame a bear... they eat anything.) +-- THESE STRINGS MUST BE LOCALIZED for Feed-O-Matic to work properly in other locales. + FOM_DIET_MEAT = "viande"; + FOM_DIET_FISH = "poisson"; + FOM_DIET_BREAD = "pain"; + FOM_DIET_CHEESE = "fromage"; + FOM_DIET_FRUIT = "fruit"; + FOM_DIET_FUNGUS = "champignon"; + + -- If the player has a buff whose icon name contains "Ability_Mount" and whose name contains one of these substrings, we assume the player is mounted. We have to use this rather oblique way to check for mounted because some other buffs use the Ability_Mount_* icons. If these aren't localized, we may not recognize when the player is mounted and still try to feed the pet. + FOM_MOUNT_NAME_SUBSTRINGS = { "kodo", "loup", "raptor", "steed", "cheval", "bélier", "tigre", "mécanotrotteur", }; + + -- It's not clear whether the 5th and 6th returns from GetItemInfo() are supposed to be localized... so we have localized keys for them just in case. If these aren't localized, we'll just needlessly search your ammo bags for food. + FOM_AMMO_POUCH = "Giberne"; + FOM_QUIVER = "Carquois"; + + +-- From here on down, the localized strings are just for readability... they don't affect whether Feed-O-Matic works. + + -- Two special diets; one is used for tracking food that provides a bonus (or at least, that you don't want to have fed to your pet when the 'savebonus' option is on), and another that's used in the chat-line interface for listing all diets. + FOM_DIET_BONUS = "bonus"; + FOM_DIET_ALL = "tous"; + + -- Used in tooltips to indicate food quality. + FOM_QUALITY_UNKNOWN = "%s peut le manger, mais n'a jamais essayé."; + FOM_QUALITY_UNDER = "%s n'en mange plus désormais."; + FOM_QUALITY_MIGHT = "%s en mangera peut-être."; + FOM_QUALITY_WILL = "%s en mangera."; + FOM_QUALITY_LIKE = "%s aime en manger."; + FOM_QUALITY_LOVE = "%s adore en manger."; + + -- User-visible errors + FOM_ERROR_NO_PET = "Vous n'avez pas de familier."; + FOM_ERROR_PET_DEAD = "Votre familier est mort."; + FOM_ERROR_NO_FEEDABLE_PET = "Vous n'avez pas de familier capable de manger."; + FOM_ERROR_FOOD_NOT_FOUND = "%s n'a pas trouvé de %s dans votre sac."; + FOM_ERROR_NO_FOOD = "%s n'a pas trouvé de nourriture dans votre sac."; + + -- Feeding status messages + FOM_FEEDING_EAT_ANOTHER = "%s n'aime pas cette nourriture et va essayer autre chose..." + FOM_FEEDING_FEED_ANOTHER = "cherche autre chose à donner à %s..."; + FOM_FEEDING_EAT = "%s mange un(e) %s."; + FOM_FEEDING_FEED = "donne à %s à manger un(e) %s. "; + + -- Beast family names + BAT = "Chauve-souris"; + BEAR = "Ours"; + BOAR = "Sanglier"; + CARRION_BIRD = "Charognard"; + CAT = "Félin"; + CRAB = "Crabe"; + CROCOLISK = "Crocilisque"; + GORILLA = "Gorille"; + HYENA = "Hyène"; + OWL = "Chouette"; + RAPTOR = "Raptor"; + SCORPID = "Scorpide"; + SPIDER = "Araignée"; + TALLSTRIDER = "Haut-trotteur"; + TURTLE = "Tortue"; + WIND_SERPENT = "Serpent des vents"; + WOLF = "Loup"; + + FOM_PET_HUNGRY = "%s a faim!"; + FOM_PET_VERY_HUNGRY = "%s a très faim!"; + + FOM_OPTIONS_HEADER = "Feed-O-Matic Options"; + FOM_OPTIONS_GENERAL = "Options générales:"; + FOM_OPTIONS_FOOD_CHOICE = "Eviter la nourriture utilisée en cuisine:"; + FOM_OPTIONS_FEED_NOTIFY = "Prévenir lorsque votre familier est nourrit:"; + FOM_OPTIONS_WARNING = "Avertir quand votre familier a besoin d'être nourrit:"; + + FOM_KEEPOPEN = "Essayer de garder au moins"; + FOM_KEEPOPEN_SLOTS = "emplacements de sac libres"; + FOM_KEEPOPEN_INFO = "(Le familier choisira la nourriture des piles les plus petites, sans tenir compte de ses préférences.)"; + + FOM_OptionsButtonText = { + ["Tooltip"] = "Afficher l'avis de votre familier concernant la nourriture", + ["AvoidQuestFood"] = "Eviter la nourriture utile dans les quêtes", + ["AvoidBonusFood"] = "Eviter la nourriture donnant un bonus", + ["PreferHigherQuality"] = "Privilégier la nourriture que votre familier préfère", + ["Fallback"] = "Utiliser la nourriture évitée s'il n'y en a plus d'autres", + ["SaveForCook_All"] = "Toutes nourritures utilisées pour la cuisine", + ["SaveForCook_Green"] = "si elle est utilisée dans les recettes |cff3fbf3f\"faciles\"|r ou supérieures", + ["SaveForCook_Yellow"] = "si elle est utilisée dans les recettes |cffffff00\"moyennes\"|r ou supérieures", + ["SaveForCook_Orange"] = "si elle est utilisée dans les recettes |cffff7f3f\"difficiles\"|r", + ["SaveForCook_None"] = "Ne pas éviter la nourriture utilisée en cuisine", + ["IconWarning"] = "Clignoter l'icône", + ["TextWarning"] = "Afficher un texte", + ["AudioWarning"] = "Jouer un son", + ["AudioWarningBell"] = "Un son de cloche au lieu du cri du familier", + ["AlertEmote"] = "Via emote (visible par les autres)", + ["AlertChat"] = "Dans la fenêtre de discussion (visible uniquement par vous)", + ["AlertNone"] = "Ne pas prévenir", + ["LevelContent"] = "quand \"content\"", + ["LevelUnhappy"] = "quand \"mécontent\"", + ["LevelOff"] = "Ne pas avertir", + }; +end + +------------------------------------------------------ + +if (GetLocale() == "deDE") then + +-- Diet names. These should be the all-lowercase versions of the six diets returned from GetPetFoodTypes(). (Want to get them all nice and quick for your localization? Go tame a bear... they eat anything.) +-- THESE STRINGS MUST BE LOCALIZED for Feed-O-Matic to work properly in other locales. + FOM_DIET_MEAT = "fleisch"; + FOM_DIET_FISH = "fisch"; + FOM_DIET_BREAD = "brot"; + FOM_DIET_CHEESE = "käse"; + FOM_DIET_FRUIT = "obst"; +-- FOM_DIET_FUNGUS = "fungus"; -- same as English (?), so we don't need to re-define it. + + -- If the player has a buff whose icon name contains "Ability_Mount" and whose name contains one of these substrings, we assume the player is mounted. We have to use this rather oblique way to check for mounted because some other buffs use the Ability_Mount_* icons. If these aren't localized, we may not recognize when the player is mounted and still try to feed the pet. +-- FOM_MOUNT_NAME_SUBSTRINGS = { "kodo", "wolf", "raptor", "steed", "horse", "ram", "saber", "mechanostrider" }; + + -- It's not clear whether the 5th and 6th returns from GetItemInfo() are supposed to be localized... so we have localized keys for them just in case. + FOM_AMMO_POUCH = "Munitionsbeutel"; + FOM_QUIVER = "Köcher"; + +-- From here on down, the localized strings are just for readability; they don't affect whether Feed-O-Matic works. + + -- Two special diets; one is used for tracking food that provides a bonus (or at least, that you don't want to have fed to your pet when the 'savebonus' option is on), and another that's used in the chat-line interface for listing all diets. +-- FOM_DIET_BONUS = "bonus"; -- same as English(?), so we don't need to re-define it. + FOM_DIET_ALL = "alles"; + + -- Used in tooltips to indicate food quality. + FOM_QUALITY_UNKNOWN = "%s kann das fressen, hat es aber noch nicht probiert."; + FOM_QUALITY_UNDER = "%s mag das nicht mehr fressen."; + FOM_QUALITY_MIGHT = "%s mag das möglicherweise fressen."; + FOM_QUALITY_WILL = "%s mag das fressen."; + FOM_QUALITY_LIKE = "%s frisst das gerne."; + FOM_QUALITY_LOVE = "%s liebt es, das zu fressen."; + + -- User-visible errors + FOM_ERROR_NO_PET = "Du hast kein Tier um es zu füttern."; + FOM_ERROR_PET_DEAD = "Dein Tier ist tot."; + FOM_ERROR_NO_FEEDABLE_PET = "Du hast kein Tier das man füttern kann."; + FOM_ERROR_FOOD_NOT_FOUND = "%s kann %s nicht in Deinem Rucksack finden."; + FOM_ERROR_NO_FOOD = "%s findet nichts zu fressen in Deinem Rucksack."; + + -- Feeding status messages + FOM_FEEDING_EAT_ANOTHER = "%s mag dieses Futter nicht und probiert etwas anderes..."; + FOM_FEEDING_FEED_ANOTHER = "sucht nach etwas anderem um %s zu füttern..."; + FOM_FEEDING_EAT = "%s frisst ein %s aus Deinem Rucksack."; + FOM_FEEDING_FEED = "füttert %s ein %s. "; + + -- Beast family names + BAT = "Fledermaus"; + BEAR = "Bär"; + BOAR = "Eber"; + CARRION_BIRD = "Aasvogel"; + CAT = "Katze"; + CRAB = "Krebs"; + CROCOLISK = "Krokilisk"; +-- GORILLA = "Gorilla"; -- same as enUS + HYENA = "Hyäne"; + OWL = "Eule"; +-- RAPTOR = "Raptor"; -- same as enUS + SCORPID = "Skorpid"; + SPIDER = "Spinne"; + TALLSTRIDER = "Weitschreiter"; + TURTLE = "Schildkröte"; + WIND_SERPENT = "Windnatter"; +-- WOLF = "Wolf"; -- same as enUS + +end + +------------------------------------------------------ + +if (GetLocale() == "koKR") then + +-- Diet names. These should be the all-lowercase versions of the six diets returned from GetPetFoodTypes(). (Want to get them all nice and quick for your localization? Go tame a bear... they eat anything.) +-- THESE STRINGS MUST BE LOCALIZED for Feed-O-Matic to work properly in other locales. +-- FOM_DIET_MEAT = "meat"; +-- FOM_DIET_FISH = "fish"; +-- FOM_DIET_BREAD = "bread"; +-- FOM_DIET_CHEESE = "cheese"; +-- FOM_DIET_FRUIT = "fruit"; +-- FOM_DIET_FUNGUS = "fungus"; + + -- If the player has a buff whose icon name contains "Ability_Mount" and whose name contains one of these substrings, we assume the player is mounted. We have to use this rather oblique way to check for mounted because some other buffs use the Ability_Mount_* icons. +-- FOM_MOUNT_NAME_SUBSTRINGS = { "kodo", "wolf", "raptor", "steed", "horse", "ram", "saber", "mechanostrider" }; + + -- It's not clear whether the 5th and 6th returns from GetItemInfo() are supposed to be localized... so we have localized keys for them just in case. +-- FOM_AMMO_POUCH = "Ammo Pouch"; + FOM_QUIVER = "화살통"; + +end \ No newline at end of file diff --git a/modules/feedomatic/module.lua b/modules/feedomatic/module.lua new file mode 100644 index 0000000..af61e9c --- /dev/null +++ b/modules/feedomatic/module.lua @@ -0,0 +1,340 @@ +------------------------------------------------------ +-- MetaHunt: FeedOMatic Module Wrapper +-- Modern module abstraction for the FeedOMatic addon +------------------------------------------------------ + +local MTH_FeedOMatic = { + name = "feedomatic", + enabled = false, + version = "1.0.3", + events = { + "VARIABLES_LOADED", + "MERCHANT_SHOW", + "MERCHANT_UPDATE", + "MTH_PET_LIVE_STATE_CHANGED", + "PET_ATTACK_START", + "PET_ATTACK_STOP", + "PET_STABLE_SHOW", + "PET_STABLE_UPDATE", + "TRADE_SKILL_SHOW", + "TRADE_SKILL_UPDATE", + "CHAT_MSG_SPELL_TRADESKILLS", + "CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS", + "UI_ERROR_MESSAGE", + "PET_BAR_SHOWGRID", + "PET_BAR_UPDATE", + "PET_UI_UPDATE", + "UNIT_HAPPINESS", + "PLAYER_REGEN_ENABLED", + }, + initialized = false, + variablesLoadedProcessed = false, +} + +local MTH_PETL_HOOK_BOUNDARY_KEY = "pet-lifecycle-hooks" +local MTH_FeedOMatic_MerchantProbeFrame = nil + +function MTH_FeedOMatic_OnUpdate(elapsed) + if not elapsed then + return + end + if not (MTH and MTH.IsModuleEnabled and MTH:IsModuleEnabled("feedomatic", true)) then + return + end + if type(FOM_OnUpdate) == "function" then + FOM_OnUpdate(elapsed) + end +end + +local function MTH_FeedOMatic_GetRuntimeFrame() + return getglobal("Frame_GFW_FeedOMatic") +end + +local function MTH_FeedOMatic_CaptureHookBoundary() + if not (MTH and MTH.CaptureHookBoundary) then + return + end + MTH:CaptureHookBoundary(MTH_PETL_HOOK_BOUNDARY_KEY, { + { globalName = "PetAbandon", originalName = "MTH_PETL_Original_PetAbandon" }, + }) +end + +local function MTH_FeedOMatic_RestoreHookBoundary() + if not (MTH and MTH.RestoreHookBoundary) then + return + end + MTH:RestoreHookBoundary(MTH_PETL_HOOK_BOUNDARY_KEY) +end + +local function MTH_FeedOMatic_ApplyLegacyHooks() + local canTouchPetAbandon = true + if MTH and MTH.GetGlobalHookOwner then + local abandonOwner = MTH:GetGlobalHookOwner("PetAbandon") + if abandonOwner and abandonOwner ~= MTH_PETL_HOOK_BOUNDARY_KEY then + canTouchPetAbandon = false + end + end + + local abandonHook = MTH_PetAbandonHook or FOM_PetAbandon + local abandonOriginal = MTH_PETL_Original_PetAbandon or FOM_Original_PetAbandon + if type(abandonHook) == "function" and type(abandonOriginal) == "function" then + if canTouchPetAbandon and (PetAbandon == abandonOriginal or PetAbandon == abandonHook) then + PetAbandon = abandonHook + end + end +end + +local function MTH_FeedOMatic_CallLegacyOnEvent(evt, a1, a2, a3, a4, a5, a6, a7, a8, a9) + if type(FOM_OnEvent) ~= "function" then + return + end + + local frame = MTH_FeedOMatic_GetRuntimeFrame() + if not frame then + return + end + + local oldThis = this + local oldEvent = event + local oldArg1, oldArg2, oldArg3, oldArg4, oldArg5 = arg1, arg2, arg3, arg4, arg5 + local oldArg6, oldArg7, oldArg8, oldArg9 = arg6, arg7, arg8, arg9 + + this = frame + event = evt + arg1, arg2, arg3, arg4, arg5 = a1, a2, a3, a4, a5 + arg6, arg7, arg8, arg9 = a6, a7, a8, a9 + + local ok = pcall(function() + FOM_OnEvent(evt, a1) + end) + + this = oldThis + event = oldEvent + arg1, arg2, arg3, arg4, arg5 = oldArg1, oldArg2, oldArg3, oldArg4, oldArg5 + arg6, arg7, arg8, arg9 = oldArg6, oldArg7, oldArg8, oldArg9 + + if not ok and MTH and MTH.DebugPrint then + MTH:DebugPrint("FeedOMatic legacy OnEvent call failed for event: " .. tostring(evt)) + end +end + +local function MTH_FeedOMatic_EnsureVariablesLoaded(module) + if not module or module.variablesLoadedProcessed then + return + end + + MTH_FeedOMatic_CallLegacyOnEvent("VARIABLES_LOADED") + module.variablesLoadedProcessed = true +end + +local function MTH_FeedOMatic_SyncSavedVariables() + if not MTH or not MTH.GetModuleSavedVariables then + return + end + + local moduleStore = MTH:GetModuleSavedVariables("feedomatic") + if not moduleStore.legacy then + moduleStore.legacy = {} + end + + if type(FOM_Config) == "table" then + moduleStore.legacy.FOM_Config = FOM_Config + elseif type(moduleStore.legacy.FOM_Config) == "table" then + FOM_Config = moduleStore.legacy.FOM_Config + end + + if type(FOM_FoodQuality) == "table" then + moduleStore.legacy.FOM_FoodQuality = FOM_FoodQuality + elseif type(moduleStore.legacy.FOM_FoodQuality) == "table" then + FOM_FoodQuality = moduleStore.legacy.FOM_FoodQuality + end + + if type(FOM_AddedFoods) == "table" then + moduleStore.legacy.FOM_AddedFoods = FOM_AddedFoods + elseif type(moduleStore.legacy.FOM_AddedFoods) == "table" then + FOM_AddedFoods = moduleStore.legacy.FOM_AddedFoods + end + + if type(FOM_RemovedFoods) == "table" then + moduleStore.legacy.FOM_RemovedFoods = FOM_RemovedFoods + elseif type(moduleStore.legacy.FOM_RemovedFoods) == "table" then + FOM_RemovedFoods = moduleStore.legacy.FOM_RemovedFoods + end + + if type(FOM_Cooking) == "table" then + moduleStore.legacy.FOM_Cooking = FOM_Cooking + elseif type(moduleStore.legacy.FOM_Cooking) == "table" then + FOM_Cooking = moduleStore.legacy.FOM_Cooking + end + + if type(FOM_QuestFood) == "table" then + moduleStore.legacy.FOM_QuestFood = FOM_QuestFood + elseif type(moduleStore.legacy.FOM_QuestFood) == "table" then + FOM_QuestFood = moduleStore.legacy.FOM_QuestFood + end + + if type(FOM_LocaleInfo) == "table" then + moduleStore.legacy.FOM_LocaleInfo = FOM_LocaleInfo + elseif type(moduleStore.legacy.FOM_LocaleInfo) == "table" then + FOM_LocaleInfo = moduleStore.legacy.FOM_LocaleInfo + end +end + +local function MTH_FeedOMatic_Log(message, severity) + if type(MTH_Log) == "function" then + local logSeverity = severity or "debug" + MTH_Log("[FeedOMatic] " .. tostring(message or ""), logSeverity) + end +end + +local function MTH_FeedOMatic_DebugMerchantScan(sourceEvent) + if type(MTH_GetMerchantFoodsByDiet) ~= "function" then + MTH_FeedOMatic_Log(tostring(sourceEvent) .. ": MTH_GetMerchantFoodsByDiet is not available yet") + return + end + + local merchantFoods = MTH_GetMerchantFoodsByDiet() + local knownCount = 0 + local unknownCount = 0 + if type(merchantFoods) == "table" then + if type(merchantFoods.items) == "table" then + knownCount = table.getn(merchantFoods.items) + end + if type(merchantFoods.unknown) == "table" then + unknownCount = table.getn(merchantFoods.unknown) + end + end + + local merchantName = UnitName("npc") or UnitName("target") or "unknown" + MTH_FeedOMatic_Log(tostring(sourceEvent) .. ": wrapper scan for " .. tostring(merchantName) .. " | known=" .. tostring(knownCount) .. " | unknown=" .. tostring(unknownCount)) + + if type(merchantFoods.byDiet) == "table" then + for dietName, rows in merchantFoods.byDiet do + local dietCount = 0 + if type(rows) == "table" then + dietCount = table.getn(rows) + end + MTH_FeedOMatic_Log("diet " .. tostring(dietName) .. ": " .. tostring(dietCount) .. " item(s)", "debug") + if type(rows) == "table" then + for _, row in rows do + MTH_FeedOMatic_Log(" - [" .. tostring(row.id) .. "] " .. tostring(row.name) .. " (slot=" .. tostring(row.index) .. ")", "debug") + end + end + end + end + + if unknownCount > 0 and type(merchantFoods.unknown) == "table" then + for _, row in merchantFoods.unknown do + MTH_FeedOMatic_Log("unknown map: [" .. tostring(row.id) .. "] " .. tostring(row.name) .. " (slot=" .. tostring(row.index) .. ")", "debug") + end + end +end + +local function MTH_FeedOMatic_SetMerchantProbeEnabled(enabled) + if not MTH_FeedOMatic_MerchantProbeFrame then + MTH_FeedOMatic_MerchantProbeFrame = CreateFrame("Frame", "MTH_FeedOMatic_MerchantProbeFrame") + MTH_FeedOMatic_MerchantProbeFrame:SetScript("OnEvent", function() + local evt = event + if evt == "MERCHANT_SHOW" or evt == "MERCHANT_UPDATE" then + MTH_FeedOMatic_Log("probe event received: " .. tostring(evt), "debug") + MTH_FeedOMatic_DebugMerchantScan(evt) + end + end) + end + + if enabled then + MTH_FeedOMatic_MerchantProbeFrame:RegisterEvent("MERCHANT_SHOW") + MTH_FeedOMatic_MerchantProbeFrame:RegisterEvent("MERCHANT_UPDATE") + MTH_FeedOMatic_Log("merchant probe enabled", "debug") + else + MTH_FeedOMatic_MerchantProbeFrame:UnregisterEvent("MERCHANT_SHOW") + MTH_FeedOMatic_MerchantProbeFrame:UnregisterEvent("MERCHANT_UPDATE") + end +end + +function MTH_FeedOMatic:init() + -- FeedOMatic initializes via its VARIABLES_LOADED event + -- This is called after the core MTH framework is loaded + MTH_FEED_WRAPPER_MODE = true + MTH_FOM_WRAPPER_MODE = MTH_FEED_WRAPPER_MODE + MTH_FeedOMatic_SyncSavedVariables() + + if not self.initialized and not getglobal("FOM_METAHUNT_ONLOAD_DONE") and FOM_OnLoad then + local frame = MTH_FeedOMatic_GetRuntimeFrame() + if frame then + local oldThis = this + this = frame + pcall(function() + FOM_OnLoad() + end) + this = oldThis + FOM_METAHUNT_ONLOAD_DONE = true + end + end + + MTH_FeedOMatic_CaptureHookBoundary() + + self.initialized = true + if self.enabled then + MTH_FeedOMatic_EnsureVariablesLoaded(self) + end +end + +function MTH_FeedOMatic:setEnabled(enabled) + if enabled then + if not self.initialized then + self:init() + end + MTH_FeedOMatic_ApplyLegacyHooks() + MTH_FeedOMatic_CaptureHookBoundary() + MTH_FeedOMatic_SyncSavedVariables() + MTH_FeedOMatic_EnsureVariablesLoaded(self) + MTH_FeedOMatic_SetMerchantProbeEnabled(false) + if FOM_FeedButton then + FOM_FeedButton:Show() + end + else + -- Disable FeedOMatic functionality + MTH_FeedOMatic_RestoreHookBoundary() + if type(FOM_State) == "table" then + FOM_State.ShouldFeed = false + end + MTH_FeedOMatic_SetMerchantProbeEnabled(false) + if FOM_FeedButton then + FOM_FeedButton:Hide() + end + end +end + +function MTH_FeedOMatic:onEvent(event, arg1, arg2, arg3, arg4, arg5) + if event == "VARIABLES_LOADED" then + MTH_FeedOMatic_EnsureVariablesLoaded(self) + return + end + + if event == "MTH_PET_LIVE_STATE_CHANGED" then + MTH_FeedOMatic_CallLegacyOnEvent("PET_BAR_UPDATE") + MTH_FeedOMatic_CallLegacyOnEvent("PET_UI_UPDATE") + MTH_FeedOMatic_CallLegacyOnEvent("UNIT_HAPPINESS", "pet") + return + end + + MTH_FeedOMatic_CallLegacyOnEvent(event, arg1, arg2, arg3, arg4, arg5) +end + +function MTH_FeedOMatic:cleanup() + -- Cleanup code when module is unloaded + MTH_FeedOMatic_RestoreHookBoundary() + if type(FOM_State) == "table" then + FOM_State.ShouldFeed = false + end + MTH_FeedOMatic_SetMerchantProbeEnabled(false) + if FOM_FeedButton then + FOM_FeedButton:Hide(); + end +end + +-- Register with MTH +MTH:RegisterModule("feedomatic", MTH_FeedOMatic) + +MTH_FOM_OnUpdate = MTH_FeedOMatic_OnUpdate diff --git a/modules/feedomatic/readme.txt b/modules/feedomatic/readme.txt new file mode 100644 index 0000000..ab1d909 --- /dev/null +++ b/modules/feedomatic/readme.txt @@ -0,0 +1,87 @@ +------------------------------------------------------ +Fizzwidget Feed-O-Matic +by Gazmik Fizzwidget +http://fizzwidget.com +gazmik@fizzwidget.com +------------------------------------------------------ + +As my Hunter friends can readily attest, keeping a wild pet can be a full-time job. Why, just feeding the critter when he gets hungry can throw off your routine -- you've got to rummage around in your bags, find a piece of food, make sure it's appropriate for his diet, and check your aim before tossing it to him (lest you accidentally destroy a tasty morsel). So inconvenient! Not to mention potentially dangerous... you don't want to spend so long digging through your bags that you or your pet become someone else's snack. + +Never fear, Gazmik Fizzwidget is here with a new gadget to automate all your pet-food-management tasks! My incredible Feed-O-Matic features state-of-the-art nutritional analyzers to make sure your pet's hunger is satisfied with a minimum of fuss, advanced selective logic to make your pet doesn't eat anything you have another use for, and a weight optimizer to make sure the food in your bags stays well organized! Just press the "Feed Now" button and it'll intelligently choose a food and accurately toss it to your pet. This is actually one of the first gizmos I started work on... but because I'm a perfectionist I haven't considered it ready for release until now. + +------------------------------------------------------ + +INSTALLATION: Put this folder into your World Of Warcraft/Interface/AddOns folder and launch WoW. + +USAGE: +Makes feeding your pet quick, easy, and fun: + - Bind a key to "Feed Pet", and Feed-O-Matic will automatically choose an appropriate food and give it to the pet whenever you press it. + - Can use an emote to notify you when it's feeding your pet, with custom randomized messages. See FeedOMatic_Emotes.lua to customize them to your characters! + +Helps you manage all the pet food in your inventory: + - Keeps track of which foods your pet likes more, and prioritizes "better" foods when choosing what to feed the pet. Also remembers what foods your pet has "outgrown", and doesn't choose them in the future (unless you've switched to a pet who might have different tastes). + - If you're on a quest to collect several of a certain item which also happens to be something your pet likes to eat, Feed-O-Matic can avoid consuming it. (Unless you're carrying more than is needed for the quest or there's nothing else for your pet to eat.) + - Feed-O-Matic can keep track of what foods are used by the Cooking recipes you know and avoid choosing food you'd rather cook; this behavior can be customized based on the difficulty of the recipies (so, for example, you can either save all cookable food or only save the foods most likely to increase your Cooking skill). + - If you'd prefer to save the better food (that is, those which provide a "well fed" bonus or other effect when eaten) for yourself, Feed-O-Matic can also avoid it when looking for pet food. + - All other things being equal, Feed-O-Matic will try to pick foods from smaller stacks, making sure your food supply doesn't take up all your bag space. When your bags get close to full, Feed-O-Matic will start ignoring other criteria and always choosing the smallest stack so that you won't run out of inventory sooner. + +CHAT COMMANDS: + `/feedomatic` (or `/fom` or `/feed`) - Show the Options window. +or: + `/feedomatic` (or `/fom` or `/feed`) +where can be any of the following: + `help` - Print this helplist. + `status` - Check current settings. + `reset` - Reset to default settings. + `feed` - Feed your pet (automatically finds an appropriate food). + `feed ` - Feed your pet a specific food. + `add ` - Add food to the list for a specific diet (meat, fish, fruit, etc). + `remove ` - Remove food from the list. + `show ` - Show food list for a specific diet (or for `all`). + +CAVEATS, ETC.: + - Feed-O-Matic has a database of many known foods, but it's not guaranteed to be comprehensive. You can use the `add`, `remove`, and `show` commands noted above to manage these lists directly. (Please drop me a line if you find a food that should be on there, or discover that something on the list shouldn't be there.) + - Currently, Feed-O-Matic can only update its list of which foods you know how to cook when your Cooking window is open. (So if you read a new recipe, we won't know to preserve the ingredients until you open your Cooking window again.) + - Only works in English, French, and German (for now). Localizers interested in helping with other WoW client languages please contact gazmik@fizzwidget.com. + +------------------------------------------------------ +VERSION HISTORY + +v. 12000.2 - 2006/10/27 +- Now always prefers conjured foods if available when feeding a pet that can eat them. (This was supposed to have been the case before, but the implementation turned out to be unreliable.) +- Fixed some issues which could result in Feed-O-Matic feeding the pet a food you'd told it to avoid, even if the "fall back" option was turned off. Also, a clearer error message is given if "fall back" is off and we can't find any allowed foods. +- The various reminder options for when your pet needs feeding (flashing the happiness icon, text messages, sounds) are now suppressed while you or your pet is in combat or if your pet is dead. +- Feed-O-Matic posts its own error message if you attempt use it to feed your pet while in combat. (Instead of trying to feed the pet anyway, causing WoW itself to post an error message.) +- Added a number of items to the default foods list (including some holiday treats and the new level 55 mage-conjured bread). + +v. 11200.1 - 2006/08/22 +- Updated TOC to reflect compatibility with WoW patch 1.12. +- Updated use of WoW's UnitSex() function to match changes in its behavior from WoW patch 1.11... your pet's gender should be reported correctly again. + +v. 11100.1 - 2006/06/20 +- Updated TOC to reflect compatibility with WoW patch 1.11. (No actual changes were needed.) +- Added French localization by "Artaher Medivh". + +v. 11000.4 - 2006/04/17 +- Fixed some issues with the tooltip code shared across Fizzwidget addons; Feed-O-Matic's addition to item tooltips should now be able to show up in just about every place you can get an item tooltip. + +v. 11000.3 - 2006/04/11 +- Feed-O-Matic settings can now be edited via a new Options panel. Typing `/fom` alone shows this panel (the various configuration subcommands, e.g. `/fom alert`, etc., are still available), which controls all aspects of Feed-O-Matic's behavior except for its list of known foods (for now, you'll still need to use slash commands to edit that). +- Additional options are available for warning you that your pet needs feeding: besides flashing the default UI's pet happiness icon, Feed-O-Matic can now also alert you via text message or with the sounds of your pet begging for food. (If you don't like these species-specific pet noises, there's also an option to have a simple bell sound as reminder.) +- You can now click the default UI's pet happiness icon to invoke Feed-O-Matic's feeding function. (This makes three ways to feed your pet with Feed-O-Matic: click the icon, set up a key for Feed Pet in the standard Key Bindings menu, or type or macro `/fom feed`.) Right-click the icon to show the new Options panel. +- Fixed a bug where we could bloat the SavedVariables file when attempting to clean it up upon visiting a stable master. (Now we properly discard data from pets no longer in service.) +- Fixed a bug where we'd still keep trying to feed a food to the pet after finding out it's too low level. + +v. 11000.2 - 2006/04/01 +- Due to changes in WoW patch 1.10, it's no longer possible for UI addons to feed your pet automatically without direct input; the automatic feeding function is thus no longer available in Feed-O-Matic. You may still find Feed-O-Matic quite useful for its ability to automatically choose foods for your pet and feed them at the touch of a button -- the "Feed Pet" button (configurable in WoW's Key Bindings menu) and `/fom feed` chat/macro command still work just great. +- As an aid to those suffering auto-feed withdrawal, there's now an option for Feed-O-Matic to provide an extra reminder when your pet needs feeding (in case you find the icon next to your pet's health bar too subtle). This is accessed by the same `/fom level` command that previously controlled automatic feeding; when your pet's happiness falls below the specified level, the happiness icon will blink until you feed him enough to sate his hunger. +- Fixed a bug where an error panel could appear when attempting to feed the pet with the `fallback` option turned off and the only foods in your inventory being types you'd prefer to avoid. +- KNOWN BUG: The tooltip additions aren't showing up in all cases where they should. Another update to address this issue should be coming along soon. + +v. 11000.1 - 2006/03/28 +- Updated for compatibility with WoW patch 1.10. (Minor changes were needed.) +- Should no longer disconnect if you have feed emotes turned on and you try to feed your pet a Legendary item. (Probably not a bug many experienced, but still...) +- Minor improvements to utility code shared across Fizzwidget addons. +- KNOWN BUG: The tooltip additions aren't showing up in all cases where they should. Another update to address this issue should be coming along soon. + +See http://fizzwidget.com/notes/feedomatic/ for older release notes. \ No newline at end of file diff --git a/modules/feedomatic/runtime.xml b/modules/feedomatic/runtime.xml new file mode 100644 index 0000000..c8af4c5 --- /dev/null +++ b/modules/feedomatic/runtime.xml @@ -0,0 +1,25 @@ + + + + + + if (MTH_FOM_OnUpdate) then + MTH_FOM_OnUpdate(arg1); + elseif (FOM_OnUpdate) then + FOM_OnUpdate(arg1); + end + + + + + + diff --git a/modules/smartammo/engine.lua b/modules/smartammo/engine.lua new file mode 100644 index 0000000..e86fee9 --- /dev/null +++ b/modules/smartammo/engine.lua @@ -0,0 +1,672 @@ +local function MTHSmartAmmo_GetSaved() + return MTH_SA_GetSavedTable("MTHSmartAmmo") +end + +local function MTHSmartAmmo_GetModuleStore() + if MTH and MTH.GetModuleCharSavedVariables then + return MTH:GetModuleCharSavedVariables("smartammo") + end + return nil +end + +local function MTHSmartAmmo_IsSmartEnabled() + local moduleStore = MTHSmartAmmo_GetModuleStore() + if type(moduleStore) == "table" and moduleStore.smartEnabled ~= nil then + return moduleStore.smartEnabled and true or false + end + local saved = MTHSmartAmmo_GetSaved() + return saved["enabled"] +end + +local function MTHSmartAmmo_IsReloadEnabled() + local moduleStore = MTHSmartAmmo_GetModuleStore() + if type(moduleStore) == "table" and moduleStore.reloadEnabled ~= nil then + return moduleStore.reloadEnabled and true or false + end + local saved = MTHSmartAmmo_GetSaved() + if saved["reload"] == nil then + return true + end + return saved["reload"] and true or false +end + +local MTHSmartAmmo_InitializeHooks +MTHSmartAmmo_EnsureHooks = nil + +function MTHSmartAmmo_SetSmartEnabled(enabled, silent) + local saved = MTHSmartAmmo_GetSaved() + if enabled then + saved["enabled"] = 1 + if MTHSmartAmmo_InitializeHooks then + MTHSmartAmmo_InitializeHooks() + end + if MTHSmartAmmo_EnsureHooks then + MTHSmartAmmo_EnsureHooks("SetSmartEnabled") + end + if not silent and DEFAULT_CHAT_FRAME then + MTH_SA_Print("Smart Ammo Enabled.") + end + else + saved["enabled"] = false + if not silent and DEFAULT_CHAT_FRAME then + MTH_SA_Print("Smart Ammo Disabled.") + end + end + + local moduleStore = MTHSmartAmmo_GetModuleStore() + if type(moduleStore) == "table" then + moduleStore.smartEnabled = enabled and true or false + end +end + +function MTHSmartAmmo_GetSmartEnabled() + return MTHSmartAmmo_IsSmartEnabled() and true or false +end + +function MTHSmartAmmo_SetReloadEnabled(enabled, silent) + local saved = MTHSmartAmmo_GetSaved() + if enabled then + saved["reload"] = 1 + else + saved["reload"] = false + end + local moduleStore = MTHSmartAmmo_GetModuleStore() + if type(moduleStore) == "table" then + moduleStore.reloadEnabled = enabled and true or false + end + if not silent and DEFAULT_CHAT_FRAME then + MTH_SA_Print("Smart Ammo reload fallback " .. (enabled and "Enabled." or "Disabled.")) + end +end + +function MTHSmartAmmo_GetReloadEnabled() + return MTHSmartAmmo_IsReloadEnabled() and true or false +end + +local function MTHSmartAmmo_Log(msg) + return +end + +local MTHSmartAmmo_CastSpell_Hook +local MTHSmartAmmo_UseAction_Hook +local MTHSmartAmmo_CastSpellByName_Hook +local MTHSmartAmmo_HookEnsureElapsed = 0 +local MTHSmartAmmo_InCastSpellHook = false +local MTHSmartAmmo_InUseActionHook = false +local MTHSmartAmmo_InCastSpellByNameHook = false +local MTHSmartAmmo_OriginalCastSpell = CastSpell +local MTHSmartAmmo_OriginalUseAction = UseAction +local MTHSmartAmmo_OriginalCastSpellByName = CastSpellByName + +local function MTHSmartAmmo_AreHooksInstalled() + return CastSpell == MTHSmartAmmo_CastSpell_Hook, + UseAction == MTHSmartAmmo_UseAction_Hook, + CastSpellByName == MTHSmartAmmo_CastSpellByName_Hook +end + +local function MTHSmartAmmo_ForceHookGlobals() + if type(MTHSmartAmmo_CastSpell_Hook) == "function" and CastSpell ~= MTHSmartAmmo_CastSpell_Hook then + MTH_SA_CastSpell = CastSpell + CastSpell = MTHSmartAmmo_CastSpell_Hook + end + if type(MTHSmartAmmo_UseAction_Hook) == "function" and UseAction ~= MTHSmartAmmo_UseAction_Hook then + MTH_SA_UseAction = UseAction + UseAction = MTHSmartAmmo_UseAction_Hook + end + if type(MTHSmartAmmo_CastSpellByName_Hook) == "function" and CastSpellByName ~= MTHSmartAmmo_CastSpellByName_Hook then + MTH_SA_CastSpellByName = CastSpellByName + CastSpellByName = MTHSmartAmmo_CastSpellByName_Hook + end +end + +MTH_SA = {} +MTH_SA_List = {} + +MTH_SA_LastCheck = 0 + +MTH_SA_Tooltip = CreateFrame("GameTooltip", "MTH_SA_Tooltip", nil, "GameTooltipTemplate") +MTH_SA_Tooltip:Hide() + +local _, class = UnitClass("player") + + if class ~= "HUNTER" then + return + end + +MTH_SA_IsWaitingSwapJunk = nil +MTH_SA_IsWaitingSwapGood = nil +MTH_SA_AutoShot = nil +MTH_SA_LastSpell = nil +MTH_SA_UpdateInterval = 0.1 +MTH_SA_Quantity = 0 +MTH_SA_LastFallbackAttempt = 0 +MTH_SA_LastKnownEquippedName = nil +MTH_SA_LastFallbackNotice = { key = nil, time = 0 } + +local function MTHSmartAmmo_RefreshAmmoButtonDisplay() + local createButtons = getglobal("zButtonAmmo_CreateButtons") + local setupSizeAndPosition = getglobal("zButtonAmmo_SetupSizeAndPosition") + local ammoButton = getglobal("zButtonAmmo") + if type(createButtons) == "function" and ammoButton then + createButtons() + if type(setupSizeAndPosition) == "function" then + setupSizeAndPosition() + end + end +end + +local function MTHSmartAmmo_ShowFallbackWarning(previousAmmo, newAmmo) + if not previousAmmo or previousAmmo == "" or not newAmmo or newAmmo == "" then + return + end + if previousAmmo == newAmmo then + return + end + + local now = GetTime() or 0 + local key = tostring(previousAmmo) .. "=>" .. tostring(newAmmo) + if MTH_SA_LastFallbackNotice.key == key and (now - (MTH_SA_LastFallbackNotice.time or 0)) < 1.0 then + return + end + MTH_SA_LastFallbackNotice.key = key + MTH_SA_LastFallbackNotice.time = now + + local warning = "YOU RAN OUT OF " .. tostring(previousAmmo) .. ", I EQUIPPED " .. tostring(newAmmo) .. " !" + if MTH_SA_Print then + MTH_SA_Print(warning) + elseif DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + DEFAULT_CHAT_FRAME:AddMessage(warning) + end + + if RaidWarningFrame and RaidNotice_AddMessage and ChatTypeInfo and ChatTypeInfo["RAID_WARNING"] then + RaidNotice_AddMessage(RaidWarningFrame, warning, ChatTypeInfo["RAID_WARNING"]) + elseif UIErrorsFrame and UIErrorsFrame.AddMessage then + UIErrorsFrame:AddMessage(warning, 1.0, 0.15, 0.15, 1.0) + end +end + +local function MTHSmartAmmo_HandleFallbackResult(previousAmmo) + local checkFrame = CreateFrame("Frame") + if not checkFrame then + return + end + checkFrame.elapsed = 0 + checkFrame:SetScript("OnUpdate", function() + this.elapsed = (this.elapsed or 0) + (arg1 or 0) + if this.elapsed < 0.2 then + return + end + + local equippedNow = MTH_SA_GetEquippedAmmoName() + if equippedNow and equippedNow ~= "" then + MTH_SA_LastKnownEquippedName = equippedNow + end + + MTHSmartAmmo_RefreshAmmoButtonDisplay() + MTHSmartAmmo_ShowFallbackWarning(previousAmmo, equippedNow) + + this:SetScript("OnUpdate", nil) + this:Hide() + end) +end + +local function MTHSmartAmmo_TryFallbackEquip(reason) + if not MTH_SA_IsModuleEnabled() then + return + end + if not MTHSmartAmmo_IsSmartEnabled() then + return + end + if not MTHSmartAmmo_IsReloadEnabled() then + return + end + if MTH_SA_GetEquippedAmmoName() then + return + end + + local now = GetTime() or 0 + if MTH_SA_LastFallbackAttempt and (now - MTH_SA_LastFallbackAttempt) < 0.5 then + return + end + MTH_SA_LastFallbackAttempt = now + local previousAmmo = MTH_SA_GetEquippedAmmoName() or MTH_SA_LastKnownEquippedName + + MTHSmartAmmo_Log("Fallback equip attempt reason=" .. tostring(reason or "unknown")) + if MTH_SA_Check() or MTH_SA_FindAmmo() then + MTH_SA_EquipAmmo() + MTHSmartAmmo_HandleFallbackResult(previousAmmo) + end +end + +local frame = CreateFrame("Frame") +frame:RegisterEvent("START_AUTOREPEAT_SPELL") +frame:RegisterEvent("STOP_AUTOREPEAT_SPELL") +frame:RegisterEvent("SPELLCAST_INTERRUPTED") +frame:RegisterEvent("SPELLCAST_FAILED") +frame:RegisterEvent("SPELLCAST_STOP") +frame:RegisterEvent("SPELLCAST_DELAYED") + + +frame:SetScript("OnEvent", function() + if event == "START_AUTOREPEAT_SPELL" then + if MTHSmartAmmo_EnsureHooks then + MTHSmartAmmo_EnsureHooks("EVENT:START_AUTOREPEAT_SPELL") + end + local castHook, actionHook, byNameHook = MTHSmartAmmo_AreHooksInstalled() + MTHSmartAmmo_Log("EVENT START_AUTOREPEAT_SPELL smart=" .. tostring(MTHSmartAmmo_IsSmartEnabled()) .. " module=" .. tostring(MTH_SA_IsModuleEnabled()) .. " hooks=" .. tostring(castHook) .. "/" .. tostring(actionHook) .. "/" .. tostring(byNameHook)) + MTH_SA_AutoShot = 1 + MTHSmartAmmo_TryFallbackEquip("START_AUTOREPEAT_SPELL") + elseif event == "STOP_AUTOREPEAT_SPELL" then + MTHSmartAmmo_Log("EVENT STOP_AUTOREPEAT_SPELL") + MTH_SA_AutoShot = nil + + elseif event == "SPELLCAST_START" then + --DEFAULT_CHAT_FRAME:AddMessage("SPELLCAST_START ", 0, 1, 1) + + elseif event == "SPELLCAST_STOP" or event == "SPELLCAST_FAILED" or event == "SPELLCAST_INTERRUPTED" or event == "SPELLCAST_DELAYED" then + local castHook, actionHook, byNameHook = MTHSmartAmmo_AreHooksInstalled() + MTHSmartAmmo_Log("EVENT " .. tostring(event) .. " waitJunk=" .. tostring(MTH_SA_IsWaitingSwapJunk) .. " waitGood=" .. tostring(MTH_SA_IsWaitingSwapGood) .. " lastSpell=" .. tostring(MTH_SA_LastSpell) .. " smart=" .. tostring(MTHSmartAmmo_IsSmartEnabled()) .. " module=" .. tostring(MTH_SA_IsModuleEnabled()) .. " hooks=" .. tostring(castHook) .. "/" .. tostring(actionHook) .. "/" .. tostring(byNameHook)) + if MTH_SA_IsWaitingSwapJunk then + MTHSmartAmmo_Log("EVENT swap request -> JUNK equipped=" .. tostring(MTH_SA_GetEquippedAmmoName()) .. " current=" .. tostring(MTH_SA.curname)) + MTH_SA_EquipAmmo(1) + elseif MTH_SA_IsWaitingSwapGood then + MTHSmartAmmo_Log("EVENT swap request -> GOOD equipped=" .. tostring(MTH_SA_GetEquippedAmmoName()) .. " current=" .. tostring(MTH_SA.curname)) + MTH_SA_EquipAmmo() + end + MTHSmartAmmo_Log("EVENT clear pending flags") + MTH_SA_IsWaitingSwapJunk = nil + MTH_SA_IsWaitingSwapGood = nil + MTH_SA_LastSpell = nil + end +end) + + + + +frame:SetScript("OnUpdate", function(self, elapsed) + MTHSmartAmmo_HookEnsureElapsed = MTHSmartAmmo_HookEnsureElapsed + (elapsed or 0) + if MTHSmartAmmo_HookEnsureElapsed >= 0.5 then + MTHSmartAmmo_HookEnsureElapsed = 0 + if MTH_SA_IsModuleEnabled() then + MTHSmartAmmo_InitializeHooks() + end + end + + MTH_SA_CountEquippedAmmo() + if MTH_SA_Quantity == 0 and not MTH_SA_IsWaitingSwapJunk and not MTH_SA_IsWaitingSwapGood then + MTHSmartAmmo_TryFallbackEquip("OnUpdate-quantity-zero") + end + + if MTH_SA_AmmoLastSwitch and (GetTime()-MTH_SA_AmmoLastSwitch > 0.5) and (MTH_SA_IsWaitingSwapJunk or MTH_SA_IsWaitingSwapGood) then + MTHSmartAmmo_Log("OnUpdate pending swap elapsed>0.5 equipped=" .. tostring(MTH_SA_GetEquippedAmmoName()) .. " waitJunk=" .. tostring(MTH_SA_IsWaitingSwapJunk) .. " waitGood=" .. tostring(MTH_SA_IsWaitingSwapGood)) + if MTH_SA_IsWaitingSwapJunk then + MTHSmartAmmo_Log("OnUpdate swap request -> JUNK") + MTH_SA_EquipAmmo(1) + elseif MTH_SA_IsWaitingSwapGood then + MTHSmartAmmo_Log("OnUpdate swap request -> GOOD") + MTH_SA_EquipAmmo() + end + MTHSmartAmmo_Log("OnUpdate clear pending flags") + MTH_SA_IsWaitingSwapJunk = nil + MTH_SA_IsWaitingSwapGood = nil + MTH_SA_LastSpell = nil + end +end) + +function MTH_SA_CountEquippedAmmo() + local ammo = GetInventoryItemCount("player", 0) + local equippedName = MTH_SA_GetEquippedAmmoName() + if equippedName and equippedName ~= "" then + MTH_SA_LastKnownEquippedName = equippedName + end + if not ammo then + MTH_SA_Quantity=0 + return + elseif ammo ~= MTH_SA_Quantity then + MTH_SA_Quantity = ammo + end + +end + + +function MTH_SA_GetEquippedWeaponAmmoType() + local itemlink = GetInventoryItemLink("player", 18) + if not itemlink then return end + local _, _, itemid = string.find(itemlink, "item:(%d+)") + if not itemid then return end + local _, _, _, _, _, wpntype = GetItemInfo(itemid) + if wpntype == MTH_WEAPON_BOWS or wpntype == MTH_WEAPON_CROSSBOWS then + return "Arrows" + elseif wpntype == MTH_WEAPON_GUNS then + return "Bullets" + end +end + +function MTH_SA_GetEquippedAmmoName() + MTH_SA_Tooltip:SetOwner(UIParent, "ANCHOR_NONE") + if MTH_SA_Tooltip:SetInventoryItem("player", 0) then + return MTH_SA_TooltipTextLeft1:GetText() + end + return nil +end + +function MTH_SA_GetContainerItemName(bag, slot) + if not (bag and slot) then return nil end + local item = GetContainerItemLink(bag, slot) + local _, _, name = string.find(item or "", "%[(.+)%]") + return name +end + +function MTH_SA_EquipAmmo(junk) + MTHSmartAmmo_Log("EquipAmmo enter junk=" .. tostring(junk) .. " equipped=" .. tostring(MTH_SA_GetEquippedAmmoName()) .. " cur=" .. tostring(MTH_SA.curname) .. " junk=" .. tostring(MTH_SA.junkname)) + + if not MTH_SA_AmmoLastSwitch then MTH_SA_AmmoLastSwitch = GetTime() end + + if not (MTH_SA_Check() or MTH_SA_FindAmmo()) then + MTHSmartAmmo_Log("EquipAmmo abort: cache invalid and FindAmmo failed") + return + end + local bag, slot + if junk then + --DEFAULT_CHAT_FRAME:AddMessage("Time= " .. GetTime()-MTH_SA_AmmoLastSwitch , 0, 1, 1) + if GetTime()-MTH_SA_AmmoLastSwitch > 0.5 then + --DEFAULT_CHAT_FRAME:AddMessage("JUNK ! GetTime > 0.5 | Equipped=" .. MTH_SA_GetEquippedAmmoName() .. ' | Current=' .. MTH_SA.curname , 0, 1, 1) + else + --DEFAULT_CHAT_FRAME:AddMessage("JUNK ! GetTime <= 0.5 | Equipped=" .. MTH_SA_GetEquippedAmmoName() .. ' | Current=' .. MTH_SA.curname , 0, 1, 1) + end + + if MTH_SA_GetEquippedAmmoName() ~= MTH_SA.curname and GetTime()-MTH_SA_AmmoLastSwitch > 0.5 then + if not MTH_SA_FindAmmo() then + MTHSmartAmmo_Log("EquipAmmo abort: AMMO NOT FOUND during junk swap") + return + end + end + bag, slot = MTH_SA.junkbag, MTH_SA.junkslot + MTHSmartAmmo_Log("Swap GOOD->JUNK bag=" .. tostring(bag) .. " slot=" .. tostring(slot) .. " cur=" .. tostring(MTH_SA.curname) .. " curSlot=" .. tostring(MTH_SA.curslot) .. " junk=" .. tostring(MTH_SA.junkname) .. " junkSlot=" .. tostring(MTH_SA.junkslot)) + else + bag, slot = MTH_SA.curbag, MTH_SA.curslot + MTHSmartAmmo_Log("Swap JUNK->GOOD bag=" .. tostring(bag) .. " slot=" .. tostring(slot) .. " cur=" .. tostring(MTH_SA.curname) .. " curSlot=" .. tostring(MTH_SA.curslot) .. " junk=" .. tostring(MTH_SA.junkname) .. " junkSlot=" .. tostring(MTH_SA.junkslot)) + end + + + + --UseContainerItem(bag, slot) + PickupContainerItem(bag,slot) + EquipCursorItem(0) + MTHSmartAmmo_Log("EquipCursorItem applied bag=" .. tostring(bag) .. " slot=" .. tostring(slot)) + if junk then + MTH_SA_IsWaitingSwapJunk = nil + MTH_SA_IsWaitingSwapGood = 1 + else + MTH_SA_IsWaitingSwapJunk = nil + MTH_SA_IsWaitingSwapGood = nil + end + MTHSmartAmmo_Log("Pending flags set waitJunk=" .. tostring(MTH_SA_IsWaitingSwapJunk) .. " waitGood=" .. tostring(MTH_SA_IsWaitingSwapGood)) + --PickupInventoryItem(0) + --AutoEquipCursorItem() + --EquipCursorItem(0) + + MTH_SA_AmmoLastSwitch = GetTime() +end + +function MTH_SA_FindAmmo() + MTHSmartAmmo_Log("FindAmmo start") + MTH_SA = {} + MTH_SA_List = {} + local ammotype = MTH_SA_GetEquippedWeaponAmmoType() + if not ammotype then + MTHSmartAmmo_Log("FindAmmo abort: no ranged weapon ammo type") + return + end + local curammo = MTH_SA_GetEquippedAmmoName() + if not curammo and not MTHSmartAmmo_IsReloadEnabled() then + MTHSmartAmmo_Log("FindAmmo abort: no equipped ammo and reload disabled") + return + end + MTHSmartAmmo_Log("FindAmmo type=" .. tostring(ammotype) .. " equipped=" .. tostring(curammo)) + local ammolist = (ammotype == "Bullets" and MTH_AMMO_BULLET_RANK) or MTH_AMMO_ARROW_RANK or {} + MTH_SA_List = ammolist + local item, value + local junkvalue = 999 + local goodvalue + for bag=4, 0, -1 do + for slot=GetContainerNumSlots(bag), 1, -1 do + item = MTH_SA_GetContainerItemName(bag, slot) + + if item then + --DEFAULT_CHAT_FRAME:AddMessage("item = " .. item .. " | value = " .. ammolist[item], 0, 1, 1) + value = ammolist[item] + if value then + + if not goodvalue and item == curammo then + MTH_SA.curvalue = value + MTH_SA.curname = item + MTH_SA.curbag = bag + MTH_SA.curslot = slot + goodvalue = value + --DEFAULT_CHAT_FRAME:AddMessage("goodvalue A = " .. value , 0, 1, 1) + if junkvalue == 1 then + return 1 + end + end + if value < junkvalue then + MTH_SA.junkvalue = item + MTH_SA.junkname = item + MTH_SA.junkbag = bag + MTH_SA.junkslot = slot + junkvalue = value + --DEFAULT_CHAT_FRAME:AddMessage("junkvalue A = " .. value , 0, 1, 1) + if not curammo then + MTH_SA.curvalue = value + MTH_SA.curname = item + MTH_SA.curbag = bag + MTH_SA.curslot = slot + goodvalue = value + --DEFAULT_CHAT_FRAME:AddMessage("goodvalue B = " .. value , 0, 1, 1) + end + if junkvalue == 1 and goodvalue then + return 1 + end + end + end + end + end + end + if not goodvalue or junkvalue > goodvalue then + MTHSmartAmmo_Log("FindAmmo failed: goodvalue=" .. tostring(goodvalue) .. " junkvalue=" .. tostring(junkvalue)) + MTH_SA.error = 1 + return + end + MTHSmartAmmo_Log("FindAmmo selected cur=" .. tostring(MTH_SA.curname) .. " junk=" .. tostring(MTH_SA.junkname) .. " curValue=" .. tostring(MTH_SA.curvalue) .. " junkValue=" .. tostring(junkvalue)) + return 1 +end + +function MTH_SA_Check() + if not MTH_SA.error and MTH_SA.curname and MTH_SA.junkname then + if MTH_SA.curname == MTH_SA_GetContainerItemName(MTH_SA.curbag, MTH_SA.curslot) and + MTH_SA.junkname == MTH_SA_GetContainerItemName(MTH_SA.junkbag, MTH_SA.junkslot) then + return 1 + end + end +end + +MTHSmartAmmo_CastSpell_Hook = function(spell, tab) + if MTHSmartAmmo_InCastSpellHook then + if MTHSmartAmmo_OriginalCastSpell then + return MTHSmartAmmo_OriginalCastSpell(spell, tab) + end + return + end + MTHSmartAmmo_InCastSpellHook = true + + if MTHSmartAmmo_IsSmartEnabled() then + local name = GetSpellName(spell, tab) + MTH_SA_LastSpell = name + MTHSmartAmmo_Log("HOOK CastSpell name=" .. tostring(name)) + if name and MTH_AMMO_JUNKSHOT_SET and MTH_AMMO_JUNKSHOT_SET[name] then + MTHSmartAmmo_Log("HOOK CastSpell junk-shot detected name=" .. tostring(name)) + if MTH_SA_Check() or MTH_SA_FindAmmo() then + MTHSmartAmmo_Log("HOOK CastSpell pre-swap -> junk") + MTH_SA_EquipAmmo(1) + end + end + end + + local result + local original = MTH_SA_CastSpell + if original == MTHSmartAmmo_CastSpell_Hook or original == nil then + original = MTHSmartAmmo_OriginalCastSpell + end + if original then + result = original(spell, tab) + end + + MTHSmartAmmo_InCastSpellHook = false + return result +end + +MTHSmartAmmo_UseAction_Hook = function(slot, checkCursor, onSelf) + if MTHSmartAmmo_InUseActionHook then + if MTHSmartAmmo_OriginalUseAction then + return MTHSmartAmmo_OriginalUseAction(slot, checkCursor, onSelf) + end + return + end + MTHSmartAmmo_InUseActionHook = true + + if not GetActionText(slot) and MTHSmartAmmo_IsSmartEnabled() then + MTH_SA_Tooltip:SetOwner(UIParent, "ANCHOR_NONE") + MTH_SA_Tooltip:SetAction(slot) + local name = MTH_SA_TooltipTextLeft1:GetText() + MTH_SA_LastSpell = name + MTHSmartAmmo_Log("HOOK UseAction slot=" .. tostring(slot) .. " name=" .. tostring(name)) + if name and MTH_AMMO_JUNKSHOT_SET and MTH_AMMO_JUNKSHOT_SET[name] then + MTHSmartAmmo_Log("HOOK UseAction junk-shot detected name=" .. tostring(name)) + if MTH_SA_Check() or MTH_SA_FindAmmo() then + MTHSmartAmmo_Log("HOOK UseAction pre-swap -> junk") + MTH_SA_EquipAmmo(1) + end + end + end + + local result + local original = MTH_SA_UseAction + if original == MTHSmartAmmo_UseAction_Hook or original == nil then + original = MTHSmartAmmo_OriginalUseAction + end + if original then + result = original(slot, checkCursor, onSelf) + end + + MTHSmartAmmo_InUseActionHook = false + return result +end + +MTHSmartAmmo_CastSpellByName_Hook = function(spell, onSelf) + if MTHSmartAmmo_InCastSpellByNameHook then + if MTHSmartAmmo_OriginalCastSpellByName then + return MTHSmartAmmo_OriginalCastSpellByName(spell, onSelf) + end + return + end + MTHSmartAmmo_InCastSpellByNameHook = true + + local _, _, name = string.find(spell or "", "([%w%'%s]+)") + MTH_SA_LastSpell = name + MTHSmartAmmo_Log("HOOK CastSpellByName name=" .. tostring(name)) + if name and MTHSmartAmmo_IsSmartEnabled() and MTH_AMMO_JUNKSHOT_SET and MTH_AMMO_JUNKSHOT_SET[name] then + MTHSmartAmmo_Log("HOOK CastSpellByName junk-shot detected name=" .. tostring(name)) + if MTH_SA_Check() or MTH_SA_FindAmmo() then + MTHSmartAmmo_Log("HOOK CastSpellByName pre-swap -> junk") + MTH_SA_EquipAmmo(1) + end + end + + local result + local original = MTH_SA_CastSpellByName + if original == MTHSmartAmmo_CastSpellByName_Hook or original == nil then + original = MTHSmartAmmo_OriginalCastSpellByName + end + if original then + result = original(spell, onSelf) + end + + MTHSmartAmmo_InCastSpellByNameHook = false + return result +end + +-- Defer hooking until after tables are initialized +MTHSmartAmmo_InitializeHooks = function() + if not MTH_SA_IsModuleEnabled() then + MTHSmartAmmo_Log("InitializeHooks skip: module disabled") + return + end + + if CastSpell ~= MTHSmartAmmo_CastSpell_Hook then + MTH_SA_CastSpell = CastSpell + CastSpell = MTHSmartAmmo_CastSpell_Hook + MTHSmartAmmo_Log("InitializeHooks: CastSpell hook installed") + end + + if UseAction ~= MTHSmartAmmo_UseAction_Hook then + MTH_SA_UseAction = UseAction + UseAction = MTHSmartAmmo_UseAction_Hook + MTHSmartAmmo_Log("InitializeHooks: UseAction hook installed") + end + + if CastSpellByName ~= MTHSmartAmmo_CastSpellByName_Hook then + MTH_SA_CastSpellByName = CastSpellByName + CastSpellByName = MTHSmartAmmo_CastSpellByName_Hook + MTHSmartAmmo_Log("InitializeHooks: CastSpellByName hook installed") + end +end + +MTHSmartAmmo_EnsureHooks = function(reason) + if not MTH_SA_IsModuleEnabled() then + return false + end + if not MTHSmartAmmo_IsSmartEnabled() then + return false + end + + if MTHSmartAmmo_InitializeHooks then + local ok, err = pcall(MTHSmartAmmo_InitializeHooks) + if not ok then end + end + + local castHook, actionHook, byNameHook = MTHSmartAmmo_AreHooksInstalled() + if castHook and actionHook and byNameHook then + return true + end + + MTHSmartAmmo_ForceHookGlobals() + castHook, actionHook, byNameHook = MTHSmartAmmo_AreHooksInstalled() + return castHook and actionHook and byNameHook +end + +-- Initialize hooks on module load +if not MTH_SA_MANAGED_HOOKS then + MTHSmartAmmo_InitializeHooks() +end + +SLASH_MTHSmartAmmo1 = "/smartammo" +SlashCmdList["MTHSmartAmmo"] = function(msg) + if not MTH_SA_IsModuleEnabled() then + MTH_SA_Print("Smart Ammo is disabled while module 'smartammo' is disabled.") + return + end + + local saved = MTHSmartAmmo_GetSaved() + + if msg == "status" then + local castHook, actionHook, byNameHook = MTHSmartAmmo_AreHooksInstalled() + MTH_SA_Print("Smart Ammo status: smart=" .. tostring(saved["enabled"] and true or false) .. ", module=" .. tostring(MTH_SA_IsModuleEnabled()) .. ", hooks=" .. tostring(castHook) .. "/" .. tostring(actionHook) .. "/" .. tostring(byNameHook)) + return + end + + if saved["enabled"] then + MTHSmartAmmo_SetSmartEnabled(nil) + else + MTHSmartAmmo_SetSmartEnabled(1) + end +end \ No newline at end of file diff --git a/modules/smartammo/loader.xml b/modules/smartammo/loader.xml new file mode 100644 index 0000000..671ad14 --- /dev/null +++ b/modules/smartammo/loader.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/modules/smartammo/module.lua b/modules/smartammo/module.lua new file mode 100644 index 0000000..4a11333 --- /dev/null +++ b/modules/smartammo/module.lua @@ -0,0 +1,171 @@ +------------------------------------------------------ +-- MetaHunt: SmartAmmo Module +------------------------------------------------------ + +MTH_SA_MANAGED_HOOKS = true + +local MTH_SmartAmmo = { + name = "smartammo", + enabled = true, + version = "1.0.3", + events = { + "VARIABLES_LOADED", + "PLAYER_ENTERING_WORLD", + }, +} + +local MTH_SA_BOUNDARY_KEY = "smartammo.core" + +function MTH_SA_GetSavedTable(tableName) + if not MTH or not MTH.GetModuleCharSavedVariables then + if type(_G[tableName]) ~= "table" then + _G[tableName] = {} + end + return _G[tableName] + end + + local moduleStore = MTH:GetModuleCharSavedVariables("smartammo") + if type(moduleStore) ~= "table" then + moduleStore = {} + end + + if not moduleStore.legacy then + moduleStore.legacy = {} + end + if type(moduleStore.legacy[tableName]) ~= "table" then + moduleStore.legacy[tableName] = {} + end + + if type(_G[tableName]) == "table" then + local legacyTable = moduleStore.legacy[tableName] + if not next(legacyTable) then + for key, value in pairs(_G[tableName]) do + legacyTable[key] = value + end + end + end + _G[tableName] = moduleStore.legacy[tableName] + + return moduleStore.legacy[tableName] +end + +function MTH_SA_IsModuleEnabled() + if MTH and MTH.IsModuleEnabled then + return MTH:IsModuleEnabled("smartammo", true) and true or false + end + return true +end + +function MTH_SA_Print(msg, severity) + if MTH and MTH.Print then + MTH:Print(tostring(msg), severity) + return + end + if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + DEFAULT_CHAT_FRAME:AddMessage(tostring(msg)) + end +end + +local function MTH_SA_ApplySavedState() + if type(MTHSmartAmmo_SetSmartEnabled) ~= "function" then + return + end + + local saved = MTH_SA_GetSavedTable("MTHSmartAmmo") + local moduleStore = MTH and MTH.GetModuleCharSavedVariables and MTH:GetModuleCharSavedVariables("smartammo") or nil + if type(moduleStore) == "table" then + if moduleStore.smartEnabled ~= nil then + saved["enabled"] = moduleStore.smartEnabled and 1 or false + end + if moduleStore.reloadEnabled ~= nil then + saved["reload"] = moduleStore.reloadEnabled and 1 or false + end + end + if saved["enabled"] == nil then + saved["enabled"] = 1 + if type(moduleStore) == "table" then + moduleStore.smartEnabled = true + end + end + if saved["reload"] == nil and type(moduleStore) == "table" then + moduleStore.reloadEnabled = saved["reload"] ~= false + end + + MTHSmartAmmo_SetSmartEnabled(saved["enabled"] and 1 or nil, 1) + if type(MTHSmartAmmo_SetReloadEnabled) == "function" then + MTHSmartAmmo_SetReloadEnabled(saved["reload"] ~= false and 1 or nil, 1) + end + if type(MTHSmartAmmo_EnsureHooks) == "function" then + MTHSmartAmmo_EnsureHooks("module-apply") + end + + if not (MTH and MTH.CaptureHookBoundary) then + return + end + + local castHook = getglobal("MTHSmartAmmo_CastSpell_Hook") + local useActionHook = getglobal("MTHSmartAmmo_UseAction_Hook") + local castByNameHook = getglobal("MTHSmartAmmo_CastSpellByName_Hook") + if type(castHook) ~= "function" or type(useActionHook) ~= "function" or type(castByNameHook) ~= "function" then + return + end + if CastSpell ~= castHook or UseAction ~= useActionHook or CastSpellByName ~= castByNameHook then + return + end + + MTH:CaptureHookBoundary(MTH_SA_BOUNDARY_KEY, { + { globalName = "CastSpell", originalName = "MTH_SA_CastSpell" }, + { globalName = "UseAction", originalName = "MTH_SA_UseAction" }, + { globalName = "CastSpellByName", originalName = "MTH_SA_CastSpellByName" }, + }) +end + +local function MTH_SA_RestoreHooks() + if MTH and MTH.RestoreHookBoundary and MTH:RestoreHookBoundary(MTH_SA_BOUNDARY_KEY) then + return + end + + local castHook = getglobal("MTHSmartAmmo_CastSpell_Hook") + local useActionHook = getglobal("MTHSmartAmmo_UseAction_Hook") + local castByNameHook = getglobal("MTHSmartAmmo_CastSpellByName_Hook") + + if CastSpell == castHook and type(MTH_SA_CastSpell) == "function" then + CastSpell = MTH_SA_CastSpell + end + if UseAction == useActionHook and type(MTH_SA_UseAction) == "function" then + UseAction = MTH_SA_UseAction + end + if CastSpellByName == castByNameHook and type(MTH_SA_CastSpellByName) == "function" then + CastSpellByName = MTH_SA_CastSpellByName + end +end + +function MTH_SmartAmmo:init() + MTH_SA_GetSavedTable("MTHSmartAmmo") + if self.enabled then + MTH_SA_ApplySavedState() + end +end + +function MTH_SmartAmmo:setEnabled(enabled) + if enabled then + MTH_SA_ApplySavedState() + else + MTH_SA_RestoreHooks() + end +end + +function MTH_SmartAmmo:onEvent(evt) + if not self.enabled then + return + end + if evt == "VARIABLES_LOADED" or evt == "PLAYER_ENTERING_WORLD" then + MTH_SA_ApplySavedState() + end +end + +function MTH_SmartAmmo:cleanup() + self:setEnabled(false) +end + +MTH:RegisterModule("smartammo", MTH_SmartAmmo) diff --git a/modules/tooltips/loader.xml b/modules/tooltips/loader.xml new file mode 100644 index 0000000..668324c --- /dev/null +++ b/modules/tooltips/loader.xml @@ -0,0 +1,7 @@ + + + + diff --git a/modules/tooltips/module.lua b/modules/tooltips/module.lua new file mode 100644 index 0000000..e1ab945 --- /dev/null +++ b/modules/tooltips/module.lua @@ -0,0 +1,1366 @@ +------------------------------------------------------ +-- MetaHunt: Tooltips Module +-- Standalone beast tooltip augmentation from MetaHunt data +------------------------------------------------------ + +local MTH_Tooltips = { + name = "tooltips", + enabled = true, + version = "1.0.3", + events = { + "UPDATE_MOUSEOVER_UNIT", + "UNIT_NAME_UPDATE", + }, + initialized = false, + beastIndex = nil, + vendorIndex = nil, + debugState = { + events = 0, + pollTicks = 0, + pollTriggers = 0, + injectAttempts = 0, + added = 0, + lookupMiss = 0, + skipNoUnit = 0, + skipPlayerControlled = 0, + skipTooltipApi = 0, + skipNoName = 0, + skipExistingLine = 0, + lastUnitName = "", + lastNormalized = "", + lastOutcome = "", + }, + hookInstalled = false, + injectFrame = nil, + pollFrame = nil, + pollElapsed = 0, + lastMouseoverKey = "", +} + + +local function MTH_TT_GetOptionsStore() + if MTH and MTH.GetModuleSavedVariables then + local store = MTH:GetModuleSavedVariables("tooltips") + if type(store) == "table" then + if store.beastTooltips == nil then + store.beastTooltips = true + end + if store.ammoVendorTooltips == nil then + store.ammoVendorTooltips = true + end + if store.foodItemTooltips == nil then + if type(FOM_Config) == "table" and FOM_Config.Tooltip ~= nil then + store.foodItemTooltips = FOM_Config.Tooltip and true or false + else + store.foodItemTooltips = true + end + end + if store.ownPetTooltips == nil then + store.ownPetTooltips = false + end + return store + end + end + return nil +end + +local function MTH_TT_IsBeastTooltipsEnabled() + local store = MTH_TT_GetOptionsStore() + if type(store) == "table" then + return store.beastTooltips and true or false + end + return true +end + +local function MTH_TT_IsAmmoVendorTooltipsEnabled() + local store = MTH_TT_GetOptionsStore() + if type(store) == "table" then + return store.ammoVendorTooltips and true or false + end + return true +end + +local function MTH_TT_IsFoodItemTooltipsEnabled() + local store = MTH_TT_GetOptionsStore() + if type(store) == "table" then + return store.foodItemTooltips and true or false + end + return true +end + +local function MTH_TT_IsOwnPetTooltipsEnabled() + local store = MTH_TT_GetOptionsStore() + if type(store) == "table" then + return store.ownPetTooltips and true or false + end + return false +end + +local function MTH_TT_Loc(key, fallback) + if MTH and MTH.GetLocalization then + return MTH:GetLocalization(key, fallback) + end + return fallback +end + +local function MTH_TT_Lower(value) + if value == nil then return "" end + return string.lower(tostring(value)) +end + +local function MTH_TT_Trim(value) + if value == nil then return "" end + local text = tostring(value) + text = string.gsub(text, "^%s+", "") + text = string.gsub(text, "%s+$", "") + return text +end + +local function MTH_TT_NormalizeName(value) + local text = MTH_TT_Trim(value) + text = MTH_TT_Lower(text) + text = string.gsub(text, "[^%w%s]", " ") + text = string.gsub(text, "%s+", " ") + text = MTH_TT_Trim(text) + return text +end + +local function MTH_TT_Log(line) + return +end + +local function MTH_TT_StateSet(outcome, unitName, normalized) + local state = MTH_Tooltips and MTH_Tooltips.debugState + if not state then return end + state.lastOutcome = tostring(outcome or "") + state.lastUnitName = tostring(unitName or "") + state.lastNormalized = tostring(normalized or "") +end + +local function MTH_TT_BuildIndex() + local index = { byName = {} } + if type(MTH_DS_Beasts) ~= "table" then + MTH_TT_Log("build index skipped: MTH_DS_Beasts missing") + return index + end + + local count = 0 + for _, row in pairs(MTH_DS_Beasts) do + if type(row) == "table" then + local beastName = MTH_TT_Trim(row.name) + if beastName ~= "" then + local key = MTH_TT_NormalizeName(beastName) + if not index.byName[key] then + index.byName[key] = row + count = count + 1 + end + end + end + end + + MTH_TT_Log("build index complete: uniqueNames=" .. tostring(count)) + + return index +end + +local function MTH_TT_FindBeastRow(name) + if not MTH_Tooltips.beastIndex then + MTH_Tooltips.beastIndex = MTH_TT_BuildIndex() + end + + local lookup = MTH_TT_NormalizeName(name) + if lookup == "" then return nil end + + return MTH_Tooltips.beastIndex.byName[lookup] +end + +local function MTH_TT_BuildVendorIndex() + local index = { byName = {} } + if type(MTH_DS_AmmoItems) ~= "table" or type(MTH_DS_Vendors) ~= "table" then + MTH_TT_Log("build vendor index skipped: ammo items or vendors missing") + return index + end + + for _, item in pairs(MTH_DS_AmmoItems) do + if type(item) == "table" and type(item.vendors) == "table" then + local subtype = MTH_TT_Lower(item.subtype) + local hasArrows = subtype == "arrow" + local hasBullets = subtype == "bullet" + if hasArrows or hasBullets then + for vendorId in pairs(item.vendors) do + local id = tonumber(vendorId) or vendorId + local vendor = MTH_DS_Vendors[id] + if type(vendor) == "table" then + local vendorName = MTH_TT_Trim(vendor.name) + local key = MTH_TT_NormalizeName(vendorName) + if key ~= "" then + local entry = index.byName[key] + if not entry then + entry = { + name = vendorName, + arrows = false, + bullets = false, + } + index.byName[key] = entry + end + if hasArrows then + entry.arrows = true + end + if hasBullets then + entry.bullets = true + end + end + end + end + end + end + end + + return index +end + +local function MTH_TT_FindVendorInfo(name) + if not MTH_Tooltips.vendorIndex then + MTH_Tooltips.vendorIndex = MTH_TT_BuildVendorIndex() + end + + local lookup = MTH_TT_NormalizeName(name) + if lookup == "" then return nil end + + return MTH_Tooltips.vendorIndex.byName[lookup] +end + +local function MTH_TT_HasExistingTooltipLine() + if not GameTooltip or not GameTooltip.NumLines then return false end + for i = 1, GameTooltip:NumLines() do + local fontString = getglobal("GameTooltipTextLeft" .. i) + if fontString and fontString.GetText then + local text = fontString:GetText() + if text and (string.find(text, "Pet Abilities", 1, true) or string.find(text, "MetaHunt Abilities", 1, true)) then + return true + end + end + end + return false +end + +local function MTH_TT_HasExistingVendorTooltipLine() + if not GameTooltip or not GameTooltip.NumLines then return false end + for i = 1, GameTooltip:NumLines() do + local fontString = getglobal("GameTooltipTextLeft" .. i) + if fontString and fontString.GetText then + local text = fontString:GetText() + if text and ( + string.find(text, "Sells Arrows and Bullets", 1, true) + or string.find(text, "Sells Arrows", 1, true) + or string.find(text, "Sells Bullets", 1, true) + ) then + return true + end + end + end + return false +end + +local function MTH_TT_HasExistingFoodTooltipLine() + if not GameTooltip or not GameTooltip.NumLines then return false end + local markers = { + MTH_TT_Loc("TOOLTIPS_FOOD_MARKER_UNKNOWN", "can eat this, but hasn't tried it yet."), + MTH_TT_Loc("TOOLTIPS_FOOD_MARKER_UNDER", "doesn't like this anymore."), + MTH_TT_Loc("TOOLTIPS_FOOD_MARKER_MIGHT", "might eat this."), + MTH_TT_Loc("TOOLTIPS_FOOD_MARKER_WILL", "will eat this."), + MTH_TT_Loc("TOOLTIPS_FOOD_MARKER_LIKE", "likes to eat this."), + MTH_TT_Loc("TOOLTIPS_FOOD_MARKER_LOVE", "loves to eat this."), + } + for i = 1, GameTooltip:NumLines() do + local fontString = getglobal("GameTooltipTextLeft" .. i) + if fontString and fontString.GetText then + local text = fontString:GetText() + if text then + for j = 1, table.getn(markers) do + local marker = markers[j] + if marker and marker ~= "" and string.find(text, marker, 1, true) then + return true + end + end + end + end + end + return false +end + +local function MTH_TT_ExtractItemIdFromLink(link) + if type(link) ~= "string" then + return nil + end + local _, _, itemIdText = string.find(link, "item:(%d+):") + if itemIdText then + return tonumber(itemIdText) + end + return nil +end + +local function MTH_TT_GetQuestColor(name, defaultR, defaultG, defaultB) + if type(QuestDifficultyColor) == "table" then + local color = QuestDifficultyColor[name] + if type(color) == "table" and color.r and color.g and color.b then + return color.r, color.g, color.b + end + end + return defaultR, defaultG, defaultB +end + +local function MTH_TT_FormatElapsedStableStyle(timestamp) + local value = tonumber(timestamp) + if not value or value <= 0 then + return nil + end + local now = tonumber(time()) or 0 + local elapsed = now - value + if elapsed < 0 then elapsed = 0 end + local days = math.floor(elapsed / 86400) + local remAfterDays = elapsed - (days * 86400) + local hours = math.floor(remAfterDays / 3600) + local remAfterHours = remAfterDays - (hours * 3600) + local mins = math.floor(remAfterHours / 60) + if days > 0 then + if hours > 0 then + return tostring(days) .. " days " .. tostring(hours) .. " hours" + end + return tostring(days) .. " days" + end + if hours > 0 then + if mins > 0 then + return tostring(hours) .. " hours " .. tostring(mins) .. " minutes" + end + return tostring(hours) .. " hours" + end + return tostring(mins) .. " minutes" +end + +local function MTH_TT_GetTooltipUnitToken() + if not GameTooltip or type(GameTooltip.GetUnit) ~= "function" then + return nil + end + local ok, unitA, unitB = pcall(function() + return GameTooltip:GetUnit() + end) + if not ok then + return nil + end + if type(unitB) == "string" and unitB ~= "" then + return unitB + end + if type(unitA) == "string" and unitA ~= "" then + return unitA + end + return nil +end + +local function MTH_TT_IsOwnerUnderPetFrame(owner) + if not owner then + return false + end + local petFrame = (_G and _G["PetFrame"]) or (type(getglobal) == "function" and getglobal("PetFrame")) + local petFrameHappiness = (_G and _G["PetFrameHappiness"]) or (type(getglobal) == "function" and getglobal("PetFrameHappiness")) + local cursor = owner + local guard = 0 + while cursor and guard < 20 do + if cursor == petFrame or cursor == petFrameHappiness then + return true + end + if type(cursor.GetParent) == "function" then + cursor = cursor:GetParent() + else + break + end + guard = guard + 1 + end + return false +end + +local function MTH_TT_IsMouseOverPetFrame() + if type(MouseIsOver) ~= "function" then + return false + end + local petFrame = (_G and _G["PetFrame"]) or (type(getglobal) == "function" and getglobal("PetFrame")) + local petFrameHappiness = (_G and _G["PetFrameHappiness"]) or (type(getglobal) == "function" and getglobal("PetFrameHappiness")) + if petFrame and MouseIsOver(petFrame) then + return true + end + if petFrameHappiness and MouseIsOver(petFrameHappiness) then + return true + end + return false +end + +local function MTH_TT_IsTooltipOnOwnPet() + if type(UnitExists) ~= "function" or type(UnitIsUnit) ~= "function" then + return false + end + if not UnitExists("pet") then + return false + end + + local owner = nil + local ownerName = "" + if GameTooltip and type(GameTooltip.GetOwner) == "function" then + owner = GameTooltip:GetOwner() + if owner and owner.GetName then + ownerName = tostring(owner:GetName() or "") + end + end + + if ownerName ~= "" then + if string.find(ownerName, "PetActionButton", 1, true) + or string.find(ownerName, "PetActionBar", 1, true) + or string.find(ownerName, "BT4PetButton", 1, true) then + return false + end + end + + local token = MTH_TT_GetTooltipUnitToken() + if token and token ~= "" then + local ok, same = pcall(UnitIsUnit, token, "pet") + if ok and same then + return true + end + end + + if UnitExists("mouseover") then + local ok, same = pcall(UnitIsUnit, "mouseover", "pet") + if ok and same then + return true + end + end + + if owner then + if MTH_TT_IsOwnerUnderPetFrame(owner) then + return true + end + if ownerName ~= "" and string.find(ownerName, "PetFrame", 1, true) then + return true + end + end + + if MTH_TT_IsMouseOverPetFrame() then + return true + end + + local hasMouseover = UnitExists("mouseover") and true or false + if (not hasMouseover) and (token == nil or token == "") then + local petName = (type(UnitName) == "function") and tostring(UnitName("pet") or "") or "" + if petName ~= "" then + local titleRegion = (type(getglobal) == "function" and getglobal("GameTooltipTextLeft1")) or (_G and _G["GameTooltipTextLeft1"]) + if titleRegion and type(titleRegion.GetText) == "function" then + local titleText = tostring(titleRegion:GetText() or "") + if titleText ~= "" and MTH_TT_NormalizeName(titleText) == MTH_TT_NormalizeName(petName) then + return true + end + end + end + end + + return false +end + +local function MTH_TT_HasExistingOwnPetTooltipLines() + if not GameTooltip or not GameTooltip.NumLines then return false end + local function hasMarker(text) + text = tostring(text or "") + if text == "" then + return false + end + if string.find(text, "Mood :", 1, true) + or string.find(text, "Loyalty :", 1, true) + or string.find(text, "XP :", 1, true) + or string.find(text, "By my side for:", 1, true) + or string.find(text, "Has eaten:", 1, true) + or string.find(text, "times this session", 1, true) then + return true + end + return false + end + for i = 1, GameTooltip:NumLines() do + local left = getglobal("GameTooltipTextLeft" .. i) + if left and left.GetText then + if hasMarker(left:GetText()) then + return true + end + end + local right = getglobal("GameTooltipTextRight" .. i) + if right and right.GetText then + if hasMarker(right:GetText()) then + return true + end + end + end + return false +end + +local function MTH_TT_GetHeatColorByRatio(ratio) + ratio = tonumber(ratio) or 0 + if ratio < 0 then ratio = 0 end + if ratio > 1 then ratio = 1 end + local red = 1 - ratio + local green = ratio + local blue = 0.15 + return red, green, blue +end + +local function MTH_TT_GetLoyaltyHeatColor(level) + local numeric = tonumber(level) + if not numeric then + return 0.90, 0.90, 0.90 + end + if numeric < 1 then numeric = 1 end + if numeric > 6 then numeric = 6 end + return MTH_TT_GetHeatColorByRatio((numeric - 1) / 5) +end + +local function MTH_TT_FormatPercentCompact(percentValue) + local numeric = tonumber(percentValue) + if not numeric then + return "?" + end + local rounded = math.floor((numeric * 10) + 0.5) / 10 + local integerPart = math.floor(rounded) + if math.abs(rounded - integerPart) < 0.001 then + return tostring(integerPart) + end + return string.format("%.1f", rounded) +end + +local function MTH_TT_ColorizeDigitsBlue(text) + local source = tostring(text or "") + if source == "" then + return "" + end + return string.gsub(source, "(%d+)", "|cff73bfff%1|r") +end + +local function MTH_TT_AddOwnPetTooltipHint() + if not (MTH_Tooltips and MTH_Tooltips.enabled) then + return + end + if not MTH_TT_IsOwnPetTooltipsEnabled() then + return + end + if not GameTooltip or not GameTooltip.AddLine then + return + end + if MTH_TT_HasExistingOwnPetTooltipLines() then + return + end + if not MTH_TT_IsTooltipOnOwnPet() then + return + end + if type(MTH_GetCurrentPetInfo) ~= "function" then + return + end + local petInfo = MTH_GetCurrentPetInfo() + if type(petInfo) ~= "table" then + return + end + + local happinessValue = tonumber(petInfo.happiness) + local happinessText = "?" + local happinessR, happinessG, happinessB = 0.90, 0.90, 0.90 + if type(GetPetHappiness) == "function" then + local h = GetPetHappiness() + if h ~= nil then + happinessValue = tonumber(h) or happinessValue + end + end + if happinessValue == 1 then + happinessText = "Unhappy" + happinessR, happinessG, happinessB = 1.00, 0.25, 0.25 + elseif happinessValue == 2 then + happinessText = "Content" + happinessR, happinessG, happinessB = 1.00, 0.90, 0.20 + elseif happinessValue == 3 then + happinessText = "Happy" + happinessR, happinessG, happinessB = 0.25, 1.00, 0.25 + end + if type(GameTooltip.AddDoubleLine) == "function" then + GameTooltip:AddDoubleLine("Mood :", tostring(happinessText), 0.90, 0.90, 0.90, happinessR, happinessG, happinessB) + else + GameTooltip:AddLine("Mood : " .. tostring(happinessText), happinessR, happinessG, happinessB) + end + + local loyaltyDisplay = tostring(petInfo.loyaltyDisplay or "") + if loyaltyDisplay == "" then + local loyaltyText = tostring(petInfo.loyaltyText or "") + if loyaltyText ~= "" then + loyaltyDisplay = loyaltyText + end + end + if loyaltyDisplay ~= "" then + local loyaltyR, loyaltyG, loyaltyB = MTH_TT_GetLoyaltyHeatColor(petInfo.loyaltyLevel) + if type(GameTooltip.AddDoubleLine) == "function" then + GameTooltip:AddDoubleLine("Loyalty :", tostring(loyaltyDisplay), 0.90, 0.90, 0.90, loyaltyR, loyaltyG, loyaltyB) + else + GameTooltip:AddLine("Loyalty : " .. tostring(loyaltyDisplay), loyaltyR, loyaltyG, loyaltyB) + end + end + + local petLevel = tonumber(petInfo.level) + local xpCur = tonumber(petInfo.xp) + local xpTot = tonumber(petInfo.xpMax) + if (petLevel == nil or petLevel < 60) and xpCur ~= nil and xpTot ~= nil and xpTot > 0 then + local xpPercent = tonumber(petInfo.xpPercent) + if xpPercent == nil then + xpPercent = math.floor((xpCur / xpTot) * 1000 + 0.5) / 10 + end + local xpRatio = xpCur / xpTot + local xpR, xpG, xpB = MTH_TT_GetHeatColorByRatio(xpRatio) + local xpCurInt = math.floor(xpCur + 0.5) + local xpTotInt = math.floor(xpTot + 0.5) + local xpValue = MTH_TT_FormatPercentCompact(xpPercent) .. "% (" .. tostring(xpCurInt) .. "/" .. tostring(xpTotInt) .. ")" + if type(GameTooltip.AddDoubleLine) == "function" then + GameTooltip:AddDoubleLine("XP :", xpValue, 0.90, 0.90, 0.90, xpR, xpG, xpB) + else + GameTooltip:AddLine("XP : " .. xpValue, xpR, xpG, xpB) + end + end + + local withMeAt = tonumber(petInfo.withMeSinceAt) or nil + local withMeText = MTH_TT_FormatElapsedStableStyle(withMeAt) + if withMeText and withMeText ~= "" then + local withMeValue = MTH_TT_ColorizeDigitsBlue(tostring(withMeText)) + if type(GameTooltip.AddDoubleLine) == "function" then + GameTooltip:AddDoubleLine("By my side for:", withMeValue, 0.90, 0.90, 0.90, 0.90, 0.90, 0.90) + else + GameTooltip:AddLine("By my side for: " .. withMeValue, 0.90, 0.90, 0.90) + end + end + + local sessionFeeds = 0 + if type(MTH_FEED_GetSessionFeedCount) == "function" then + sessionFeeds = tonumber(MTH_FEED_GetSessionFeedCount(petInfo.id)) or 0 + end + local feedValue = MTH_TT_ColorizeDigitsBlue(tostring(sessionFeeds) .. " times this session") + if type(GameTooltip.AddDoubleLine) == "function" then + GameTooltip:AddDoubleLine("Has eaten:", feedValue, 0.90, 0.90, 0.90, 0.90, 0.90, 0.90) + else + GameTooltip:AddLine("Has eaten: " .. feedValue, 0.90, 0.90, 0.90) + end + GameTooltip:Show() +end + +local function MTH_TT_BuildFoodOpinionLine(itemLink) + if type(itemLink) ~= "string" then + return nil + end + + local pet = type(MTH_FEED_GetCurrentPetContext) == "function" and MTH_FEED_GetCurrentPetContext() or nil + if type(pet) ~= "table" or not pet.liveExists then + return nil + end + + local petName = MTH_TT_Trim(pet.name) + if petName == "" then + return nil + end + + local itemId = MTH_TT_ExtractItemIdFromLink(itemLink) + if not itemId then + return nil + end + + if type(FOM_IsInDiet) == "function" then + local ok, inDiet = pcall(FOM_IsInDiet, itemId) + if not ok or not inDiet then + return nil + end + elseif type(MTH_FEED_GetFoodMeta) == "function" then + local foodMeta = MTH_FEED_GetFoodMeta(itemId) + if type(foodMeta) ~= "table" or not foodMeta.diet then + return nil + end + end + + local realmPlayer = tostring(FOM_RealmPlayer or "") + if realmPlayer == "" and type(GetCVar) == "function" and type(UnitName) == "function" then + local realmName = tostring(GetCVar("realmName") or "") + local playerName = tostring(UnitName("player") or "") + if realmName ~= "" and playerName ~= "" then + realmPlayer = realmName .. "." .. playerName + end + end + + local absoluteQuality = nil + if realmPlayer ~= "" and type(FOM_FoodQuality) == "table" then + local realmMap = FOM_FoodQuality[realmPlayer] + if type(realmMap) == "table" then + local petMap = realmMap[petName] + if type(petMap) == "table" then + absoluteQuality = petMap[itemId] + end + end + end + + if absoluteQuality == nil then + local text = string.format(MTH_TT_Loc("TOOLTIPS_FOOD_QUALITY_UNKNOWN", "%s can eat this, but hasn't tried it yet."), petName) + local color = HIGHLIGHT_FONT_COLOR + if color and color.r and color.g and color.b then + return text, color.r, color.g, color.b + end + return text, 1.0, 0.82, 0.0 + end + + local petLevel = tonumber(pet.level) or 0 + if petLevel <= 0 then + return nil + end + + local currentQuality = tonumber(absoluteQuality) / petLevel + if currentQuality < 0 then + local r, g, b = MTH_TT_GetQuestColor("trivial", 0.5, 0.5, 0.5) + return string.format(MTH_TT_Loc("TOOLTIPS_FOOD_QUALITY_UNDER", "%s doesn't like this anymore."), petName), r, g, b + elseif currentQuality == 0 then + local r, g, b = MTH_TT_GetQuestColor("trivial", 0.5, 0.5, 0.5) + return string.format(MTH_TT_Loc("TOOLTIPS_FOOD_QUALITY_MIGHT", "%s might eat this."), petName), r, g, b + elseif currentQuality <= 8 then + local r, g, b = MTH_TT_GetQuestColor("standard", 1.0, 1.0, 0.0) + return string.format(MTH_TT_Loc("TOOLTIPS_FOOD_QUALITY_WILL", "%s will eat this."), petName), r, g, b + elseif currentQuality <= 17 then + local r, g, b = MTH_TT_GetQuestColor("difficult", 1.0, 0.5, 0.0) + return string.format(MTH_TT_Loc("TOOLTIPS_FOOD_QUALITY_LIKE", "%s likes to eat this."), petName), r, g, b + elseif currentQuality <= 35 then + local r, g, b = MTH_TT_GetQuestColor("verydifficult", 1.0, 0.1, 0.1) + return string.format(MTH_TT_Loc("TOOLTIPS_FOOD_QUALITY_LOVE", "%s loves to eat this."), petName), r, g, b + end + + return nil +end + +local function MTH_TT_AddFoodTooltipHint() + if not (MTH_Tooltips and MTH_Tooltips.enabled) then + return + end + if not MTH_TT_IsFoodItemTooltipsEnabled() then + return + end + if not GameTooltip or not GameTooltip.GetItem or not GameTooltip.AddLine then + return + end + if MTH_TT_HasExistingFoodTooltipLine() then + return + end + + local _, itemLink = GameTooltip:GetItem() + if not itemLink then + return + end + + local text, r, g, b = MTH_TT_BuildFoodOpinionLine(itemLink) + if text and text ~= "" then + GameTooltip:AddLine(text, r or 1.0, g or 1.0, b or 1.0) + GameTooltip:Show() + end +end + +local function MTH_TT_HasExistingNotLearnedLine() + if not GameTooltip or not GameTooltip.NumLines then return false end + for i = 1, GameTooltip:NumLines() do + local fontString = getglobal("GameTooltipTextLeft" .. i) + if fontString and fontString.GetText then + local text = fontString:GetText() + if text and string.find(text, "Not learned yet, keep going !", 1, true) then + return true + end + end + end + return false +end + +local function MTH_TT_GetKnownSpellMap() + if type(MTH_GetKnownPetSpellMap) == "function" then + return MTH_GetKnownPetSpellMap() + end + if not MTH_CharSavedVariables or not MTH_CharSavedVariables.petTraining then + return nil + end + return MTH_CharSavedVariables.petTraining.spellMap +end + +local function MTH_TT_ParseAbilityToken(token) + local text = MTH_TT_Trim(token) + if text == "" then + return "", "", nil + end + + local _, _, abilityName, rankText = string.find(text, "^(.-)%s+(%d+)$") + if abilityName and abilityName ~= "" then + abilityName = MTH_TT_Trim(abilityName) + return abilityName, MTH_TT_Lower(abilityName), tonumber(rankText) + end + + return text, MTH_TT_Lower(text), nil +end + +local function MTH_TT_HunterKnowsAbility(abilityLower, rankNumber) + if abilityLower == "" then return false end + + local spellMap = MTH_TT_GetKnownSpellMap() + if type(spellMap) ~= "table" then + return false + end + + for _, row in pairs(spellMap) do + if row and row.name and row.isKnown ~= false then + if MTH_TT_Lower(row.name) == abilityLower then + if rankNumber then + if tonumber(row.rankNumber) == tonumber(rankNumber) then + return true + end + else + return true + end + end + end + end + + return false +end + +local function MTH_TT_FindAbilityCanonicalName(abilityName) + local name = MTH_TT_Trim(abilityName) + if name == "" then + return nil + end + if not (MTH_DS_PetSpells and type(MTH_DS_PetSpells.byAbility) == "table") then + return nil + end + if MTH_DS_PetSpells.byAbility[name] then + return name + end + local key = MTH_TT_Lower(name) + for abilityKey in pairs(MTH_DS_PetSpells.byAbility) do + if MTH_TT_Lower(abilityKey) == key then + return abilityKey + end + end + return nil +end + +local function MTH_TT_ParseRankFromText(value) + local text = MTH_TT_Trim(value) + if text == "" then + return nil + end + local _, _, rankText = string.find(text, "[Rr]ank%s*(%d+)") + if rankText then + return tonumber(rankText) + end + local _, _, trailing = string.find(text, "(%d+)$") + if trailing then + return tonumber(trailing) + end + return nil +end + +local function MTH_TT_CleanSpellName(value) + local text = MTH_TT_Trim(value) + if text == "" then + return "" + end + text = string.gsub(text, "%s*%([Rr]ank%s*%d+%)", "") + text = string.gsub(text, "%s+[Rr]ank%s*%d+$", "") + text = string.gsub(text, "%s+%d+$", "") + text = MTH_TT_Trim(text) + return text +end + +local function MTH_TT_GetTooltipSpellNameAndRank() + local spellName = nil + local rankNumber = nil + + if GameTooltip and type(GameTooltip.GetSpell) == "function" then + local ok, a, b = pcall(function() + return GameTooltip:GetSpell() + end) + if ok then + if type(a) == "string" then + spellName = a + rankNumber = MTH_TT_ParseRankFromText(b) + elseif type(b) == "string" then + spellName = b + rankNumber = MTH_TT_ParseRankFromText(a) + end + end + end + + if not spellName or spellName == "" then + local left1 = getglobal("GameTooltipTextLeft1") + if left1 and left1.GetText then + spellName = MTH_TT_Trim(left1:GetText()) + end + end + + if not rankNumber then + local right1 = getglobal("GameTooltipTextRight1") + if right1 and right1.GetText then + rankNumber = MTH_TT_ParseRankFromText(right1:GetText()) + end + end + + if not rankNumber then + local left2 = getglobal("GameTooltipTextLeft2") + if left2 and left2.GetText then + rankNumber = MTH_TT_ParseRankFromText(left2:GetText()) + end + end + + if not rankNumber then + local right2 = getglobal("GameTooltipTextRight2") + if right2 and right2.GetText then + rankNumber = MTH_TT_ParseRankFromText(right2:GetText()) + end + end + + if (not rankNumber) and GameTooltip and GameTooltip.NumLines then + for i = 2, GameTooltip:NumLines() do + local left = getglobal("GameTooltipTextLeft" .. i) + if left and left.GetText then + local text = MTH_TT_Trim(left:GetText()) + local parsed = MTH_TT_ParseRankFromText(text) + if parsed and parsed > 0 then + rankNumber = parsed + break + end + end + local right = getglobal("GameTooltipTextRight" .. i) + if right and right.GetText then + local parsedRight = MTH_TT_ParseRankFromText(right:GetText()) + if parsedRight and parsedRight > 0 then + rankNumber = parsedRight + break + end + end + end + end + + return MTH_TT_CleanSpellName(spellName), rankNumber +end + +local function MTH_TT_IsPetActionTooltipOwner() + if not GameTooltip or type(GameTooltip.GetOwner) ~= "function" then + return false + end + local owner = GameTooltip:GetOwner() + if not owner then + return false + end + local ownerName = owner.GetName and owner:GetName() or "" + ownerName = tostring(ownerName or "") + if ownerName ~= "" and ( + string.find(ownerName, "PetActionButton", 1, true) + or string.find(ownerName, "PetActionBar", 1, true) + or string.find(ownerName, "BT4PetButton", 1, true) + ) then + return true + end + + return false +end + +local function MTH_TT_IsSpellOnPetActionBar(spellName) + local wanted = MTH_TT_Lower(MTH_TT_CleanSpellName(spellName)) + if wanted == "" then + return false + end + if type(GetPetActionInfo) ~= "function" then + return false + end + for slot = 1, 12 do + local name = GetPetActionInfo(slot) + if type(name) == "string" and name ~= "" then + local normalized = MTH_TT_Lower(MTH_TT_CleanSpellName(name)) + if normalized == wanted then + return true + end + end + end + return false +end + +local function MTH_TT_GetTooltipOwnerName() + if not GameTooltip or type(GameTooltip.GetOwner) ~= "function" then + return "" + end + local owner = GameTooltip:GetOwner() + if not owner then + return "" + end + if owner.GetName then + local name = tostring(owner:GetName() or "") + if name ~= "" then + return name + end + end + return "" +end + +local function MTH_TT_AddPetActionNotLearnedHint() + if not (MTH_Tooltips and MTH_Tooltips.enabled) then + return + end + if not GameTooltip or not GameTooltip.AddLine then + return + end + if MTH_TT_HasExistingNotLearnedLine() then + return + end + + local spellName, rankNumber = MTH_TT_GetTooltipSpellNameAndRank() + if not spellName or spellName == "" then + return + end + + local ownerName = MTH_TT_GetTooltipOwnerName() + local ownerMatch = MTH_TT_IsPetActionTooltipOwner() + local barMatch = MTH_TT_IsSpellOnPetActionBar(spellName) + if not ownerMatch and not barMatch then + return + end + + local canonical = MTH_TT_FindAbilityCanonicalName(spellName) + if not canonical then + return + end + + local known = MTH_TT_HunterKnowsAbility(MTH_TT_Lower(canonical), rankNumber) + if known then + return + end + + GameTooltip:AddLine("Not learned yet, keep going ! ", 1.0, 0.25, 0.25) + GameTooltip:Show() +end + +local function MTH_TT_ParseAbilities(abilitiesText) + local text = MTH_TT_Trim(abilitiesText) + local parsed = {} + if text == "" or text == "None" then + return parsed + end + + local startPos = 1 + local len = string.len(text) + while startPos <= len do + local commaPos = string.find(text, ",", startPos, true) + local token + if commaPos then + token = string.sub(text, startPos, commaPos - 1) + startPos = commaPos + 1 + else + token = string.sub(text, startPos) + startPos = len + 1 + end + + token = MTH_TT_Trim(token) + if token ~= "" then + local _, abilityLower, rankNumber = MTH_TT_ParseAbilityToken(token) + local known = MTH_TT_HunterKnowsAbility(abilityLower, rankNumber) + table.insert(parsed, { text = token, known = known }) + end + end + + return parsed +end + +local function MTH_TT_AddTooltip(unit) + local state = MTH_Tooltips.debugState + state.injectAttempts = (state.injectAttempts or 0) + 1 + + if not UnitExists(unit) then + state.skipNoUnit = (state.skipNoUnit or 0) + 1 + MTH_TT_StateSet("skip-no-unit") + return + end + if UnitPlayerControlled(unit) then + state.skipPlayerControlled = (state.skipPlayerControlled or 0) + 1 + MTH_TT_StateSet("skip-player-controlled") + return + end + if not GameTooltip or not GameTooltip.AddLine then + state.skipTooltipApi = (state.skipTooltipApi or 0) + 1 + MTH_TT_StateSet("skip-tooltip-api") + MTH_TT_Log("skip: GameTooltip API unavailable") + return + end + + local name = UnitName(unit) + if not name or name == "" then + state.skipNoName = (state.skipNoName or 0) + 1 + MTH_TT_StateSet("skip-no-name") + MTH_TT_Log("skip: mouseover has no unit name") + return + end + + local normalized = MTH_TT_NormalizeName(name) + MTH_TT_StateSet("lookup", name, normalized) + + local beastTooltipsEnabled = MTH_TT_IsBeastTooltipsEnabled() + local ammoVendorTooltipsEnabled = MTH_TT_IsAmmoVendorTooltipsEnabled() + local vendorInfo = ammoVendorTooltipsEnabled and MTH_TT_FindVendorInfo(name) or nil + local row = beastTooltipsEnabled and MTH_TT_FindBeastRow(name) or nil + if not row and not vendorInfo then + state.lookupMiss = (state.lookupMiss or 0) + 1 + MTH_TT_StateSet("lookup-miss", name, normalized) + MTH_TT_Log("lookup miss: unit='" .. tostring(name) .. "' normalized='" .. tostring(normalized) .. "'") + return + end + + local addedAnything = nil + + if vendorInfo and (vendorInfo.arrows or vendorInfo.bullets) and not MTH_TT_HasExistingVendorTooltipLine() then + if vendorInfo.arrows and vendorInfo.bullets then + GameTooltip:AddLine("Sells Arrows and Bullets", 0.45, 0.90, 1.00) + elseif vendorInfo.arrows then + GameTooltip:AddLine("Sells Arrows", 0.45, 0.90, 1.00) + elseif vendorInfo.bullets then + GameTooltip:AddLine("Sells Bullets", 0.45, 0.90, 1.00) + end + addedAnything = 1 + MTH_TT_Log("vendor tooltip added: unit='" .. tostring(name) .. "'") + end + + if row then + local abilities = MTH_TT_ParseAbilities(row.abilities) + if table.getn(abilities) == 0 then + MTH_TT_StateSet("skip-no-abilities", name, normalized) + MTH_TT_Log("skip: beast has no abilities ('" .. tostring(name) .. "')") + elseif MTH_TT_HasExistingTooltipLine() then + state.skipExistingLine = (state.skipExistingLine or 0) + 1 + MTH_TT_StateSet("skip-existing-line", name, normalized) + MTH_TT_Log("skip: tooltip line already present for '" .. tostring(name) .. "'") + else + GameTooltip:AddLine("Pet Abilities:", 1.0, 0.95, 0.45) + for i = 1, table.getn(abilities) do + local entry = abilities[i] + if entry and entry.known then + GameTooltip:AddLine(" " .. tostring(entry.text or ""), 0.2, 1.0, 0.2) + else + GameTooltip:AddLine(" " .. tostring(entry and entry.text or ""), 1.0, 0.25, 0.25) + end + end + addedAnything = 1 + MTH_TT_Log("beast tooltip added: unit='" .. tostring(name) .. "' entries='" .. tostring(table.getn(abilities)) .. "'") + end + end + + if addedAnything then + GameTooltip:Show() + state.added = (state.added or 0) + 1 + MTH_TT_StateSet("added", name, normalized) + end +end + +local function MTH_TT_EnsureInjectFrame() + if MTH_Tooltips.injectFrame then + return MTH_Tooltips.injectFrame + end + local frame = CreateFrame("Frame", "MTH_TooltipsInjectFrame") + frame.pending = false + frame:SetScript("OnUpdate", function(self) + self = self or this + if not self or not self.pending then + return + end + self.pending = false + if MTH_Tooltips and MTH_Tooltips.enabled then + MTH_TT_AddTooltip("mouseover") + end + end) + MTH_Tooltips.injectFrame = frame + return frame +end + +local function MTH_TT_RequestInject() + local frame = MTH_TT_EnsureInjectFrame() + if frame then + frame.pending = true + end +end + +local function MTH_TT_EnsurePollFrame() + if MTH_Tooltips.pollFrame then + return MTH_Tooltips.pollFrame + end + + local frame = CreateFrame("Frame", "MTH_TooltipsPollFrame") + frame:SetScript("OnUpdate", function(_, elapsed) + if not (MTH_Tooltips and MTH_Tooltips.enabled) then + return + end + + if GameTooltip and GameTooltip.IsShown and GameTooltip:IsShown() then + MTH_TT_AddOwnPetTooltipHint() + if not MTH_TT_HasExistingNotLearnedLine() then + MTH_TT_AddPetActionNotLearnedHint() + end + end + + elapsed = elapsed or arg1 + MTH_Tooltips.pollElapsed = (MTH_Tooltips.pollElapsed or 0) + (elapsed or 0) + if MTH_Tooltips.pollElapsed < 0.15 then + return + end + MTH_Tooltips.pollElapsed = 0 + + local state = MTH_Tooltips.debugState + state.pollTicks = (state.pollTicks or 0) + 1 + + if not UnitExists("mouseover") then + MTH_Tooltips.lastMouseoverKey = "" + return + end + + local name = UnitName("mouseover") + if not name or name == "" then + return + end + local lvl = UnitLevel and UnitLevel("mouseover") or 0 + local key = tostring(name) .. "#" .. tostring(lvl) + + if key ~= MTH_Tooltips.lastMouseoverKey then + MTH_Tooltips.lastMouseoverKey = key + state.pollTriggers = (state.pollTriggers or 0) + 1 + MTH_TT_RequestInject() + return + end + + if not MTH_TT_HasExistingTooltipLine() then + MTH_TT_RequestInject() + end + end) + + MTH_Tooltips.pollFrame = frame + return frame +end + +local function MTH_TT_InstallTooltipHook() + if MTH_Tooltips.hookInstalled then + return + end + if not GameTooltip then + MTH_TT_Log("hook skipped: GameTooltip missing") + return + end + if type(GameTooltip.HookScript) == "function" then + local ok, err = pcall(function() + GameTooltip:HookScript("OnTooltipSetUnit", function() + if not (MTH_Tooltips and MTH_Tooltips.enabled) then + return + end + MTH_TT_AddOwnPetTooltipHint() + MTH_TT_RequestInject() + end) + GameTooltip:HookScript("OnTooltipSetItem", function() + MTH_TT_AddFoodTooltipHint() + end) + GameTooltip:HookScript("OnTooltipSetSpell", function() + MTH_TT_AddPetActionNotLearnedHint() + end) + end) + if ok then + MTH_Tooltips.hookInstalled = true + MTH_TT_Log("hook installed: GameTooltip OnTooltipSetUnit+OnTooltipSetItem+OnTooltipSetSpell") + else + MTH_TT_Log("hook skipped: OnTooltipSetUnit unavailable (" .. tostring(err or "unknown") .. ")") + end + else + MTH_TT_Log("hook skipped: HookScript API unavailable") + end + +end + +function MTH_Tooltips:init() + self.beastIndex = MTH_TT_BuildIndex() + self.initialized = true + MTH_TT_InstallTooltipHook() + MTH_TT_EnsureInjectFrame() + MTH_TT_EnsurePollFrame() + MTH_TT_Log("module init complete") +end + +function MTH_Tooltips:onEvent(evt, arg1) + if evt ~= "UPDATE_MOUSEOVER_UNIT" and evt ~= "UNIT_NAME_UPDATE" then return end + if not self.enabled then return end + if evt == "UNIT_NAME_UPDATE" and arg1 ~= "mouseover" then return end + self.debugState.events = (self.debugState.events or 0) + 1 + MTH_TT_Log("event: UPDATE_MOUSEOVER_UNIT") + if not self.initialized then + self:init() + end + MTH_TT_RequestInject() +end + +function MTH_Tooltips:setEnabled(enabled) + self.enabled = enabled and true or false + MTH_TT_Log("setEnabled: " .. tostring(self.enabled)) + if self.enabled and not self.initialized then + self:init() + elseif self.enabled then + MTH_TT_EnsurePollFrame() + MTH_TT_EnsureInjectFrame() + end +end + +function MTH_Tooltips:SetBeastTooltipsEnabled(enabled) + local store = MTH_TT_GetOptionsStore() + if type(store) == "table" then + store.beastTooltips = enabled and true or false + end +end + +function MTH_Tooltips:SetAmmoVendorTooltipsEnabled(enabled) + local store = MTH_TT_GetOptionsStore() + if type(store) == "table" then + store.ammoVendorTooltips = enabled and true or false + end + self.vendorIndex = nil +end + +function MTH_Tooltips:SetFoodItemTooltipsEnabled(enabled) + local store = MTH_TT_GetOptionsStore() + if type(store) == "table" then + store.foodItemTooltips = enabled and true or false + end +end + +function MTH_Tooltips:SetOwnPetTooltipsEnabled(enabled) + local store = MTH_TT_GetOptionsStore() + if type(store) == "table" then + store.ownPetTooltips = enabled and true or false + end +end + +function MTH_Tooltips:GetBeastTooltipsEnabled() + return MTH_TT_IsBeastTooltipsEnabled() +end + +function MTH_Tooltips:GetAmmoVendorTooltipsEnabled() + return MTH_TT_IsAmmoVendorTooltipsEnabled() +end + +function MTH_Tooltips:GetFoodItemTooltipsEnabled() + return MTH_TT_IsFoodItemTooltipsEnabled() +end + +function MTH_Tooltips:GetOwnPetTooltipsEnabled() + return MTH_TT_IsOwnPetTooltipsEnabled() +end + +function MTH_Tooltips:cleanup() + self.enabled = false +end + +function MTH_Tooltips:GetDebugSnapshot() + return self.debugState +end + +MTH:RegisterModule("tooltips", MTH_Tooltips) diff --git a/modules/zhunter/!---VERSION---!.txt b/modules/zhunter/!---VERSION---!.txt new file mode 100644 index 0000000..6e78211 --- /dev/null +++ b/modules/zhunter/!---VERSION---!.txt @@ -0,0 +1,67 @@ +Version 2.0 ----> Metahunt integration :-) + + +Version 1.12.14 + +September 15 - 1.12.14 +-Fixed some translation errors + +September 12 - 1.12.13 +-ZMarkTarget() should no longer lockup some clients +-Fixed some translations + +September 9 - 1.12.12 +-Should've fixed the AntiDaze lockup, once and for all! + +September 8 - 1.12.11 +-/zammo has been changed back to a toggle ability +-Added Tame Beast, Beast Training, and Beast Lore to the pet buttons +-Added the function /script ZMarkTarget() +-Altered saved variables for easier patch transitions hopefully (current spell order will be reset) + +August 26 - 1.12.10 +-Using the ZTrap Key Binding will now put your pet in passive mode as well +-More French translations +-Split up the localization files for ease of use + +August 25 - 1.12.9 +-ZTrap buttons will now fade when you enter combat +-Added /zhunter to remind you of all of the other slash commands +-You can drag food on the ZPet button to feed your pet + +August 25 - 1.12.8 +-Fixed an error if you only had 1 of the button spells + +August 24 - 1.12.7 +-Woops... fixed AntiDaze even further + +August 24 - 1.12.6 +-Fixed the lock up issue with the AntiDaze feature + +August 23 - 1.12.5 +-Might've fixed some lockup issues +-Made the buttons overlap the user interface +-More translations + +August 23 - 1.12.4 +-Added some more translations +-Possibly fixed some button load errors + +August 22 - 1.12.3 +-Updated for Patch 1.12 +-Should've removed the error for ZPet (Although the button probably still won't work because I still don't have the translations. Let me know if theres an error on the English client) +-Added some German translations + +August 21 - 1.12.2 +-Fixed the aspect bar + +August 21 - 1.12.1 +-Fixed a problem with the number of buttons displayed + +August 21 - 1.12.0 +-Altered (and hopefully fixed) all of the spell buttons +-Fixed Smart Ammo to work with all ammo +-Added a Pet Spell action bar +-Added an Anti-Daze feature +-Added a feature to automatically change the frenzy timer based on the boss +-Added a /script IsPetDead() command \ No newline at end of file diff --git a/modules/zhunter/Bindings.xml b/modules/zhunter/Bindings.xml new file mode 100644 index 0000000..ff94607 --- /dev/null +++ b/modules/zhunter/Bindings.xml @@ -0,0 +1,98 @@ + + + zButtonAspect_KeyBinding() + + + zButtonAspect_KeyBinding(1) + + + zButtonAspect_KeyBinding(2) + + + zButtonAspect_KeyBinding(3) + + + zButtonAspect_KeyBinding(4) + + + zButtonAspect_KeyBinding(5) + + + zButtonAspect_KeyBinding(6) + + + zButtonTrack_KeyBinding() + + + zButtonTrack_KeyBinding(1) + + + zButtonTrack_KeyBinding(2) + + + zButtonTrack_KeyBinding(3) + + + zButtonTrack_KeyBinding(4) + + + zButtonTrack_KeyBinding(5) + + + zButtonTrack_KeyBinding(6) + + + zButtonTrack_KeyBinding(7) + + + zButtonTrack_KeyBinding(8) + + + zButtonTrack_KeyBinding(9) + + + zButtonTrack_KeyBinding(10) + + + zButtonTrack_KeyBinding(11) + + + zButtonTrap_KeyBinding() + + + zButtonTrap_KeyBinding(1) + + + zButtonTrap_KeyBinding(2) + + + zButtonTrap_KeyBinding(3) + + + zButtonTrap_KeyBinding(4) + + + zButtonTrap_KeyBinding(5) + + + zButtonPet_KeyBinding() + + + zButtonPet_KeyBinding(1) + + + zButtonPet_KeyBinding(2) + + + zButtonPet_KeyBinding(3) + + + zButtonPet_KeyBinding(4) + + + zButtonPet_KeyBinding(5) + + + zButtonPet_KeyBinding(6) + + diff --git a/modules/zhunter/DeferredInit.lua b/modules/zhunter/DeferredInit.lua new file mode 100644 index 0000000..22d9f41 --- /dev/null +++ b/modules/zhunter/DeferredInit.lua @@ -0,0 +1,237 @@ +-- Deferred initialization for ZHunterMod +-- This file runs at the END after all frames are created and all Lua is parsed + +-- Wrap ZSpellButton_CreateChildren to automatically initialize child buttons +local original_CreateChildren = ZSpellButton_CreateChildren +function ZSpellButton_CreateChildren(parent, name, count) + -- Call original function to create children + original_CreateChildren(parent, name, count) + + -- Now initialize each child button's properties + if parent and name and count then + for i = 1, count do + local button = getglobal(name .. i) + if button then + local oldThis = this + local oldEvent = event + this = button + ZSpellButton_OnLoad() + this = oldThis + event = oldEvent + end + end + end +end + +local function ZHunterMod_RunButtonInit(frame, onEventFunc) + if not (frame and onEventFunc) then + return + end + local oldThis = this + local oldEvent = event + this = frame + ZSpellButtonParent_OnLoad() + event = "VARIABLES_LOADED" + onEventFunc() + this = oldThis + event = oldEvent +end + +local function ZHunterMod_LogDeferred(msg) + local deferredDebug = _G and _G["MTH_ZH_DEFERRED_DEBUG"] + if not (MTH and MTH.debug and deferredDebug) then + return + end + MTH_ZH_Print("[ZHUNTER] " .. tostring(msg), "error") +end + +local function ZHunterMod_EnsureButtonFrame(buttonName) + local frame = getglobal(buttonName) + if frame then + return frame + end + if type(CreateFrame) ~= "function" then + return nil + end + local created = CreateFrame("CheckButton", buttonName, UIParent, "ZSpellButtonTemplate") + if created then + created:ClearAllPoints() + if buttonName == "zButtonAspect" then + created:SetPoint("CENTER", UIParent, "CENTER", -60, 0) + elseif buttonName == "zButtonAmmo" then + created:SetPoint("CENTER", UIParent, "CENTER", 0, 0) + elseif buttonName == "zButtonTrack" then + created:SetPoint("CENTER", UIParent, "CENTER", 0, 0) + elseif buttonName == "zButtonTrap" then + created:SetPoint("CENTER", UIParent, "CENTER", 60, 0) + elseif buttonName == "zButtonPet" then + created:SetPoint("CENTER", UIParent, "CENTER", 0, 60) + elseif buttonName == "zButtonMounts" then + created:SetPoint("TOP", UIParent, "TOP", 0, -410) + elseif buttonName == "zButtonCompanions" then + created:SetPoint("TOP", UIParent, "TOP", 0, -464) + elseif buttonName == "zButtonToys" then + created:SetPoint("TOP", UIParent, "TOP", 0, -518) + else + created:SetPoint("CENTER", UIParent, "CENTER", 0, 0) + end + ZHunterMod_LogDeferred("DeferredInit: created missing frame " .. tostring(buttonName)) + end + return created +end + +function ZHunterMod_DeferredInit() + -- Check if frames were created + local pet = ZHunterMod_EnsureButtonFrame("zButtonPet") + local aspect = ZHunterMod_EnsureButtonFrame("zButtonAspect") + local track = ZHunterMod_EnsureButtonFrame("zButtonTrack") + local trap = ZHunterMod_EnsureButtonFrame("zButtonTrap") + local ammo = ZHunterMod_EnsureButtonFrame("zButtonAmmo") + local mounts = ZHunterMod_EnsureButtonFrame("zButtonMounts") + local companions = ZHunterMod_EnsureButtonFrame("zButtonCompanions") + local toys = ZHunterMod_EnsureButtonFrame("zButtonToys") + + ZHunterMod_LogDeferred("DeferredInit: Pet=" .. tostring(pet ~= nil)) + ZHunterMod_LogDeferred("DeferredInit: Aspect=" .. tostring(aspect ~= nil)) + ZHunterMod_LogDeferred("DeferredInit: Track=" .. tostring(track ~= nil)) + ZHunterMod_LogDeferred("DeferredInit: Trap=" .. tostring(trap ~= nil)) + ZHunterMod_LogDeferred("DeferredInit: Ammo=" .. tostring(ammo ~= nil)) + ZHunterMod_LogDeferred("DeferredInit: Mounts=" .. tostring(mounts ~= nil)) + ZHunterMod_LogDeferred("DeferredInit: Companions=" .. tostring(companions ~= nil)) + ZHunterMod_LogDeferred("DeferredInit: Toys=" .. tostring(toys ~= nil)) + + -- Continue with all frames that exist; do not hard-abort when one optional frame is missing. + local missing = {} + if not pet then table.insert(missing, "Pet") end + if not aspect then table.insert(missing, "Aspect") end + if not track then table.insert(missing, "Track") end + if not trap then table.insert(missing, "Trap") end + if not ammo then table.insert(missing, "Ammo") end + if not mounts then table.insert(missing, "Mounts") end + if not companions then table.insert(missing, "Companions") end + if not toys then table.insert(missing, "Toys") end + + if table.getn(missing) > 0 then + MTH_ZH_Print("[ZHUNTER] Warning: missing frames: " .. table.concat(missing, ", ") .. ". Continuing with available buttons.", "error") + end + + if not (pet or aspect or track or trap or ammo or mounts or companions or toys) then + MTH_ZH_Print("zhunter buttons not created - check for addon conflicts!", "error") + return + end + + -- Initialize each button by calling template OnLoad then button-specific OnEvent + -- We set up global 'this' and 'event' for the OnEvent functions (Lua 5.0 style) + + local initialized = 0 + local function ZHunterMod_SafeRunButtonInit(frame, onEventFunc, buttonTag) + if not (frame and onEventFunc) then + return false + end + local ok, err = pcall(ZHunterMod_RunButtonInit, frame, onEventFunc) + if not ok then + MTH_ZH_Print("[ZHUNTER] DeferredInit failed for " .. tostring(buttonTag) .. ": " .. tostring(err), "error") + return false + end + return true + end + + if pet and zButtonPet_OnEvent then + if ZHunterMod_SafeRunButtonInit(pet, zButtonPet_OnEvent, "zButtonPet") then + initialized = initialized + 1 + end + end + + if aspect and zButtonAspect_OnEvent then + if ZHunterMod_SafeRunButtonInit(aspect, zButtonAspect_OnEvent, "zButtonAspect") then + initialized = initialized + 1 + end + end + + if track and zButtonTrack_OnEvent then + if ZHunterMod_SafeRunButtonInit(track, zButtonTrack_OnEvent, "zButtonTrack") then + initialized = initialized + 1 + end + end + + if trap and zButtonTrap_OnEvent then + if ZHunterMod_SafeRunButtonInit(trap, zButtonTrap_OnEvent, "zButtonTrap") then + initialized = initialized + 1 + end + end + + if ammo and zButtonAmmo_OnEvent then + if ZHunterMod_SafeRunButtonInit(ammo, zButtonAmmo_OnEvent, "zButtonAmmo") then + initialized = initialized + 1 + end + end + + if mounts and zButtonMounts_OnEvent then + if ZHunterMod_SafeRunButtonInit(mounts, zButtonMounts_OnEvent, "zButtonMounts") then + initialized = initialized + 1 + end + end + + if companions and zButtonCompanions_OnEvent then + if ZHunterMod_SafeRunButtonInit(companions, zButtonCompanions_OnEvent, "zButtonCompanions") then + initialized = initialized + 1 + end + end + + if toys and zButtonToys_OnEvent then + if ZHunterMod_SafeRunButtonInit(toys, zButtonToys_OnEvent, "zButtonToys") then + initialized = initialized + 1 + end + end + + if initialized <= 0 then + MTH_ZH_Print("zhunter buttons not created - check for addon conflicts!", "error") + return + end + + if type(MTH_ZH_OnDeferredInitComplete) == "function" then + local ok, err = pcall(MTH_ZH_OnDeferredInitComplete) + if not ok and MTH_DebugFrame and MTH_DebugFrame.AddError then + MTH_ZH_Print("[ZHUNTER] DeferredInit callback error: " .. tostring(err), "error") + end + end +end + +-- Smart initialization that handles both initial load and reload +local function TryInitialize() + -- Check if spell data is available (hunters get aspect of monkey at level 4) + -- If player is low level without spells, we still want to initialize + local _, _, _, numSpells = GetNumSpellTabs() + + -- Only initialize if we have some basic game data ready + if numSpells and numSpells > 0 then + ZHunterMod_DeferredInit() + return true + end + return false +end + +-- Try immediate initialization (works for /reload) +if not TryInitialize() then + -- If immediate init failed, we're on initial game load + -- Set up event-based initialization + local initFrame = CreateFrame("Frame") + initFrame:RegisterEvent("PLAYER_LOGIN") + initFrame:SetScript("OnEvent", function() + if event == "PLAYER_LOGIN" then + -- Delay slightly to ensure spell book is ready + local elapsed = 0 + initFrame:SetScript("OnUpdate", function() + elapsed = elapsed + arg1 + if elapsed >= 0.3 then + initFrame:SetScript("OnUpdate", nil) + ZHunterMod_DeferredInit() + -- Master options will be initialized on-demand + end + end) + end + end) +else + -- Immediate init succeeded, do not initialize master options here + -- It will be created on-demand when first opened +end diff --git a/modules/zhunter/Localization.lua b/modules/zhunter/Localization.lua new file mode 100644 index 0000000..448854d --- /dev/null +++ b/modules/zhunter/Localization.lua @@ -0,0 +1,325 @@ +if not ZHunterMod_Saved then + ZHunterMod_Saved = {} +end + +BINDING_HEADER_ZAspectHeader = "ZAspect Buttons" +BINDING_HEADER_ZTrackHeader = "ZTrack Buttons" +BINDING_HEADER_ZTrapHeader = "ZTrap Buttons" +BINDING_HEADER_ZPetHeader = "ZPet Buttons" + +function ZHunterMod_AlignButtons() + zButtonTrack:ClearAllPoints() + zButtonTrack:SetPoint("TOP", zButtonAspect, "BOTTOM", 0, -15) + zButtonTrap:ClearAllPoints() + zButtonTrap:SetPoint("TOP", zButtonTrack, "BOTTOM", 0, -15) + if AutoStripDisplay then + AutoStripDisplay:ClearAllPoints() + AutoStripDisplay:SetPoint("TOP", zButtonTrap, "BOTTOM", 0, -10) + end +end + +-- //////////////////////////////////////////////////////////////// +-- ENGLISH +-- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + +ZHUNTER_MAGMADAR = "Magmadar" +ZHUNTER_FLAMEGORE = "Flamegore" +ZHUNTER_CHROMMAGUS = "Chrommagus" +ZHUNTER_HUHURAN = "Princess Huhuran" +ZHUNTER_GLUTH = "Gluth" + +ZHUNTER_YOU = "You" +ZHUNTER_HUNTER = "Hunter" +ZHUNTER_BOWS = "Bows" +ZHUNTER_CROSSBOWS = "Crossbows" +ZHUNTER_GUNS = "Guns" +ZHUNTER_AFFLICTED_DAZED = "(.+) (.+) afflicted by Dazed" + +ZHUNTER_BULLETS_LIGHT = "Light Shot" +ZHUNTER_BULLETS_CRAFTLIGHT = "Crafted Light Shot" +ZHUNTER_BULLETS_FLASH = "Flash Pellet" +ZHUNTER_BULLETS_HEAVY = "Heavy Shot" +ZHUNTER_BULLETS_PEBBLE = "Smooth Pebble" +ZHUNTER_BULLETS_CRAFTHEAVY = "Crafted Heavy Shot" +ZHUNTER_BULLETS_SOLID = "Solid Shot" +ZHUNTER_BULLETS_CRAFTSOLID = "Crafted Solid Shot" +ZHUNTER_BULLETS_EXPLODING = "Exploding Shot" +ZHUNTER_BULLETS_MITHRILSLUG = "Hi-impact Mithril Slugs" +ZHUNTER_BULLETS_ACCURATE = "Accurate Slugs" +ZHUNTER_BULLETS_GYROSHOT = "Mithril Gyro-Shot" +ZHUNTER_BULLETS_ICETHREADED = "Ice Threaded Bullet" +ZHUNTER_BULLETS_THORIUM = "Thorium Shells" +ZHUNTER_BULLETS_ROCKSHARD = "Rockshard Pellets" +ZHUNTER_BULLETS_CANNONBALL = "Miniature Cannon Balls" + +ZHUNTER_ARROWS_ROUGH = "Rough Arrow" +ZHUNTER_ARROWS_SHARP = "Sharp Arrow" +ZHUNTER_ARROWS_RAZOR = "Razor Arrow" +ZHUNTER_ARROWS_FEATHER = "Feathered Arrow" +ZHUNTER_ARROWS_PRECISION = "Precision Arrow" +ZHUNTER_ARROWS_JAGGED = "Jagged Arrow" +ZHUNTER_ARROWS_ICETHREADED = "Ice Threaded Arrow" +ZHUNTER_ARROWS_THORIUM = "Thorium Headed Arrow" +ZHUNTER_ARROWS_DOOMSHOT = "Doomshot" + +ZHUNTER_RAPIDFIRE = "Rapid Fire" +ZHUNTER_IMPROVEDHAWK = "Quick Shots" +ZHUNTER_BERSERKING = "Berserking" +ZHUNTER_AUTO = "Auto Shot" +ZHUNTER_AIMED = "Aimed Shot" +ZHUNTER_SERPENT = "Serpent Sting" +ZHUNTER_SCORPID = "Scorpid Sting" +ZHUNTER_VIPER = "Viper Sting" +ZHUNTER_ARCANE = "Arcane Shot" +ZHUNTER_CONCUSSIVE = "Concussive Shot" +ZHUNTER_DISTRACTING = "Distracting Shot" +ZHUNTER_VOLLEY = "Volley" + +ZHUNTER_TRACK_HUMANOIDS = "Track Humanoids" +ZHUNTER_TRACK_HIDDEN = "Track Hidden" +ZHUNTER_TRACK_BEASTS = "Track Beasts" +ZHUNTER_TRACK_ELEMENTALS = "Track Elementals" +ZHUNTER_TRACK_DEMONS = "Track Demons" +ZHUNTER_TRACK_DRAGONKIN = "Track Dragonkin" +ZHUNTER_TRACK_UNDEAD = "Track Undead" +ZHUNTER_TRACK_GIANTS = "Track Giants" +ZHUNTER_TRACK_MINERALS = "Find Minerals" +ZHUNTER_TRACK_HERBS = "Find Herbs" +ZHUNTER_TRACK_TREASURE = "Find Treasure" + +ZHUNTER_TRAP_FREEZING = "Freezing Trap" +ZHUNTER_TRAP_FROST = "Frost Trap" +ZHUNTER_TRAP_IMMOLATION = "Immolation Trap" +ZHUNTER_TRAP_EXPLOSIVE = "Explosive Trap" + +ZHUNTER_ASPECT_HAWK = "Aspect of the Hawk" +ZHUNTER_ASPECT_MONKEY = "Aspect of the Monkey" +ZHUNTER_ASPECT_CHEETAH = "Aspect of the Cheetah" +ZHUNTER_ASPECT_BEAST = "Aspect of the Beast" +ZHUNTER_ASPECT_PACK = "Aspect of the Pack" +ZHUNTER_ASPECT_WILD = "Aspect of the Wild" + +ZHUNTER_PET_EYES = "Eyes of the Beast" +ZHUNTER_PET_DISMISS = "Dismiss Pet" +ZHUNTER_PET_MEND = "Mend Pet" +ZHUNTER_PET_FEED = "Feed Pet" +ZHUNTER_PET_CALL = "Call Pet" +ZHUNTER_PET_REVIVE = "Revive Pet" +ZHUNTER_PET_LORE = "Beast Lore" +ZHUNTER_PET_TRAINING = "Beast Training" +ZHUNTER_PET_TAMING = "Tame Beast" + +-------------------------------------------------------------------- +-- Meta 09 Feb 2026 +-- Twow spells addition +ZHUNTER_PET_POSITION = "Pet Command: Take Position" + +ZHUNTER_ASPECT_WOLF = "Aspect of the Wolf" +ZHUNTER_ASPECT_VIPER = "Aspect of the Viper" + +ZHUNTER_STEADY = "Steady Shot" +ZHUNTER_BAITED = "Baited Shot" +ZHUNTER_INTIMIDATION = "Intimidation" +ZHUNTER_KILLCOMMAND = "Kill Command" +ZHUNTER_CARVE = "Carve" +ZHUNTER_LACERATE = "Lacerate" + +-------------------------------------------------------------------- + + + +-- //////////////////////////////////////////////////////////////// +-- FRENCH +-- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + +-- � = \195\136 � = \195\168 +-- � = \195\137 � = \195\169 +-- � = \195\138 � = \195\170 +-- � = \195\143 � = \195\175 +-- � = \195\135 � = \195\167 + +if ( GetLocale() == "frFR" ) then + +--ZHUNTER_MAGMADAR = "Magmadar" +--ZHUNTER_FLAMEGORE = "Flamegore" +--ZHUNTER_CHROMMAGUS = "Chrommagus" +--ZHUNTER_HUHURAN = "Princess Huhuran" +--ZHUNTER_GLUTH = "Gluth" + +ZHUNTER_YOU = "Vous" +ZHUNTER_HUNTER = "Chasseur" +--ZHUNTER_BOWS = "Bows" +ZHUNTER_CROSSBOWS = "Arbal\195\168tes" +ZHUNTER_GUNS = "Armes\195\160feus" +--ZHUNTER_AFFLICTED_DAZED = "(.+) (.+) afflicted by Dazed" + +ZHUNTER_BULLETS_LIGHT = "Balle l\195\169g\195\168re" +ZHUNTER_BULLETS_CRAFTLIGHT = "Balle l\195\169g\195\168re fabriqu\195\169e" +ZHUNTER_BULLETS_FLASH = "Bille d'\195\169clair" +ZHUNTER_BULLETS_HEAVY = "Balle lourde" +ZHUNTER_BULLETS_PEBBLE = "Caillou lisse" +ZHUNTER_BULLETS_CRAFTHEAVY = "Balle lourde fabriqu\195\169e" +ZHUNTER_BULLETS_SOLID = "Balle dure" +ZHUNTER_BULLETS_CRAFTSOLID = "Balle dure fabriqu\195\169e" +ZHUNTER_BULLETS_EXPLODING = "Balle explosive" +ZHUNTER_BULLETS_MITHRILSLUG = "Balle per\195\167antes en mithril" +ZHUNTER_BULLETS_ACCURATE = "Balles de pr\195\169cision" +ZHUNTER_BULLETS_GYROSHOT = "Balle gyroscopique en mithril" +ZHUNTER_BULLETS_ICETHREADED = "Balle de glace" +ZHUNTER_BULLETS_THORIUM = "Obus en thorium" +ZHUNTER_BULLETS_ROCKSHARD = "Balle de roche" +ZHUNTER_BULLETS_CANNONBALL = "Boulet de canon miniature" + +ZHUNTER_ARROWS_ROUGH = "Fl\195\168che grossi\195\168re" +ZHUNTER_ARROWS_SHARP = "Fl\195\168che pointue" +ZHUNTER_ARROWS_RAZOR = "Fl\195\168che rasoir" +ZHUNTER_ARROWS_FEATHER = "Fl\195\168che \195\160 plumes" +ZHUNTER_ARROWS_PRECISION = "Fl\195\168che de pr\195\169cision" +ZHUNTER_ARROWS_JAGGED = "Fl\195\168che barbel\195\169e" +ZHUNTER_ARROWS_ICETHREADED = "Fl\195\168che de glace" +ZHUNTER_ARROWS_THORIUM = "Fl\195\168ches \195\160 pointe de thorium" +ZHUNTER_ARROWS_DOOMSHOT = "Tir maudit" + +ZHUNTER_RAPIDFIRE = "Tir rapide" +--ZHUNTER_IMPROVEDHAWK = "Quick Shots" +--ZHUNTER_BERSERKING = "Berserking" +ZHUNTER_AUTO = "Tir automatique" +ZHUNTER_AIMED = "Vis\195\169e" +ZHUNTER_SERPENT = "Morsure de serpent" +ZHUNTER_SCORPID = "Piq\195\187re de scorpide" +ZHUNTER_VIPER = "Morsure de vip\195\168re" +ZHUNTER_ARCANE = "Tir des arcanes" +ZHUNTER_CONCUSSIVE = "Trait de choc" +ZHUNTER_DISTRACTING = "Trait provocateur" + +ZHUNTER_TRACK_HUMANOIDS = "Pistage des humano\195\175des" +ZHUNTER_TRACK_HIDDEN = "Pistage des camoufl\195\169s" +ZHUNTER_TRACK_BEASTS = "Pistage des b\195\170tes" +ZHUNTER_TRACK_ELEMENTALS = "Pistage des \195\169l\195\169mentaires" +ZHUNTER_TRACK_DEMONS = "Pistage des d\195\169mons" +ZHUNTER_TRACK_DRAGONKIN = "Pistage des draconiens" +ZHUNTER_TRACK_UNDEAD = "Pistage des morts-vivants" +ZHUNTER_TRACK_GIANTS = "Pistage des g\195\169ants" +--ZHUNTER_TRACK_MINERALS = "Find Minerals" +--ZHUNTER_TRACK_HERBS = "Find Herbs" +--ZHUNTER_TRACK_TREASURE = "Find Treasure" + +ZHUNTER_TRAP_FREEZING = "Pi\195\168ge givrant" +ZHUNTER_TRAP_FROST = "Pi\195\168ge de givre" +ZHUNTER_TRAP_IMMOLATION = "Pi\195\168ge d'Immolation" +ZHUNTER_TRAP_EXPLOSIVE = "Pi\195\168ge explosif" + +ZHUNTER_ASPECT_HAWK = "Aspect du faucon" +ZHUNTER_ASPECT_MONKEY = "Aspect du singe" +ZHUNTER_ASPECT_CHEETAH = "Aspect du gu\195\169pard" +ZHUNTER_ASPECT_BEAST = "Aspect de la b\195\170te" +ZHUNTER_ASPECT_PACK = "Aspect de la meute" +ZHUNTER_ASPECT_WILD = "Aspect de la nature" + +ZHUNTER_PET_EYES = "oeil de la b\195\170te" +--ZHUNTER_PET_DISMISS = "Dismiss Pet" +--ZHUNTER_PET_MEND = "Mend Pet" +--ZHUNTER_PET_FEED = "Feed Pet" +--ZHUNTER_PET_CALL = "Call Pet" +--ZHUNTER_PET_REVIVE = "Revive Pet" + +end + +-- //////////////////////////////////////////////////////////////// +-- GERMAN +-- \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + +-- � = /195/164 +-- � = /195/188 +-- � = /195/182 + +if ( GetLocale() == "deDE" ) then + +ZHUNTER_MAGMADAR = "Magmadar" +ZHUNTER_FLAMEGORE = "Flammenmaul" +ZHUNTER_CHROMMAGUS = "Chrommagus" +ZHUNTER_HUHURAN = "Prinzessin Huhuran" +--ZHUNTER_GLUTH = "Gluth" + +ZHUNTER_YOU = "Ihr" +ZHUNTER_HUNTER = "J\195\164ger" +ZHUNTER_BOWS = "B\195\182gen" +ZHUNTER_CROSSBOWS = "Armbr\195\188ste" +ZHUNTER_GUNS = "Schusswaffen" +ZHUNTER_AFFLICTED_DAZED = "(.+) (.+) ersch\195\188ttert" + +ZHUNTER_BULLETS_LIGHT = "Leichtes Geschoss" +ZHUNTER_BULLETS_CRAFTLIGHT = "Gefertigtes leichtes Geschoss" +ZHUNTER_BULLETS_FLASH = "Blitzk\195\188gelchen" +ZHUNTER_BULLETS_HEAVY = "Schweres Geschoss" +ZHUNTER_BULLETS_PEBBLE = "Glatter Kiesel" +ZHUNTER_BULLETS_CRAFTHEAVY = "Gefertigtes schweres Geschoss" +ZHUNTER_BULLETS_SOLID = "Robustes Geschoss" +ZHUNTER_BULLETS_CRAFTSOLID = "Gefertigtes robustes Geschoss" +ZHUNTER_BULLETS_EXPLODING = "Explosivgeschoss" +ZHUNTER_BULLETS_MITHRILSLUG = "Stark einschlagende Mithrilpatronen" +ZHUNTER_BULLETS_ACCURATE = "Genaue Patronen" +ZHUNTER_BULLETS_GYROSHOT = "Gyro-Mithrilgeschoss" +ZHUNTER_BULLETS_ICETHREADED = "Eisgewirkte Kugel" +ZHUNTER_BULLETS_THORIUM = "Thoriumpatronen" +ZHUNTER_BULLETS_ROCKSHARD = "Felssplitterk\195\188gelchen" +ZHUNTER_BULLETS_CANNONBALL = "Miniaturkanonenkugeln" + +ZHUNTER_ARROWS_ROUGH = "Rauer Pfeil" +ZHUNTER_ARROWS_SHARP = "Scharfer Pfeil" +ZHUNTER_ARROWS_RAZOR = "Schneidenpfeil" +ZHUNTER_ARROWS_FEATHER = "Gefiederter Pfeil" +ZHUNTER_ARROWS_PRECISION = "Pr\195\164zisionspfeil" +ZHUNTER_ARROWS_JAGGED = "Gezackter Pfeil" +ZHUNTER_ARROWS_ICETHREADED = "Eisgewirkter Pfeil" +ZHUNTER_ARROWS_THORIUM = "Thoriumpfeile" +ZHUNTER_ARROWS_DOOMSHOT = "Verdammnisgeschoss" + +ZHUNTER_RAPIDFIRE = "Schnellfeuer" +ZHUNTER_IMPROVEDHAWK = "Schnelle Sch\195\188sse" +ZHUNTER_BERSERKING = "Berserker" +ZHUNTER_AUTO = "Autom. Schuss" +ZHUNTER_AIMED = "Gezielter Schuss" +ZHUNTER_SERPENT = "Schlangenbiss" +ZHUNTER_SCORPID = "Skorpidstich" +ZHUNTER_VIPER = "Vipernbiss" +ZHUNTER_ARCANE = "Arkaner Schuss" +ZHUNTER_CONCUSSIVE = "Ersch\195\188tternder Schuss" +ZHUNTER_DISTRACTING = "Ablenkender Schuss" + +ZHUNTER_TRACK_HUMANOIDS = "Humanoide aufsp\195\188ren" +ZHUNTER_TRACK_HIDDEN = "Verborgenes aufsp\195\188ren" +ZHUNTER_TRACK_BEASTS = "Wildtiere aufsp\195\188ren" +ZHUNTER_TRACK_ELEMENTALS = "Elementare aufsp\195\188ren" +ZHUNTER_TRACK_DEMONS = "D\195\164monen aufsp\195\188ren" +ZHUNTER_TRACK_DRAGONKIN = "Drachkin aufsp\195\188ren" +ZHUNTER_TRACK_UNDEAD = "Untote aufsp\195\188ren" +ZHUNTER_TRACK_GIANTS = "Riesen aufsp\195\188ren" +ZHUNTER_TRACK_MINERALS = "Mineraliensuche" +ZHUNTER_TRACK_HERBS = "Kr\195\164utersuche" +ZHUNTER_TRACK_TREASURE = "Schatzsucher" + +ZHUNTER_TRAP_FREEZING = "Eisk\195\164ltefalle" +ZHUNTER_TRAP_FROST = "Frostfalle" +ZHUNTER_TRAP_IMMOLATION = "Feuerbrandfalle" +ZHUNTER_TRAP_EXPLOSIVE = "Sprengfalle" + +ZHUNTER_ASPECT_HAWK = "Aspekt des Falken" +ZHUNTER_ASPECT_MONKEY = "Aspekt des Affen" +ZHUNTER_ASPECT_CHEETAH = "Aspekt des Geparden" +ZHUNTER_ASPECT_BEAST = "Aspekt des Wildtiers" +ZHUNTER_ASPECT_PACK = "Aspekt des Rudels" +ZHUNTER_ASPECT_WILD = "Aspekt der Wildnis" + +ZHUNTER_PET_EYES = "Augen des Wildtiers" +ZHUNTER_PET_DISMISS = "Tier freigeben" +ZHUNTER_PET_MEND = "Tier heilen" +ZHUNTER_PET_FEED = "Tier f\195\188ttern" +ZHUNTER_PET_CALL = "Tier rufen" +ZHUNTER_PET_REVIVE = "Tier wiederbeleben" +ZHUNTER_PET_TAMING = "Wildtier z\195\164hmen" +ZHUNTER_PET_TRAINING = "Wildtierausbildung" + +ZHUNTER_PET_POSITION = "Pet Command: Take Position" + +end \ No newline at end of file diff --git a/modules/zhunter/ZHunterMod.lua b/modules/zhunter/ZHunterMod.lua new file mode 100644 index 0000000..6f4396e --- /dev/null +++ b/modules/zhunter/ZHunterMod.lua @@ -0,0 +1,149 @@ +if not ZHunterMod_Saved then + ZHunterMod_Saved = {} +end + +BINDING_HEADER_ZAspectHeader = "ZAspect Buttons" +BINDING_HEADER_ZTrackHeader = "ZTrack Buttons" +BINDING_HEADER_ZTrapHeader = "ZTrap Buttons" +BINDING_HEADER_ZPetHeader = "ZPet Buttons" +BINDING_HEADER_ZAmmoHeader = "ZAmmo Buttons" +BINDING_HEADER_ZMountsHeader = "ZMounts Buttons" +BINDING_HEADER_ZCompanionsHeader = "ZCompanions Buttons" +BINDING_HEADER_ZToysHeader = "ZToys Buttons" + +ZHunterModTooltip = CreateFrame("GameTooltip", "ZHunterModTooltip", nil, "GameTooltipTemplate") + +function MTH_ZH_IsModuleEnabled() + if not (MTH and MTH.IsModuleEnabled) then + return true + end + return MTH:IsModuleEnabled("zhunter", true) +end + +function MTH_ZH_GetSavedRoot() + if type(ZHunterMod_Saved) ~= "table" then + ZHunterMod_Saved = {} + end + return ZHunterMod_Saved +end + +function MTH_ZH_GetSavedTable(key) + local root = MTH_ZH_GetSavedRoot() + if type(key) ~= "string" or key == "" then + return root + end + if type(root[key]) ~= "table" then + root[key] = {} + end + return root[key] +end + +local function MTH_ZH_MigrateButtonSavedKeys_Replace() + local root = MTH_ZH_GetSavedRoot() + if type(root) ~= "table" then + return + end + + local mapping = { + {"ZHunterButtonAspect", "zButtonAspect"}, + {"ZHunterButtonTrack", "zButtonTrack"}, + {"ZHunterButtonTrap", "zButtonTrap"}, + {"ZHunterButtonAmmo", "zButtonAmmo"}, + {"ZHunterButtonPet", "zButtonPet"}, + {"ZHunterButtonMounts", "zButtonMounts"}, + {"ZHunterButtonCompanions", "zButtonCompanions"}, + {"ZHunterButtonToys", "zButtonToys"}, + } + + for i = 1, table.getn(mapping) do + local oldKey = mapping[i][1] + local newKey = mapping[i][2] + if root[oldKey] ~= nil then + root[newKey] = root[oldKey] + root[oldKey] = nil + end + end +end + +MTH_ZH_MigrateButtonSavedKeys_Replace() + +function MTH_ZH_Print(message, severity) + if type(MTH_Log) == "function" then + MTH_Log(message, severity) + return + end + if MTH and MTH.Print then + MTH:Print(message, severity) + return + end + if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + DEFAULT_CHAT_FRAME:AddMessage("[MetaHunt] " .. tostring(message or ""), 1, 0.9, 0.3) + end +end + +function MTH_ZH_HandleDisabledSlash(message, tableKey, fieldKey) + if MTH_ZH_IsModuleEnabled() then + return false + end + + if type(tableKey) == "string" and tableKey ~= "" then + if fieldKey then + local tbl = MTH_ZH_GetSavedTable(tableKey) + tbl[fieldKey] = nil + else + local root = MTH_ZH_GetSavedRoot() + root[tableKey] = nil + end + end + + if message then + MTH_ZH_Print(message) + end + + return true +end + +function ZHunterMod_AlignButtons() + zButtonAmmo:ClearAllPoints() + zButtonAmmo:SetPoint("TOP", zButtonAspect, "BOTTOM", 0, -15) + zButtonTrack:ClearAllPoints() + zButtonTrack:SetPoint("TOP", zButtonAmmo, "BOTTOM", 0, -15) + zButtonTrap:ClearAllPoints() + zButtonTrap:SetPoint("TOP", zButtonTrack, "BOTTOM", 0, -15) + zButtonPet:ClearAllPoints() + zButtonPet:SetPoint("TOP", zButtonTrap, "BOTTOM", 0, -15) + zButtonMounts:ClearAllPoints() + zButtonMounts:SetPoint("TOP", zButtonPet, "BOTTOM", 0, -15) + zButtonCompanions:ClearAllPoints() + zButtonCompanions:SetPoint("TOP", zButtonMounts, "BOTTOM", 0, -15) + zButtonToys:ClearAllPoints() + zButtonToys:SetPoint("TOP", zButtonCompanions, "BOTTOM", 0, -15) + if AutoStripDisplay then + AutoStripDisplay:ClearAllPoints() + AutoStripDisplay:SetPoint("TOP", zButtonToys, "BOTTOM", 0, -10) + end +end + +function ZMarkTarget() + if not MTH_ZH_IsModuleEnabled() then + return + end + + if not UnitExists("target") then return end + for i=1, 40 do + if not UnitDebuff("target", i) then break end + ZHunterModTooltip:SetOwner(UIParent, "ANCHOR_NONE") + ZHunterModTooltip:SetUnitDebuff("target", i) + local spell = ZHunterModTooltipTextLeft1:GetText() + if spell == ZHUNTER_HUNTERSMARK then + return + end + end + CastSpellByName(ZHUNTER_HUNTERSMARK) + return 1 +end + +SLASH_ZHunterMod1 = "/zhunter" +SlashCmdList["ZHunterMod"] = function(msg) + MTH_ZH_Print("Possible Slash Commands: \"/zammo\", \"/zaspect\", \"/ztrack\", \"/ztrap\", \"/zpet\", \"/zmounts\", \"/zcompanions\", \"/zminipets\", \"/ztoys\", \"/zstrip\", \"/zantidaze\"") +end diff --git a/modules/zhunter/ZSpellButtonTemplate.lua b/modules/zhunter/ZSpellButtonTemplate.lua new file mode 100644 index 0000000..86eff17 --- /dev/null +++ b/modules/zhunter/ZSpellButtonTemplate.lua @@ -0,0 +1,508 @@ +-- MAX_SPELLS = 1024 + +local function ZSpellButton_ApplySavedVisibility(parent, foundCount) + if not parent then + return + end + + local shouldShow = true + if foundCount and tonumber(foundCount) then + if tonumber(foundCount) <= 0 then + shouldShow = false + end + end + + if type(MTH_ZH_GetSavedTable) == "function" and parent.name then + local saved = MTH_ZH_GetSavedTable(parent.name) + if saved then + if saved["enabled"] == false or saved["enabled"] == 0 then + shouldShow = false + end + if saved["parent"] and saved["parent"]["hide"] then + shouldShow = false + end + end + end + + if shouldShow then + parent:Show() + else + parent:Hide() + end +end + + +function ZSpellButton_SetButtons(parent, spells) + --DEFAULT_CHAT_FRAME:AddMessage(" Parent = "..parent.name, 0, 1, 1) + if not (parent and parent.count and parent.name and spells) then return end + parent.spells = spells + + if type(parent.customSetButtons) == "function" then + local customFound = parent.customSetButtons(parent, spells) + ZSpellButton_ApplySavedVisibility(parent, customFound) + return customFound + end + + if not GetSpellName(1, "spell") then + parent:RegisterEvent("SPELLS_CHANGED") + return -1 + end + local spell, finished, temp + local info = {} + -- Find All Spell IDs + for i=1, MAX_SPELLS do + spell = GetSpellName(i, "spell") + if not spell or finished then + break + end + finished = 1 + for i2=1, table.getn(spells) do + if not info[i2] then + finished = nil + if spells[i2] == spell then + temp = i + while spells[i2] == spell do + temp = temp + 1 + spell = GetSpellName(temp, "spell") + end + info[i2] = (temp-1) + break + end + end + end + end + -- Hide All Children + for i=1, parent.count do + local child = getglobal(parent.name..i) + child:Hide() + child.id = nil + child.icon = nil + child.isspell = nil + end + local button + local count = 1 + -- Set Children IDs + for i, v in info do + if count > parent.count then break end + button = getglobal(parent.name..count) + button.id = v + button.isspell=1 + ZSpellButton_UpdateButton(button) + button:Show() + -- Set The Parent To The First Spell + if count == 1 then + parent.id = v + parent.isspell=1 + ZSpellButton_UpdateButton(parent) + parent:Enable() + end + count = count + 1 + end + local foundCount = count - 1 + ZSpellButton_ApplySavedVisibility(parent, foundCount) + return foundCount +end + +local function ZHunterMod_UpdateButtonCount(parent, found) + return +end + +function ZSpellButton_SetExpandDirection(parent, direction) + local button = getglobal(parent.name.."1") + local offset = parent:GetWidth() / 36 + local circleVisible = true + if type(MTH_ZH_GetSavedTable) == "function" and parent and parent.name then + local saved = MTH_ZH_GetSavedTable(parent.name) + if saved and saved["parent"] and saved["parent"]["circle"] ~= nil then + circleVisible = saved["parent"]["circle"] and true or false + end + end + if parent and parent.circle then + if circleVisible then + parent.circle:Show() + else + parent.circle:Hide() + end + end + if circleVisible then + offset = offset * 5 + else + offset = offset * 3 + end + button:ClearAllPoints() + if direction == "TOP" then + button:SetPoint("BOTTOM", parent, "TOP", 0, offset) + elseif direction == "BOTTOM" then + button:SetPoint("TOP", parent, "BOTTOM", 0, -1 * offset) + elseif direction == "LEFT" then + button:SetPoint("RIGHT", parent, "LEFT", -1 * offset, 0) + elseif direction == "RIGHT" then + button:SetPoint("LEFT", parent, "RIGHT", offset, 0) + end +end + +function ZSpellButton_ArrangeChildren(parent, rows, count, horizontal, vertical) + if not (parent and parent.count and parent.name) then return end + local left, right, top, bottom = "LEFT", "RIGHT", "TOP", "BOTTOM" + local xoffset = parent:GetWidth() / 36 + xoffset = xoffset * 3 + local yoffset = -1 * xoffset + if horizontal then + left, right = "RIGHT", "LEFT" + xoffset = xoffset * -1 + end + if vertical then + top, bottom = "BOTTOM", "TOP" + yoffset = yoffset * -1 + end + if count > parent.count then + count = parent.count + end + local cols = ceil(count / rows) + local temp = 1 + local button + for i=2, parent.count do + button = getglobal(parent.name..i) + if i > count then + button:Hide() + else + if button.id then + button:Show() + end + button:ClearAllPoints() + if temp >= cols then + button:SetPoint(top, getglobal(parent.name..(i-temp)), bottom, 0, yoffset) + temp = 1 + else + button:SetPoint(left, getglobal(parent.name..(i-1)), right, xoffset, 0) + temp = temp + 1 + end + + end + end +end + +function ZSpellButton_SetSize(parent, size, setChildren) + local bgscale = 64 / 36 + local cdscale = 0.75 / 36 + if setChildren then + local button + for i=1, parent.count do + button = getglobal(parent.name..i) + button:SetHeight(size) + button:SetWidth(size) + button.background:SetWidth(size * bgscale) + button.background:SetHeight(size * bgscale) + button.cooldown:SetModelScale(size * cdscale) + end + else + local cscale = 100/36 + local cxscale = 20 / 36 + local cyscale = -22 / 36 + local circleVisible = true + if type(MTH_ZH_GetSavedTable) == "function" and parent and parent.name then + local saved = MTH_ZH_GetSavedTable(parent.name) + if saved and saved["parent"] and saved["parent"]["circle"] ~= nil then + circleVisible = saved["parent"]["circle"] and true or false + end + end + parent:SetHeight(size) + parent:SetWidth(size) + parent.background:SetWidth(size * bgscale) + parent.background:SetHeight(size * bgscale) + parent.cooldown:SetModelScale(size * cdscale) + parent.circle:SetWidth(size * cscale) + parent.circle:SetHeight(size * cscale) + parent.circle:SetPoint("CENTER", size * cxscale, size * cyscale) + if circleVisible then + parent.circle:Show() + else + parent.circle:Hide() + end + end +end + +function ZSpellButton_UpdateButton(button) + local texture + if not button then + button = this + end + if not (button.id and button.icontexture) then + return + end + texture = button.icon or GetSpellTexture(button.id, "spell") + button.icontexture:SetTexture(texture) + button.icontexture:Show() +end + +function ZSpellButton_UpdateCooldown(button) + if not button then + button = this + end + if not button.id or not button.isspell then return end + local start, duration, enable = GetSpellCooldown(button.id, "spell") + CooldownFrame_SetTimer(button.cooldown, start, duration, enable) + if not button.customcolor then + local r, g, b = 1.0, 1.0, 1.0 + if enable ~= 1 then + r, g, b = 0.4, 0.4, 0.4 + end + button.icontexture:SetVertexColor(r, g, b) + end +end + +function ZSpellButton_CreateChildren(parent, name, count) + if not (parent and name and count) then return end + parent.children = CreateFrame("Frame", parent:GetName().."Children", UIParent) + parent.count = count + parent.name = name + local button + for i=1, count do + button = CreateFrame("CheckButton", parent.name..i, parent.children, "ZSpellButtonChildTemplate") + button.parent = parent + end + if type(ZSpellButton_ApplyChildrenExpanded) == "function" then + ZSpellButton_ApplyChildrenExpanded(parent) + end +end + +function ZSpellButton_OnLoad() + this:RegisterEvent("SPELL_UPDATE_COOLDOWN") + this:RegisterForClicks("LeftButtonUp", "RightButtonUp") + local name = this:GetName() + this.icontexture = getglobal(name.."IconTexture") + this.cooldown = getglobal(name.."Cooldown") + this.background = getglobal(name.."Background") +end + +function ZSpellButton_OnEvent() + if event == "SPELL_UPDATE_COOLDOWN" then + ZSpellButton_UpdateCooldown() + end +end + +local function ZSpellButton_EnsureParentFlags(parent) + if not (parent and parent.name) then + return + end + local saved = MTH_ZH_GetSavedTable(parent.name) + if parent.tooltip == nil and saved["tooltip"] ~= nil then + parent.tooltip = saved["tooltip"] and true or false + end + if parent.hideonclick == nil and saved["children"] and saved["children"]["hideonclick"] ~= nil then + parent.hideonclick = saved["children"]["hideonclick"] and true or false + end +end + +function ZSpellButton_GetChildrenExpanded(parent) + if not (parent and parent.name) then + return true + end + local saved = MTH_ZH_GetSavedTable(parent.name) + if not saved["children"] then + saved["children"] = {} + end + if saved["children"]["expanded"] == nil then + saved["children"]["expanded"] = 1 + end + local expanded = saved["children"]["expanded"] ~= false and saved["children"]["expanded"] ~= 0 + return expanded +end + +function ZSpellButton_SetChildrenExpanded(parent, expanded) + if not (parent and parent.name) then + return + end + local saved = MTH_ZH_GetSavedTable(parent.name) + if not saved["children"] then + saved["children"] = {} + end + if expanded then + saved["children"]["expanded"] = 1 + if parent.children then + parent.children:Show() + end + else + saved["children"]["expanded"] = 0 + if parent.children then + parent.children:Hide() + end + end +end + +function ZSpellButton_ApplyChildrenExpanded(parent) + if not (parent and parent.children) then + return + end + if ZSpellButton_GetChildrenExpanded(parent) then + parent.children:Show() + else + parent.children:Hide() + end +end + +function ZSpellButton_OnEnter() + ZSpellButton_EnsureParentFlags(this.parent) + if this.parent.tooltip then + GameTooltip:SetOwner(this, "ANCHOR_TOPRIGHT") + if this.isspell then + GameTooltip:SetSpell(this.id, "spell") + else + GameTooltip:SetText(this.ammocount .. " x "..this.ammolink, 1, 1, 1) + --GameTooltip:AddLine("Q="..this.ammocount) + end + end +end + +function ZSpellButton_OnClick() + ZSpellButton_EnsureParentFlags(this.parent) + this:SetChecked(0) + if type(this.parent.beforeclick) == "function" then + if this.parent.beforeclick(this) then + return + end + end + if this.isspell then + CastSpell(this.id, "spell") + else + PickupContainerItem(this.ammobag,this.ammoslot) + EquipCursorItem(0) + + -- Update parent button immediately to show newly equipped ammo + if not this.isspell then + this.parent.id = this.ammoid + this.parent.icon = this.icon + this.parent.isspell = nil + this.parent.ammoname = this.ammoname + this.parent.ammobrol = this.ammobrol + this.parent.ammoqual = this.ammoqual + this.parent.ammolvl = this.ammolvl + this.parent.ammotype = this.ammotype + this.parent.ammobag = this.ammobag + this.parent.ammoslot = this.ammoslot + this.parent.ammoid = this.ammoid + this.parent.ammolink = this.ammolink + this.parent.ammocount = this.ammocount + if type(this.parent.customUpdateButton) == "function" then + this.parent.customUpdateButton(this.parent) + else + ZSpellButton_UpdateButton(this.parent) + end + end + end + if type(this.parent.afterclick) == "function" then + if this.parent.afterclick(this) then + return + end + end + if this.parent.hideonclick then + if type(ZSpellButton_SetChildrenExpanded) == "function" then + ZSpellButton_SetChildrenExpanded(this.parent, false) + elseif this.parent.children then + this.parent.children:Hide() + end + end +end + +function ZSpellButtonParent_OnLoad() + this:RegisterEvent("SPELL_UPDATE_COOLDOWN") + this:RegisterEvent("LEARNED_SPELL_IN_TAB") + this:RegisterForDrag("LeftButton") + this:RegisterForClicks("LeftButtonUp", "RightButtonUp") + local name = this:GetName() + this.icontexture = getglobal(name.."IconTexture") + this.cooldown = getglobal(name.."Cooldown") + this.background = getglobal(name.."Background") + this.circle = getglobal(name.."Circle") + -- Keep enabled so empty buttons can be moved on low-level characters. + this:Enable() +end + +function ZSpellButtonParent_OnEvent() + if event == "SPELL_UPDATE_COOLDOWN" then + ZSpellButton_UpdateCooldown() + elseif event == "LEARNED_SPELL_IN_TAB" then + if this.name and this.count and this.spells then + local found = ZSpellButton_SetButtons(this, this.spells) + ZHunterMod_UpdateButtonCount(this, found) + end + elseif event == "SPELLS_CHANGED" then + this.found = ZSpellButton_SetButtons(this, this.spells) + ZHunterMod_UpdateButtonCount(this, this.found) + if this.found == 0 then + this:Hide() + end + if this.found > -1 then + this:UnregisterEvent("SPELLS_CHANGED") + end + end +end + +function ZSpellButtonParent_OnEnter(frame) + if not frame then + frame = this + end + ZSpellButton_EnsureParentFlags(frame) + if frame.tooltip and frame.id then + GameTooltip:SetOwner(frame, "ANCHOR_TOPLEFT") + local msg, rank + + if frame.isspell then + msg, rank = GetSpellName(frame.id, "spell") + if string.len(rank) > 0 then + msg = msg.." ("..rank..")" + end + else + msg = frame.ammoname + end + + GameTooltip:SetText(msg, 1, 1, 1) + GameTooltip:AddLine("Alt+Drag To Move This Button") + GameTooltip:Show() + end +end + +function ZSpellButtonParent_OnClick() + ZSpellButton_EnsureParentFlags(this) + this:SetChecked(0) + if arg1 == "RightButton" then + local showChildren = not this.children:IsVisible() + if type(ZSpellButton_SetChildrenExpanded) == "function" then + ZSpellButton_SetChildrenExpanded(this, showChildren) + elseif showChildren then + this.children:Show() + else + this.children:Hide() + end + else + if type(this.beforeclick) == "function" then + if this.beforeclick(this) then + return + end + end + if this.isspell then + if not this.id then + return + end + CastSpell(this.id, "spell") + else + if not (this.ammobag and this.ammoslot) then + return + end + PickupContainerItem(this.ammobag,this.ammoslot) + EquipCursorItem(0) + end + if type(this.afterclick) == "function" then + if this.afterclick(this) then + return + end + end + if this.hideonclick then + if type(ZSpellButton_SetChildrenExpanded) == "function" then + ZSpellButton_SetChildrenExpanded(this, false) + elseif this.children then + this.children:Hide() + end + end + end +end \ No newline at end of file diff --git a/modules/zhunter/ZSpellButtonTemplate.xml b/modules/zhunter/ZSpellButtonTemplate.xml new file mode 100644 index 0000000..a3c77fe --- /dev/null +++ b/modules/zhunter/ZSpellButtonTemplate.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ZSpellButtonParent_OnClick() + + + ZSpellButtonParent_OnEnter() + + + GameTooltip:Hide() + + + if ( this.isMoving ) then + this:StopMovingOrSizing() + this.isMoving = false + end + + + if IsAltKeyDown() then + this:StartMoving() + this.isMoving = true + end + + + this:StopMovingOrSizing() + this.isMoving = false + + + + + + + + + + + \ No newline at end of file diff --git a/modules/zhunter/loader.xml b/modules/zhunter/loader.xml new file mode 100644 index 0000000..a8fd50b --- /dev/null +++ b/modules/zhunter/loader.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/zhunter/module.lua b/modules/zhunter/module.lua new file mode 100644 index 0000000..920ff73 --- /dev/null +++ b/modules/zhunter/module.lua @@ -0,0 +1,326 @@ +------------------------------------------------------ +-- MetaHunt: zhunter Module Wrapper +-- Modern module abstraction for the zhunter addon +------------------------------------------------------ + +MTH_ZH_MANAGED_HOOKS = true + +local MTH_ZHunter = { + name = "zhunter", + enabled = true, + version = "1.0.3", + events = { + "VARIABLES_LOADED", + "PLAYER_ENTERING_WORLD", + "MTH_PET_LIVE_STATE_CHANGED", + }, + features = { + aspect = true, + tracking = true, + traps = true, + ammo = true, + pet = true, + } +} + +local function MTH_ZH_Log(msg) + if not (MTH and MTH.debug) then + return + end + MTH:Print("[ZH MODULE] " .. tostring(msg), "debug") +end + +local MTH_ZH_PostLoginRestoreFrame = nil +local MTH_ZH_BootstrapFrame = nil +local MTH_ZH_SyncSavedVariables +local MTH_ZH_SetButtonsVisible +local MTH_ZH_SetAuxFramesVisible +local MTH_ZH_RestoreRuntimeFeatureFlags +local MTH_ZH_ApplyEnabledRuntimeState +local MTH_ZH_QueuePostLoginRestore + +local function MTH_ZH_ApplyDefaultWidgetSpawnLayoutOnce() + local savedRoot = nil + if type(MTH_ZH_GetSavedRoot) == "function" then + savedRoot = MTH_ZH_GetSavedRoot() + elseif type(ZHunterMod_Saved) == "table" then + savedRoot = ZHunterMod_Saved + end + + if type(savedRoot) ~= "table" then + return false + end + + if savedRoot["_mth_widget_spawn_layout_v1"] then + return false + end + + local orderedButtons = { + "zButtonAspect", + "zButtonAmmo", + "zButtonTrack", + "zButtonTrap", + "zButtonPet", + "zButtonMounts", + "zButtonCompanions", + "zButtonToys", + } + + local startY = -140 + local stepY = -54 + + local orderedCount = table.getn(orderedButtons) + for i = 1, orderedCount do + if not getglobal(orderedButtons[i]) then + MTH_ZH_Log("skipped one-time default widget spawn layout (missing frame: " .. tostring(orderedButtons[i]) .. ")") + return false + end + end + + for i = 1, orderedCount do + local buttonName = orderedButtons[i] + local button = getglobal(buttonName) + if button then + button:ClearAllPoints() + button:SetPoint("TOP", UIParent, "TOP", 0, startY + ((i - 1) * stepY)) + end + end + + savedRoot["_mth_widget_spawn_layout_v1"] = 1 + MTH_ZH_Log("applied one-time default widget spawn layout") + return true +end + +function MTH_ZH_OnDeferredInitComplete() + if not (MTH and MTH.IsModuleEnabled and MTH:IsModuleEnabled("zhunter", true)) then + if MTH_ZH_SetButtonsVisible then + MTH_ZH_SetButtonsVisible(false) + end + if MTH_ZH_SetAuxFramesVisible then + MTH_ZH_SetAuxFramesVisible(false) + end + MTH_ZH_Log("deferred init applied disabled-state hide") + return + end + MTH_ZH_SyncSavedVariables() + MTH_ZH_ApplyDefaultWidgetSpawnLayoutOnce() + MTH_ZH_RestoreRuntimeFeatureFlags() + MTH_ZH_QueuePostLoginRestore("deferred-init") +end + +MTH_ZH_QueuePostLoginRestore = function(tag) + if not MTH_ZH_PostLoginRestoreFrame then + MTH_ZH_PostLoginRestoreFrame = CreateFrame("Frame", "MTH_ZH_PostLoginRestoreFrame") + if not MTH_ZH_PostLoginRestoreFrame then + return + end + end + + MTH_ZH_PostLoginRestoreFrame._mthElapsed = 0 + MTH_ZH_PostLoginRestoreFrame:SetScript("OnUpdate", function() + this._mthElapsed = (this._mthElapsed or 0) + (arg1 or 0) + if this._mthElapsed < 0.2 then + return + end + + this:SetScript("OnUpdate", nil) + if not (MTH and MTH.IsModuleEnabled and MTH:IsModuleEnabled("zhunter", true)) then + return + end + + MTH_ZH_SyncSavedVariables() + MTH_ZH_RestoreRuntimeFeatureFlags() + MTH_ZH_Log("post-login restore applied: " .. tostring(tag)) + end) +end + +local function MTH_ZH_EnsureBootstrapRestoreFrame() + if MTH_ZH_BootstrapFrame then + return MTH_ZH_BootstrapFrame + end + + MTH_ZH_BootstrapFrame = CreateFrame("Frame", "MTH_ZH_BootstrapRestoreFrame") + if not MTH_ZH_BootstrapFrame then + return nil + end + + MTH_ZH_BootstrapFrame:RegisterEvent("VARIABLES_LOADED") + MTH_ZH_BootstrapFrame:RegisterEvent("PLAYER_ENTERING_WORLD") + MTH_ZH_BootstrapFrame:SetScript("OnEvent", function() + if not (MTH and MTH.IsModuleEnabled and MTH:IsModuleEnabled("zhunter", true)) then + return + end + + if event == "VARIABLES_LOADED" then + MTH_ZH_SyncSavedVariables() + return + end + + if event == "PLAYER_ENTERING_WORLD" then + MTH_ZH_ApplyEnabledRuntimeState("bootstrap:" .. tostring(event)) + MTH_ZH_QueuePostLoginRestore("bootstrap:" .. tostring(event)) + this:UnregisterAllEvents() + this:SetScript("OnEvent", nil) + end + end) + + return MTH_ZH_BootstrapFrame +end + +MTH_ZH_ApplyEnabledRuntimeState = function(source) + MTH_ZH_SyncSavedVariables() + MTH_ZH_SetButtonsVisible(true) + MTH_ZH_SetAuxFramesVisible(true) + MTH_ZH_RestoreRuntimeFeatureFlags() +end + +MTH_ZH_SyncSavedVariables = function() + if not MTH or not MTH.GetModuleCharSavedVariables then + return + end + + local moduleStore = MTH:GetModuleCharSavedVariables("zhunter") + + if type(ZHunterMod_Saved) == "table" then + if MTH_CharSavedVariables and MTH_CharSavedVariables.modules then + MTH_CharSavedVariables.modules.zhunter = ZHunterMod_Saved + end + elseif type(moduleStore) == "table" then + ZHunterMod_Saved = moduleStore + end +end + +MTH_ZH_SetButtonsVisible = function(visible) + local buttonNames = { + "zButtonAspect", + "zButtonTrack", + "zButtonTrap", + "zButtonAmmo", + "zButtonPet", + "zButtonMounts", + "zButtonCompanions", + "zButtonToys", + } + + local function MTH_ZH_SetChildButtonsVisible(parentButton, parentName, show) + if not parentButton then + return + end + + local showChildren = false + if show then + if type(ZSpellButton_GetChildrenExpanded) == "function" then + showChildren = ZSpellButton_GetChildrenExpanded(parentButton) + else + showChildren = true + end + end + + if parentButton.children then + if showChildren then + parentButton.children:Show() + else + parentButton.children:Hide() + end + end + + if not parentButton.count then + return + end + + local baseName = parentButton.name or parentName + if not baseName then + return + end + + for index = 1, parentButton.count do + local child = getglobal(baseName .. index) + if child then + if showChildren then + if child.id then + child:Show() + else + child:Hide() + end + else + child:Hide() + end + end + end + end + + for _, buttonName in ipairs(buttonNames) do + local button = getglobal(buttonName) + if button then + MTH_ZH_SetChildButtonsVisible(button, buttonName, visible) + if visible then + button:Show() + else + button:Hide() + end + end + end +end + +MTH_ZH_SetAuxFramesVisible = function(visible) + return +end + +local function MTH_ZH_ClearRuntimeFeatureFlags() + return +end + +MTH_ZH_RestoreRuntimeFeatureFlags = function() + return +end + +function MTH_ZHunter:init() + -- ZHunterMod initializes via the game events + -- Features: Aspect, Tracking, Trap, Ammo, Pet management buttons + MTH_ZH_SyncSavedVariables() + MTH_ZH_EnsureBootstrapRestoreFrame() + if self.enabled then + MTH_ZH_SyncSavedVariables() + end +end + +function MTH_ZHunter:setEnabled(enabled) + MTH_ZH_SyncSavedVariables() + if enabled then + MTH_ZH_EnsureBootstrapRestoreFrame() + MTH_ZH_ApplyEnabledRuntimeState("setEnabled") + else + MTH_ZH_ClearRuntimeFeatureFlags() + MTH_ZH_SetButtonsVisible(false) + MTH_ZH_SetAuxFramesVisible(false) + end +end + +function MTH_ZHunter:onEvent(evt, arg1, arg2) + if not self.enabled then + return + end + if evt == "MTH_PET_LIVE_STATE_CHANGED" then + if type(zButtonPet_RefreshLiveState) == "function" then + zButtonPet_RefreshLiveState(arg1, arg2) + end + return + end + if evt == "VARIABLES_LOADED" then + MTH_ZH_SyncSavedVariables() + MTH_ZH_RestoreRuntimeFeatureFlags() + return + end + if evt == "PLAYER_ENTERING_WORLD" then + MTH_ZH_ApplyEnabledRuntimeState("event:" .. tostring(evt)) + MTH_ZH_QueuePostLoginRestore(evt) + end +end + +function MTH_ZHunter:cleanup() + -- Cleanup code when module is unloaded + self:setEnabled(false) +end + +-- Register with MTH +MTH:RegisterModule("zhunter", MTH_ZHunter) diff --git a/modules/zhunter/zButtonAmmo.lua b/modules/zhunter/zButtonAmmo.lua new file mode 100644 index 0000000..9b42bb6 --- /dev/null +++ b/modules/zhunter/zButtonAmmo.lua @@ -0,0 +1,1037 @@ +local function zButtonAmmo_GetRoot() + return MTH_ZH_GetSavedRoot() +end + +local root = zButtonAmmo_GetRoot() +if not root["zButtonAmmo"] then + root["zButtonAmmo"] = {} + root["zButtonAmmo"]["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9} + root["zButtonAmmo"]["visible"] = {1, 1, 1, 1, 1, 1, 1, 1, 1} + root["zButtonAmmo"]["rows"] = 1 + root["zButtonAmmo"]["horizontal"] = nil + root["zButtonAmmo"]["vertical"] = nil + root["zButtonAmmo"]["firstbutton"] = "RIGHT" + root["zButtonAmmo"]["enabled"] = 1 + root["zButtonAmmo"]["tooltip"] = 1 + root["zButtonAmmo"]["parent"] = {} + root["zButtonAmmo"]["parent"]["size"] = 36 + root["zButtonAmmo"]["parent"]["hide"] = nil + root["zButtonAmmo"]["parent"]["circle"] = 1 + root["zButtonAmmo"]["children"] = {} + root["zButtonAmmo"]["children"]["size"] = 36 + root["zButtonAmmo"]["children"]["hideonclick"] = 1 + root["zButtonAmmo"]["showammoname"] = 1 + root["zButtonAmmo"]["lastEquipped"] = nil + root["zButtonAmmo"]["debug"] = false +end + +ZHunterMod_Ammo_Buttons = nil + +local ZHUNTER_AMMO_MAX_COUNT = math.max(table.getn(MTH_AMMO_ARROWS or {}), table.getn(MTH_AMMO_BULLETS or {})) +local zButtonAmmo_Cache = {} + +local function zButtonAmmo_GetSaved() + local currentRoot = zButtonAmmo_GetRoot() + if not currentRoot["zButtonAmmo"] then + currentRoot["zButtonAmmo"] = {} + end + return currentRoot["zButtonAmmo"] +end + +local zButtonAmmo_ShortArrowLabels = { + [ARROWS_ROUGH] = "Rough", + [ARROWS_SHARP] = "Sharp", + [ARROWS_RAZOR] = "Razor", + [ARROWS_JAGGED] = "Jagged", + [ARROWS_ICETHREADED] = "Ice", + [ARROWS_THORIUM] = "Thorium", + [ARROWS_DOOMSHOT] = "Doom", +} + +local zButtonAmmo_ShortBulletLabels = { + [BULLETS_LIGHT] = "Light", + [BULLETS_CRAFTLIGHT] = "C.Light", + [BULLETS_FLASH] = "Flash", + [BULLETS_HEAVY] = "Heavy", + [BULLETS_PEBBLE] = "Smooth", + [BULLETS_CRAFTHEAVY] = "Heavy", + [BULLETS_SOLID] = "Solid", + [BULLETS_CRAFTSOLID] = "C.Solid", + [BULLETS_EXPLODING] = "Explo", + [BULLETS_ROCKSHARD] = "Rock", + [BULLETS_CANNONBALL] = "Cannon", + [BULLETS_MITHRILSLUG] = "Hi-imp", + [BULLETS_ACCURATE] = "Accur", + [BULLETS_GYROSHOT] = "Gyro", + [BULLETS_THORIUM] = "Thorium", + [BULLETS_ICETHREADED] = "Ice", +} + +local function zButtonAmmo_GetColorGradient(perc) + perc = perc > 1 and 1 or perc + perc = perc < 0 and 0 or perc + local r1, g1, b1, r2, g2, b2 + if perc <= 0.5 then + perc = perc * 2 + r1, g1, b1 = 1, 0, 0 + r2, g2, b2 = 1, 1, 0 + else + perc = perc * 2 - 1 + r1, g1, b1 = 1, 1, 0 + r2, g2, b2 = 0, 1, 0 + end + local r = r1 + (r2 - r1) * perc + local g = g1 + (g2 - g1) * perc + local b = b1 + (b2 - b1) * perc + return r, g, b +end + +local function zButtonAmmo_EnsureCountText(button) + if button and not button.ammoCountText then + button.ammoCountText = button:CreateFontString(nil, "OVERLAY") + button.ammoCountText:SetPoint("BOTTOMRIGHT", button) + button.ammoCountText:SetFont("Fonts\\ARIALN.ttf", 14, "OUTLINE") + end + return button and button.ammoCountText +end + +local function zButtonAmmo_EnsureShortLabelText(button) + if button and not button.ammoShortLabelText then + button.ammoShortLabelText = button:CreateFontString(nil, "OVERLAY") + button.ammoShortLabelText:SetPoint("TOPLEFT", button, "TOPLEFT", 1, -1) + button.ammoShortLabelText:SetFont("Fonts\\ARIALN.ttf", 9, "OUTLINE") + end + return button and button.ammoShortLabelText +end + +local function zButtonAmmo_GetQualityColor(quality) + local tableRef = MTH_ITEM_QUALITY_COLORS or ITEM_QUALITY_COLORS + if tableRef then + local q = tonumber(quality) + if q and tableRef[q] then + local c = tableRef[q] + if c.r and c.g and c.b then + return c.r, c.g, c.b + end + end + end + return 1, 1, 1 +end + +local function zButtonAmmo_ShouldShowShortLabel() + local saved = zButtonAmmo_GetSaved() + if saved["showammoname"] == nil then + saved["showammoname"] = 1 + end + return saved["showammoname"] and true or false +end + +local function zButtonAmmo_GetAmmoSlotId() + if GetInventorySlotInfo then + local slotId = GetInventorySlotInfo("AmmoSlot") + if slotId and slotId > 0 then + return slotId + end + end + return 0 +end + +local function zButtonAmmo_GetEquippedAmmoLink() + local link = GetInventoryItemLink("player", 0) + if link then + return link + end + + local ammoSlot = zButtonAmmo_GetAmmoSlotId() + if ammoSlot and ammoSlot ~= 0 then + link = GetInventoryItemLink("player", ammoSlot) + if link then + return link + end + end + + if ZHunterModTooltip then + ZHunterModTooltip:SetOwner(UIParent, "ANCHOR_NONE") + if ZHunterModTooltip:SetInventoryItem("player", ammoSlot) and ZHunterModTooltipTextLeft1 then + local tooltipName = ZHunterModTooltipTextLeft1:GetText() + if tooltipName and tooltipName ~= "" then + return "[" .. tooltipName .. "]" + end + end + end + + return nil +end + +local function zButtonAmmo_RebuildCache() + local knownAmmo = {} + for i = 1, table.getn(MTH_AMMO_ARROWS or {}) do + knownAmmo[(MTH_AMMO_ARROWS or {})[i]] = "Arrow" + end + for i = 1, table.getn(MTH_AMMO_BULLETS or {}) do + knownAmmo[(MTH_AMMO_BULLETS or {})[i]] = "Bullet" + end + + local equippedAmmo = nil + local equippedLink = zButtonAmmo_GetEquippedAmmoLink() + if equippedLink then + local _, _, equippedName = string.find(equippedLink, "%[(.+)%]") + if equippedName then + equippedAmmo = equippedName + end + local _, _, equippedId = string.find(equippedLink, "Hitem:((%d+).-)") + if equippedId and not equippedAmmo then + equippedAmmo = GetItemInfo(equippedId) + end + end + + zButtonAmmo_Cache = {} + for bag = 0, 4 do + for slot = 1, GetContainerNumSlots(bag) do + local link = GetContainerItemLink(bag, slot) + if link then + local _, _, id = string.find(link, "Hitem:((%d+).-)") + if id then + local _, _, linkName = string.find(link, "%[(.+)%]") + local itemTexture, itemCount = GetContainerItemInfo(bag, slot) + local itemName, itemBrol, itemQuality, itemReqlvl, _, itemSubtype, _, itemSlot, itemIcon = GetItemInfo(id) + local ammoName = itemName or linkName + local ammoType = itemSubtype + local isAmmo = false + if itemName and itemSlot == "INVTYPE_AMMO" then + isAmmo = true + elseif ammoName and knownAmmo[ammoName] then + isAmmo = true + if not ammoType then + ammoType = knownAmmo[ammoName] + end + end + + if isAmmo and ammoName then + local ammoInfo = zButtonAmmo_Cache[ammoName] + if not ammoInfo then + ammoInfo = { + ammo = ammoName, + brol = itemBrol, + quality = itemQuality, + lvl = itemReqlvl, + type = ammoType, + bag = bag, + slot = slot, + id = id, + link = link, + icon = itemIcon or itemTexture, + count = 0, + equip = nil, + } + zButtonAmmo_Cache[ammoName] = ammoInfo + end + ammoInfo.count = (ammoInfo.count or 0) + (itemCount or 0) + if equippedAmmo and equippedAmmo == ammoName then + ammoInfo.equip = 1 + end + end + end + end + end + end +end + +local function zButtonAmmo_AssignButtonFromAmmoInfo(button, ammoInfo) + if not (button and ammoInfo) then + return false + end + button.id = ammoInfo.id + button.icon = ammoInfo.icon + button.isspell = nil + button.ammoname = ammoInfo.ammo + button.ammobrol = ammoInfo.brol + button.ammoqual = ammoInfo.quality + button.ammolvl = ammoInfo.lvl + button.ammotype = ammoInfo.type + button.ammobag = ammoInfo.bag + button.ammoslot = ammoInfo.slot + button.ammoid = ammoInfo.id + button.ammolink = ammoInfo.link + button.ammocount = ammoInfo.count + return true +end + +local function zButtonAmmo_GetEquippedAmmoInfo() + local ammoLink = zButtonAmmo_GetEquippedAmmoLink() + if not ammoLink then + return nil + end + + local _, _, ammoName = string.find(ammoLink, "%[(.+)%]") + local _, _, ammoId = string.find(ammoLink, "Hitem:((%d+).-)") + local itemName, itemBrol, itemQuality, itemReqlvl, _, itemSubtype, _, _, itemIcon + if ammoId then + itemName, itemBrol, itemQuality, itemReqlvl, _, itemSubtype, _, _, itemIcon = GetItemInfo(ammoId) + end + + local ammoSlot = zButtonAmmo_GetAmmoSlotId() + local slotForTexture = ammoSlot and ammoSlot ~= 0 and ammoSlot or 0 + local ammoIcon = itemIcon or GetInventoryItemTexture("player", slotForTexture) + local ammoCount = nil + if GetInventoryItemCount then + ammoCount = GetInventoryItemCount("player", slotForTexture) + end + + return { + ammo = itemName or ammoName, + brol = itemBrol, + quality = itemQuality, + lvl = itemReqlvl, + type = itemSubtype, + id = ammoId, + link = ammoLink, + icon = ammoIcon, + count = ammoCount, + bag = nil, + slot = nil, + } +end + +function zButtonAmmo_UpdateButton(button) + if not button then + button = this + end + if not (button and button.id and button.icontexture) then + return + end + if button.isspell then + button.icontexture:SetTexture(button.icon or GetSpellTexture(button.id, "spell")) + button.icontexture:Show() + if button.ammoCountText then + button.ammoCountText:SetText("") + end + if button.ammoShortLabelText then + button.ammoShortLabelText:SetText("") + end + return + end + + local ammoInfo = button.ammoname and zButtonAmmo_Cache[button.ammoname] or nil + local countText = zButtonAmmo_EnsureCountText(button) + local shortLabelText = zButtonAmmo_EnsureShortLabelText(button) + local shortLabel = nil + if button.ammoname then + shortLabel = zButtonAmmo_ShortArrowLabels[button.ammoname] or zButtonAmmo_ShortBulletLabels[button.ammoname] + end + button.icontexture:SetTexture(button.icon) + button.icontexture:Show() + if shortLabelText then + if shortLabel and zButtonAmmo_ShouldShowShortLabel() then + local cr, cg, cb = zButtonAmmo_GetQualityColor(button.ammoqual) + shortLabelText:SetTextColor(cr, cg, cb, 1) + shortLabelText:SetText(shortLabel) + else + shortLabelText:SetText("") + end + end + if ammoInfo then + button.ammobag = ammoInfo.bag + button.ammoslot = ammoInfo.slot + button.ammocount = ammoInfo.count + if countText then + local r, g, b = zButtonAmmo_GetColorGradient((ammoInfo.count or 0) / 2000) + countText:SetTextColor(r, g, b, 1) + countText:SetText(ammoInfo.count) + end + else + button.ammocount = 0 + if countText then + countText:SetTextColor(1, 0, 0, 1) + countText:SetText("Out!") + end + end +end + +function zButtonAmmo_SetButtons(parent, ammoList) + zButtonAmmo_RebuildCache() + + for i = 1, parent.count do + local button = getglobal(parent.name .. i) + button:Hide() + button.id = nil + button.icon = nil + button.isspell = nil + button.ammoname = nil + button.ammoqual = nil + button.ammobag = nil + button.ammoslot = nil + button.ammocount = nil + if button.ammoShortLabelText then + button.ammoShortLabelText:SetText("") + end + end + + local count = 1 + local parentSet = nil + for i = 1, table.getn(ammoList) do + if count > parent.count then + break + end + local ammoName = ammoList[i] + local ammoInfo = ammoName and zButtonAmmo_Cache[ammoName] or nil + if ammoInfo then + local button = getglobal(parent.name .. count) + button.id = ammoInfo.id + button.icon = ammoInfo.icon + button.isspell = nil + button.ammoname = ammoInfo.ammo + button.ammobrol = ammoInfo.brol + button.ammoqual = ammoInfo.quality + button.ammolvl = ammoInfo.lvl + button.ammotype = ammoInfo.type + button.ammobag = ammoInfo.bag + button.ammoslot = ammoInfo.slot + button.ammoid = ammoInfo.id + button.ammolink = ammoInfo.link + button.ammocount = ammoInfo.count + zButtonAmmo_UpdateButton(button) + button:Show() + + if ammoInfo.equip then + parentSet = 1 + parent.id = ammoInfo.id + parent.icon = ammoInfo.icon + parent.isspell = nil + parent.ammoname = ammoInfo.ammo + parent.ammobrol = ammoInfo.brol + parent.ammoqual = ammoInfo.quality + parent.ammolvl = ammoInfo.lvl + parent.ammotype = ammoInfo.type + parent.ammoid = ammoInfo.id + parent.ammolink = ammoInfo.link + zButtonAmmo_UpdateButton(parent) + parent:Enable() + end + count = count + 1 + end + end + + if not parentSet then + for i = 1, table.getn(ammoList) do + local ammoName = ammoList[i] + local ammoInfo = ammoName and zButtonAmmo_Cache[ammoName] or nil + if ammoInfo then + parent.id = ammoInfo.id + parent.icon = ammoInfo.icon + parent.isspell = nil + parent.ammoname = ammoInfo.ammo + parent.ammobrol = ammoInfo.brol + parent.ammoqual = ammoInfo.quality + parent.ammolvl = ammoInfo.lvl + parent.ammotype = ammoInfo.type + parent.ammoid = ammoInfo.id + parent.ammolink = ammoInfo.link + zButtonAmmo_UpdateButton(parent) + parent:Enable() + break + end + end + end + + if count - 1 == 0 then + parent:Hide() + else + parent:Show() + end + return count - 1 +end + +local function zButtonAmmo_GetAmmoList() + local bulletList = MTH_AMMO_BULLETS or {} + local arrowList = MTH_AMMO_ARROWS or {} + local link = GetInventoryItemLink("player", 18) + if link then + local _, _, id = string.find(link, "Hitem:((%d+).-)") + local _, _, _, _, _, weaponType = GetItemInfo(id) + if weaponType == MTH_WEAPON_GUNS then + return bulletList + elseif weaponType == MTH_WEAPON_BOWS or weaponType == MTH_WEAPON_CROSSBOWS then + return arrowList + end + end + local ammoLink = GetInventoryItemLink("player", 0) + if ammoLink then + local _, _, ammoId = string.find(ammoLink, "Hitem:((%d+).-)") + if ammoId then + local _, _, _, _, itemType, itemSubtype = GetItemInfo(ammoId) + if itemType == "Projectile" then + if itemSubtype == "Bullet" then + return bulletList + elseif itemSubtype == "Arrow" then + return arrowList + end + end + end + end + return arrowList +end + +local function zButtonAmmo_EnsureSpellOrder(ammoList) + local saved = zButtonAmmo_GetSaved() + saved["count"] = nil + if saved["rows"] == nil then + saved["rows"] = 1 + end + if saved["firstbutton"] == nil then + saved["firstbutton"] = "RIGHT" + end + if saved["enabled"] == nil then + saved["enabled"] = 1 + end + if saved["tooltip"] == nil then + saved["tooltip"] = 1 + end + if saved["showammoname"] == nil then + saved["showammoname"] = 1 + end + if not saved["parent"] then + saved["parent"] = {} + end + if saved["parent"]["size"] == nil then + saved["parent"]["size"] = 36 + end + if saved["parent"]["circle"] == nil then + saved["parent"]["circle"] = 1 + end + if not saved["children"] then + saved["children"] = {} + end + if saved["children"]["size"] == nil then + saved["children"]["size"] = 36 + end + if saved["children"]["hideonclick"] == nil then + saved["children"]["hideonclick"] = 1 + end + local count = table.getn(ammoList) + local spells = saved["spells"] + local visible = saved["visible"] + if not spells or table.getn(spells) ~= count then + saved["spells"] = {} + for i = 1, count do + saved["spells"][i] = i + end + end + if not visible then + saved["visible"] = {} + visible = saved["visible"] + end + for i = 1, count do + if visible[i] == nil then + visible[i] = 1 + end + end +end + +local function zButtonAmmo_ApplyRuntimeSettings() + if not zButtonAmmo then + return + end + local saved = zButtonAmmo_GetSaved() + zButtonAmmo.tooltip = saved["tooltip"] and true or false + zButtonAmmo.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false + if saved["showammoname"] == nil then + saved["showammoname"] = 1 + end + zButtonAmmo.showammoname = saved["showammoname"] and true or false +end + +local function zButtonAmmo_GetEquippedAmmoName() + local ammoLink = zButtonAmmo_GetEquippedAmmoLink() + if not ammoLink then + return nil + end + local _, _, ammoName = string.find(ammoLink, "%[(.+)%]") + if ammoName then + return ammoName + end + return ammoLink +end + +local function zButtonAmmo_IsDebugEnabled() + local saved = zButtonAmmo_GetSaved() + return saved["debug"] +end + +local function zButtonAmmo_LogInit(msg) + if not zButtonAmmo_IsDebugEnabled() then + return + end + if DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage then + MTH_ZH_Print("[AMMO INIT] " .. tostring(msg), "debug") + end + MTH_ZH_Print("[AMMO INIT] " .. tostring(msg), "debug") +end + +local zButtonAmmo_EquipFromButton + +local function zButtonAmmo_DeferredStartupSync() + if not zButtonAmmo then + return + end + + local attempts = 0 + local syncFrame = CreateFrame("Frame", "zButtonAmmoStartupSync") + if not syncFrame then + return + end + + syncFrame:SetScript("OnUpdate", function() + attempts = attempts + 1 + if attempts < 5 then + return + end + + syncFrame:SetScript("OnUpdate", nil) + local equippedNow = zButtonAmmo_GetEquippedAmmoName() + local saved = zButtonAmmo_GetSaved() + zButtonAmmo_LogInit("deferred sync equippedNow=" .. tostring(equippedNow) .. " saved.lastEquipped=" .. tostring(saved and saved["lastEquipped"])) + + zButtonAmmo_RebuildCache() + + if equippedNow and (not saved["lastEquipped"] or saved["lastEquipped"] == "") then + zButtonAmmo_SaveEquippedAmmo(equippedNow) + zButtonAmmo_LogInit("deferred sync seeded lastEquipped=" .. tostring(equippedNow)) + return + end + + if saved["lastEquipped"] and equippedNow and saved["lastEquipped"] ~= equippedNow and zButtonAmmo_Cache[saved["lastEquipped"]] then + local restored = false + for i = 1, zButtonAmmo.count do + local child = getglobal("zButtonAmmo" .. i) + if child and child.ammoname == saved["lastEquipped"] then + restored = zButtonAmmo_EquipFromButton(child) + break + end + end + zButtonAmmo_LogInit("deferred sync restore attempt target=" .. tostring(saved["lastEquipped"]) .. " ok=" .. tostring(restored)) + end + end) +end + +local zButtonAmmo_LastEquipAttempt = { key = nil, time = 0 } + +local function zButtonAmmo_CanEquip(button) + if not (button and button.ammobag and button.ammoslot) then + return false + end + local bag = tonumber(button.ammobag) + local slot = tonumber(button.ammoslot) + if not bag or not slot then + return false + end + if bag < 0 or bag > 4 then + return false + end + local bagSlots = GetContainerNumSlots(bag) or 0 + if slot < 1 or slot > bagSlots then + return false + end + local link = GetContainerItemLink(bag, slot) + if not link then + return false + end + if button.ammoid then + local _, _, linkId = string.find(link, "Hitem:((%d+).-)") + if linkId and tostring(linkId) ~= tostring(button.ammoid) then + return false + end + end + return true +end + +zButtonAmmo_EquipFromButton = function(button) + if not zButtonAmmo_CanEquip(button) then + return false + end + local now = GetTime and GetTime() or 0 + local key = tostring(button.ammoname or "") .. ":" .. tostring(button.ammobag) .. ":" .. tostring(button.ammoslot) + if zButtonAmmo_LastEquipAttempt.key == key and now > 0 and (now - zButtonAmmo_LastEquipAttempt.time) < 0.5 then + return false + end + zButtonAmmo_LastEquipAttempt.key = key + zButtonAmmo_LastEquipAttempt.time = now + + PickupContainerItem(button.ammobag, button.ammoslot) + if CursorHasItem() then + EquipCursorItem(0) + end + if CursorHasItem() then + if ClearCursor then ClearCursor() end + return false + end + if button.ammoname then + zButtonAmmo_SaveEquippedAmmo(button.ammoname) + end + return true +end + + +function zButtonAmmo_OnLoad() + this:RegisterEvent("VARIABLES_LOADED") +end + +function zButtonAmmo_OnEvent() + if event == "VARIABLES_LOADED" then + zButtonAmmo_LogInit("VARIABLES_LOADED fired") + if not zButtonAmmo then + zButtonAmmo_LogInit("abort: zButtonAmmo frame missing") + return + end + if UnitClass("player") ~= ZHUNTER_HUNTER then + zButtonAmmo_LogInit("abort: non-hunter") + zButtonAmmo:UnregisterAllEvents() + zButtonAmmo:Hide() + return + end + zButtonAmmo.customSetButtons = zButtonAmmo_SetButtons + zButtonAmmo.customUpdateButton = zButtonAmmo_UpdateButton + zButtonAmmo_CreateButtons() + zButtonAmmoAdjustment = CreateFrame("Frame", "zButtonAmmoAdjustment") + zButtonAmmoAdjustment:RegisterEvent("UNIT_INVENTORY_CHANGED") + zButtonAmmoAdjustment:RegisterEvent("BAG_UPDATE") + zButtonAmmoAdjustment:SetScript("OnEvent", zButtonAmmoAdjustment_OnEvent) + if zButtonAmmo_IsDebugEnabled() then + MTH_ZH_Print("[AMMO] zButtonAmmoAdjustment frame created and events registered", "debug") + end + zButtonAmmo_SetupSizeAndPosition() + local saved = zButtonAmmo_GetSaved() + zButtonAmmo_LogInit("post-init saved.lastEquipped=" .. tostring(saved and saved["lastEquipped"])) + zButtonAmmo_LogInit("post-init equippedNow=" .. tostring(zButtonAmmo_GetEquippedAmmoName())) + zButtonAmmo_DeferredStartupSync() + end +end + +function zButtonAmmo_SaveEquippedAmmo(ammoname) + -- Save the currently equipped ammo to persistent storage + local saved = zButtonAmmo_GetSaved() + if saved["lastEquipped"] == ammoname then + return + end + saved["lastEquipped"] = ammoname + if zButtonAmmo_IsDebugEnabled() then + MTH_ZH_Print("[AMMO] Saved equipped ammo to persistent storage: " .. ammoname, "debug") + end +end + +function zButtonAmmo_LoadLastEquippedAmmo() + -- Load the last equipped ammo from persistent storage + local saved = zButtonAmmo_GetSaved() + if saved["lastEquipped"] then + return saved["lastEquipped"] + end + return nil +end + +function zButtonAmmo_CreateButtons() + local ammoList = zButtonAmmo_GetAmmoList() + ZHunterMod_Ammo_Buttons = ammoList + zButtonAmmo_EnsureSpellOrder(ammoList) + local saved = zButtonAmmo_GetSaved() + if not zButtonAmmo.children then + ZSpellButton_CreateChildren(zButtonAmmo, "zButtonAmmo", ZHUNTER_AMMO_MAX_COUNT) + else + zButtonAmmo.count = ZHUNTER_AMMO_MAX_COUNT + end + local info = {} + local infoIndex = 1 + for i=1, table.getn(ammoList) do + --DEFAULT_CHAT_FRAME:AddMessage(i.." "..saved["spells"][i], 0, 1, 1) + if not tonumber(saved["spells"][i]) then + info = ammoList + zButtonAmmo_EnsureSpellOrder(ammoList) + saved = zButtonAmmo_GetSaved() + break + end + local ammoIndex = saved["spells"][i] + if saved["visible"][ammoIndex] ~= false then + info[infoIndex] = ammoList[ammoIndex] + infoIndex = infoIndex + 1 + end + end + zButtonAmmo.found = ZSpellButton_SetButtons(zButtonAmmo, info) + zButtonAmmo_ApplyRuntimeSettings() + + -- Try to restore the last equipped ammo from saved variables + local lastEquipped = zButtonAmmo_LoadLastEquippedAmmo() + local equippedAmmoNameAtInit = zButtonAmmo_GetEquippedAmmoName() + zButtonAmmo_LogInit("CreateButtons saved.lastEquipped=" .. tostring(lastEquipped) .. " equippedNow=" .. tostring(equippedAmmoNameAtInit)) + if lastEquipped and zButtonAmmo_Cache[lastEquipped] then + if zButtonAmmo_IsDebugEnabled() then + MTH_ZH_Print("[AMMO] Restoring last equipped ammo from save: " .. lastEquipped, "debug") + end + -- Set the parent button directly from ammo cache data + local ammoInfo = zButtonAmmo_Cache[lastEquipped] + zButtonAmmo.ammoname = lastEquipped + zButtonAmmo.ammoid = ammoInfo["id"] + zButtonAmmo.icon = ammoInfo["icon"] + zButtonAmmo.isspell = nil + zButtonAmmo.ammobrol = ammoInfo["brol"] + zButtonAmmo.ammoqual = ammoInfo["quality"] + zButtonAmmo.ammolvl = ammoInfo["lvl"] + zButtonAmmo.ammotype = ammoInfo["type"] + zButtonAmmo.ammobag = ammoInfo["bag"] + zButtonAmmo.ammoslot = ammoInfo["slot"] + zButtonAmmo.ammolink = ammoInfo["link"] + zButtonAmmo_UpdateButton(zButtonAmmo) + zButtonAmmo:Enable() + + local equippedAmmoName = zButtonAmmo_GetEquippedAmmoName() + if equippedAmmoName ~= lastEquipped then + local restored = false + for i = 1, zButtonAmmo.count do + local child = getglobal("zButtonAmmo" .. i) + if child and child.ammoname == lastEquipped then + restored = zButtonAmmo_EquipFromButton(child) + break + end + end + if not restored then + restored = zButtonAmmo_EquipFromButton({ + ammoname = lastEquipped, + ammobag = ammoInfo["bag"], + ammoslot = ammoInfo["slot"], + ammoid = ammoInfo["id"], + }) + end + if restored then + zButtonAmmo_SaveEquippedAmmo(lastEquipped) + zButtonAmmo_LogInit("restored to saved ammo=" .. tostring(lastEquipped)) + else + zButtonAmmo_LogInit("restore failed for saved ammo=" .. tostring(lastEquipped)) + end + else + zButtonAmmo_LogInit("restore skipped: equipped already matches saved") + end + else + zButtonAmmo_LogInit("restore skipped: no saved lastEquipped in cache") + if equippedAmmoNameAtInit then + zButtonAmmo_SaveEquippedAmmo(equippedAmmoNameAtInit) + zButtonAmmo_LogInit("seeded lastEquipped from equippedNow=" .. tostring(equippedAmmoNameAtInit)) + else + local bestAvailable = nil + for i = 1, table.getn(ammoList) do + local ammoName = ammoList[i] + if ammoName and zButtonAmmo_Cache[ammoName] then + bestAvailable = ammoName + end + end + + if bestAvailable and zButtonAmmo_Cache[bestAvailable] then + local bestInfo = zButtonAmmo_Cache[bestAvailable] + local equippedBest = zButtonAmmo_EquipFromButton({ + ammoname = bestAvailable, + ammobag = bestInfo["bag"], + ammoslot = bestInfo["slot"], + ammoid = bestInfo["id"], + }) + if equippedBest then + zButtonAmmo_SaveEquippedAmmo(bestAvailable) + zButtonAmmo_LogInit("fallback equip bestAvailable=" .. tostring(bestAvailable)) + else + zButtonAmmo_LogInit("fallback equip failed bestAvailable=" .. tostring(bestAvailable)) + end + end + end + end +end + +function zButtonAmmo_SetupSizeAndPosition() + zButtonAmmo_EnsureSpellOrder(ZHunterMod_Ammo_Buttons or zButtonAmmo_GetAmmoList()) + local saved = zButtonAmmo_GetSaved() + if saved["enabled"] == false or saved["enabled"] == 0 then + if zButtonAmmo and zButtonAmmo.Hide then + zButtonAmmo:Hide() + end + return + end + local ammoList = ZHunterMod_Ammo_Buttons or zButtonAmmo_GetAmmoList() + local arrangeCount = zButtonAmmo.found or table.getn(ammoList) + if arrangeCount < 0 then + arrangeCount = 0 + end + ZSpellButton_SetSize(zButtonAmmo, saved["parent"]["size"]) + ZSpellButton_SetSize(zButtonAmmo, saved["children"]["size"], 1) + ZSpellButton_SetExpandDirection(zButtonAmmo, saved["firstbutton"]) + ZSpellButton_ArrangeChildren(zButtonAmmo, saved["rows"], + arrangeCount, saved["horizontal"], + saved["vertical"]) +end + +function zButtonAmmo_Reset() + local ammoList = zButtonAmmo_GetAmmoList() + local ammoCount = table.getn(ammoList) + local currentRoot = zButtonAmmo_GetRoot() + currentRoot["zButtonAmmo"] = {} + local saved = zButtonAmmo_GetSaved() + saved["spells"] = {} + saved["visible"] = {} + for i = 1, ammoCount do + saved["spells"][i] = i + saved["visible"][i] = 1 + end + saved["rows"] = 1 + saved["horizontal"] = nil + saved["vertical"] = nil + saved["firstbutton"] = "RIGHT" + saved["enabled"] = 1 + saved["tooltip"] = 1 + saved["parent"] = {} + saved["parent"]["size"] = 36 + saved["parent"]["hide"] = nil + saved["parent"]["circle"] = 1 + saved["children"] = {} + saved["children"]["size"] = 36 + saved["children"]["hideonclick"] = 1 + saved["showammoname"] = 1 + saved["lastEquipped"] = nil + saved["debug"] = false +end + +function zButtonAmmoAdjustment_OnEvent() + if not zButtonAmmo or not zButtonAmmo.count then + return + end + local saved = zButtonAmmo_GetSaved() + + if event == "UNIT_INVENTORY_CHANGED" or event == "BAG_UPDATE" then + local ammoList = zButtonAmmo_GetAmmoList() + if ZHunterMod_Ammo_Buttons ~= ammoList then + ZHunterMod_Ammo_Buttons = ammoList + zButtonAmmo_EnsureSpellOrder(ammoList) + local info = {} + local infoIndex = 1 + for i=1, table.getn(ammoList) do + local ammoIndex = saved["spells"][i] + if saved["visible"][ammoIndex] ~= false then + info[infoIndex] = ammoList[ammoIndex] + infoIndex = infoIndex + 1 + end + end + zButtonAmmo.found = ZSpellButton_SetButtons(zButtonAmmo, info) + zButtonAmmo_SetupSizeAndPosition() + end + zButtonAmmo_RebuildCache() + -- Update ammo counts for child buttons + for i=1, zButtonAmmo.count do + local button = getglobal("zButtonAmmo"..i) + if button and button.ammoname and not button.isspell then + zButtonAmmo_UpdateButton(button) + end + end + local equippedAmmoName = zButtonAmmo_GetEquippedAmmoName() + local equippedInfo = nil + if equippedAmmoName then + equippedInfo = zButtonAmmo_Cache[equippedAmmoName] + if not equippedInfo then + for i=1, zButtonAmmo.count do + local child = getglobal("zButtonAmmo"..i) + if child and child.ammoname == equippedAmmoName and not child.isspell then + equippedInfo = { + ammo = child.ammoname, + brol = child.ammobrol, + quality = child.ammoqual, + lvl = child.ammolvl, + type = child.ammotype, + bag = child.ammobag, + slot = child.ammoslot, + id = child.ammoid or child.id, + link = child.ammolink, + icon = child.icon, + count = child.ammocount, + } + break + end + end + end + end + + if not equippedInfo then + equippedInfo = zButtonAmmo_GetEquippedAmmoInfo() + if equippedInfo and equippedInfo.ammo and not equippedAmmoName then + equippedAmmoName = equippedInfo.ammo + end + end + + if equippedInfo and equippedInfo.ammo and equippedInfo.ammo ~= zButtonAmmo.ammoname then + zButtonAmmo_AssignButtonFromAmmoInfo(zButtonAmmo, equippedInfo) + end + + -- Also update parent if it's showing ammo (not a spell) + if zButtonAmmo and not zButtonAmmo.isspell and zButtonAmmo.ammoname then + zButtonAmmo_UpdateButton(zButtonAmmo) + end + + -- Save the currently equipped ammo for restoration on next reload + if equippedAmmoName then + zButtonAmmo_SaveEquippedAmmo(equippedAmmoName) + end + end +end + +function zButtonAmmo_KeyBinding(index) + if MTH_ZH_IsModuleEnabled and not MTH_ZH_IsModuleEnabled() then + return + end + + local button + if index then + button = getglobal("zButtonAmmo"..index) + else + button = zButtonAmmo + end + if button and button.id then + if button.isspell then + CastSpell(button.id, "spell") + elseif button.ammobag and button.ammoslot then + zButtonAmmo_EquipFromButton(button) + end + if zButtonAmmo.hideonclick then + if type(ZSpellButton_SetChildrenExpanded) == "function" then + ZSpellButton_SetChildrenExpanded(zButtonAmmo, false) + elseif zButtonAmmo.children then + zButtonAmmo.children:Hide() + end + end + end +end + +SLASH_zButtonAmmo1 = "/zammobutton" +SLASH_zButtonAmmo2 = "/zammo" +SlashCmdList["zButtonAmmo"] = function(msg) + if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Ammo button is disabled while module 'zhunter' is disabled.") then + return + end + if msg == "reset" then + zButtonAmmo_Reset() + zButtonAmmo:ClearAllPoints() + zButtonAmmo:SetPoint("CENTER", UIParent, "CENTER", 0, 0) + elseif msg == "options" then + MTH_OpenOptions("Ammo") + elseif msg == "debug" then + -- Manually trigger ammo rescan with debug output + local currentAmmoList = ZHunterMod_Ammo_Buttons or zButtonAmmo_GetAmmoList() + zButtonAmmo_EnsureSpellOrder(currentAmmoList) + local saved = zButtonAmmo_GetSaved() + local info = {} + local infoIndex = 1 + for i=1, table.getn(currentAmmoList) do + local ammoIndex = saved["spells"][i] + if saved["visible"][ammoIndex] ~= false then + info[infoIndex] = currentAmmoList[ammoIndex] + infoIndex = infoIndex + 1 + end + end + zButtonAmmo.found = ZSpellButton_SetButtons(zButtonAmmo, info) + zButtonAmmo_UpdateButton(zButtonAmmo) + MTH_ZH_Print("Ammo button rescanned") + else + MTH_ZH_Print("Possible Commands: \"options\", \"reset\", \"debug\"") + end +end + diff --git a/modules/zhunter/zButtonAmmo.xml b/modules/zhunter/zButtonAmmo.xml new file mode 100644 index 0000000..3ded181 --- /dev/null +++ b/modules/zhunter/zButtonAmmo.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/zhunter/zButtonAspect.lua b/modules/zhunter/zButtonAspect.lua new file mode 100644 index 0000000..1e6cb03 --- /dev/null +++ b/modules/zhunter/zButtonAspect.lua @@ -0,0 +1,281 @@ +local function zButtonAspect_GetRoot() + return MTH_ZH_GetSavedRoot() +end + +local root = zButtonAspect_GetRoot() +if not root["zButtonAspect"] then + root["zButtonAspect"] = {} + root["zButtonAspect"]["spells"] = {1, 2, 3, 4, 5, 6, 7} + root["zButtonAspect"]["rows"] = 1 + root["zButtonAspect"]["horizontal"] = nil + root["zButtonAspect"]["vertical"] = nil + root["zButtonAspect"]["firstbutton"] = "RIGHT" + root["zButtonAspect"]["enabled"] = 1 + root["zButtonAspect"]["tooltip"] = 1 + root["zButtonAspect"]["parent"] = {} + root["zButtonAspect"]["parent"]["size"] = 36 + root["zButtonAspect"]["parent"]["hide"] = nil + root["zButtonAspect"]["parent"]["circle"] = 1 + root["zButtonAspect"]["children"] = {} + root["zButtonAspect"]["children"]["size"] = 36 + root["zButtonAspect"]["children"]["hideonclick"] = 1 +end + +ZHunterMod_Aspect_Spells = { + ZHUNTER_ASPECT_HAWK, + ZHUNTER_ASPECT_MONKEY, + ZHUNTER_ASPECT_WOLF, + ZHUNTER_ASPECT_CHEETAH, + ZHUNTER_ASPECT_PACK, + ZHUNTER_ASPECT_WILD, + ZHUNTER_ASPECT_BEAST +} + +local ZHUNTER_ASPECT_MAX = table.getn(ZHunterMod_Aspect_Spells) + +local function zButtonAspect_GetSaved() + local currentRoot = zButtonAspect_GetRoot() + if not currentRoot["zButtonAspect"] then + currentRoot["zButtonAspect"] = {} + end + return currentRoot["zButtonAspect"] +end + +local function zButtonAspect_EnsureConfig() + local saved = zButtonAspect_GetSaved() + saved["count"] = nil + if saved["rows"] == nil then + saved["rows"] = 1 + end + if saved["firstbutton"] == nil then + saved["firstbutton"] = "RIGHT" + end + if saved["enabled"] == nil then + saved["enabled"] = 1 + end + if saved["tooltip"] == nil then + saved["tooltip"] = 1 + end + if not saved["parent"] then + saved["parent"] = {} + end + if saved["parent"]["size"] == nil then + saved["parent"]["size"] = 36 + end + if saved["parent"]["circle"] == nil then + saved["parent"]["circle"] = 1 + end + if not saved["children"] then + saved["children"] = {} + end + if saved["children"]["size"] == nil then + saved["children"]["size"] = 36 + end + if saved["children"]["hideonclick"] == nil then + saved["children"]["hideonclick"] = 1 + end + if not saved["spells"] then + saved["spells"] = {} + end + if not saved["visible"] then + saved["visible"] = {} + end + for i = 1, ZHUNTER_ASPECT_MAX do + if not tonumber(saved["spells"][i]) then + saved["spells"][i] = i + end + if saved["visible"][i] == nil then + saved["visible"][i] = 1 + end + end +end + +local function zButtonAspect_ApplyRuntimeSettings() + if not zButtonAspect then + return + end + local saved = zButtonAspect_GetSaved() + zButtonAspect.tooltip = saved["tooltip"] and true or false + zButtonAspect.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false +end + +function zButtonAspect_OnLoad() + this:RegisterEvent("VARIABLES_LOADED") +end + +function zButtonAspect_OnEvent() + if event == "VARIABLES_LOADED" then + if not zButtonAspect then + return + end + if UnitClass("player") ~= ZHUNTER_HUNTER then + zButtonAspect:UnregisterAllEvents() + zButtonAspect:Hide() + return + end + zButtonAspect_CreateButtons() + zButtonAspectAdjustment = CreateFrame("Frame", "zButtonAspectAdjustment") + zButtonAspectAdjustment:RegisterEvent("PLAYER_AURAS_CHANGED") + zButtonAspectAdjustment:RegisterEvent("PLAYER_ENTERING_WORLD") + zButtonAspectAdjustment:SetScript("OnEvent", zButtonAspectAdjustment_OnEvent) + zButtonAspect_Tooltip = CreateFrame("GameTooltip", "zButtonAspect_Tooltip", nil, "GameTooltipTemplate") + zButtonAspect_SetupSizeAndPosition() + end +end + +function zButtonAspect_CreateButtons() + zButtonAspect_EnsureConfig() + local saved = zButtonAspect_GetSaved() + + ZSpellButton_CreateChildren(zButtonAspect, "zButtonAspect", ZHUNTER_ASPECT_MAX) + local info = {} + local infoIndex = 1 + for i=1, table.getn(ZHunterMod_Aspect_Spells) do + if not tonumber(saved["spells"][i]) then + info = ZHunterMod_Aspect_Spells + saved["spells"] = {1, 2, 3, 4, 5, 6, 7} + break + end + local spellIndex = saved["spells"][i] + if saved["visible"][spellIndex] ~= false then + info[infoIndex] = ZHunterMod_Aspect_Spells[spellIndex] + infoIndex = infoIndex + 1 + end + end + zButtonAspect.found = ZSpellButton_SetButtons(zButtonAspect, info) + zButtonAspect_ApplyRuntimeSettings() +end + +function zButtonAspect_SetupSizeAndPosition() + zButtonAspect_EnsureConfig() + local saved = zButtonAspect_GetSaved() + if saved["enabled"] == false or saved["enabled"] == 0 then + if zButtonAspect and zButtonAspect.Hide then + zButtonAspect:Hide() + end + return + end + local displayCount = zButtonAspect.found or ZHUNTER_ASPECT_MAX + if displayCount < 0 then + displayCount = 0 + end + ZSpellButton_SetSize(zButtonAspect, saved["parent"]["size"]) + ZSpellButton_SetSize(zButtonAspect, saved["children"]["size"], 1) + ZSpellButton_SetExpandDirection(zButtonAspect, saved["firstbutton"]) + ZSpellButton_ArrangeChildren(zButtonAspect, saved["rows"], + displayCount, saved["horizontal"], + saved["vertical"]) +end + +function zButtonAspect_Reset() + local currentRoot = zButtonAspect_GetRoot() + currentRoot["zButtonAspect"] = {} + local saved = zButtonAspect_GetSaved() + saved["spells"] = {1, 2, 3, 4, 5, 6, 7} + saved["rows"] = 1 + saved["horizontal"] = nil + saved["vertical"] = nil + saved["firstbutton"] = "RIGHT" + saved["enabled"] = 1 + saved["tooltip"] = 1 + saved["parent"] = {} + saved["parent"]["size"] = 36 + saved["parent"]["hide"] = nil + saved["parent"]["circle"] = 1 + saved["children"] = {} + saved["children"]["size"] = 36 + saved["children"]["hideonclick"] = 1 + saved["visible"] = {1, 1, 1, 1, 1, 1, 1} + zButtonAspect_EnsureConfig() +end + +function zButtonAspectAdjustment_OnEvent() + if not zButtonAspect or not zButtonAspect.count then + return + end + if event == "PLAYER_AURAS_CHANGED" or event == "PLAYER_ENTERING_WORLD" then + if not zButtonAspect1.id then + return + end + local buttontextures = {} + local button + for i=1, zButtonAspect.count do + button = getglobal(zButtonAspect.name..i) + button.icon = nil + if button.id then + buttontextures[GetSpellTexture(button.id, "spell")] = button + end + end + local i = 1 + local texture = GetSpellTexture(zButtonAspect1.id, "spell") + local buff = UnitBuff("player", i) + local spellname, buffname + zButtonAspect.id = zButtonAspect1.id + while buff do + if texture == buff and zButtonAspect2.id then + zButtonAspect.id = zButtonAspect2.id + end + if buttontextures[buff] then + zButtonAspect_Tooltip:SetOwner(this, "ANCHOR_NONE") + zButtonAspect_Tooltip:SetUnitBuff("player", i) + buffname = zButtonAspect_TooltipTextLeft1:GetText() + spellname = GetSpellName(buttontextures[buff].id, "spell") + if buffname == spellname then + buttontextures[buff].icon = "Interface\\Icons\\Spell_Nature_WispSplode" + end + end + i = i + 1 + buff = UnitBuff("player", i) + end + ZSpellButton_UpdateButton(zButtonAspect) + ZSpellButton_UpdateCooldown(zButtonAspect) + for i=1, zButtonAspect.count do + button = getglobal(zButtonAspect.name..i) + if button.id then + ZSpellButton_UpdateButton(button) + end + end + if GameTooltip:IsOwned(zButtonAspect) then + ZSpellButtonParent_OnEnter(zButtonAspect) + end + end +end + +function zButtonAspect_KeyBinding(index) + if MTH_ZH_IsModuleEnabled and not MTH_ZH_IsModuleEnabled() then + return + end + + local button + if index then + button = getglobal("zButtonAspect"..index) + else + button = zButtonAspect + end + if button.id then + CastSpell(button.id, "spell") + if zButtonAspect.hideonclick then + if type(ZSpellButton_SetChildrenExpanded) == "function" then + ZSpellButton_SetChildrenExpanded(zButtonAspect, false) + elseif zButtonAspect.children then + zButtonAspect.children:Hide() + end + end + end +end + +SLASH_zButtonAspect1 = "/ZAspect" +SlashCmdList["zButtonAspect"] = function(msg) + if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Aspect button is disabled while module 'zhunter' is disabled.") then + return + end + if msg == "reset" then + zButtonAspect_Reset() + zButtonAspect:ClearAllPoints() + zButtonAspect:SetPoint("CENTER", UIParent, "CENTER", -60, 0) + elseif msg == "options" then + MTH_OpenOptions("Aspect") + else + MTH_ZH_Print("Possible Commands: \"options\", \"reset\"") + end +end \ No newline at end of file diff --git a/modules/zhunter/zButtonAspect.xml b/modules/zhunter/zButtonAspect.xml new file mode 100644 index 0000000..e4ec8a2 --- /dev/null +++ b/modules/zhunter/zButtonAspect.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/zhunter/zButtonCompanions.lua b/modules/zhunter/zButtonCompanions.lua new file mode 100644 index 0000000..f778032 --- /dev/null +++ b/modules/zhunter/zButtonCompanions.lua @@ -0,0 +1,323 @@ +local function zButtonCompanions_GetRoot() + return MTH_ZH_GetSavedRoot() +end + +local root = zButtonCompanions_GetRoot() +if not root["zButtonCompanions"] then + root["zButtonCompanions"] = {} + root["zButtonCompanions"]["spells"] = {} + root["zButtonCompanions"]["rows"] = 1 + root["zButtonCompanions"]["horizontal"] = nil + root["zButtonCompanions"]["vertical"] = nil + root["zButtonCompanions"]["firstbutton"] = "RIGHT" + root["zButtonCompanions"]["enabled"] = 0 + root["zButtonCompanions"]["tooltip"] = 1 + root["zButtonCompanions"]["parent"] = {} + root["zButtonCompanions"]["parent"]["size"] = 36 + root["zButtonCompanions"]["parent"]["hide"] = nil + root["zButtonCompanions"]["parent"]["circle"] = 1 + root["zButtonCompanions"]["children"] = {} + root["zButtonCompanions"]["children"]["size"] = 36 + root["zButtonCompanions"]["children"]["hideonclick"] = 1 +end + +local ZHUNTER_COMPANIONS_BUTTON_MAX = 80 + +local function zButtonCompanions_GetSaved() + local currentRoot = zButtonCompanions_GetRoot() + if not currentRoot["zButtonCompanions"] then + currentRoot["zButtonCompanions"] = {} + end + return currentRoot["zButtonCompanions"] +end + +local function zButtonCompanions_EnsureConfig() + local saved = zButtonCompanions_GetSaved() + saved["count"] = nil + if saved["enabled"] == nil then + saved["enabled"] = 0 + end + if saved["tooltip"] == nil then + saved["tooltip"] = 1 + end + if not saved["children"] then + saved["children"] = {} + end + if saved["children"]["hideonclick"] == nil then + saved["children"]["hideonclick"] = 1 + end + if not saved["spells"] then + saved["spells"] = {} + end + if not saved["parent"] then + saved["parent"] = {} + end + if not saved["children"] then + saved["children"] = {} + end + if not saved["parent"]["size"] then + saved["parent"]["size"] = 36 + end + if saved["parent"]["circle"] == nil then + saved["parent"]["circle"] = 1 + end + if not saved["children"]["size"] then + saved["children"]["size"] = 36 + end + if not saved["rows"] or saved["rows"] < 1 then + saved["rows"] = 1 + end + if not saved["firstbutton"] then + saved["firstbutton"] = "RIGHT" + end +end + +local function zButtonCompanions_ApplyRuntimeSettings() + if not zButtonCompanions then + return + end + local saved = zButtonCompanions_GetSaved() + zButtonCompanions.tooltip = saved["tooltip"] and true or false + zButtonCompanions.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false +end + +local function zButtonCompanions_NormalizeTabName(value) + local text = string.lower(tostring(value or "")) + text = string.gsub(text, "%s+", "") + text = string.gsub(text, "[^%a]", "") + while string.sub(text, 1, 1) == "z" and string.len(text) > 1 do + text = string.sub(text, 2) + end + return text +end + +local function zButtonCompanions_FindTabBounds() + if type(GetNumSpellTabs) ~= "function" or type(GetSpellTabInfo) ~= "function" then + return nil, nil + end + local tabCount = GetNumSpellTabs() or 0 + for tabIndex = 1, tabCount do + local tabName, _, tabOffset, tabEntries = GetSpellTabInfo(tabIndex) + local normalized = zButtonCompanions_NormalizeTabName(tabName) + if string.find(normalized, "companion", 1, true) or string.find(normalized, "minipet", 1, true) then + return tonumber(tabOffset) or 0, tonumber(tabEntries) or 0 + end + end + return nil, nil +end + +local function zButtonCompanions_CollectTabSpellIds() + local tabOffset, tabEntries = zButtonCompanions_FindTabBounds() + if not tabOffset or not tabEntries or tabEntries <= 0 then + return {} + end + + local spellIds = {} + local bookType = BOOKTYPE_SPELL or "spell" + local maxIndex = tabOffset + tabEntries + for spellIndex = tabOffset + 1, maxIndex do + local spellName = GetSpellName(spellIndex, bookType) + if spellName and spellName ~= "" then + table.insert(spellIds, spellIndex) + end + end + return spellIds +end + +local function zButtonCompanions_ApplySpellIds(spellIds) + if not zButtonCompanions then + return 0 + end + + local parent = zButtonCompanions + local childCount = parent.count or ZHUNTER_COMPANIONS_BUTTON_MAX + + for i = 1, childCount do + local child = getglobal("zButtonCompanions" .. i) + if child then + child:Hide() + child.id = nil + child.icon = nil + child.isspell = nil + end + end + + local found = 0 + for i = 1, table.getn(spellIds) do + if found >= childCount then + break + end + local spellId = spellIds[i] + if spellId then + found = found + 1 + local child = getglobal("zButtonCompanions" .. found) + if child then + child.id = spellId + child.isspell = 1 + ZSpellButton_UpdateButton(child) + child:Show() + end + end + end + + if found > 0 then + parent.id = spellIds[1] + parent.isspell = 1 + ZSpellButton_UpdateButton(parent) + parent:Enable() + parent:Show() + else + parent.id = nil + parent:Hide() + end + + return found +end + +local function zButtonCompanions_RefreshSpells() + if not zButtonCompanions then + return 0 + end + local saved = zButtonCompanions_GetSaved() + local spellIds = zButtonCompanions_CollectTabSpellIds() + local spellNames = {} + local bookType = BOOKTYPE_SPELL or "spell" + for i = 1, table.getn(spellIds) do + local n = GetSpellName(spellIds[i], bookType) + if n and n ~= "" then + table.insert(spellNames, n) + end + end + saved["spellIds"] = spellIds + saved["spells"] = spellNames + zButtonCompanions.spells = spellNames + zButtonCompanions.found = zButtonCompanions_ApplySpellIds(spellIds) + zButtonCompanions_ApplyRuntimeSettings() + return zButtonCompanions.found or 0 +end + +function zButtonCompanions_OnLoad() + this:RegisterEvent("VARIABLES_LOADED") +end + +function zButtonCompanions_OnEvent() + if event == "VARIABLES_LOADED" then + if not zButtonCompanions then + return + end + if UnitClass("player") ~= ZHUNTER_HUNTER then + zButtonCompanions:UnregisterAllEvents() + zButtonCompanions:Hide() + return + end + zButtonCompanions_CreateButtons() + zButtonCompanionsAdjustment = CreateFrame("Frame", "zButtonCompanionsAdjustment") + zButtonCompanionsAdjustment:RegisterEvent("SPELLS_CHANGED") + zButtonCompanionsAdjustment:RegisterEvent("PLAYER_ENTERING_WORLD") + zButtonCompanionsAdjustment:SetScript("OnEvent", zButtonCompanionsAdjustment_OnEvent) + zButtonCompanions_SetupSizeAndPosition() + end +end + +function zButtonCompanions_CreateButtons() + zButtonCompanions_EnsureConfig() + + if not zButtonCompanions._mthChildrenCreated then + ZSpellButton_CreateChildren(zButtonCompanions, "zButtonCompanions", ZHUNTER_COMPANIONS_BUTTON_MAX) + zButtonCompanions._mthChildrenCreated = 1 + end + + zButtonCompanions_RefreshSpells() +end + +function zButtonCompanions_SetupSizeAndPosition() + local saved = zButtonCompanions_GetSaved() + if saved["enabled"] == false or saved["enabled"] == 0 then + if zButtonCompanions and zButtonCompanions.Hide then + zButtonCompanions:Hide() + end + return + end + local displayCount = zButtonCompanions.found or 0 + if displayCount < 0 then + displayCount = 0 + end + ZSpellButton_SetSize(zButtonCompanions, saved["parent"]["size"]) + ZSpellButton_SetSize(zButtonCompanions, saved["children"]["size"], 1) + ZSpellButton_SetExpandDirection(zButtonCompanions, saved["firstbutton"]) + ZSpellButton_ArrangeChildren(zButtonCompanions, saved["rows"], + displayCount, saved["horizontal"], + saved["vertical"]) +end + +function zButtonCompanions_Reset() + local currentRoot = zButtonCompanions_GetRoot() + currentRoot["zButtonCompanions"] = {} + local saved = zButtonCompanions_GetSaved() + saved["spells"] = {} + saved["rows"] = 1 + saved["horizontal"] = nil + saved["vertical"] = nil + saved["firstbutton"] = "RIGHT" + saved["enabled"] = 1 + saved["tooltip"] = 1 + saved["parent"] = {} + saved["parent"]["size"] = 36 + saved["parent"]["hide"] = nil + saved["parent"]["circle"] = 1 + saved["children"] = {} + saved["children"]["size"] = 36 + saved["children"]["hideonclick"] = 1 + zButtonCompanions_EnsureConfig() +end + +function zButtonCompanionsAdjustment_OnEvent() + if not zButtonCompanions then + return + end + zButtonCompanions_RefreshSpells() + zButtonCompanions_SetupSizeAndPosition() + if GameTooltip:IsOwned(zButtonCompanions) then + ZSpellButtonParent_OnEnter(zButtonCompanions) + end +end + +function zButtonCompanions_KeyBinding(index) + if MTH_ZH_IsModuleEnabled and not MTH_ZH_IsModuleEnabled() then + return + end + + local button + if index then + button = getglobal("zButtonCompanions"..index) + else + button = zButtonCompanions + end + if button and button.id then + CastSpell(button.id, "spell") + if zButtonCompanions.hideonclick then + if type(ZSpellButton_SetChildrenExpanded) == "function" then + ZSpellButton_SetChildrenExpanded(zButtonCompanions, false) + elseif zButtonCompanions.children then + zButtonCompanions.children:Hide() + end + end + end +end + +SLASH_zButtonCompanions1 = "/ZCompanions" +SLASH_zButtonCompanions2 = "/ZMinipets" +SlashCmdList["zButtonCompanions"] = function(msg) + if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Companions button is disabled while module 'zhunter' is disabled.") then + return + end + if msg == "reset" then + zButtonCompanions_Reset() + zButtonCompanions:ClearAllPoints() + zButtonCompanions:SetPoint("TOP", UIParent, "TOP", 0, -464) + zButtonCompanions_RefreshSpells() + zButtonCompanions_SetupSizeAndPosition() + else + MTH_ZH_Print("Possible Commands: \"reset\"") + end +end diff --git a/modules/zhunter/zButtonCompanions.xml b/modules/zhunter/zButtonCompanions.xml new file mode 100644 index 0000000..56350ef --- /dev/null +++ b/modules/zhunter/zButtonCompanions.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/modules/zhunter/zButtonMounts.lua b/modules/zhunter/zButtonMounts.lua new file mode 100644 index 0000000..85e42fd --- /dev/null +++ b/modules/zhunter/zButtonMounts.lua @@ -0,0 +1,322 @@ +local function zButtonMounts_GetRoot() + return MTH_ZH_GetSavedRoot() +end + +local root = zButtonMounts_GetRoot() +if not root["zButtonMounts"] then + root["zButtonMounts"] = {} + root["zButtonMounts"]["spells"] = {} + root["zButtonMounts"]["rows"] = 1 + root["zButtonMounts"]["horizontal"] = nil + root["zButtonMounts"]["vertical"] = nil + root["zButtonMounts"]["firstbutton"] = "RIGHT" + root["zButtonMounts"]["enabled"] = 0 + root["zButtonMounts"]["tooltip"] = 1 + root["zButtonMounts"]["parent"] = {} + root["zButtonMounts"]["parent"]["size"] = 36 + root["zButtonMounts"]["parent"]["hide"] = nil + root["zButtonMounts"]["parent"]["circle"] = 1 + root["zButtonMounts"]["children"] = {} + root["zButtonMounts"]["children"]["size"] = 36 + root["zButtonMounts"]["children"]["hideonclick"] = 1 +end + +local ZHUNTER_MOUNTS_BUTTON_MAX = 80 + +local function zButtonMounts_GetSaved() + local currentRoot = zButtonMounts_GetRoot() + if not currentRoot["zButtonMounts"] then + currentRoot["zButtonMounts"] = {} + end + return currentRoot["zButtonMounts"] +end + +local function zButtonMounts_EnsureConfig() + local saved = zButtonMounts_GetSaved() + saved["count"] = nil + if saved["enabled"] == nil then + saved["enabled"] = 0 + end + if saved["tooltip"] == nil then + saved["tooltip"] = 1 + end + if not saved["children"] then + saved["children"] = {} + end + if saved["children"]["hideonclick"] == nil then + saved["children"]["hideonclick"] = 1 + end + if not saved["spells"] then + saved["spells"] = {} + end + if not saved["parent"] then + saved["parent"] = {} + end + if not saved["children"] then + saved["children"] = {} + end + if not saved["parent"]["size"] then + saved["parent"]["size"] = 36 + end + if saved["parent"]["circle"] == nil then + saved["parent"]["circle"] = 1 + end + if not saved["children"]["size"] then + saved["children"]["size"] = 36 + end + if not saved["rows"] or saved["rows"] < 1 then + saved["rows"] = 1 + end + if not saved["firstbutton"] then + saved["firstbutton"] = "RIGHT" + end +end + +local function zButtonMounts_ApplyRuntimeSettings() + if not zButtonMounts then + return + end + local saved = zButtonMounts_GetSaved() + zButtonMounts.tooltip = saved["tooltip"] and true or false + zButtonMounts.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false +end + +local function zButtonMounts_NormalizeTabName(value) + local text = string.lower(tostring(value or "")) + text = string.gsub(text, "%s+", "") + text = string.gsub(text, "[^%a]", "") + while string.sub(text, 1, 1) == "z" and string.len(text) > 1 do + text = string.sub(text, 2) + end + return text +end + +local function zButtonMounts_FindTabBounds() + if type(GetNumSpellTabs) ~= "function" or type(GetSpellTabInfo) ~= "function" then + return nil, nil + end + local tabCount = GetNumSpellTabs() or 0 + for tabIndex = 1, tabCount do + local tabName, _, tabOffset, tabEntries = GetSpellTabInfo(tabIndex) + local normalized = zButtonMounts_NormalizeTabName(tabName) + if string.find(normalized, "mount", 1, true) then + return tonumber(tabOffset) or 0, tonumber(tabEntries) or 0 + end + end + return nil, nil +end + +local function zButtonMounts_CollectTabSpellIds() + local tabOffset, tabEntries = zButtonMounts_FindTabBounds() + if not tabOffset or not tabEntries or tabEntries <= 0 then + return {} + end + + local spellIds = {} + local bookType = BOOKTYPE_SPELL or "spell" + local maxIndex = tabOffset + tabEntries + for spellIndex = tabOffset + 1, maxIndex do + local spellName = GetSpellName(spellIndex, bookType) + if spellName and spellName ~= "" then + table.insert(spellIds, spellIndex) + end + end + return spellIds +end + +local function zButtonMounts_ApplySpellIds(spellIds) + if not zButtonMounts then + return 0 + end + + local parent = zButtonMounts + local childCount = parent.count or ZHUNTER_MOUNTS_BUTTON_MAX + + for i = 1, childCount do + local child = getglobal("zButtonMounts" .. i) + if child then + child:Hide() + child.id = nil + child.icon = nil + child.isspell = nil + end + end + + local found = 0 + for i = 1, table.getn(spellIds) do + if found >= childCount then + break + end + local spellId = spellIds[i] + if spellId then + found = found + 1 + local child = getglobal("zButtonMounts" .. found) + if child then + child.id = spellId + child.isspell = 1 + ZSpellButton_UpdateButton(child) + child:Show() + end + end + end + + if found > 0 then + parent.id = spellIds[1] + parent.isspell = 1 + ZSpellButton_UpdateButton(parent) + parent:Enable() + parent:Show() + else + parent.id = nil + parent:Hide() + end + + return found +end + +local function zButtonMounts_RefreshSpells() + if not zButtonMounts then + return 0 + end + local saved = zButtonMounts_GetSaved() + local spellIds = zButtonMounts_CollectTabSpellIds() + local spellNames = {} + local bookType = BOOKTYPE_SPELL or "spell" + for i = 1, table.getn(spellIds) do + local n = GetSpellName(spellIds[i], bookType) + if n and n ~= "" then + table.insert(spellNames, n) + end + end + saved["spellIds"] = spellIds + saved["spells"] = spellNames + zButtonMounts.spells = spellNames + zButtonMounts.found = zButtonMounts_ApplySpellIds(spellIds) + zButtonMounts_ApplyRuntimeSettings() + return zButtonMounts.found or 0 +end + +function zButtonMounts_OnLoad() + this:RegisterEvent("VARIABLES_LOADED") +end + +function zButtonMounts_OnEvent() + if event == "VARIABLES_LOADED" then + if not zButtonMounts then + return + end + if UnitClass("player") ~= ZHUNTER_HUNTER then + zButtonMounts:UnregisterAllEvents() + zButtonMounts:Hide() + return + end + zButtonMounts_CreateButtons() + zButtonMountsAdjustment = CreateFrame("Frame", "zButtonMountsAdjustment") + zButtonMountsAdjustment:RegisterEvent("SPELLS_CHANGED") + zButtonMountsAdjustment:RegisterEvent("PLAYER_ENTERING_WORLD") + zButtonMountsAdjustment:SetScript("OnEvent", zButtonMountsAdjustment_OnEvent) + zButtonMounts_SetupSizeAndPosition() + end +end + +function zButtonMounts_CreateButtons() + zButtonMounts_EnsureConfig() + + if not zButtonMounts._mthChildrenCreated then + ZSpellButton_CreateChildren(zButtonMounts, "zButtonMounts", ZHUNTER_MOUNTS_BUTTON_MAX) + zButtonMounts._mthChildrenCreated = 1 + end + + zButtonMounts_RefreshSpells() +end + +function zButtonMounts_SetupSizeAndPosition() + local saved = zButtonMounts_GetSaved() + if saved["enabled"] == false or saved["enabled"] == 0 then + if zButtonMounts and zButtonMounts.Hide then + zButtonMounts:Hide() + end + return + end + local displayCount = zButtonMounts.found or 0 + if displayCount < 0 then + displayCount = 0 + end + ZSpellButton_SetSize(zButtonMounts, saved["parent"]["size"]) + ZSpellButton_SetSize(zButtonMounts, saved["children"]["size"], 1) + ZSpellButton_SetExpandDirection(zButtonMounts, saved["firstbutton"]) + ZSpellButton_ArrangeChildren(zButtonMounts, saved["rows"], + displayCount, saved["horizontal"], + saved["vertical"]) +end + +function zButtonMounts_Reset() + local currentRoot = zButtonMounts_GetRoot() + currentRoot["zButtonMounts"] = {} + local saved = zButtonMounts_GetSaved() + saved["spells"] = {} + saved["rows"] = 1 + saved["horizontal"] = nil + saved["vertical"] = nil + saved["firstbutton"] = "RIGHT" + saved["enabled"] = 1 + saved["tooltip"] = 1 + saved["parent"] = {} + saved["parent"]["size"] = 36 + saved["parent"]["hide"] = nil + saved["parent"]["circle"] = 1 + saved["children"] = {} + saved["children"]["size"] = 36 + saved["children"]["hideonclick"] = 1 + zButtonMounts_EnsureConfig() +end + +function zButtonMountsAdjustment_OnEvent() + if not zButtonMounts then + return + end + zButtonMounts_RefreshSpells() + zButtonMounts_SetupSizeAndPosition() + if GameTooltip:IsOwned(zButtonMounts) then + ZSpellButtonParent_OnEnter(zButtonMounts) + end +end + +function zButtonMounts_KeyBinding(index) + if MTH_ZH_IsModuleEnabled and not MTH_ZH_IsModuleEnabled() then + return + end + + local button + if index then + button = getglobal("zButtonMounts"..index) + else + button = zButtonMounts + end + if button and button.id then + CastSpell(button.id, "spell") + if zButtonMounts.hideonclick then + if type(ZSpellButton_SetChildrenExpanded) == "function" then + ZSpellButton_SetChildrenExpanded(zButtonMounts, false) + elseif zButtonMounts.children then + zButtonMounts.children:Hide() + end + end + end +end + +SLASH_zButtonMounts1 = "/ZMounts" +SlashCmdList["zButtonMounts"] = function(msg) + if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Mounts button is disabled while module 'zhunter' is disabled.") then + return + end + if msg == "reset" then + zButtonMounts_Reset() + zButtonMounts:ClearAllPoints() + zButtonMounts:SetPoint("TOP", UIParent, "TOP", 0, -410) + zButtonMounts_RefreshSpells() + zButtonMounts_SetupSizeAndPosition() + else + MTH_ZH_Print("Possible Commands: \"reset\"") + end +end diff --git a/modules/zhunter/zButtonMounts.xml b/modules/zhunter/zButtonMounts.xml new file mode 100644 index 0000000..282b975 --- /dev/null +++ b/modules/zhunter/zButtonMounts.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/modules/zhunter/zButtonPet.lua b/modules/zhunter/zButtonPet.lua new file mode 100644 index 0000000..45dfa22 --- /dev/null +++ b/modules/zhunter/zButtonPet.lua @@ -0,0 +1,454 @@ +local function zButtonPet_GetRoot() + return MTH_ZH_GetSavedRoot() +end + +local root = zButtonPet_GetRoot() + +-- Initialize with defaults only if not already configured +if not root["zButtonPet"] then + root["zButtonPet"] = {} + root["zButtonPet"]["pet"] = {} + root["zButtonPet"]["pet"]["happiness"] = nil + root["zButtonPet"]["pet"]["status"] = nil + root["zButtonPet"]["pet"]["dead"] = nil + root["zButtonPet"]["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + root["zButtonPet"]["rows"] = 1 + root["zButtonPet"]["horizontal"] = nil + root["zButtonPet"]["vertical"] = nil + root["zButtonPet"]["firstbutton"] = "RIGHT" + root["zButtonPet"]["enabled"] = 1 + root["zButtonPet"]["tooltip"] = 1 + root["zButtonPet"]["parent"] = {} + root["zButtonPet"]["parent"]["size"] = 36 + root["zButtonPet"]["parent"]["hide"] = nil + root["zButtonPet"]["parent"]["circle"] = 1 + root["zButtonPet"]["children"] = {} + root["zButtonPet"]["children"]["size"] = 36 + root["zButtonPet"]["children"]["hideonclick"] = 1 +end +root["zButtonPet"]["food"] = {} + +ZHunterMod_Pet_Spells = { + ZHUNTER_PET_EYES, + ZHUNTER_PET_DISMISS, + ZHUNTER_PET_MEND, + ZHUNTER_PET_FEED, + ZHUNTER_PET_CALL, + ZHUNTER_PET_REVIVE, + ZHUNTER_PET_LORE, + ZHUNTER_PET_TRAINING, + ZHUNTER_PET_TAMING, + ZHUNTER_PET_POSITION +} + +local ZHUNTER_PET_MAX = table.getn(ZHunterMod_Pet_Spells) + +local function zButtonPet_GetSaved() + local currentRoot = zButtonPet_GetRoot() + if not currentRoot["zButtonPet"] then + currentRoot["zButtonPet"] = {} + end + return currentRoot["zButtonPet"] +end + +local function zButtonPet_EnsureConfig() + local saved = zButtonPet_GetSaved() + saved["count"] = nil + if not saved["pet"] then + saved["pet"] = {} + end + if saved["pet"]["happiness"] == nil then + saved["pet"]["happiness"] = nil + end + if saved["pet"]["status"] == nil then + saved["pet"]["status"] = nil + end + if saved["pet"]["dead"] == nil then + saved["pet"]["dead"] = nil + end + if not saved["parent"] then + saved["parent"] = {} + end + if saved["parent"]["size"] == nil then + saved["parent"]["size"] = 36 + end + if saved["parent"]["hide"] == nil then + saved["parent"]["hide"] = nil + end + if saved["parent"]["circle"] == nil then + saved["parent"]["circle"] = 1 + end + if saved["enabled"] == nil then + saved["enabled"] = 1 + end + if saved["tooltip"] == nil then + saved["tooltip"] = 1 + end + if saved["rows"] == nil then + saved["rows"] = 1 + end + if saved["horizontal"] == nil then + saved["horizontal"] = nil + end + if saved["vertical"] == nil then + saved["vertical"] = nil + end + if saved["firstbutton"] == nil then + saved["firstbutton"] = "RIGHT" + end + if not saved["children"] then + saved["children"] = {} + end + if saved["children"]["size"] == nil then + saved["children"]["size"] = 36 + end + if saved["children"]["hideonclick"] == nil then + saved["children"]["hideonclick"] = 1 + end + if saved["food"] == nil then + saved["food"] = {} + end + if not saved["spells"] then + saved["spells"] = {} + end + if not saved["visible"] then + saved["visible"] = {} + end + for i = 1, ZHUNTER_PET_MAX do + if not tonumber(saved["spells"][i]) then + saved["spells"][i] = i + end + if saved["visible"][i] == nil then + saved["visible"][i] = 1 + end + end +end + +local function zButtonPet_ApplyRuntimeSettings() + if not zButtonPet then + return + end + local saved = zButtonPet_GetSaved() + zButtonPet.tooltip = saved["tooltip"] and true or false + zButtonPet.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false +end + +function zButtonPet_OnLoad() + this:RegisterEvent("VARIABLES_LOADED") +end + +function zButtonPet_OnEvent() + if event == "VARIABLES_LOADED" then + if not zButtonPet then + return + end + if UnitClass("player") ~= ZHUNTER_HUNTER then + zButtonPet:UnregisterAllEvents() + zButtonPet:Hide() + return + end + zButtonPet_CreateButtons() + zButtonPet.beforeclick = zButtonPetAdjustment_BeforeClick + zButtonPet.afterclick = zButtonPet_AfterClick + zButtonPetAdjustment = CreateFrame("Frame", "zButtonPetAdjustment") + zButtonPetAdjustment:RegisterEvent("UNIT_HEALTH") + zButtonPetAdjustment:RegisterEvent("UNIT_HAPPINESS") + zButtonPetAdjustment:RegisterEvent("UNIT_PET") + zButtonPetAdjustment:RegisterEvent("PET_BAR_UPDATE") + zButtonPetAdjustment:RegisterEvent("PLAYER_ENTERING_WORLD") + zButtonPetAdjustment:RegisterEvent("SPELLS_CHANGED") + zButtonPetAdjustment:SetScript("OnEvent", zButtonPetAdjustment_OnEvent) + zButtonPet_Tooltip = CreateFrame("GameTooltip", "zButtonPet_Tooltip", nil, "GameTooltipTemplate") + zButtonPet_SetupSizeAndPosition() + zButtonPetAdjustment_OnEvent() + end +end + +function zButtonPet_CreateButtons() + zButtonPet_EnsureConfig() + local saved = zButtonPet_GetSaved() + + ZSpellButton_CreateChildren(zButtonPet, "zButtonPet", ZHUNTER_PET_MAX) + local info = {} + local infoIndex = 1 + for i=1, table.getn(ZHunterMod_Pet_Spells) do + if not tonumber(saved["spells"][i]) then + info = ZHunterMod_Pet_Spells + saved["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + break + end + local spellIndex = saved["spells"][i] + if saved["visible"][spellIndex] ~= false then + info[infoIndex] = ZHunterMod_Pet_Spells[spellIndex] + infoIndex = infoIndex + 1 + end + end + zButtonPet.found = ZSpellButton_SetButtons(zButtonPet, info) + zButtonPet_ApplyRuntimeSettings() +end + +function zButtonPet_SetupSizeAndPosition() + local saved = zButtonPet_GetSaved() + if saved["enabled"] == false or saved["enabled"] == 0 then + if zButtonPet and zButtonPet.Hide then + zButtonPet:Hide() + end + return + end + local displayCount = zButtonPet.found or ZHUNTER_PET_MAX + if displayCount < 0 then + displayCount = 0 + end + ZSpellButton_SetSize(zButtonPet, saved["parent"]["size"]) + ZSpellButton_SetSize(zButtonPet, saved["children"]["size"], 1) + ZSpellButton_SetExpandDirection(zButtonPet, saved["firstbutton"]) + ZSpellButton_ArrangeChildren(zButtonPet, saved["rows"], + displayCount, saved["horizontal"], + saved["vertical"]) +end + +function zButtonPet_Reset() + local currentRoot = zButtonPet_GetRoot() + currentRoot["zButtonPet"] = {} + local saved = zButtonPet_GetSaved() + saved["pet"] = {} + saved["pet"]["happiness"] = nil + saved["pet"]["status"] = nil + saved["pet"]["dead"] = nil + saved["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + saved["rows"] = 1 + saved["horizontal"] = nil + saved["vertical"] = nil + saved["firstbutton"] = "RIGHT" + saved["enabled"] = 1 + saved["tooltip"] = 1 + saved["parent"] = {} + saved["parent"]["size"] = 36 + saved["parent"]["hide"] = nil + saved["parent"]["circle"] = 1 + saved["children"] = {} + saved["children"]["size"] = 36 + saved["children"]["hideonclick"] = 1 + saved["visible"] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + zButtonPet_EnsureConfig() +end + +function IsPetDead() + local saved = zButtonPet_GetSaved() + return saved["pet"] and saved["pet"]["dead"] +end + +local function zButtonPet_GetCorePetInfo() + if type(MTH_GetCurrentPetInfo) == "function" then + local info = MTH_GetCurrentPetInfo() + if type(info) == "table" then + return info + end + end + return nil +end + +local function zButtonPet_RefreshSavedPetState(saved, corePetInfo) + local hasLivePet = false + if type(UnitExists) == "function" then + hasLivePet = UnitExists("pet") and true or false + elseif type(corePetInfo) == "table" and corePetInfo.liveExists ~= nil then + hasLivePet = corePetInfo.liveExists and true or false + end + + if not hasLivePet then + saved["pet"]["status"] = nil + saved["pet"]["dead"] = nil + saved["pet"]["happiness"] = nil + return false + end + + local currentHealth = (corePetInfo and tonumber(corePetInfo.health)) or (type(UnitHealth) == "function" and UnitHealth("pet")) or nil + local currentHealthMax = (corePetInfo and tonumber(corePetInfo.healthMax)) or (type(UnitHealthMax) == "function" and UnitHealthMax("pet")) or nil + local isDead = (corePetInfo and corePetInfo.dead) and true or false + if not isDead and type(UnitIsDead) == "function" then + isDead = UnitIsDead("pet") and true or false + end + if isDead then + saved["pet"]["dead"] = 1 + else + saved["pet"]["dead"] = nil + end + + local happiness = corePetInfo and corePetInfo.happiness or nil + if happiness == nil and type(GetPetHappiness) == "function" then + happiness = GetPetHappiness() + end + saved["pet"]["happiness"] = happiness + + if isDead then + saved["pet"]["status"] = 1 + elseif currentHealth and currentHealthMax and currentHealthMax > 0 then + if (currentHealth / currentHealthMax) > 0.75 then + saved["pet"]["status"] = 2 + else + saved["pet"]["status"] = 1 + end + else + saved["pet"]["status"] = 2 + end + + return true +end + +local function zButtonPet_FindSpellIdByName(spellName) + if not spellName or spellName == "" then + return nil + end + local maxSpells = tonumber((_G and _G["MAX_SPELLS"]) or nil) or 1024 + for i = 1, maxSpells do + local current = GetSpellName(i, "spell") + if not current then + break + end + if current == spellName then + return i + end + end + return nil +end + +function zButtonPetAdjustment_BeforeClick() + if CursorHasItem() then + DropItemOnUnit("pet") + return 1 + end +end + +local function zButtonPet_RefreshParentSpell() + if not zButtonPet or not zButtonPet.count then + return + end + zButtonPet_EnsureConfig() + local saved = zButtonPet_GetSaved() + local corePetInfo = zButtonPet_GetCorePetInfo() + local hasCurrentPet = nil + local hasLivePet = nil + if type(corePetInfo) == "table" and corePetInfo.exists ~= nil then + hasCurrentPet = corePetInfo.exists and true or false + end + if type(corePetInfo) == "table" and corePetInfo.liveExists ~= nil then + hasLivePet = corePetInfo.liveExists and true or false + end + zButtonPet_RefreshSavedPetState(saved, corePetInfo) + + local status, happiness, dead + local spells = {} + local name + local choice + for i=1, zButtonPet.count do + local button + button = getglobal(zButtonPet.name..i) + if button and button.id then + name = GetSpellName(button.id, "spell") + spells[name] = button + if not choice and (name == ZHUNTER_PET_DISMISS or name == ZHUNTER_PET_EYES or name == ZHUNTER_PET_POSITION) then + choice = button + end + end + end + status = saved["pet"]["status"] + happiness = saved["pet"]["happiness"] + dead = saved["pet"]["dead"] + local id = zButtonPet.id + name = nil + + if hasCurrentPet == false and hasLivePet == false then + name = ZHUNTER_PET_TAMING + elseif not status then + name = ZHUNTER_PET_CALL + elseif dead then + name = ZHUNTER_PET_REVIVE + elseif status == 1 then + name = ZHUNTER_PET_MEND + elseif happiness ~= 3 then + name = ZHUNTER_PET_FEED + elseif choice then + id = choice.id + end + + if name and spells[name] then + id = spells[name].id + elseif name then + local fallbackId = zButtonPet_FindSpellIdByName(name) + if fallbackId then + id = fallbackId + end + end + zButtonPet.id = id + ZSpellButton_UpdateButton(zButtonPet) + ZSpellButton_UpdateCooldown(zButtonPet) + if GameTooltip:IsOwned(zButtonPet) then + ZSpellButtonParent_OnEnter(zButtonPet) + end +end + +function zButtonPet_AfterClick() + zButtonPet_RefreshParentSpell() +end + +function zButtonPet_RefreshLiveState(_liveState, _source) + zButtonPet_RefreshParentSpell() +end + +function zButtonPetAdjustment_OnEvent() + if not zButtonPet or not zButtonPet.count then + return + end + + if (event == "UNIT_HEALTH" or event == "UNIT_HAPPINESS") and arg1 and arg1 ~= "pet" then + return + end + if event == "UNIT_PET" and arg1 and arg1 ~= "player" then + return + end + zButtonPet_RefreshParentSpell() +end + +function zButtonPet_KeyBinding(index) + if MTH_ZH_IsModuleEnabled and not MTH_ZH_IsModuleEnabled() then + return + end + + local button + if index then + button = getglobal("zButtonPet"..index) + else + button = zButtonPet + end + if button.id then + CastSpell(button.id, "spell") + if zButtonPet.hideonclick then + if type(ZSpellButton_SetChildrenExpanded) == "function" then + ZSpellButton_SetChildrenExpanded(zButtonPet, false) + elseif zButtonPet.children then + zButtonPet.children:Hide() + end + end + end +end + +function zButtonPet_FeedPet() + +end + +SLASH_zButtonPet1 = "/ZPet" +SlashCmdList["zButtonPet"] = function(msg) + if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Pet button is disabled while module 'zhunter' is disabled.") then + return + end + if msg == "reset" then + zButtonPet_Reset() + zButtonPet:ClearAllPoints() + zButtonPet:SetPoint("CENTER", UIParent, "CENTER", 0, 60) + elseif msg == "options" then + MTH_OpenOptions("Pet") + else + MTH_ZH_Print("Possible Commands: \"options\", \"reset\"") + end +end \ No newline at end of file diff --git a/modules/zhunter/zButtonPet.xml b/modules/zhunter/zButtonPet.xml new file mode 100644 index 0000000..07a1fd5 --- /dev/null +++ b/modules/zhunter/zButtonPet.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/zhunter/zButtonToys.lua b/modules/zhunter/zButtonToys.lua new file mode 100644 index 0000000..d040c7a --- /dev/null +++ b/modules/zhunter/zButtonToys.lua @@ -0,0 +1,322 @@ +local function zButtonToys_GetRoot() + return MTH_ZH_GetSavedRoot() +end + +local root = zButtonToys_GetRoot() +if not root["zButtonToys"] then + root["zButtonToys"] = {} + root["zButtonToys"]["spells"] = {} + root["zButtonToys"]["rows"] = 1 + root["zButtonToys"]["horizontal"] = nil + root["zButtonToys"]["vertical"] = nil + root["zButtonToys"]["firstbutton"] = "RIGHT" + root["zButtonToys"]["enabled"] = 0 + root["zButtonToys"]["tooltip"] = 1 + root["zButtonToys"]["parent"] = {} + root["zButtonToys"]["parent"]["size"] = 36 + root["zButtonToys"]["parent"]["hide"] = nil + root["zButtonToys"]["parent"]["circle"] = 1 + root["zButtonToys"]["children"] = {} + root["zButtonToys"]["children"]["size"] = 36 + root["zButtonToys"]["children"]["hideonclick"] = 1 +end + +local ZHUNTER_TOYS_BUTTON_MAX = 80 + +local function zButtonToys_GetSaved() + local currentRoot = zButtonToys_GetRoot() + if not currentRoot["zButtonToys"] then + currentRoot["zButtonToys"] = {} + end + return currentRoot["zButtonToys"] +end + +local function zButtonToys_EnsureConfig() + local saved = zButtonToys_GetSaved() + saved["count"] = nil + if saved["enabled"] == nil then + saved["enabled"] = 0 + end + if saved["tooltip"] == nil then + saved["tooltip"] = 1 + end + if not saved["children"] then + saved["children"] = {} + end + if saved["children"]["hideonclick"] == nil then + saved["children"]["hideonclick"] = 1 + end + if not saved["spells"] then + saved["spells"] = {} + end + if not saved["parent"] then + saved["parent"] = {} + end + if not saved["children"] then + saved["children"] = {} + end + if not saved["parent"]["size"] then + saved["parent"]["size"] = 36 + end + if saved["parent"]["circle"] == nil then + saved["parent"]["circle"] = 1 + end + if not saved["children"]["size"] then + saved["children"]["size"] = 36 + end + if not saved["rows"] or saved["rows"] < 1 then + saved["rows"] = 1 + end + if not saved["firstbutton"] then + saved["firstbutton"] = "RIGHT" + end +end + +local function zButtonToys_ApplyRuntimeSettings() + if not zButtonToys then + return + end + local saved = zButtonToys_GetSaved() + zButtonToys.tooltip = saved["tooltip"] and true or false + zButtonToys.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false +end + +local function zButtonToys_NormalizeTabName(value) + local text = string.lower(tostring(value or "")) + text = string.gsub(text, "%s+", "") + text = string.gsub(text, "[^%a]", "") + while string.sub(text, 1, 1) == "z" and string.len(text) > 1 do + text = string.sub(text, 2) + end + return text +end + +local function zButtonToys_FindTabBounds() + if type(GetNumSpellTabs) ~= "function" or type(GetSpellTabInfo) ~= "function" then + return nil, nil + end + local tabCount = GetNumSpellTabs() or 0 + for tabIndex = 1, tabCount do + local tabName, _, tabOffset, tabEntries = GetSpellTabInfo(tabIndex) + local normalized = zButtonToys_NormalizeTabName(tabName) + if string.find(normalized, "toy", 1, true) then + return tonumber(tabOffset) or 0, tonumber(tabEntries) or 0 + end + end + return nil, nil +end + +local function zButtonToys_CollectTabSpellIds() + local tabOffset, tabEntries = zButtonToys_FindTabBounds() + if not tabOffset or not tabEntries or tabEntries <= 0 then + return {} + end + + local spellIds = {} + local bookType = BOOKTYPE_SPELL or "spell" + local maxIndex = tabOffset + tabEntries + for spellIndex = tabOffset + 1, maxIndex do + local spellName = GetSpellName(spellIndex, bookType) + if spellName and spellName ~= "" then + table.insert(spellIds, spellIndex) + end + end + return spellIds +end + +local function zButtonToys_ApplySpellIds(spellIds) + if not zButtonToys then + return 0 + end + + local parent = zButtonToys + local childCount = parent.count or ZHUNTER_TOYS_BUTTON_MAX + + for i = 1, childCount do + local child = getglobal("zButtonToys" .. i) + if child then + child:Hide() + child.id = nil + child.icon = nil + child.isspell = nil + end + end + + local found = 0 + for i = 1, table.getn(spellIds) do + if found >= childCount then + break + end + local spellId = spellIds[i] + if spellId then + found = found + 1 + local child = getglobal("zButtonToys" .. found) + if child then + child.id = spellId + child.isspell = 1 + ZSpellButton_UpdateButton(child) + child:Show() + end + end + end + + if found > 0 then + parent.id = spellIds[1] + parent.isspell = 1 + ZSpellButton_UpdateButton(parent) + parent:Enable() + parent:Show() + else + parent.id = nil + parent:Hide() + end + + return found +end + +local function zButtonToys_RefreshSpells() + if not zButtonToys then + return 0 + end + local saved = zButtonToys_GetSaved() + local spellIds = zButtonToys_CollectTabSpellIds() + local spellNames = {} + local bookType = BOOKTYPE_SPELL or "spell" + for i = 1, table.getn(spellIds) do + local n = GetSpellName(spellIds[i], bookType) + if n and n ~= "" then + table.insert(spellNames, n) + end + end + saved["spellIds"] = spellIds + saved["spells"] = spellNames + zButtonToys.spells = spellNames + zButtonToys.found = zButtonToys_ApplySpellIds(spellIds) + zButtonToys_ApplyRuntimeSettings() + return zButtonToys.found or 0 +end + +function zButtonToys_OnLoad() + this:RegisterEvent("VARIABLES_LOADED") +end + +function zButtonToys_OnEvent() + if event == "VARIABLES_LOADED" then + if not zButtonToys then + return + end + if UnitClass("player") ~= ZHUNTER_HUNTER then + zButtonToys:UnregisterAllEvents() + zButtonToys:Hide() + return + end + zButtonToys_CreateButtons() + zButtonToysAdjustment = CreateFrame("Frame", "zButtonToysAdjustment") + zButtonToysAdjustment:RegisterEvent("SPELLS_CHANGED") + zButtonToysAdjustment:RegisterEvent("PLAYER_ENTERING_WORLD") + zButtonToysAdjustment:SetScript("OnEvent", zButtonToysAdjustment_OnEvent) + zButtonToys_SetupSizeAndPosition() + end +end + +function zButtonToys_CreateButtons() + zButtonToys_EnsureConfig() + + if not zButtonToys._mthChildrenCreated then + ZSpellButton_CreateChildren(zButtonToys, "zButtonToys", ZHUNTER_TOYS_BUTTON_MAX) + zButtonToys._mthChildrenCreated = 1 + end + + zButtonToys_RefreshSpells() +end + +function zButtonToys_SetupSizeAndPosition() + local saved = zButtonToys_GetSaved() + if saved["enabled"] == false or saved["enabled"] == 0 then + if zButtonToys and zButtonToys.Hide then + zButtonToys:Hide() + end + return + end + local displayCount = zButtonToys.found or 0 + if displayCount < 0 then + displayCount = 0 + end + ZSpellButton_SetSize(zButtonToys, saved["parent"]["size"]) + ZSpellButton_SetSize(zButtonToys, saved["children"]["size"], 1) + ZSpellButton_SetExpandDirection(zButtonToys, saved["firstbutton"]) + ZSpellButton_ArrangeChildren(zButtonToys, saved["rows"], + displayCount, saved["horizontal"], + saved["vertical"]) +end + +function zButtonToys_Reset() + local currentRoot = zButtonToys_GetRoot() + currentRoot["zButtonToys"] = {} + local saved = zButtonToys_GetSaved() + saved["spells"] = {} + saved["rows"] = 1 + saved["horizontal"] = nil + saved["vertical"] = nil + saved["firstbutton"] = "RIGHT" + saved["enabled"] = 1 + saved["tooltip"] = 1 + saved["parent"] = {} + saved["parent"]["size"] = 36 + saved["parent"]["hide"] = nil + saved["parent"]["circle"] = 1 + saved["children"] = {} + saved["children"]["size"] = 36 + saved["children"]["hideonclick"] = 1 + zButtonToys_EnsureConfig() +end + +function zButtonToysAdjustment_OnEvent() + if not zButtonToys then + return + end + zButtonToys_RefreshSpells() + zButtonToys_SetupSizeAndPosition() + if GameTooltip:IsOwned(zButtonToys) then + ZSpellButtonParent_OnEnter(zButtonToys) + end +end + +function zButtonToys_KeyBinding(index) + if MTH_ZH_IsModuleEnabled and not MTH_ZH_IsModuleEnabled() then + return + end + + local button + if index then + button = getglobal("zButtonToys"..index) + else + button = zButtonToys + end + if button and button.id then + CastSpell(button.id, "spell") + if zButtonToys.hideonclick then + if type(ZSpellButton_SetChildrenExpanded) == "function" then + ZSpellButton_SetChildrenExpanded(zButtonToys, false) + elseif zButtonToys.children then + zButtonToys.children:Hide() + end + end + end +end + +SLASH_zButtonToys1 = "/ZToys" +SlashCmdList["zButtonToys"] = function(msg) + if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Toys button is disabled while module 'zhunter' is disabled.") then + return + end + if msg == "reset" then + zButtonToys_Reset() + zButtonToys:ClearAllPoints() + zButtonToys:SetPoint("TOP", UIParent, "TOP", 0, -518) + zButtonToys_RefreshSpells() + zButtonToys_SetupSizeAndPosition() + else + MTH_ZH_Print("Possible Commands: \"reset\"") + end +end diff --git a/modules/zhunter/zButtonToys.xml b/modules/zhunter/zButtonToys.xml new file mode 100644 index 0000000..4b30600 --- /dev/null +++ b/modules/zhunter/zButtonToys.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/modules/zhunter/zButtonTrack.lua b/modules/zhunter/zButtonTrack.lua new file mode 100644 index 0000000..49e6f9f --- /dev/null +++ b/modules/zhunter/zButtonTrack.lua @@ -0,0 +1,260 @@ +local function zButtonTrack_GetRoot() + return MTH_ZH_GetSavedRoot() +end + +local root = zButtonTrack_GetRoot() +if not root["zButtonTrack"] then + root["zButtonTrack"] = {} + root["zButtonTrack"]["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} + root["zButtonTrack"]["rows"] = 1 + root["zButtonTrack"]["horizontal"] = nil + root["zButtonTrack"]["vertical"] = nil + root["zButtonTrack"]["firstbutton"] = "RIGHT" + root["zButtonTrack"]["enabled"] = 1 + root["zButtonTrack"]["tooltip"] = 1 + root["zButtonTrack"]["parent"] = {} + root["zButtonTrack"]["parent"]["size"] = 36 + root["zButtonTrack"]["parent"]["hide"] = nil + root["zButtonTrack"]["parent"]["circle"] = 1 + root["zButtonTrack"]["children"] = {} + root["zButtonTrack"]["children"]["size"] = 36 + root["zButtonTrack"]["children"]["hideonclick"] = 1 +end + +ZHunterMod_Track_Spells = { + ZHUNTER_TRACK_HIDDEN, + ZHUNTER_TRACK_HUMANOIDS, + ZHUNTER_TRACK_UNDEAD, + ZHUNTER_TRACK_BEASTS, + ZHUNTER_TRACK_DEMONS, + ZHUNTER_TRACK_ELEMENTALS, + ZHUNTER_TRACK_DRAGONKIN, + ZHUNTER_TRACK_GIANTS, + ZHUNTER_TRACK_MINERALS, + ZHUNTER_TRACK_HERBS, + ZHUNTER_TRACK_TREASURE +} + +local ZHUNTER_TRACK_MAX = table.getn(ZHunterMod_Track_Spells) + +local function zButtonTrack_GetSaved() + local currentRoot = zButtonTrack_GetRoot() + if not currentRoot["zButtonTrack"] then + currentRoot["zButtonTrack"] = {} + end + return currentRoot["zButtonTrack"] +end + +local function zButtonTrack_EnsureConfig() + local saved = zButtonTrack_GetSaved() + saved["count"] = nil + if saved["rows"] == nil then + saved["rows"] = 1 + end + if saved["firstbutton"] == nil then + saved["firstbutton"] = "RIGHT" + end + if saved["enabled"] == nil then + saved["enabled"] = 1 + end + if saved["tooltip"] == nil then + saved["tooltip"] = 1 + end + if not saved["parent"] then + saved["parent"] = {} + end + if saved["parent"]["size"] == nil then + saved["parent"]["size"] = 36 + end + if saved["parent"]["circle"] == nil then + saved["parent"]["circle"] = 1 + end + if not saved["children"] then + saved["children"] = {} + end + if saved["children"]["size"] == nil then + saved["children"]["size"] = 36 + end + if saved["children"]["hideonclick"] == nil then + saved["children"]["hideonclick"] = 1 + end + if not saved["spells"] then + saved["spells"] = {} + end + if not saved["visible"] then + saved["visible"] = {} + end + for i = 1, ZHUNTER_TRACK_MAX do + if not tonumber(saved["spells"][i]) then + saved["spells"][i] = i + end + if saved["visible"][i] == nil then + saved["visible"][i] = 1 + end + end +end + +local function zButtonTrack_ApplyRuntimeSettings() + if not zButtonTrack then + return + end + local saved = zButtonTrack_GetSaved() + zButtonTrack.tooltip = saved["tooltip"] and true or false + zButtonTrack.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false +end + +function zButtonTrack_OnLoad() + this:RegisterEvent("VARIABLES_LOADED") +end + +function zButtonTrack_OnEvent() + if event == "VARIABLES_LOADED" then + if not zButtonTrack then + return + end + if UnitClass("player") ~= ZHUNTER_HUNTER then + zButtonTrack:UnregisterAllEvents() + zButtonTrack:Hide() + return + end + zButtonTrack_CreateButtons() + zButtonTrackAdjustment = CreateFrame("Frame", "zButtonTrackAdjustment") + zButtonTrackAdjustment:RegisterEvent("PLAYER_AURAS_CHANGED") + zButtonTrackAdjustment:RegisterEvent("PLAYER_ENTERING_WORLD") + zButtonTrackAdjustment:RegisterEvent("SPELLS_CHANGED") + zButtonTrackAdjustment:RegisterEvent("CHARACTER_POINTS_CHANGED") + zButtonTrackAdjustment:SetScript("OnEvent", zButtonTrackAdjustment_OnEvent) + zButtonTrack_SetupSizeAndPosition() + end +end + +function zButtonTrack_CreateButtons() + zButtonTrack_EnsureConfig() + local saved = zButtonTrack_GetSaved() + + ZSpellButton_CreateChildren(zButtonTrack, "zButtonTrack", ZHUNTER_TRACK_MAX) + local info = {} + local infoIndex = 1 + for i=1, table.getn(ZHunterMod_Track_Spells) do + if not tonumber(saved["spells"][i]) then + info = ZHunterMod_Track_Spells + saved["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} + break + end + local spellIndex = saved["spells"][i] + if saved["visible"][spellIndex] ~= false then + info[infoIndex] = ZHunterMod_Track_Spells[spellIndex] + infoIndex = infoIndex + 1 + end + end + zButtonTrack.found = ZSpellButton_SetButtons(zButtonTrack, info) + zButtonTrack_ApplyRuntimeSettings() +end + +function zButtonTrack_SetupSizeAndPosition() + zButtonTrack_EnsureConfig() + local saved = zButtonTrack_GetSaved() + if saved["enabled"] == false or saved["enabled"] == 0 then + if zButtonTrack and zButtonTrack.Hide then + zButtonTrack:Hide() + end + return + end + local displayCount = zButtonTrack.found or ZHUNTER_TRACK_MAX + if displayCount < 0 then + displayCount = 0 + end + ZSpellButton_SetSize(zButtonTrack, saved["parent"]["size"]) + ZSpellButton_SetSize(zButtonTrack, saved["children"]["size"], 1) + ZSpellButton_SetExpandDirection(zButtonTrack, saved["firstbutton"]) + ZSpellButton_ArrangeChildren(zButtonTrack, saved["rows"], + displayCount, saved["horizontal"], + saved["vertical"]) +end + +function zButtonTrack_Reset() + local currentRoot = zButtonTrack_GetRoot() + currentRoot["zButtonTrack"] = {} + local saved = zButtonTrack_GetSaved() + saved["spells"] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} + saved["rows"] = 1 + saved["horizontal"] = nil + saved["vertical"] = nil + saved["firstbutton"] = "RIGHT" + saved["enabled"] = 1 + saved["tooltip"] = 1 + saved["parent"] = {} + saved["parent"]["size"] = 36 + saved["parent"]["hide"] = nil + saved["parent"]["circle"] = 1 + saved["children"] = {} + saved["children"]["size"] = 36 + saved["children"]["hideonclick"] = 1 + saved["visible"] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + zButtonTrack_EnsureConfig() +end + +function zButtonTrackAdjustment_OnEvent() + if not zButtonTrack or not zButtonTrack.count then + return + end + if event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" then + zButtonTrack_CreateButtons() + zButtonTrack_SetupSizeAndPosition() + end + if event == "PLAYER_AURAS_CHANGED" or event == "PLAYER_ENTERING_WORLD" + or event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" then + local i = 1 + local texture = zButtonTrack1IconTexture:GetTexture() + local buff = GetTrackingTexture() + if texture == buff and zButtonTrack2.id then + zButtonTrack.id = zButtonTrack2.id + else + zButtonTrack.id = zButtonTrack1.id + end + ZSpellButton_UpdateButton(zButtonTrack) + ZSpellButton_UpdateCooldown(zButtonTrack) + if GameTooltip:IsOwned(zButtonTrack) then + ZSpellButtonParent_OnEnter(zButtonTrack) + end + end +end + +function zButtonTrack_KeyBinding(index) + if MTH_ZH_IsModuleEnabled and not MTH_ZH_IsModuleEnabled() then + return + end + + local button + if index then + button = getglobal("zButtonTrack"..index) + else + button = zButtonTrack + end + if button.id then + CastSpell(button.id, "spell") + if zButtonTrack.hideonclick then + if type(ZSpellButton_SetChildrenExpanded) == "function" then + ZSpellButton_SetChildrenExpanded(zButtonTrack, false) + elseif zButtonTrack.children then + zButtonTrack.children:Hide() + end + end + end +end + +SLASH_zButtonTrack1 = "/ZTrack" +SlashCmdList["zButtonTrack"] = function(msg) + if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Track button is disabled while module 'zhunter' is disabled.") then + return + end + if msg == "reset" then + zButtonTrack_Reset() + zButtonTrack:ClearAllPoints() + zButtonTrack:SetPoint("CENTER", UIParent, "CENTER", 0, 0) + elseif msg == "options" then + MTH_OpenOptions("Track") + else + MTH_ZH_Print("Possible Commands: \"options\", \"reset\"") + end +end \ No newline at end of file diff --git a/modules/zhunter/zButtonTrack.xml b/modules/zhunter/zButtonTrack.xml new file mode 100644 index 0000000..3d56257 --- /dev/null +++ b/modules/zhunter/zButtonTrack.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/zhunter/zButtonTrap.lua b/modules/zhunter/zButtonTrap.lua new file mode 100644 index 0000000..243cdee --- /dev/null +++ b/modules/zhunter/zButtonTrap.lua @@ -0,0 +1,262 @@ +local function zButtonTrap_GetRoot() + return MTH_ZH_GetSavedRoot() +end + +local root = zButtonTrap_GetRoot() +if not root["zButtonTrap"] then + root["zButtonTrap"] = {} + root["zButtonTrap"]["spells"] = {1, 2, 3, 4} + root["zButtonTrap"]["rows"] = 1 + root["zButtonTrap"]["horizontal"] = nil + root["zButtonTrap"]["vertical"] = nil + root["zButtonTrap"]["firstbutton"] = "RIGHT" + root["zButtonTrap"]["enabled"] = 1 + root["zButtonTrap"]["tooltip"] = 1 + root["zButtonTrap"]["parent"] = {} + root["zButtonTrap"]["parent"]["size"] = 36 + root["zButtonTrap"]["parent"]["hide"] = nil + root["zButtonTrap"]["parent"]["circle"] = 1 + root["zButtonTrap"]["children"] = {} + root["zButtonTrap"]["children"]["size"] = 36 + root["zButtonTrap"]["children"]["hideonclick"] = 1 +end + +ZHunterMod_Trap_Spells = { + ZHUNTER_TRAP_FREEZING, + ZHUNTER_TRAP_FROST, + ZHUNTER_TRAP_IMMOLATION, + ZHUNTER_TRAP_EXPLOSIVE +} + +local ZHUNTER_TRAP_MAX = table.getn(ZHunterMod_Trap_Spells) + +local function zButtonTrap_GetSaved() + local currentRoot = zButtonTrap_GetRoot() + if not currentRoot["zButtonTrap"] then + currentRoot["zButtonTrap"] = {} + end + return currentRoot["zButtonTrap"] +end + +local function zButtonTrap_EnsureConfig() + local saved = zButtonTrap_GetSaved() + saved["count"] = nil + if saved["rows"] == nil then + saved["rows"] = 1 + end + if saved["firstbutton"] == nil then + saved["firstbutton"] = "RIGHT" + end + if saved["enabled"] == nil then + saved["enabled"] = 1 + end + if saved["tooltip"] == nil then + saved["tooltip"] = 1 + end + if not saved["parent"] then + saved["parent"] = {} + end + if saved["parent"]["size"] == nil then + saved["parent"]["size"] = 36 + end + if saved["parent"]["circle"] == nil then + saved["parent"]["circle"] = 1 + end + if not saved["children"] then + saved["children"] = {} + end + if saved["children"]["size"] == nil then + saved["children"]["size"] = 36 + end + if saved["children"]["hideonclick"] == nil then + saved["children"]["hideonclick"] = 1 + end + if not saved["spells"] then + saved["spells"] = {} + end + if not saved["visible"] then + saved["visible"] = {} + end + for i = 1, ZHUNTER_TRAP_MAX do + if not tonumber(saved["spells"][i]) then + saved["spells"][i] = i + end + if saved["visible"][i] == nil then + saved["visible"][i] = 1 + end + end +end + +local function zButtonTrap_ApplyRuntimeSettings() + if not zButtonTrap then + return + end + local saved = zButtonTrap_GetSaved() + zButtonTrap.tooltip = saved["tooltip"] and true or false + zButtonTrap.hideonclick = saved["children"] and saved["children"]["hideonclick"] and true or false +end + +function zButtonTrap_OnLoad() + this:RegisterEvent("VARIABLES_LOADED") +end + +function zButtonTrap_OnEvent() + if event == "VARIABLES_LOADED" then + if not zButtonTrap then + return + end + if UnitClass("player") ~= ZHUNTER_HUNTER then + zButtonTrap:UnregisterAllEvents() + zButtonTrap:Hide() + return + end + zButtonTrap_CreateButtons() + zButtonTrapAdjustment = CreateFrame("Frame", "zButtonTrapAdjustment") + zButtonTrapAdjustment:RegisterEvent("PLAYER_REGEN_ENABLED") + zButtonTrapAdjustment:RegisterEvent("PLAYER_REGEN_DISABLED") + zButtonTrapAdjustment:RegisterEvent("PLAYER_ENTERING_WORLD") + zButtonTrapAdjustment:RegisterEvent("SPELLS_CHANGED") + zButtonTrapAdjustment:RegisterEvent("CHARACTER_POINTS_CHANGED") + zButtonTrapAdjustment:SetScript("OnEvent", zButtonTrapAdjustment_OnEvent) + zButtonTrap_SetupSizeAndPosition() + end +end + +function zButtonTrap_CreateButtons() + zButtonTrap_EnsureConfig() + local saved = zButtonTrap_GetSaved() + + ZSpellButton_CreateChildren(zButtonTrap, "zButtonTrap", ZHUNTER_TRAP_MAX) + local info = {} + local infoIndex = 1 + for i=1, table.getn(ZHunterMod_Trap_Spells) do + if not tonumber(saved["spells"][i]) then + info = ZHunterMod_Trap_Spells + saved["spells"] = {1, 2, 3, 4} + break + end + local spellIndex = saved["spells"][i] + if saved["visible"][spellIndex] ~= false then + info[infoIndex] = ZHunterMod_Trap_Spells[spellIndex] + infoIndex = infoIndex + 1 + end + end + zButtonTrap.found = ZSpellButton_SetButtons(zButtonTrap, info) + zButtonTrap_ApplyRuntimeSettings() +end + +function zButtonTrap_SetupSizeAndPosition() + zButtonTrap_EnsureConfig() + local saved = zButtonTrap_GetSaved() + if saved["enabled"] == false or saved["enabled"] == 0 then + if zButtonTrap and zButtonTrap.Hide then + zButtonTrap:Hide() + end + return + end + local displayCount = zButtonTrap.found or ZHUNTER_TRAP_MAX + if displayCount < 0 then + displayCount = 0 + end + ZSpellButton_SetSize(zButtonTrap, saved["parent"]["size"]) + ZSpellButton_SetSize(zButtonTrap, saved["children"]["size"], 1) + ZSpellButton_SetExpandDirection(zButtonTrap, saved["firstbutton"]) + ZSpellButton_ArrangeChildren(zButtonTrap, saved["rows"], + displayCount, saved["horizontal"], + saved["vertical"]) +end + +function zButtonTrap_Reset() + local currentRoot = zButtonTrap_GetRoot() + currentRoot["zButtonTrap"] = {} + local saved = zButtonTrap_GetSaved() + saved["spells"] = {1, 2, 3, 4} + saved["rows"] = 1 + saved["horizontal"] = nil + saved["vertical"] = nil + saved["firstbutton"] = "RIGHT" + saved["enabled"] = 1 + saved["tooltip"] = 1 + saved["parent"] = {} + saved["parent"]["size"] = 36 + saved["parent"]["hide"] = nil + saved["parent"]["circle"] = 1 + saved["children"] = {} + saved["children"]["size"] = 36 + saved["children"]["hideonclick"] = 1 + saved["visible"] = {1, 1, 1, 1} + zButtonTrap_EnsureConfig() +end + +function zButtonTrapAdjustment_OnEvent() + if not zButtonTrap or not zButtonTrap.count then + return + end + if event == "SPELLS_CHANGED" or event == "CHARACTER_POINTS_CHANGED" then + zButtonTrap_CreateButtons() + zButtonTrap_SetupSizeAndPosition() + end + local nextbutton + local button + local combat = event == "PLAYER_REGEN_DISABLED" + for i=1, zButtonTrap.count do + button = getglobal(zButtonTrap.name..i) + button.customcolor = nil + if button.id then + if not nextbutton then + nextbutton = button + end + if combat then + button.icontexture:SetVertexColor(0.4, 0.4, 0.4) + button.customcolor = 1 + else + button.icontexture:SetVertexColor(1.0, 1.0, 1.0) + end + end + end + zButtonTrap.id = nextbutton and nextbutton.id or zButtonTrap1.id + ZSpellButton_UpdateButton(zButtonTrap) + ZSpellButton_UpdateCooldown(zButtonTrap) + if GameTooltip:IsOwned(zButtonTrap) then + ZSpellButtonParent_OnEnter(zButtonTrap) + end +end + +function zButtonTrap_KeyBinding(index) + if MTH_ZH_IsModuleEnabled and not MTH_ZH_IsModuleEnabled() then + return + end + + local button + if index then + button = getglobal("zButtonTrap"..index) + else + button = zButtonTrap + end + if button.id then + CastSpell(button.id, "spell") + if zButtonTrap.hideonclick then + if type(ZSpellButton_SetChildrenExpanded) == "function" then + ZSpellButton_SetChildrenExpanded(zButtonTrap, false) + elseif zButtonTrap.children then + zButtonTrap.children:Hide() + end + end + end +end + +SLASH_zButtonTrap1 = "/ZTrap" +SlashCmdList["zButtonTrap"] = function(msg) + if MTH_ZH_HandleDisabledSlash and MTH_ZH_HandleDisabledSlash("Trap button is disabled while module 'zhunter' is disabled.") then + return + end + if msg == "reset" then + zButtonTrap_Reset() + zButtonTrap:ClearAllPoints() + zButtonTrap:SetPoint("CENTER", UIParent, "CENTER", 60, 0) + elseif msg == "options" then + MTH_OpenOptions("Trap") + else + MTH_ZH_Print("Possible Commands: \"options\", \"reset\"") + end +end \ No newline at end of file diff --git a/modules/zhunter/zButtonTrap.xml b/modules/zhunter/zButtonTrap.xml new file mode 100644 index 0000000..43c6864 --- /dev/null +++ b/modules/zhunter/zButtonTrap.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + \ No newline at end of file