mirror of
https://codeberg.org/Duvelcorp/MetaHunt.git
synced 2026-09-22 07:36:58 +00:00
Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99e7eaa5d2 | |||
| 44288cd635 | |||
| 7d974f24b5 | |||
| 3de8088bbb | |||
| 7a0f03ba6f | |||
| 0aaa49f33b | |||
| 4770724f60 | |||
| acac6ace6f | |||
| 772299baf3 | |||
| eed6624611 | |||
| 71c84e1704 | |||
| dec41628ad | |||
| d1d2660c2c | |||
| 3bdc66770b | |||
| 67ed20532b | |||
| f3af63926e | |||
| 22232364b1 | |||
| af31acdb3e | |||
| 402cd8aef0 | |||
| aa99ee01cc | |||
| c862cdb25e | |||
| 0fb52f67ff | |||
| aeaddd9558 | |||
| ccf9d8b0b6 | |||
| adb21c336c | |||
| 64e449481f | |||
| 2863adff1c | |||
| 0609d487a7 | |||
| e7cf99facb | |||
| c5a42d8d2a | |||
| 3233ebff7f | |||
| 66ff1ae711 | |||
| d2fb04f7c5 | |||
| 566f3f4925 | |||
| 060fc555fe | |||
| de6621f41f | |||
| da645fa53c | |||
| 9379668e4b | |||
| c55cd66ffc | |||
| 38e080261c | |||
| 04e04a4853 | |||
| 6c007f2c17 | |||
| c4911ef9fe | |||
| 99268643eb | |||
| 51d4c785fa | |||
| c81ce85950 | |||
| 680ddcba8a | |||
| 5c08656532 | |||
| 8d80d0663c | |||
| 43e23e7616 | |||
| 8479607a13 | |||
| f7b0318179 | |||
| 23bb69d094 | |||
| b2060e0aab | |||
| 609a6bdd83 | |||
| c0c2c275eb | |||
| ab34946775 | |||
| d41703949d | |||
| 27d37564ce | |||
| 4aed1174dd | |||
| 233899b2a0 | |||
| 89fd8e079c | |||
| 1a28d95b87 | |||
| 6b82d0ca46 | |||
| 2c94650993 |
+14
@@ -4,3 +4,17 @@
|
||||
|
||||
# Keep this ignore file tracked
|
||||
!.gitignore
|
||||
|
||||
# Exclude non-runtime localization helper artifacts
|
||||
tools/
|
||||
LOC*.md
|
||||
LOCALIZATION*.md
|
||||
|
||||
# Dev tooling — Python generators, data processing scripts, scratch files
|
||||
# Not part of the addon, never ship this
|
||||
.tools/
|
||||
|
||||
# GitHub Copilot — never sync Copilot data to GitHub
|
||||
.copilot/
|
||||
.github/copilot-instructions.md
|
||||
**/copilot-*
|
||||
|
||||
@@ -118,4 +118,9 @@
|
||||
<Binding name="ZPet 6" header="ZPetHeader">
|
||||
zButtonPet_KeyBinding(6)
|
||||
</Binding>
|
||||
|
||||
<!-- ExpAmmo Action Button -->
|
||||
<Binding name="ExpAmmo Action" header="ExpAmmoHeader">
|
||||
MTH_EA_CastActionSpell();
|
||||
</Binding>
|
||||
</Ui>
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to MetaHunt will be documented in this file.
|
||||
|
||||
|
||||
## [2.0.0] - 2026-09-14
|
||||
|
||||
Resurrecting on Octowow.
|
||||
|
||||
### Added
|
||||
|
||||
- **SmartAmmo — low-ammo alerts**: SmartAmmo can now monitor the total matching arrows or bullets in your bags and equipped slot: Enable the option, choose a warning threshold (default: 200), and MetaHunt will ouput a chat and centered-screen alert when you cross it. Sarcasms are always enabled ofc.
|
||||
|
||||
- **New spell datastore**: Metahunt now ships a datastore of all twow 1.18.1 hunter and pet spells. You can browse them with new pages in the Book.
|
||||
|
||||
- **Trainer cost planning**: Added shared Hunter and Pet Trainer spell cost planning for spells available now, at the next training level, and still remaining.
|
||||
|
||||
- **Hunter and Pet spell scanning**: MetaHunt now reads your Hunter and Pet spellbooks to mark every spell rank you already know. When you visit a Hunter or Pet Trainer, it also records which ranks you can learn now and which ones are still unavailable.
|
||||
|
||||
- **Level-up tracking**: Added Hunter, Pet, and Pet Loyalty level-up notifications. When you hit level, you can have a summary in the chat of all new spells available and their costs.
|
||||
|
||||
- **Pet Auto-Teach — Growl**: When you tame a new beast, MetaHunt now offers to teach it Growl at the best rank for the pet's level. Click **Yes** and it opens the Beast Training window, teaches the ability and closes the window automatically.
|
||||
|
||||
- **Animations**: MetaHunt now plays optional animations that add fun and make the interface feel alive. Powered by the great PizzaSauce library of [Pizzahawaii](https://codeberg.org/Pizzahawaii/) ! Thank you man.
|
||||
|
||||
- **zTrack — Find Fish**: Added new spell Find Fish to zTrack.
|
||||
|
||||
- **DDOSers Realm Blocking**: The Addon will auto-disable itself on all Mafia's realms, we know who they are.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Complete revamp of SavedVariables**: All your settings now live in one tidy place instead of being scattered around. A migration happens the first time you log in, and you shouldn't lose any settings.
|
||||
|
||||
- **Polished options**: The option panel had become messy over time and it finally received some attention to look cleaner and coherent with same styling everywhere and better arrangement.
|
||||
|
||||
- **Book navigation**: The book navigation is no longer done with horizontal tabs, but with a left vertical menu just like in the options. This allow more pages and fix the tab overfloading issues on some resolutions and UI scales.
|
||||
|
||||
- **Web database links**: All links in the data that were pointing on old Turtle WoW DB have been replaced to point on the OctoWoW DB.
|
||||
|
||||
- **Addon's code repository change**: The addon repo is now hosted on Codeberg and the "update available" link will point on the new location.
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- **NPC Finder — zone accuracy**: Fixed NPCs that could show a wrong zone (e.g. Feathermoon vendors in Feralas were labelled "Duskwood") caused by a zone-ID mix-up. NPCs that stand on a zone border or in a capital city now list **all** the zones they appear on, most precise first — e.g. "Darnassus, Teldrassil" or "Alterac, Hilsbrad".
|
||||
|
||||
- **zCraft — profession ranks**: zCraft now refreshes after learning a profession rank, without requiring a UI reload.
|
||||
|
||||
- **Tooltips — your pet's info showing on other players**: In a party, mousing over or targeting another player could wrongly show YOUR pet's details (mood, loyalty, happiness, XP) on their tooltip. The own-pet detection no longer misfires on other units.
|
||||
|
||||
- **zCraft — Smelting**: Smelting was previously not appearing in zCraft because incorrectly hardcoded as "Mining", this is fixed.
|
||||
|
||||
- **Wrong icons on pet spells**: Fixed the Rank 0 of Bubble Barrier and Charge that had incorrect icons.
|
||||
|
||||
- **Fixed tracking data in Book's Stables**: Fixed an issue with pets' GUID returned by the server, that was not really unique and caused issues with pets' tracking, notably when you had several pets with the same name. Taming info was disapearing, amongst other weirdness. A migration is done on first load to fix your existing data.
|
||||
|
||||
- **FeedOMatic — "Set Key" button error**: Clicking **Set Key** in Options → Feed-o-Matic threw an error and wouldn't let you bind a feed key. It now works again.
|
||||
|
||||
- **FeedOMatic — pet left hungry when bags were full**: When your bags were full, FeedOMatic could grab a random tiny stack (like a single meat your pet won't eat) to free a bag slot, so your pet stayed hungry. It now always picks food your pet can actually eat.
|
||||
|
||||
- **Tooltips — "Not learned yet" hint missing on pet abilities**: When you tamed a new beast, hovering one of its abilities on your pet bar no longer showed the red "Not learned yet, keep going !" reminder, even for abilities you hadn't learned. The hint is back and now correctly shows for any ability rank your hunter hasn't learned yet.
|
||||
|
||||
- **Pet training — new rank of a known ability not registered**: When you trained a higher rank of an ability you already had (e.g. learning Charge Rank 2 after Rank 1), MetaHunt treated it as already known — so the "Pet ability learned and recorded" message never appeared and the pet-bar tooltip kept saying "Not learned yet" for the new rank. Learning a new rank is now recognised on its own, announces correctly, and the tooltip immediately reflects the rank you just learned.
|
||||
|
||||
- **Hunter's Book — fixed "Unique" beasts**: Restored a curated list of the one-of-a-kind beasts (Humar the Pridelord, Darksaber, Shar'lan, Frostsaber Pride Watcher, Mazzranache, Moonfeather, Azurebeak, Old Cliff Jumper, Rotting Agam'ar, Razzashi Serpent, Magmalash Scorpid, Spirit Fox and more).
|
||||
|
||||
|
||||
|
||||
## [1.5.1] - 2026-04-01
|
||||
|
||||
|
||||
### Added
|
||||
|
||||
- **zAmmo — bag ammo labels**: Quality-colored short-name labels overlaid on ammo items in bags and bank, with optional heat-colored DPS damage indicator. Three new checkboxes General options: bag labels, bank labels, and damage display. Works with both default bags and pfUI bags.
|
||||
|
||||
- **MM Widget — immunity detection**: The MM Widget keybind now detects when the current target is immune to a damage school (Fire / Nature / Arcane) and automatically skips the useless shot, casting Aimed or Steady Shot instead. Immunity is detected in two ways: reactively via combat log "immune" messages, and proactively via hardcoded creature-type rules (e.g. Elementals are always flagged Nature-immune on target). Immune cells show a red border.
|
||||
|
||||
- **MM Widget — rotation toggles**: Three new checkboxes in Options → ExpAmmo → Rotation let you permanently disable individual shots (Multi-Shot, Serpent Sting, Arcane Shot) — unchecked shots are skipped during their proc window.
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Zone mapping — 22 beasts with raw zone IDs**: Fixed 8 unmapped zone IDs (Blackrock Depths, Molten Core, Wailing Caverns, Maraudon, Sunnyglade, Icepoint Rock, Winter Veil Vale, Gilneas City, Karazhan) that caused 22 beasts to display as "Zone xxx" instead of their proper zone name in the Hunter Book.
|
||||
|
||||
- **MM Widget — IsStateBlocked nil error**: Moved `MTH_EA_IsStateBlocked` definition before its first use, fixing a nil function call on load.
|
||||
|
||||
|
||||
## [1.5.0] - 2026-03-31
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- **Beast spawn coordinates — custom-zone remap**: Reworked 63 beast coordinate sets in the datastore. The largest changes move placeholder vanilla-zone coords onto the proper Turtle WoW custom zones for **Grim Reaches**, **Northwind**, and **Balor**, and clean up prairie beasts that were incorrectly spilling into **Thunder Bluff**.
|
||||
|
||||
- **Beast spawn coordinates — Stonetalon recalibration**: Recalibrated several **Stonetalon Mountains** beast spawn clusters to match the Twow updated zone bounds and improve marker placement consistency.
|
||||
|
||||
- **MM Widget — ClickThrough **: The whole widget is now clickable through, and ALT-drag to reposition it is thus no longer possible
|
||||
|
||||
- **MM Widget — clearer bottom-cell display**: The bottom cell now shows the cooldown of the current shot you would use, and only shows `USE` when that shot is ready.
|
||||
|
||||
- **Feed-O-Matic — NO-BUFF quarantine disabled**: The mechanism that permanently blacklisted a food item per pet family after a failed feed (no buff detected within 2.5 s) has been disabled. Foods are no longer quarantined on a no-buff timeout — the feed will simply retry without banning the item. This avoids false positives caused by server lag or client timeouts.
|
||||
|
||||
|
||||
### Added
|
||||
|
||||
- **zCraft — new zButton for crafting professions**: A new `zCraft` button (Options → zButtons → zCraft) scans your spellbook and shows a child button for each crafting profession you know — Alchemy, Blacksmithing, Cooking, Disenchanting, Enchanting, Engineering, Fishing, First Aid, Herbalism, Jewelcrafting, Leatherworking, Mining (Smelting), Skinning, Survival, and Tailoring. Clicking a child opens the corresponding tradeskill window. Disabled by default.
|
||||
|
||||
- **zBar — unified draggable toolbar for all zButtons**: A new "zBar" tab (Options → zButtons → zBar) groups all enabled zButton bars into a single draggable anchor. Alt-drag the zBar to move the whole group together. Options: Horizontal or Vertical layout; which side children pop out (Up/Down for horizontal, Left/Right for vertical); spacing between buttons; anchor icon size; per-bar include/exclude toggle and drag-to-reorder. Disabled by default — existing layouts are untouched until you enable it.
|
||||
|
||||
- **zAspect / zTrack — Smart Parent**: A new "Smart Parent" checkbox in Options → zButtons → zAspect and zTrack. When enabled, the parent button automatically toggles between your 1st and 2nd spell: if the 1st is active, the parent shows the 2nd, and vice-versa. Perfect for PvP — put Track Hidden and Track Humanoids as your first two tracking spells, and the parent always shows the one you're *not* currently using. When disabled, the parent always shows the 1st spell. Enabled by default.
|
||||
|
||||
- **zPet — Smart Parent**: A new "Smart Parent" checkbox in Options → zButtons → zPet. When enabled, the parent button dynamically changes based on your pet's state: Revive Pet if dead, Mend Pet if hurt, Feed Pet if unhappy, or your default spell otherwise. When disabled, the parent always shows the 1st spell. Enabled by default.
|
||||
|
||||
- **zMounts / zCompanions — Random Parent**: A new "Random Parent" checkbox in Options → zButtons → zMounts and zCompanions. When enabled, a random child mount is picked as the parent icon on each login and after each use. Disabled by default.
|
||||
|
||||
- **zMounts — AQ Mount Filter**: A new "AQ Mount Filter" checkbox in Options → zButtons → zMounts. When enabled, Qiraji mounts are hidden outside AQ40, and only Qiraji mounts are shown inside AQ40. Enabled by default.
|
||||
|
||||
- **zAspect — NamPower support**: The Aspect button now listens to `NamPower` aura events when available, so aspect changes update faster and without the old polling delay.
|
||||
|
||||
- **MM Widget — NamPower support**: The widget now uses `NamPower` aura updates when available, which makes ammo proc tracking faster and more reliable. It still falls back to the old tracking method if NamPower is not installed.
|
||||
|
||||
- **MM Widget — keybind **: The MM Widget options panel now has a "Set Key" button. Press any key (with optional ALT/CTRL/SHIFT modifiers), or mouse button/wheel to bind the MM Widget action, turning it into a One-button rotation feature.
|
||||
|
||||
- **MM Widget — Quiver no-clip option**: New "Use Quiver no-clip casting" checkbox in the MM Widget options. When enabled and Quiver is installed, Aimed Shot, Steady Shot, and Multi-Shot of the Action bind are routed through Quiver's no-clip system to avoid clipping your auto-shot swing timer.
|
||||
|
||||
- **MM Widget — Anchor **: Since the widget is now clickable-through, a new anchor system was implemented to move the widget: toggling the anchor button in the options shows 4 drag handles (top, bottom, left, right) outside the widget edge. Any handle can be dragged to reposition the widget.
|
||||
|
||||
- **Hunter Book — Families stats modifiers**: The Families page now also shows `Health`, `Damage`, and `Armor` modifiers for every family. Those modifier values can be shown as raw multipliers or as `%` with a footer checkbox, and are color-coded for easier reading. Additionaly the footer recap now also shows the total number of families, and the Named/Coord counts are plugged on the new accurate server's data instead of old pfQuest data.
|
||||
|
||||
- **FOM — keybind**: The Feed-O-Matic "Set Key" button in options now also accepts modifiers and mouse.
|
||||
|
||||
- **/mth food command**: Added a new slash command to inspect Feed-O-Matic state, clear one item ban, or reset all food bans and quarantines.
|
||||
|
||||
- **AntiDaze — NamPower support**: Daze detection now uses NamPower's aura events when available, replacing chat-message parsing for faster and more reliable aspect cancellation.
|
||||
|
||||
- **Chronometer — NamPower support**: Timer bars now start and stop instantly via NamPower aura events when available. Bar durations are read directly from the NamPower aura array instead of guessed from spell data.
|
||||
|
||||
- **Feed-O-Matic — NamPower support**: Mount and buff detection (Shadowmeld, Feign Death, eating, etc.) now uses the NamPower aura array when available, replacing the old tooltip-scraping method.
|
||||
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Pet lifecycle — duplicate / ghost pets in Stable**: Reworked pet identity with GUID-based matching and fixed stable tracking so the same pet is no longer split into multiple rows after tame, rename, stable swap, or `/reload`. Old stale pets should also stop reappearing in the Stable tab as fake active/stabled entries.
|
||||
|
||||
- **Pet lifecycle — wrong taming info shown on old pets**: Fixed several cases where MetaHunt could invent tame data for pets that were only seen later through stable swaps or normal pet refreshes. Taming info is now only recorded from a real tame event, so old pets should no longer show fake tame location / tame date data.
|
||||
|
||||
- **Hunter Book — Stable tab now trusts real stable slots**: The Stable tab now shows the current pet plus the pets that are actually in your stable slots, instead of trusting stale saved slot data from older sessions.
|
||||
|
||||
- **Hunter Book — pet training kept on the correct pet**: Fixed cases where a pet could keep its row but lose its Training section after lifecycle repairs or row merges. Training/spellbook data now stays attached to the right pet.
|
||||
|
||||
- **Hunter Book — stable feed item icons**: Fixed broken food icons in the Stable tab feed summary, including cases where foods like `Roasted Quail` were showing as a red square or question mark.
|
||||
|
||||
- **Minimap markers moving with the player since 1.4.0**: Reworked minimap marker zone resolution and calibration so pins stay anchored to world locations again instead of sliding with player movement. The minimap path now uses exact current-zone lookup in pfQuest space, while the world map keeps the WMA-aware logic introduced for 1.18.1 zone support.
|
||||
|
||||
- **Chronometer — "Grow bars upward" forgotten after `/reload`**: Checking the "Grow bars upward" option and reloading the UI would revert the bars back to growing downward, even though the checkbox still appeared checked. Fixed.
|
||||
|
||||
- **Chronometer — bars appear below the anchor instead of above when "Grow bars upward" is enabled**: Even with "Grow bars upward" checked, the bars were still spawning a bit below the anchor and stacking upward from there, because of an existing issue in old `CandyBar` library. Fixed — bars now correctly start at the anchor and grow upward.
|
||||
|
||||
- **Chronometer — Aimed Shot tracker removed**: Chronometer no longer tracks Aimed Shot cooldowns. The MM Widget now owns that behavior, so Chronometer keeps its hunter timers focused on the remaining spell and proc bars.
|
||||
|
||||
- **MM Widget — Quiver no-clip covers Multi-Shot**: The Quiver no-clip option now explicitly includes Multi-Shot alongside Aimed Shot and Steady Shot.
|
||||
|
||||
- **MM Widget — stale NamPower proc state**: The widget now resyncs from the NamPower aura array after aura events, so it no longer keeps showing a proc after the debuff is gone.
|
||||
|
||||
- **Quiver no-clip macro — Steady Shot castbar not showing**: If you were using Quiver and its no-clip macro for your shots, the Quiver castbar for those shots would not appear with MetaHunt enabled. Fixed — the castbar now appears correctly when using the macro.
|
||||
|
||||
|
||||
## [1.4.1] - 2026-03-24
|
||||
|
||||
### Added
|
||||
|
||||
- **zTrack**: added `Find Trees` in tracking spells.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Performance — MM Widget / ExpAmmo**: Replaced tooltip-based aura scanning (which triggered hidden tooltip rendering on every aura check) with direct texture/icon comparison via `UnitBuff`/`UnitDebuff`. Added a 150 ms throttle on `PLAYER_AURAS_CHANGED`, which fires 10–50+ times per second in combat. Lock and Load detection and ammo cycle recovery after `/reload` both updated to use the new zero-cost texture path.
|
||||
|
||||
- **Performance — event frame leaks**: Some addon features were registering WoW game events at load time and never unregistering them when the feature was disabled from the options, causing their `OnEvent` handlers to fire even with the option turned off. Fixed for: Auto-Strip (`PLAYER_REGEN_*`), Smart Ammo (`SPELLCAST_*`, `START/STOP_AUTOREPEAT_SPELL`), and some zButton frames (Pet, Mounts, Track, Trap, Toys). Events are now properly registered on enable and unregistered on disable.
|
||||
|
||||
- **Chronometer — trap effects from other hunters**: Trap effect bars (`Immolation Trap Effect`, `Explosive Trap Effect`, `Freezing Trap Effect`, `Frost Trap Aura`) were incorrectly appearing when another hunter's trap triggered on a mob you were also targeting. .
|
||||
|
||||
- **Version check broadcast interval**: Increased update notification broadcast cooldown from 10 minutes to 1 hour to reduce channel traffic now that the user base has grown.
|
||||
|
||||
- **Profile snapshot memory**: The auto `BuildSnapshot` (called on every `/reload` via `PLAYER_LOGOUT`) was deep-copying `feedomatic.legacy`, a large migration table mirroring `FOM_Cooking`, `FOM_QuestFood`, `FOM_AddedFoods` etc. These are runtime/operational blobs, not user configuration settings, and should never be included in a profile. Introduced `MTH_Profile_CopyModules` which skips `legacy` and `history` keys, reducing the snapshot allocation from ~1 MB to a few KB.
|
||||
|
||||
|
||||
|
||||
## [1.4.0] - 2026-03-23
|
||||
|
||||
### TWoW 1.18.1 "Nightmares of Ursol" support
|
||||
|
||||
### Added
|
||||
|
||||
- **Complete beast database overhaul**: The MTH beast datastore was reconstructed from scratch, based on a full (beasts) 1.18.1 extract from live DB, gladly provided by Twow staff (thanks @Haaxor!). The beast data should now be 100% accurate, and notably all spawn coordinates, respawn times and pet abilities learnable on all beasts in the world.
|
||||
|
||||
- The **Beast Lore** page of the Book now allows to filter the list by Zone. Additionally, the beast inspector has been reformatted and all meaningful beast's data is displayed nicely.
|
||||
|
||||
- All new and missing **Beasts Models** have been (re)scanned from MPQ game files and are viewable in the Beast Lore page, and browsable in the Family Model viewer.
|
||||
|
||||
- Added **MM widget** module: tracks the new 3-state of Experimental ammo cycle (Fire → Nature → Arcane), the Lock and Load procs, and has a dynamic cell that shows either Aimed Shot availability or the right shot to use following the current Experimental ammo proc if any. Lets Make MM great again.
|
||||
|
||||
- Added new **Moth** pet family.
|
||||
|
||||
- Added new **Pollen Burst** pet ability.
|
||||
|
||||
- Added new **Aspect of the Viper** in zAspect.
|
||||
|
||||
- Added new **Chronometer** timer entries for 1.18.1 spells:
|
||||
- Aimed Shot cooldown tracker
|
||||
- Kill Command cooldown tracker
|
||||
- Lock and Load proc timer
|
||||
- Scent of Blood proc timer
|
||||
|
||||
- Added new **Chronometer** target debuff entries for the Experimental Ammo states:
|
||||
- Poisonous Ammunition: 15s armor-reduction debuff (triggered when Serpent Sting consumes the Poisonous state)
|
||||
- Enchanted Ammunition: 6s magic-resist debuff (triggered when Arcane Shot consumes the Enchanted state)
|
||||
|
||||
- Added the **5 new craftable ammo** to the Projectiles book's page, SmartAmmo and zAmmo button:
|
||||
- **Bright Wood Arrows** (Survival: Journeyman, req. 30) — 10.5 DPS
|
||||
- **Shade Wood Arrows** (Survival: Expert, req. 40) — 14.5 DPS
|
||||
- **Smooth Ironfeather Arrows** (Survival: Artisan, req. 50) — 17.5 DPS
|
||||
- **Starfeather Arrows** (Survival: Artisan, req. 55) — 19 DPS
|
||||
- **Enchanted Thorium Shells** (Engineering: Artisan, req. 55) — 19 DPS
|
||||
|
||||
- Added new hunter-friends NPC of **Moonwhisper Coast** to the NPC Finder Book's page:
|
||||
- **Hula Swiftmane** — Horde Stable Master (66.1, 39.1)
|
||||
- **Ornala** — Horde Ammo Vendor (67.3, 37.1) — sells Arrows and Slugs
|
||||
- **Badel Wildlance** — Alliance Stable Master (59.0, 24.9)
|
||||
- **Elendon Truebough** — Alliance Ammo Vendor (58.8, 24.5) — sells Arrows
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed **Chronometer** bars appearing from nearby players' combat events (e.g. another hunter's traps or DoTs triggering your own bars).
|
||||
- Fixed the list in Families Book's page, that wasn't tall enough to display all families (wolves were hidden).
|
||||
|
||||
## [1.3.0] - 2026-03-19
|
||||
|
||||
|
||||
### Added
|
||||
- Added a **3D Model Viewer** to the Hunter Book Beast Lore inspector panel. Clicking a beast in the list shows its in-game model.
|
||||
- Added a standalone **3D Model Viewer** that is shown when cliking the model of a beast in the Beast lore. The model is rotatable with the mouse, and you can browse all models of the same pet family and shows all other beasts that share the same skin, with one-click to jump to any of them in the Beast Lore.
|
||||
- Added a **Family icon** column to the Beast Lore list, replacing the text family name with the pet family icon for a more compact view.
|
||||
- Added an **Attack Speed (AS)** column to the Beast Lore list.
|
||||
- Added `displayId` and `skinId` fields to **all ~1000 beasts** in the MetaHunt datastore. `displayId` was scraped from the Turtle WoW DB, and `skinId` derived from it following an extraction on MPQ game files. This is what powers the model viewer and skin-grouping.
|
||||
- Added per-family icons to all 19 pet families in the family data table.
|
||||
- Added missing food types (`raw meat`, `raw fish`) to all pet families that accept `meat` or `fish` respectively (sourced from MPQ data).
|
||||
- Added **Profile Management** panel (Options → Profiles): save the current addon configuration as a named profile and instantly load it on any character. Profiles cover all module settings (ICU, Chronometer, AutoBuy, AutoQuest, SmartAmmo, Feed-O-Matic, zButtons layout) and per-character module enabled/disabled states.
|
||||
- Added automatic per-character **config snapshots** saved on every logout (`CharName-RealmName`), visible in the "Copy from Character" section — import any character's snapshot as a loadable profile in one click.
|
||||
|
||||
|
||||
### Fixed
|
||||
- Fixed the pet feeding counter never incrementing: `MTH_FEED_Trace` was a no-op stub and `FOM_CORE_ATTEMPT_TRACKING_ENABLED` was `false`; both are now active.
|
||||
- Fixed `FOM_AddFood` not clearing a food from `FOM_RemovedFoods` when the item belongs to the base diet list, which permanently banned it even after an explicit `/fom add`.
|
||||
- Wired `FOM_ClearItemBans` into the `/fom add` command path: explicitly re-adding a food now clears it from `FOM_RemovedFoods`, FOM quarantine, and core exceptions in one step.
|
||||
- Removed debug messages forgotten when releasing 1.2.
|
||||
- Fixed SmartAmmo + **Quiver castbar conflict**: Quiver's castbar was disappearing when both addons were active. Root cause: MetaHunt SmartAmmo was re-installing its hooks every 0.5 seconds, constantly bumping Quiver off the top of the hook chain. Fixed by removing the repeated re-hook and adding a guard that only installs once (or when no other addon is on top).
|
||||
|
||||
|
||||
## [1.2.0] - 2026-03-10
|
||||
|
||||
### Added
|
||||
- Added keybinding text overlay on zButtons: parent and child buttons now display their bound key for the 5 bindable sets (Aspect, Track, Trap, Ammo, Pet).
|
||||
- Added "Expand on Hover" option for zButton children: when enabled, hovering over the parent button automatically expands children (instead of requiring right-click).
|
||||
- Added "Auto-Hide Timer" option for zButton children: a configurable 0–10 second timer that automatically collapses expanded children after the mouse leaves the button group. Timer resets while hovering any button in the group.
|
||||
|
||||
### Fixed
|
||||
- Fixed SavedVariables data loss on login caused by initialization race condition overwriting persisted data.
|
||||
- Fixed SavedVariables bloat by adding a migration that strips redundant default-valued keys on first load.
|
||||
- Fixed six performance issues including throttling high-frequency event handlers, caching repeated `getglobal` lookups, and reducing unnecessary table allocations in hot paths.
|
||||
- Cleaned up ICU module global variable leaks by properly scoping locals.
|
||||
- Standardized all remaining `SetScript` handler patterns to use the WoW 1.12 `this`-based convention consistently.
|
||||
|
||||
|
||||
## [1.1.0] - 2026-03-09
|
||||
|
||||
### Added
|
||||
- Added new `ICU` module with dedicated options panel.
|
||||
- Added ICU custom popup anchor mode with draggable anchor and direction toggle.
|
||||
- Added ICU popup hide delay options (`INSTANT`, 1..10 seconds).
|
||||
- Added new `Auto Quest` module for :
|
||||
- `Salt of the Scorpok` (`Bloodmage Drazial`)
|
||||
- `Arrows Are For Sissies` (`Artilleryman Sheldonore`)
|
||||
- Added a Food Feed learning feature to Feed-O-Matic.
|
||||
- Added Chronometer `Entrapment` effect tracking support.
|
||||
|
||||
### Changed
|
||||
- Loads of Frame/Handlers/Processes adjustments to optimize addon's CPU time and Memory usage.
|
||||
- Updated Chronometer Improved Wing Clip effect color to `MAROON` for clearer effect-bar distinction.
|
||||
- Reworked Stable Master processing to minimize swap-time overhead:
|
||||
- No auto-scan on `PET_STABLE_SHOW`.
|
||||
- Deferred stable scan moved to `PET_STABLE_CLOSED`.
|
||||
- Optional heavy workloads are skipped while stable UI is open.
|
||||
- Changed Hunter Book page order, and default opening page to `Beast Lore`.
|
||||
- Optimized `Pet History` result build/sort path using cached precomputed sort keys.
|
||||
- Modules Auto-Buy, Auto-Quest, ICU, Feed-O-Matic are by default disabled on first use on a character.
|
||||
|
||||
### Fixed
|
||||
- Fixed issues with zBouttons,Autostrp and Chronometer bar anchor not retaining their configured spawn position after the disabling/renabling of the Addon.
|
||||
- Fixed issues with options checkbox-state not always retaining their state on startup.
|
||||
- Fixed multiple issues in Feeed-O-Matic with some foods that were accepted by lvl 60 pets in Vanilla and are not anymore on Turtle.
|
||||
- Fixed Chronometer spell/event disable toggles in options so disabled entries now reliably block bar creation.
|
||||
- Fixed Chronometer `Feed Pet` bar missing trigger by adding `UNIT_AURA` fallback detection.
|
||||
- Fixed stable swap stutter/freezes by removing heavy processing from repeated `UNIT_PET` bursts during stable interactions.
|
||||
|
||||
|
||||
## [1.0.6] - 2026-03-05
|
||||
|
||||
### Fixed
|
||||
- Added missing abilities to all pet families.
|
||||
- Rescraped from Twow DB the previously in correct `Roar of Fortitude` pet-ability.
|
||||
- Fixed an issue with Tooltip on pet action bar abilities, that were falsely displayed as not learned yet for rankless abilities.
|
||||
- Restored visible Stable Master auto-scan feedback (`Stable scan complete: X slot(s).`) when opening the stable window.
|
||||
- Fixed stable scan icon persistence for active pets that were never stabled by capturing current-pet icon data during stable scans.
|
||||
- Hardened stable-scan bootstrap to initialize on both `PET_STABLE_SHOW` and `PET_STABLE_UPDATE` event paths.
|
||||
- Fixed issues with taming pet metadata that were not properly recorded since the last update.
|
||||
- Fixed issues with pet runaway interception that was broken since the last update.
|
||||
- Updated version-update notification text to include clearer upgrade guidance and plain GitHub URL text for updates.
|
||||
- Fixed an issue with `zTrack` buttons spells being incorrect after having respec the talents using brainwashing device.
|
||||
|
||||
|
||||
## [1.0.5] - 2026-03-01
|
||||
|
||||
### Fixed
|
||||
- Multiple changes to the addon to reduce its memory and CPU time footprint to the strict minimum possible.
|
||||
- Restored FeedOMatic pet hunger notifications so hungry/very hungry warnings are emitted reliably again when enabled.
|
||||
- Fixed Chronometer `Wing Clip` tracking by adding a safe fallback timer path for the missing trigger case.
|
||||
- Fixed Chronometer `Quick Shots` bar icon by mapping the event to the correct icon texture.
|
||||
- Fixed module enabled/disabled persistence to be truly per-character (`MTH_CharSavedVariables.moduleStates`) instead of account-shared.
|
||||
- Fixed AutoBuy persistence fallback by removing undeclared `MTH_AutoBuy_Saved` paths and using module stores/transient runtime fallback only.
|
||||
- Consolidated FeedOMatic legacy persistence to a single module-backed store (`modules.feedomatic.legacy`) with globals bound to that source.
|
||||
- Switched Chronometer profile persistence to per-character module storage with one-time migration from existing account profile data.
|
||||
- Switched Tooltips module options persistence to per-character module storage with one-time migration from existing account settings.
|
||||
|
||||
## [1.0.4] - 2026-02-27
|
||||
|
||||
### Added
|
||||
- Added new zButton bar `zRanged` that tracks all ranged weapons in bags and allows to quickly swap them.
|
||||
- Added structured localization architecture with dedicated locale packs under `locales/` and loader wiring through `init/localization.xml`.
|
||||
- Added a dedicated `Messages` options panel with per-message toggles.
|
||||
- Added Smart Ammo option `Enable Weapon-Swap Auto Ammo` to instantly equip best bullets/arrows when switching ranged weapon type.
|
||||
- Added two new Hunter Book pages in the main tab bar: `Projectiles` and `Ammo Bags`
|
||||
|
||||
### Changed
|
||||
- Reworked core localization runtime to support explicit locale fallback rules (`enUS`, `deDE`, `esES`, `ptBR`, `ruRU`, `zhCN`; no `frFR` target).
|
||||
- Updated Hunter Book NPC finder and map vendor markers to use localized NPC names by NPC ID.
|
||||
- Updated Hunter Book, item search/sort, AutoBuy item labels, and drop map markers to use localized item names by item ID.
|
||||
|
||||
|
||||
### Fixed
|
||||
- Fixed version metadata consistency so all first-party module descriptors report the same release version as the main addon.
|
||||
- Fixed SmartAmmo junk-shot detection by including `Baited Shot` and `Tranquilizing Shot` in `MTH_AMMO_JUNKSHOT_SET`.
|
||||
- Fixed zButtons options side effect where opening Mounts/Companions/Toys options could implicitly enable those buttons.
|
||||
- Normalized zButtons checkbox defaults (`enabled`, `tooltip`, `hideonclick`, `parent.hide`, `parent.circle`, `showammoname`) so toggle states are always explicit and never nil.
|
||||
- Fixed Beast Lore ability dropdown to exclude trainer-only abilities and list beast-learned abilities only.
|
||||
- Removed junk `TBD` ability value from Wind Serpent dataset entry (`Venomflayer Serpent`) so it no longer appears in Beast Lore filters.
|
||||
- Fixed zhunter child-button visibility restore so collapsed bars no longer hard-hide children (`ztrack` / `ztrap` expansion regression).
|
||||
- Fixed pet tame metadata fallback so existing pets are not assigned current zone/time unless a real tame attempt is pending.
|
||||
- Fixed Smart Ammo options copy mismatch by clarifying junk-shot swap behavior and restoring ammo target as previous equipped ammo.
|
||||
- Fixed Smart Ammo default state so junk-shot swaps are enabled when no saved value exists.
|
||||
- Fixed Beast Training startup prompt behavior so low-level hunters no longer receive training-scan warnings before level threshold.
|
||||
- Restored hidden `/mth err` slash command behavior for debug-frame access.
|
||||
- Fixed `zammo` children refresh on bag/inventory updates so newly acquired ammo types appear without `/reload`.
|
||||
- Preserved `zammo` out-of-stock behavior so previously shown ammo entries still remain visible with zero/out state.
|
||||
- Fixed intermittent `ztrack`/`ztrap` right-click expand/collapse issue after fresh login by preventing duplicate child-frame recreation during startup.
|
||||
- Fixed `zpet` spell resolution after trainer updates by using robust spellbook lookup and immediate refresh on `LEARNED_SPELL_IN_TAB`.
|
||||
|
||||
|
||||
## [1.0.3] - 2026-02-24
|
||||
|
||||
### Added
|
||||
- Initial published release.
|
||||
@@ -2,6 +2,15 @@
|
||||
-- MetaHunt: Hunter's Toolkit Addon
|
||||
-- A unified addon merging FeedOMatic, Tooltips, and zhunter
|
||||
-- Modern modular architecture with legacy addon support
|
||||
--
|
||||
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
-- TARGET RUNTIME: LUA 5.0 (WoW 1.12 / Twow 1.18)
|
||||
-- - Max 32 upvalues per function
|
||||
-- - No # operator (use table.getn)
|
||||
-- - No string.gmatch (use string.gfind)
|
||||
-- - NEVER validate with Lua 5.1+ tools (false passes)
|
||||
-- - Errors: check Logs/FrameXML.log first
|
||||
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
------------------------------------------------------
|
||||
|
||||
-- Initialize beast database after all files loaded
|
||||
|
||||
+13
-11
@@ -1,40 +1,42 @@
|
||||
## Interface: 11200
|
||||
## Version: 1.0.3
|
||||
## Version: 2.0.0
|
||||
## 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
|
||||
## Author: Metasploit and his Copilot ;)
|
||||
## Notes: Unified addon suite for huntards making old addons compatible with Twow, and adding an arsenal of never seen Hunter's utilities.
|
||||
## SavedVariables: MTH_SavedVariables
|
||||
## SavedVariablesPerCharacter: 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-origins.lua
|
||||
data\ds-bags.lua
|
||||
data\ds-items-atlas-sources.lua
|
||||
data\ds-zones.lua
|
||||
data\ds-zones-fallback.lua
|
||||
data\ds-zones-wma.lua
|
||||
data\ds-minimap-sizes.lua
|
||||
data\ds-unit-coords.lua
|
||||
data\ds-creature-coords.lua
|
||||
data\ds-beasts-families.lua
|
||||
data\ds-beasts-families-dbc.lua
|
||||
data\ds-beasts-skins.lua
|
||||
data\ds-pet-spells.lua
|
||||
data\ds-pet-spells-trainer.lua
|
||||
data\ds-hunter-spells.lua
|
||||
data\ds-hunter-spells-dbc.lua
|
||||
data\ds-trainer-catalog.lua
|
||||
data\ds-racial.lua
|
||||
data\init.lua
|
||||
|
||||
|
||||
@@ -1,52 +1,114 @@
|
||||
# MetaHunt
|
||||
|
||||
MetaHunt is a Turtle WoW Hunter toolkit.
|
||||
It combines never seen pet-centric features and revives old broken hunter add-ons.
|
||||
MetaHunt is a Twow Hunter toolkit.
|
||||
|
||||
It is a modern modular addon compiling brand new features, and improving a few old hunter add-ons that were broken or disfunctional with twow.
|
||||
|
||||
It provides a bunch of useful functionalities for all huntards, wheter they are still levelling, lone-wolf, or HL raider.
|
||||
You play the best class, now you have the best companion for it (and its not your pet).
|
||||
|
||||
### Important notes
|
||||
|
||||
- Altough Nampower isn't mandatory for most of the features, it is a must-have for some, and certainly the MM Widget.
|
||||
- If you were using old versions of Feed-O-Matic, ICU, zHunterMod and HunterHelper, you dont need those with MetaHunt and you SHOULD disable them to avoid conflicts.
|
||||
- MetaHunt is fully compatible with QUIVER <3, and is not a replacement for it.
|
||||
- The addon purposely disable itself on all Capycraft/Ravencraft Realms, following the DDOS attacks they conducted on other servers.
|
||||
|
||||
## Twow Data
|
||||
|
||||
Metahunt ships with larges up-to-date datastores for all data a hunter needs :
|
||||
MetaHunt ships with large twow datastores, and notably:
|
||||
|
||||
- All beasts, their location, and the abilities they can learn you
|
||||
- 100% accurate beasts' data, their locations, 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 hunter and pet spells
|
||||
- All Ammo and ranged weapons
|
||||
- All Hunter/Pet/Stable Masters
|
||||
- All Ammo vendors
|
||||
|
||||
Online bestiary is also available here https://duvelcorp.codeberg.page/MetaHunt-Web/
|
||||
|
||||
|
||||
## 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.
|
||||
- Auto swap your main ammo to junk ammo for shots that doesn't scale on damage, and re-equip you main ammo right away. Don't waste your Doomshots on nothing anymore, save money !
|
||||
- Auto equip another ammo instantly during combat whenever you get out of your current ammo.
|
||||
- Auto equip the right ammo when you swap from a box/xbow to a gun and the other way around.
|
||||
|
||||
# Tooltips
|
||||
## MM Widget
|
||||
|
||||
1.18.1 weapon of mass-destruction. NAMPOWER MANDATORY. The widget is composed of 5 cells, tracking the 3-state of Experimental ammo cycle (Fire → Nature → Arcane), the Lock & Load procs, and a smart dynamic cell. The smart cell shows either Aimed Shot availability or the right shot to use following the current Experimental ammo proc if any, and it bypasses the proc spell if your current target is immune to the school. You can bind the smart cell to turn the widget into a very efficient one-button rotation. Lets make MM great again !
|
||||
|
||||
## Tooltips
|
||||
|
||||
Get enhanced tooltips on beasts, ammo vendors, and your pet.
|
||||
Get enhanced mouseover tooltips on :
|
||||
- Beasts : Shows which abilities a beast can teach. They appear green if you already know them, and red if not.
|
||||
- Vendors : Shows if a vendor sells Arrows and/or Bullets.
|
||||
- Your pet : Shows your pet's XP, loyalty, happiness and how much you fed it this session.
|
||||
|
||||
# zBouttons
|
||||
## zButtons
|
||||
|
||||
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
|
||||
- zPet : Intelligent collapsible pet Bar regrouping all pet spells and providing a main button that always swap on 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.
|
||||
- zAspect: All your aspects in one collapsible bar
|
||||
- zRanged : All the ranged weapons currently in your bags to swap them fast
|
||||
- But also zCrafts, 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.
|
||||
Additionally, a special _zBar_ can regroup all the zButtons above into a single bar for people that like clean and ordered stuff.
|
||||
|
||||
# Chronometer
|
||||
## Feed-O-Matic
|
||||
|
||||
The good old Chronometer addon, purged of non-necessary things and reconfigured for a pure hunter use.
|
||||
Now compatible with Twow, feed your pet with one key bind, it will always pickup the best food for your pet, according to your preferences.
|
||||
|
||||
# Autobuy
|
||||
## Chronometer
|
||||
|
||||
Define your own simple rules for auto buy ammo and pet food.
|
||||
The good old Chronometer addon, purged of non-necessary things and reconfigured for a pure hunter usage.
|
||||
|
||||
# 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
|
||||
## ICU (I see you)
|
||||
|
||||
This old addon is mostly a PVP weapon used to display targets' enhanced information when you click on your minimap tracked things. MetaHunt offers many more customization options for it.
|
||||
|
||||
## Ammo labels
|
||||
|
||||
You can display some labels on all ammo in your bags/bank to instantly see which of those damn same arrows icons really are.
|
||||
|
||||
## Auto-Buy
|
||||
|
||||
Define your own simple rules for auto buy ammo and pet food when you open a vendor. You can also make a food-auto-buy planning based on all your stabled pet's diets.
|
||||
|
||||
## Auto-Quest
|
||||
|
||||
Just spam SHIFT-click on the NPC to validate quests Scorpok and Thorium arrows. Additionally you can have an enhanced tooltip that display your actual Scorpok-related items when you mouseover the Blasted Land mobs involved.
|
||||
|
||||
## Efficient Scanners
|
||||
|
||||
The add-on smartly scan your character and pet tabs, spellbook, beast training, stable and learning masters windows. This is done automatically and fast in the background, and allows to enrich the static datastore with YOUR own data, and to provide meaningfull dynamic information following the context. It also scan your Pet's state constantly, but with minimal ressources.
|
||||
|
||||
## Animations
|
||||
|
||||
Metahunt is the first add-on that uses the great PizzaSauce library of [Pizzahawaii](https://codeberg.org/Pizzahawaii/). This adds fun and makes the interface feel alive.
|
||||
|
||||
## The Great Book of Huntards
|
||||
|
||||
The Book is the main GUI of MetaHunt, it allows to browse the addon's data to find anything a Hunter needs.
|
||||
|
||||
- Search Hunter-related NPCs (Beasts, vendors, masters) and open the map to locate them like in pfQuest.
|
||||
- Beast model Viewer : Display the model of all beasts in the world. You can also browse all skin models of the same family to find the best-looking pet according to your tastes.
|
||||
- Instantly find where the beasts having a specific ability are lying and plan ahead your tracking sessions.
|
||||
- Get a full understanding of all Pet families.
|
||||
- Display meaningfully the pet abilities that you know and don't know yet
|
||||
- Get your stable's state everywhere and track all possible information about all your pets
|
||||
- Keep a detailed history of all the pets you abandonned or those that ran away.
|
||||
- Browse all twow 1.18.1 hunter and pet spells. Get the status of what you already learned or not, and a detailed cost planning.
|
||||
|
||||
## Tune it the way you like
|
||||
|
||||
Metahunt option panel is huge. There are hundreds of settings you can configure in order to tweak it like a boss so it fits YOUR preferences.
|
||||
Its modular architecture also allows you to completely disable any module in order to decrease the addon's memory footprint and background processes.
|
||||
|
||||
|
||||
### The world is yours. Happy Hunting Fellas !
|
||||
|
||||
|
||||
+65
-10
@@ -10,10 +10,23 @@ local function AntiDaze_GetSaved()
|
||||
if type(MTH_CharSavedVariables) ~= "table" then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.antiDaze) ~= "table" then
|
||||
MTH_CharSavedVariables.antiDaze = {}
|
||||
local store
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
store = MTH:GetModuleCharSavedVariables("antidaze")
|
||||
end
|
||||
return MTH_CharSavedVariables.antiDaze
|
||||
if type(store) ~= "table" then
|
||||
if type(MTH_CharSavedVariables.modules) ~= "table" then
|
||||
MTH_CharSavedVariables.modules = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.modules.antidaze) ~= "table" then
|
||||
MTH_CharSavedVariables.modules.antidaze = {}
|
||||
end
|
||||
store = MTH_CharSavedVariables.modules.antidaze
|
||||
end
|
||||
if type(store.settings) ~= "table" then
|
||||
store.settings = {}
|
||||
end
|
||||
return store.settings
|
||||
end
|
||||
|
||||
local function AntiDaze_IsEnabled()
|
||||
@@ -25,16 +38,21 @@ 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")
|
||||
if MTH and MTH.nampower then
|
||||
AntiDazeFrame:RegisterEvent("DEBUFF_ADDED_SELF")
|
||||
else
|
||||
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
|
||||
end
|
||||
|
||||
local function AntiDaze_UnregisterCombatEvents()
|
||||
if not AntiDazeFrame then
|
||||
return
|
||||
end
|
||||
AntiDazeFrame:UnregisterEvent("DEBUFF_ADDED_SELF")
|
||||
AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE")
|
||||
AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE")
|
||||
AntiDazeFrame:UnregisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
|
||||
@@ -87,10 +105,31 @@ local function AntiDaze_ResolvePlayerFromMessage(msg)
|
||||
return nil
|
||||
end
|
||||
|
||||
local AntiDaze_DazeSpellIdCache = {} -- [spellId] = true/false
|
||||
|
||||
local function AntiDaze_IsDazeSpellId(spellId)
|
||||
local cached = AntiDaze_DazeSpellIdCache[spellId]
|
||||
if cached ~= nil then return cached end
|
||||
if type(GetSpellRecField) ~= "function" then return false end
|
||||
local name = GetSpellRecField(spellId, "name")
|
||||
if name and (name == "Daze" or name == "Dazed") then
|
||||
AntiDaze_DazeSpellIdCache[spellId] = true
|
||||
return true
|
||||
end
|
||||
AntiDaze_DazeSpellIdCache[spellId] = false
|
||||
return false
|
||||
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"
|
||||
|
||||
-- Always use GetPlayerBuff + tooltip scan for cancellation.
|
||||
-- The aura-array slot index from GetUnitField does NOT map 1:1
|
||||
-- to the sequential buff index that CancelPlayerBuff expects
|
||||
-- (empty slots create gaps), so we cannot use the NamPower
|
||||
-- array for the cancel call. NamPower is still used for daze
|
||||
-- DETECTION via DEBUFF_ADDED_SELF in the event handler.
|
||||
for i = 0, 32 do
|
||||
if GetPlayerBuff(i, "HELPFUL") < 0 then
|
||||
break
|
||||
@@ -98,7 +137,7 @@ local function AntiDaze_CancelRelevantAspect(isSelfDazed)
|
||||
|
||||
AntiDazeTooltip:SetOwner(UIParent, "ANCHOR_NONE")
|
||||
AntiDazeTooltip:SetPlayerBuff(i, "HELPFUL")
|
||||
local text = AntiDazeTooltipTextLeft1 and AntiDazeTooltipTextLeft1:GetText()
|
||||
local text = MTH_AntiDazeProbeTextLeft1 and MTH_AntiDazeProbeTextLeft1:GetText()
|
||||
if text == packName or (isSelfDazed and text == cheetahName) then
|
||||
CancelPlayerBuff(i)
|
||||
return 1
|
||||
@@ -112,12 +151,18 @@ local function AntiDaze_HandleSlash()
|
||||
AntiDaze_SetEnabled(not AntiDaze_IsEnabled())
|
||||
end
|
||||
|
||||
AntiDazeTooltip = CreateFrame("GameTooltip", "AntiDazeTooltip", nil, "GameTooltipTemplate")
|
||||
AntiDazeFrame = CreateFrame("Frame", "AntiDazeFrame")
|
||||
AntiDazeTooltip = CreateFrame("GameTooltip", "MTH_AntiDazeProbe", nil, "GameTooltipTemplate")
|
||||
AntiDazeFrame = CreateFrame("Frame", "MTH_AntiDazeEvent")
|
||||
if AntiDazeFrame then
|
||||
AntiDazeFrame:RegisterEvent("VARIABLES_LOADED")
|
||||
AntiDazeFrame:SetScript("OnEvent", function()
|
||||
if event == "VARIABLES_LOADED" then
|
||||
-- Class gate: only hunters use AntiDaze.
|
||||
if MTH and MTH.IsClassGateBlocked and MTH:IsClassGateBlocked() then
|
||||
AntiDazeFrame:UnregisterAllEvents()
|
||||
AntiDazeFrame:SetScript("OnEvent", nil)
|
||||
return
|
||||
end
|
||||
if AntiDaze_IsEnabled() then
|
||||
AntiDaze_RegisterCombatEvents()
|
||||
else
|
||||
@@ -126,6 +171,16 @@ if AntiDazeFrame then
|
||||
return
|
||||
end
|
||||
|
||||
-- NamPower path: DEBUFF_ADDED_SELF fires with spellId in arg3
|
||||
if event == "DEBUFF_ADDED_SELF" then
|
||||
local spellId = tonumber(arg3)
|
||||
if spellId and AntiDaze_IsDazeSpellId(spellId) then
|
||||
AntiDaze_CancelRelevantAspect(true)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Legacy path: parse chat messages
|
||||
local player = AntiDaze_ResolvePlayerFromMessage(arg1)
|
||||
if player then
|
||||
local you = _G["ZHUNTER_YOU"] or "You"
|
||||
|
||||
+186
-6
@@ -6,20 +6,137 @@ local function AutoStrip_Print(msg)
|
||||
end
|
||||
end
|
||||
|
||||
local AutoStrip_TraceEnabled = false
|
||||
|
||||
local function AutoStrip_Trace(msg)
|
||||
if not AutoStrip_TraceEnabled then
|
||||
return
|
||||
end
|
||||
AutoStrip_Print("[ASTRACE] " .. tostring(msg))
|
||||
end
|
||||
|
||||
function AutoStrip_CommandTrace(mode)
|
||||
local action = string.lower(tostring(mode or "status"))
|
||||
if action == "on" or action == "1" or action == "true" then
|
||||
AutoStrip_TraceEnabled = true
|
||||
AutoStrip_Trace("trace enabled")
|
||||
return
|
||||
elseif action == "off" or action == "0" or action == "false" then
|
||||
AutoStrip_Trace("trace disabled")
|
||||
AutoStrip_TraceEnabled = false
|
||||
return
|
||||
end
|
||||
AutoStrip_Print("[ASTRACE] status=" .. tostring(AutoStrip_TraceEnabled and true or false))
|
||||
end
|
||||
|
||||
function AutoStrip_GetSaved()
|
||||
if type(MTH_CharSavedVariables) ~= "table" then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.autoStrip) ~= "table" then
|
||||
MTH_CharSavedVariables.autoStrip = {}
|
||||
local store
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
store = MTH:GetModuleCharSavedVariables("autostrip")
|
||||
end
|
||||
return MTH_CharSavedVariables.autoStrip
|
||||
if type(store) ~= "table" then
|
||||
if type(MTH_CharSavedVariables.modules) ~= "table" then
|
||||
MTH_CharSavedVariables.modules = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.modules.autostrip) ~= "table" then
|
||||
MTH_CharSavedVariables.modules.autostrip = {}
|
||||
end
|
||||
store = MTH_CharSavedVariables.modules.autostrip
|
||||
end
|
||||
return store
|
||||
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
|
||||
local AutoStrip_RestoreFrame = nil
|
||||
|
||||
local function AutoStrip_DescribePoint(frame)
|
||||
if not (frame and frame.GetPoint) then
|
||||
return "<no-frame>"
|
||||
end
|
||||
local point, relTo, relPoint, x, y = frame:GetPoint()
|
||||
local relName = "nil"
|
||||
if type(relTo) == "table" and relTo.GetName then
|
||||
relName = tostring(relTo:GetName() or "<anon>")
|
||||
elseif relTo then
|
||||
relName = tostring(relTo)
|
||||
end
|
||||
return tostring(point or "nil")
|
||||
.. "->" .. relName
|
||||
.. ":" .. tostring(relPoint or "nil")
|
||||
.. " @(" .. tostring(x) .. "," .. tostring(y) .. ")"
|
||||
end
|
||||
|
||||
function AutoStrip_SaveDisplayPosition(reason)
|
||||
if not (AutoStripDisplay and AutoStripDisplay.GetPoint) then
|
||||
return
|
||||
end
|
||||
local saved = AutoStrip_GetSaved()
|
||||
local point, _, relPoint, x, y = AutoStripDisplay:GetPoint()
|
||||
x = tonumber(x)
|
||||
y = tonumber(y)
|
||||
if not (point and relPoint and x and y) then
|
||||
return
|
||||
end
|
||||
saved["point"] = tostring(point)
|
||||
saved["relativePoint"] = tostring(relPoint)
|
||||
saved["x"] = math.floor(x + 0.5)
|
||||
saved["y"] = math.floor(y + 0.5)
|
||||
AutoStrip_Trace("persist-display-position reason=" .. tostring(reason or "")
|
||||
.. " point=" .. tostring(saved["point"])
|
||||
.. " rel=" .. tostring(saved["relativePoint"])
|
||||
.. " x=" .. tostring(saved["x"])
|
||||
.. " y=" .. tostring(saved["y"]))
|
||||
end
|
||||
|
||||
local function AutoStrip_RestoreDisplayPosition(reason)
|
||||
if not (AutoStripDisplay and AutoStripDisplay.ClearAllPoints and AutoStripDisplay.SetPoint) then
|
||||
return false
|
||||
end
|
||||
local saved = AutoStrip_GetSaved()
|
||||
local point = tostring(saved["point"] or "")
|
||||
local relPoint = tostring(saved["relativePoint"] or "")
|
||||
local x = tonumber(saved["x"])
|
||||
local y = tonumber(saved["y"])
|
||||
if point == "" or not x or not y then
|
||||
AutoStrip_Trace("restore-display-position skipped reason=" .. tostring(reason or "")
|
||||
.. " savedPoint=" .. tostring(saved["point"])
|
||||
.. " savedX=" .. tostring(saved["x"])
|
||||
.. " savedY=" .. tostring(saved["y"]))
|
||||
return false
|
||||
end
|
||||
if relPoint == "" then
|
||||
relPoint = point
|
||||
end
|
||||
AutoStripDisplay:ClearAllPoints()
|
||||
AutoStripDisplay:SetPoint(point, UIParent, relPoint, x, y)
|
||||
AutoStrip_Trace("restore-display-position reason=" .. tostring(reason or "")
|
||||
.. " frame=" .. AutoStrip_DescribePoint(AutoStripDisplay))
|
||||
return true
|
||||
end
|
||||
|
||||
local function AutoStrip_QueueDeferredRestore(reason)
|
||||
if not AutoStrip_RestoreFrame then
|
||||
AutoStrip_RestoreFrame = CreateFrame("Frame", "MTH_AutoStripRestore")
|
||||
if not AutoStrip_RestoreFrame then
|
||||
return
|
||||
end
|
||||
end
|
||||
AutoStrip_RestoreFrame._elapsed = 0
|
||||
AutoStrip_RestoreFrame:SetScript("OnUpdate", function()
|
||||
this._elapsed = (this._elapsed or 0) + (arg1 or 0)
|
||||
if this._elapsed < 0.2 then
|
||||
return
|
||||
end
|
||||
this:SetScript("OnUpdate", nil)
|
||||
AutoStrip_RestoreDisplayPosition("deferred:" .. tostring(reason or ""))
|
||||
end)
|
||||
end
|
||||
|
||||
function AutoStrip_SetDisplayIcon()
|
||||
local icon = getglobal("AutoStripDisplayIcon") or getglobal("AutoStripDisplayIconTexture")
|
||||
@@ -35,9 +152,18 @@ end
|
||||
|
||||
function AutoStrip_SetAutoStripToggle(enabled, silent)
|
||||
local saved = AutoStrip_GetSaved()
|
||||
AutoStrip_Trace("SetAutoStripToggle enabled=" .. tostring(enabled and true or false)
|
||||
.. " silent=" .. tostring(silent and true or false)
|
||||
.. " savedBefore=" .. tostring(saved["autostrip"] and 1 or 0)
|
||||
.. " runtimeBefore=" .. tostring(AutoStrip_On and 1 or 0)
|
||||
.. " castShownBefore=" .. tostring((AutoStripDisplayAutoCast and AutoStripDisplayAutoCast:IsVisible()) and 1 or 0))
|
||||
if enabled then
|
||||
saved["autostrip"] = 1
|
||||
AutoStrip_On = 1
|
||||
if AutoStrip_Frame then
|
||||
AutoStrip_Frame:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||
AutoStrip_Frame:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||||
end
|
||||
if AutoStripDisplay and AutoStripDisplay:IsVisible() and AutoStripDisplayAutoCast then
|
||||
AutoStripDisplayAutoCast:Show()
|
||||
end
|
||||
@@ -47,6 +173,10 @@ function AutoStrip_SetAutoStripToggle(enabled, silent)
|
||||
else
|
||||
saved["autostrip"] = nil
|
||||
AutoStrip_On = nil
|
||||
if AutoStrip_Frame then
|
||||
AutoStrip_Frame:UnregisterEvent("PLAYER_REGEN_DISABLED")
|
||||
AutoStrip_Frame:UnregisterEvent("PLAYER_REGEN_ENABLED")
|
||||
end
|
||||
if AutoStripDisplayAutoCast then
|
||||
AutoStripDisplayAutoCast:Hide()
|
||||
end
|
||||
@@ -54,10 +184,17 @@ function AutoStrip_SetAutoStripToggle(enabled, silent)
|
||||
AutoStrip_Print("Auto-Strip disabled.")
|
||||
end
|
||||
end
|
||||
AutoStrip_Trace("SetAutoStripToggle savedAfter=" .. tostring(saved["autostrip"] and 1 or 0)
|
||||
.. " runtimeAfter=" .. tostring(AutoStrip_On and 1 or 0)
|
||||
.. " castShownAfter=" .. tostring((AutoStripDisplayAutoCast and AutoStripDisplayAutoCast:IsVisible()) and 1 or 0))
|
||||
end
|
||||
|
||||
function AutoStrip_SetDisplayToggle(enabled, silent)
|
||||
local saved = AutoStrip_GetSaved()
|
||||
AutoStrip_Trace("SetDisplayToggle enabled=" .. tostring(enabled and true or false)
|
||||
.. " silent=" .. tostring(silent and true or false)
|
||||
.. " savedDisplayBefore=" .. tostring(saved["display"] and 1 or 0)
|
||||
.. " frameShownBefore=" .. tostring((AutoStripDisplay and AutoStripDisplay:IsVisible()) and 1 or 0))
|
||||
if enabled then
|
||||
saved["display"] = 1
|
||||
if AutoStripDisplay then
|
||||
@@ -79,9 +216,15 @@ function AutoStrip_SetDisplayToggle(enabled, silent)
|
||||
AutoStrip_Print("AutoStrip button is now hidden.")
|
||||
end
|
||||
end
|
||||
AutoStrip_Trace("SetDisplayToggle savedDisplayAfter=" .. tostring(saved["display"] and 1 or 0)
|
||||
.. " frameShownAfter=" .. tostring((AutoStripDisplay and AutoStripDisplay:IsVisible()) and 1 or 0)
|
||||
.. " castShownAfter=" .. tostring((AutoStripDisplayAutoCast and AutoStripDisplayAutoCast:IsVisible()) and 1 or 0))
|
||||
end
|
||||
|
||||
function AutoStrip_UnequipAll(weaponsOnly)
|
||||
AutoStrip_Trace("UnequipAll called weaponsOnly=" .. tostring(weaponsOnly and 1 or 0)
|
||||
.. " runtimeBefore=" .. tostring(AutoStrip_On and 1 or 0)
|
||||
.. " wasInCombat=" .. tostring(AutoStrip_WasInCombat and 1 or 0))
|
||||
AutoStrip_On = nil
|
||||
if AutoStripDisplayAutoCast then
|
||||
AutoStripDisplayAutoCast:Hide()
|
||||
@@ -102,6 +245,7 @@ function AutoStrip_UnequipAll(weaponsOnly)
|
||||
|
||||
if not hasEmptySlot then
|
||||
AutoStrip_Print("You need at least one empty bag slot to auto-strip.")
|
||||
AutoStrip_Trace("UnequipAll aborted no-empty-slot")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -121,19 +265,44 @@ function AutoStrip_UnequipAll(weaponsOnly)
|
||||
end
|
||||
end
|
||||
end
|
||||
AutoStrip_Trace("UnequipAll completed runtimeAfter=" .. tostring(AutoStrip_On and 1 or 0)
|
||||
.. " castShownAfter=" .. tostring((AutoStripDisplayAutoCast and AutoStripDisplayAutoCast:IsVisible()) and 1 or 0))
|
||||
end
|
||||
|
||||
local function AutoStrip_OnEvent()
|
||||
if AutoStrip_TraceEnabled then
|
||||
AutoStrip_Trace("OnEvent evt=" .. tostring(event or "")
|
||||
.. " runtime=" .. tostring(AutoStrip_On and 1 or 0)
|
||||
.. " wasInCombat=" .. tostring(AutoStrip_WasInCombat and 1 or 0)
|
||||
.. " playerInCombat=" .. tostring((type(UnitAffectingCombat) == "function" and UnitAffectingCombat("player")) and 1 or 0))
|
||||
end
|
||||
if event == "VARIABLES_LOADED" then
|
||||
-- Class gate: only hunters use AutoStrip.
|
||||
if MTH and MTH.IsClassGateBlocked and MTH:IsClassGateBlocked() then
|
||||
AutoStrip_Frame:UnregisterAllEvents()
|
||||
AutoStrip_Frame:SetScript("OnEvent", nil)
|
||||
return
|
||||
end
|
||||
local saved = AutoStrip_GetSaved()
|
||||
AutoStrip_Trace("VARIABLES_LOADED saved.autostrip=" .. tostring(saved["autostrip"] and 1 or 0)
|
||||
.. " saved.display=" .. tostring(saved["display"] and 1 or 0))
|
||||
AutoStrip_WasInCombat = UnitAffectingCombat("player") and 1 or nil
|
||||
AutoStrip_RestoreDisplayPosition("VARIABLES_LOADED")
|
||||
AutoStrip_SetDisplayToggle(saved["display"] and true or false, 1)
|
||||
AutoStrip_SetAutoStripToggle(saved["autostrip"] and true or false, 1)
|
||||
AutoStrip_QueueDeferredRestore("VARIABLES_LOADED")
|
||||
return
|
||||
end
|
||||
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
AutoStrip_WasInCombat = UnitAffectingCombat("player") and 1 or nil
|
||||
AutoStrip_RestoreDisplayPosition("PLAYER_ENTERING_WORLD")
|
||||
AutoStrip_QueueDeferredRestore("PLAYER_ENTERING_WORLD")
|
||||
return
|
||||
end
|
||||
|
||||
if event == "PLAYER_LOGOUT" then
|
||||
AutoStrip_SaveDisplayPosition("PLAYER_LOGOUT")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -144,6 +313,7 @@ local function AutoStrip_OnEvent()
|
||||
|
||||
if event == "PLAYER_REGEN_ENABLED" then
|
||||
if AutoStrip_On and AutoStrip_WasInCombat then
|
||||
AutoStrip_Trace("PLAYER_REGEN_ENABLED triggering auto strip")
|
||||
AutoStrip_UnequipAll()
|
||||
end
|
||||
AutoStrip_WasInCombat = nil
|
||||
@@ -152,6 +322,8 @@ end
|
||||
|
||||
local function AutoStrip_HandleSlash(msg)
|
||||
local saved = AutoStrip_GetSaved()
|
||||
AutoStrip_Trace("Slash msg='" .. tostring(msg or "") .. "' saved.autostrip=" .. tostring(saved["autostrip"] and 1 or 0)
|
||||
.. " runtime=" .. tostring(AutoStrip_On and 1 or 0))
|
||||
if msg == "display" then
|
||||
AutoStrip_SetDisplayToggle(not (saved["display"] and true or false))
|
||||
elseif msg == "toggle" then
|
||||
@@ -170,12 +342,15 @@ local function AutoStrip_HandleSlash(msg)
|
||||
end
|
||||
end
|
||||
|
||||
AutoStrip_Frame = CreateFrame("Frame", "AutoStripFrame")
|
||||
AutoStrip_Frame = CreateFrame("Frame", "MTH_AutoStripEvent")
|
||||
if AutoStrip_Frame then
|
||||
-- PLAYER_REGEN_DISABLED / PLAYER_REGEN_ENABLED are intentionally NOT registered here.
|
||||
-- They are registered only when the feature is enabled via AutoStrip_SetAutoStripToggle(true),
|
||||
-- and unregistered when it is disabled. This prevents any combat event overhead when the
|
||||
-- feature is off.
|
||||
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:RegisterEvent("PLAYER_LOGOUT")
|
||||
AutoStrip_Frame:SetScript("OnEvent", AutoStrip_OnEvent)
|
||||
end
|
||||
|
||||
@@ -187,3 +362,8 @@ SLASH_AUTOSTRIP1 = "/autostrip"
|
||||
SlashCmdList["AUTOSTRIP"] = AutoStrip_HandleSlash
|
||||
SLASH_ZSTRIP1 = "/zstrip"
|
||||
SlashCmdList["ZSTRIP"] = AutoStrip_HandleSlash
|
||||
SLASH_AUTOSTRIPTRACE1 = "/autostriptrace"
|
||||
SLASH_AUTOSTRIPTRACE2 = "/astrace"
|
||||
SlashCmdList["AUTOSTRIPTRACE"] = function(msg)
|
||||
AutoStrip_CommandTrace(msg)
|
||||
end
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
if ( this.isMoving ) then
|
||||
this:StopMovingOrSizing()
|
||||
this.isMoving = false
|
||||
if AutoStrip_SaveDisplayPosition then
|
||||
AutoStrip_SaveDisplayPosition("mouse-up-stop")
|
||||
end
|
||||
end
|
||||
</OnMouseUp>
|
||||
<OnDragStart>
|
||||
@@ -79,6 +82,9 @@
|
||||
<OnDragStop>
|
||||
this:StopMovingOrSizing()
|
||||
this.isMoving = false
|
||||
if AutoStrip_SaveDisplayPosition then
|
||||
AutoStrip_SaveDisplayPosition("drag-stop")
|
||||
end
|
||||
</OnDragStop>
|
||||
</Scripts>
|
||||
<NormalTexture name="$parentNormalTexture" file="Interface\Buttons\UI-Quickslot2">
|
||||
|
||||
+1347
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,463 @@
|
||||
-- ============================================================
|
||||
-- Bag Ammo Labels — standalone overlay for bag & bank ammo slots
|
||||
-- Uses globals from zButtonAmmo.lua:
|
||||
-- zButtonAmmo_ShortArrowLabels, zButtonAmmo_ShortBulletLabels,
|
||||
-- zButtonAmmo_GetQualityColor, zButtonAmmo_GetColorGradient,
|
||||
-- zButtonAmmo_GetKnownAmmoTypes
|
||||
-- Uses DPS data from MTH_DS_AmmoItems[id].dps
|
||||
-- ============================================================
|
||||
|
||||
-- Saved variable cache (per-character, stored in "general" module)
|
||||
local cache = {} -- bagAmmoLabels, bankAmmoLabels, bagAmmoDamage
|
||||
|
||||
local function GetStore()
|
||||
return MTH and MTH.GetModuleCharSavedVariables and MTH:GetModuleCharSavedVariables("general")
|
||||
end
|
||||
|
||||
local function GetCached(key, default)
|
||||
if cache[key] == nil then
|
||||
local store = GetStore()
|
||||
if type(store) == "table" and store[key] ~= nil then
|
||||
cache[key] = store[key] and true or false
|
||||
else
|
||||
cache[key] = default or false
|
||||
end
|
||||
end
|
||||
return cache[key]
|
||||
end
|
||||
|
||||
local function SetCached(key, enabled)
|
||||
cache[key] = enabled and true or false
|
||||
local store = GetStore()
|
||||
if type(store) == "table" then
|
||||
store[key] = cache[key]
|
||||
end
|
||||
end
|
||||
|
||||
-- Public getters/setters
|
||||
function MTH_BagAmmoLabels_GetEnabled()
|
||||
return GetCached("bagAmmoLabels")
|
||||
end
|
||||
function MTH_BagAmmoLabels_SetEnabled(enabled)
|
||||
SetCached("bagAmmoLabels", enabled)
|
||||
MTH_BagAmmoLabels_RefreshAll()
|
||||
end
|
||||
|
||||
function MTH_BankAmmoLabels_GetEnabled()
|
||||
return GetCached("bankAmmoLabels")
|
||||
end
|
||||
function MTH_BankAmmoLabels_SetEnabled(enabled)
|
||||
SetCached("bankAmmoLabels", enabled)
|
||||
MTH_BagAmmoLabels_RefreshAllBank()
|
||||
end
|
||||
|
||||
function MTH_BagAmmoDamage_GetEnabled()
|
||||
return GetCached("bagAmmoDamage")
|
||||
end
|
||||
function MTH_BagAmmoDamage_SetEnabled(enabled)
|
||||
SetCached("bagAmmoDamage", enabled)
|
||||
MTH_BagAmmoLabels_RefreshAll()
|
||||
MTH_BagAmmoLabels_RefreshAllBank()
|
||||
end
|
||||
|
||||
-- Heat color: red (7.5 DPS) → yellow → green (20.5 DPS); below 7.5 = grey
|
||||
local DPS_MIN = 7.5
|
||||
local DPS_MAX = 20.5
|
||||
|
||||
local function GetDpsHeatColor(dps)
|
||||
if not dps then return 1, 1, 1 end
|
||||
if dps < DPS_MIN then return 0.6, 0.6, 0.6 end
|
||||
local perc = (dps - DPS_MIN) / (DPS_MAX - DPS_MIN)
|
||||
return zButtonAmmo_GetColorGradient(perc)
|
||||
end
|
||||
|
||||
-- Look up DPS from MTH_DS_AmmoItems by numeric item ID
|
||||
local function GetAmmoDps(numericId)
|
||||
if not MTH_DS_AmmoItems then return nil end
|
||||
local entry = MTH_DS_AmmoItems[numericId]
|
||||
return entry and entry.dps
|
||||
end
|
||||
|
||||
-- Detect whether pfUI bags are active
|
||||
local function HasPfUI()
|
||||
return pfUI and pfUI.bags and pfUI.bag and true or false
|
||||
end
|
||||
|
||||
-- Is this bagID a bank bag?
|
||||
local BANK_BAGS = { [-1] = true, [5] = true, [6] = true, [7] = true, [8] = true, [9] = true, [10] = true, [11] = true }
|
||||
|
||||
local function IsBankBag(bagID)
|
||||
return BANK_BAGS[bagID] or false
|
||||
end
|
||||
|
||||
-- Apply or clear labels on a single bag-slot button
|
||||
local function UpdateButton(button, bagID, slot, showName, showDmg)
|
||||
if not button then return end
|
||||
|
||||
-- Lazy-create name label (TOPLEFT)
|
||||
if not button._mthAmmoLabel then
|
||||
button._mthAmmoLabel = button:CreateFontString(nil, "OVERLAY")
|
||||
button._mthAmmoLabel:SetPoint("TOPLEFT", button, "TOPLEFT", 1, -1)
|
||||
button._mthAmmoLabel:SetFont("Fonts\\ARIALN.ttf", 9, "OUTLINE")
|
||||
button._mthAmmoLabel:Hide()
|
||||
end
|
||||
-- Lazy-create damage label (CENTER, large + bold)
|
||||
if not button._mthAmmoDmgLabel then
|
||||
button._mthAmmoDmgLabel = button:CreateFontString(nil, "OVERLAY")
|
||||
button._mthAmmoDmgLabel:SetPoint("CENTER", button, "CENTER", 0, 0)
|
||||
button._mthAmmoDmgLabel:SetFont("Fonts\\ARIALN.ttf", 10, "THICKOUTLINE")
|
||||
button._mthAmmoDmgLabel:Hide()
|
||||
end
|
||||
|
||||
local nameLabel = button._mthAmmoLabel
|
||||
local dmgLabel = button._mthAmmoDmgLabel
|
||||
|
||||
if not showName and not showDmg then
|
||||
nameLabel:Hide()
|
||||
dmgLabel:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
local link = GetContainerItemLink(bagID, slot)
|
||||
if not link then
|
||||
nameLabel:Hide()
|
||||
dmgLabel:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
local _, _, id = string.find(link, "Hitem:(%d+)")
|
||||
if not id then
|
||||
nameLabel:Hide()
|
||||
dmgLabel:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
local numericId = tonumber(id)
|
||||
local itemName, _, itemQuality, _, _, _, _, _, itemSlot = GetItemInfo(id)
|
||||
if not itemName then
|
||||
nameLabel:Hide()
|
||||
dmgLabel:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
local isAmmo = (itemSlot == "INVTYPE_AMMO")
|
||||
if not isAmmo then
|
||||
local knownAmmo = zButtonAmmo_GetKnownAmmoTypes and zButtonAmmo_GetKnownAmmoTypes()
|
||||
isAmmo = knownAmmo and knownAmmo[itemName]
|
||||
end
|
||||
if not isAmmo then
|
||||
nameLabel:Hide()
|
||||
dmgLabel:Hide()
|
||||
return
|
||||
end
|
||||
|
||||
-- Name label
|
||||
if showName then
|
||||
local shortText = zButtonAmmo_ShortArrowLabels[itemName]
|
||||
or zButtonAmmo_ShortBulletLabels[itemName]
|
||||
if shortText then
|
||||
local cr, cg, cb = zButtonAmmo_GetQualityColor(itemQuality)
|
||||
nameLabel:SetTextColor(cr, cg, cb, 1)
|
||||
nameLabel:SetText(shortText)
|
||||
nameLabel:Show()
|
||||
else
|
||||
nameLabel:Hide()
|
||||
end
|
||||
else
|
||||
nameLabel:Hide()
|
||||
end
|
||||
|
||||
-- Damage label
|
||||
if showDmg then
|
||||
local dps = GetAmmoDps(numericId)
|
||||
if dps then
|
||||
local cr, cg, cb = GetDpsHeatColor(dps)
|
||||
dmgLabel:SetTextColor(cr, cg, cb, 1)
|
||||
dmgLabel:SetText(dps)
|
||||
dmgLabel:Show()
|
||||
else
|
||||
dmgLabel:Hide()
|
||||
end
|
||||
else
|
||||
dmgLabel:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
-- Clear both labels on a button
|
||||
local function ClearButton(button)
|
||||
if not button then return end
|
||||
if button._mthAmmoLabel then button._mthAmmoLabel:Hide() end
|
||||
if button._mthAmmoDmgLabel then button._mthAmmoDmgLabel:Hide() end
|
||||
end
|
||||
|
||||
-- Forward declaration
|
||||
local RefreshAllVisibleFrames
|
||||
|
||||
-- Refresh labels on a single bag (works for any bagID)
|
||||
local function RefreshBag(bagID, showName, showDmg)
|
||||
local numSlots = GetContainerNumSlots(bagID)
|
||||
if numSlots == 0 then return end
|
||||
|
||||
-- pfUI path
|
||||
if HasPfUI() then
|
||||
local pfBag = pfUI.bags[bagID]
|
||||
if not pfBag or not pfBag.slots then return end
|
||||
for slot = 1, numSlots do
|
||||
local slotData = pfBag.slots[slot]
|
||||
local button = slotData and slotData.frame
|
||||
if button then
|
||||
if showName or showDmg then
|
||||
UpdateButton(button, bagID, slot, showName, showDmg)
|
||||
else
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Default UI path
|
||||
|
||||
-- Main bank (bagID -1): buttons are BankFrameItem1..28
|
||||
if bagID == -1 then
|
||||
for slot = 1, numSlots do
|
||||
local button = getglobal("BankFrameItem" .. slot)
|
||||
if button then
|
||||
if showName or showDmg then
|
||||
UpdateButton(button, -1, slot, showName, showDmg)
|
||||
else
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- All other bags (0-4 and 5-11): search all ContainerFrames
|
||||
for ci = 1, 13 do
|
||||
local frame = getglobal("ContainerFrame" .. ci)
|
||||
if frame and frame:IsVisible() and frame:GetID() == bagID then
|
||||
local frameName = frame:GetName()
|
||||
-- Visual button i = API slot (numSlots - i + 1)
|
||||
for i = 1, numSlots do
|
||||
local button = getglobal(frameName .. "Item" .. i)
|
||||
local apiSlot = numSlots - i + 1
|
||||
if button then
|
||||
if showName or showDmg then
|
||||
UpdateButton(button, bagID, apiSlot, showName, showDmg)
|
||||
else
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Clear stale labels on any buttons beyond numSlots
|
||||
for slot = numSlots + 1, 24 do
|
||||
local button = getglobal(frameName .. "Item" .. slot)
|
||||
if not button then break end
|
||||
ClearButton(button)
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Public: refresh all inventory bags (0-4)
|
||||
function MTH_BagAmmoLabels_RefreshAll()
|
||||
RefreshAllVisibleFrames()
|
||||
end
|
||||
|
||||
-- Public: refresh all bank bags (-1, 5-11)
|
||||
function MTH_BagAmmoLabels_RefreshAllBank()
|
||||
RefreshAllVisibleFrames()
|
||||
end
|
||||
|
||||
-- Refresh all visible default UI ContainerFrames (frame-centric: no bagID lookup)
|
||||
RefreshAllVisibleFrames = function()
|
||||
local bagShowName = MTH_BagAmmoLabels_GetEnabled()
|
||||
local bankShowName = MTH_BankAmmoLabels_GetEnabled()
|
||||
local showDmg = MTH_BagAmmoDamage_GetEnabled()
|
||||
|
||||
if HasPfUI() then
|
||||
-- pfUI: refresh via pfUI bag structure
|
||||
for bagID = 0, 4 do
|
||||
RefreshBag(bagID, bagShowName, showDmg)
|
||||
end
|
||||
local bankBags = { -1, 5, 6, 7, 8, 9, 10, 11 }
|
||||
for _, bagID in ipairs(bankBags) do
|
||||
RefreshBag(bagID, bankShowName, showDmg)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Default UI: iterate all visible ContainerFrames
|
||||
for ci = 1, 13 do
|
||||
local cf = getglobal("ContainerFrame" .. ci)
|
||||
if cf and cf:IsVisible() then
|
||||
local bagID = cf:GetID()
|
||||
local showName = IsBankBag(bagID) and bankShowName or bagShowName
|
||||
local numSlots = GetContainerNumSlots(bagID)
|
||||
local fname = cf:GetName()
|
||||
-- Visual button i = API slot (numSlots - i + 1)
|
||||
for i = 1, numSlots do
|
||||
local button = getglobal(fname .. "Item" .. i)
|
||||
local apiSlot = numSlots - i + 1
|
||||
if button then
|
||||
if showName or showDmg then
|
||||
UpdateButton(button, bagID, apiSlot, showName, showDmg)
|
||||
else
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
for slot = numSlots + 1, 24 do
|
||||
local button = getglobal(fname .. "Item" .. slot)
|
||||
if not button then break end
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Bank bag -1: BankFrameItem buttons
|
||||
if bankShowName or showDmg then
|
||||
local numSlots = GetContainerNumSlots(-1)
|
||||
for slot = 1, numSlots do
|
||||
local button = getglobal("BankFrameItem" .. slot)
|
||||
if button then
|
||||
UpdateButton(button, -1, slot, bankShowName, showDmg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Event-driven refresh: coalesce with a short delay
|
||||
do
|
||||
local frame = CreateFrame("Frame", "MTH_BagAmmoLabelsFrame", UIParent)
|
||||
local needsRefresh = false
|
||||
local elapsed = 0
|
||||
|
||||
frame:RegisterEvent("BAG_UPDATE")
|
||||
frame:RegisterEvent("PLAYERBANKSLOTS_CHANGED")
|
||||
frame:RegisterEvent("PLAYERBANKBAGSLOTS_CHANGED")
|
||||
frame:RegisterEvent("BANKFRAME_OPENED")
|
||||
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
|
||||
frame:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
-- After login/reload, delay 1s for item cache to populate
|
||||
elapsed = -0.95
|
||||
needsRefresh = true
|
||||
frame:Show()
|
||||
return
|
||||
end
|
||||
|
||||
if event == "BANKFRAME_OPENED" then
|
||||
if MTH_BankAmmoLabels_GetEnabled() or MTH_BagAmmoDamage_GetEnabled() then
|
||||
needsRefresh = true
|
||||
if not frame:IsShown() then
|
||||
elapsed = 0
|
||||
frame:Show()
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if event == "PLAYERBANKSLOTS_CHANGED" or event == "PLAYERBANKBAGSLOTS_CHANGED" then
|
||||
if MTH_BankAmmoLabels_GetEnabled() or MTH_BagAmmoDamage_GetEnabled() then
|
||||
needsRefresh = true
|
||||
if not frame:IsShown() then
|
||||
elapsed = 0
|
||||
frame:Show()
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- BAG_UPDATE
|
||||
local bagID = arg1
|
||||
if not bagID then return end
|
||||
if IsBankBag(bagID) then
|
||||
if not MTH_BankAmmoLabels_GetEnabled() and not MTH_BagAmmoDamage_GetEnabled() then return end
|
||||
else
|
||||
if not MTH_BagAmmoLabels_GetEnabled() and not MTH_BagAmmoDamage_GetEnabled() then return end
|
||||
end
|
||||
needsRefresh = true
|
||||
if not frame:IsShown() then
|
||||
elapsed = 0
|
||||
frame:Show()
|
||||
end
|
||||
end)
|
||||
|
||||
frame:SetScript("OnUpdate", function()
|
||||
elapsed = elapsed + (arg1 or 0.016)
|
||||
if elapsed < 0.05 then return end
|
||||
needsRefresh = false
|
||||
frame:Hide()
|
||||
RefreshAllVisibleFrames()
|
||||
end)
|
||||
frame:Hide()
|
||||
|
||||
-- Hook ContainerFrame OnShow: trigger a refresh when a bag becomes visible
|
||||
if not HasPfUI() then
|
||||
for ci = 1, 13 do
|
||||
local cf = getglobal("ContainerFrame" .. ci)
|
||||
if cf then
|
||||
local origOnShow = cf:GetScript("OnShow")
|
||||
cf:SetScript("OnShow", function()
|
||||
if origOnShow then origOnShow() end
|
||||
needsRefresh = true
|
||||
if not frame:IsShown() then
|
||||
elapsed = 0
|
||||
frame:Show()
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Post-hook ContainerFrame_Update for default UI (synchronous update)
|
||||
if type(ContainerFrame_Update) == "function" then
|
||||
local origCFU = ContainerFrame_Update
|
||||
ContainerFrame_Update = function(cf)
|
||||
origCFU(cf)
|
||||
if cf and cf:IsVisible() then
|
||||
local bagID = cf:GetID()
|
||||
local showDmg = MTH_BagAmmoDamage_GetEnabled()
|
||||
local showName
|
||||
if IsBankBag(bagID) then
|
||||
showName = MTH_BankAmmoLabels_GetEnabled()
|
||||
else
|
||||
showName = MTH_BagAmmoLabels_GetEnabled()
|
||||
end
|
||||
if showName or showDmg then
|
||||
local numSlots = GetContainerNumSlots(bagID)
|
||||
local fname = cf:GetName()
|
||||
-- Visual button i = API slot (numSlots - i + 1)
|
||||
for i = 1, numSlots do
|
||||
local button = getglobal(fname .. "Item" .. i)
|
||||
local apiSlot = numSlots - i + 1
|
||||
if button then
|
||||
UpdateButton(button, bagID, apiSlot, showName, showDmg)
|
||||
end
|
||||
end
|
||||
for slot = numSlots + 1, 24 do
|
||||
local button = getglobal(fname .. "Item" .. slot)
|
||||
if not button then break end
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Frame hidden: clear all labels
|
||||
if cf then
|
||||
local fname = cf:GetName()
|
||||
if fname then
|
||||
for slot = 1, 24 do
|
||||
local button = getglobal(fname .. "Item" .. slot)
|
||||
if not button then break end
|
||||
ClearButton(button)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+4
-1
@@ -38,7 +38,10 @@ local function MTH_ConfigEnsureStore(module_name)
|
||||
root.modules[module_name] = legacy
|
||||
end
|
||||
|
||||
root[module_name] = root.modules[module_name]
|
||||
-- NOTE: we intentionally do NOT mirror root[module_name] = root.modules[module_name].
|
||||
-- That old alias made WoW serialize a full DUPLICATE of every module store at the
|
||||
-- account root on disk. The canonical store is root.modules[module_name] only.
|
||||
root[module_name] = nil
|
||||
MTH_SavedVariables = root
|
||||
return root.modules[module_name]
|
||||
end
|
||||
|
||||
+21
-5
@@ -1,5 +1,5 @@
|
||||
MTH_CONST = MTH_CONST or {}
|
||||
MTH_CONST.version = MTH_CONST.version or "1.0.3"
|
||||
MTH_CONST.version = MTH_CONST.version or "2.0.0"
|
||||
|
||||
MTH_CONST.WEAPON_TYPES = {
|
||||
BOWS = "Bows",
|
||||
@@ -18,12 +18,13 @@ MTH_CONST.AMMO = {
|
||||
SOLID = "Solid Shot",
|
||||
CRAFTSOLID = "Crafted Solid Shot",
|
||||
EXPLODING = "Exploding Shot",
|
||||
MITHRILSLUG = "Hi-impact Mithril Slugs",
|
||||
MITHRILSLUG = "Hi-Impact Mithril Slugs",
|
||||
ACCURATE = "Accurate Slugs",
|
||||
GYROSHOT = "Mithril Gyro-Shot",
|
||||
ICETHREADED = "Ice Threaded Bullet",
|
||||
THORIUM = "Thorium Shells",
|
||||
ROCKSHARD = "Rockshard Pellets",
|
||||
CRAFTEDTHORIUM = "Enchanted Thorium Shells",
|
||||
CANNONBALL = "Miniature Cannon Balls",
|
||||
},
|
||||
ARROWS = {
|
||||
@@ -32,16 +33,19 @@ MTH_CONST.AMMO = {
|
||||
RAZOR = "Razor Arrow",
|
||||
FEATHER = "Feathered Arrow",
|
||||
PRECISION = "Precision Arrow",
|
||||
BRIGHT = "Bright Wood Arrows",
|
||||
JAGGED = "Jagged Arrow",
|
||||
SHADE = "Shade Wood Arrows",
|
||||
ICETHREADED = "Ice Threaded Arrow",
|
||||
SMOOTH = "Smooth Ironfeather Arrows",
|
||||
THORIUM = "Thorium Headed Arrow",
|
||||
STAR = "Starfeather Arrows",
|
||||
DOOMSHOT = "Doomshot",
|
||||
},
|
||||
JUNKSHOTS = {
|
||||
SERPENT = "Serpent Sting",
|
||||
SCORPID = "Scorpid Sting",
|
||||
VIPER = "Viper Sting",
|
||||
ARCANE = "Arcane Shot",
|
||||
CONCUSSIVE = "Concussive Shot",
|
||||
DISTRACTING = "Distracting Shot",
|
||||
BAITED = "Baited Shot",
|
||||
@@ -69,6 +73,7 @@ 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
|
||||
BULLETS_CRAFTEDTHORIUM = MTH_CONST.AMMO.BULLETS.CRAFTEDTHORIUM
|
||||
|
||||
ARROWS_ROUGH = MTH_CONST.AMMO.ARROWS.ROUGH
|
||||
ARROWS_SHARP = MTH_CONST.AMMO.ARROWS.SHARP
|
||||
@@ -77,15 +82,20 @@ 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_BRIGHT = MTH_CONST.AMMO.ARROWS.BRIGHT
|
||||
ARROWS_SHADE = MTH_CONST.AMMO.ARROWS.SHADE
|
||||
ARROWS_SMOOTH = MTH_CONST.AMMO.ARROWS.SMOOTH
|
||||
ARROWS_THORIUM = MTH_CONST.AMMO.ARROWS.THORIUM
|
||||
ARROWS_STAR = MTH_CONST.AMMO.ARROWS.STAR
|
||||
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
|
||||
JUNKSHOT_BAITED = MTH_CONST.AMMO.JUNKSHOTS.BAITED
|
||||
JUNKSHOT_TRANQ = MTH_CONST.AMMO.JUNKSHOTS.TRANQ
|
||||
|
||||
MTH_AMMO_BULLETS = {
|
||||
BULLETS_LIGHT,
|
||||
@@ -103,6 +113,7 @@ MTH_AMMO_BULLETS = {
|
||||
BULLETS_ICETHREADED,
|
||||
BULLETS_THORIUM,
|
||||
BULLETS_ROCKSHARD,
|
||||
BULLETS_CRAFTEDTHORIUM,
|
||||
BULLETS_CANNONBALL,
|
||||
}
|
||||
|
||||
@@ -112,9 +123,13 @@ MTH_AMMO_ARROWS = {
|
||||
ARROWS_RAZOR,
|
||||
ARROWS_FEATHER,
|
||||
ARROWS_PRECISION,
|
||||
ARROWS_BRIGHT,
|
||||
ARROWS_JAGGED,
|
||||
ARROWS_SHADE,
|
||||
ARROWS_ICETHREADED,
|
||||
ARROWS_SMOOTH,
|
||||
ARROWS_THORIUM,
|
||||
ARROWS_STAR,
|
||||
ARROWS_DOOMSHOT,
|
||||
}
|
||||
|
||||
@@ -122,9 +137,10 @@ MTH_AMMO_JUNKSHOTS = {
|
||||
JUNKSHOT_SERPENT,
|
||||
JUNKSHOT_SCORPID,
|
||||
JUNKSHOT_VIPER,
|
||||
JUNKSHOT_ARCANE,
|
||||
JUNKSHOT_CONCUSSIVE,
|
||||
JUNKSHOT_DISTRACTING,
|
||||
JUNKSHOT_BAITED,
|
||||
JUNKSHOT_TRANQ,
|
||||
}
|
||||
|
||||
MTH_AMMO_BULLET_RANK = {}
|
||||
|
||||
+182
-15
@@ -15,6 +15,7 @@ local MTH_FEED_TrackerFrame = nil
|
||||
local MTH_FEED_ActiveCoreAttemptId = nil
|
||||
local MTH_FEED_BootstrapFrame = nil
|
||||
local MTH_FEED_NameToItemCache = {}
|
||||
local MTH_FEED_InvalidateDietMapCache
|
||||
|
||||
local MTH_FEED_HARDCODED_LEVEL_RULES = {
|
||||
{ minPetLevel = 1, minFoodLevel = 1 },
|
||||
@@ -28,7 +29,10 @@ local MTH_FEED_HARDCODED_LEVEL_RULES = {
|
||||
MTH_FEED_TRACE = false
|
||||
|
||||
local function MTH_FEED_Trace(message)
|
||||
return
|
||||
if not MTH_FEED_TRACE then return end
|
||||
if type(DEFAULT_CHAT_FRAME) == "table" and DEFAULT_CHAT_FRAME.AddMessage then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff88aaff[MTH-FeedTrack]|r " .. tostring(message))
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_FEED_Now()
|
||||
@@ -163,7 +167,7 @@ local function MTH_FEED_IndexBagFoods()
|
||||
return true
|
||||
end
|
||||
|
||||
local function MTH_FEED_HasPetFeedBuff()
|
||||
function MTH_FEED_HasPetFeedBuff()
|
||||
if type(UnitBuff) ~= "function" then
|
||||
return false
|
||||
end
|
||||
@@ -460,6 +464,7 @@ local function MTH_FEED_CoreTrackerOnEvent()
|
||||
end
|
||||
elseif evt == "BAG_UPDATE" then
|
||||
MTH_FEED_IndexBagFoods()
|
||||
MTH_FEED_InvalidateDietMapCache()
|
||||
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")
|
||||
@@ -468,7 +473,32 @@ local function MTH_FEED_CoreTrackerOnEvent()
|
||||
end
|
||||
end
|
||||
|
||||
local MTH_FEED_CoreTrackerUpdateElapsed = 0
|
||||
local MTH_FEED_LastStaleCleanupAt = 0
|
||||
|
||||
local function MTH_FEED_CoreTrackerOnUpdate()
|
||||
MTH_FEED_CoreTrackerUpdateElapsed = MTH_FEED_CoreTrackerUpdateElapsed + (arg1 or 0)
|
||||
if MTH_FEED_CoreTrackerUpdateElapsed < 0.10 then
|
||||
return
|
||||
end
|
||||
MTH_FEED_CoreTrackerUpdateElapsed = 0
|
||||
|
||||
-- Periodic stale attempt cleanup (every ~30 seconds)
|
||||
local now = GetTime() or 0
|
||||
if (now - MTH_FEED_LastStaleCleanupAt) >= 30 then
|
||||
MTH_FEED_LastStaleCleanupAt = now
|
||||
if MTH_FEED_Runtime and type(MTH_FEED_Runtime.attempts) == "table" then
|
||||
for id, row in pairs(MTH_FEED_Runtime.attempts) do
|
||||
if type(row) == "table" then
|
||||
local age = now - (tonumber(row.startedAt) or now)
|
||||
if age > 10 then
|
||||
MTH_FEED_Runtime.attempts[id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local attemptId = MTH_FEED_ActiveCoreAttemptId
|
||||
if attemptId == nil then
|
||||
return
|
||||
@@ -499,7 +529,7 @@ local function MTH_FEED_EnsureTrackerFrame()
|
||||
if MTH_FEED_TrackerFrame then
|
||||
return MTH_FEED_TrackerFrame
|
||||
end
|
||||
local frame = CreateFrame("Frame", "MTH_FeedTrackingFrame")
|
||||
local frame = CreateFrame("Frame", "MTH_FeedTracking")
|
||||
if not frame then
|
||||
return nil
|
||||
end
|
||||
@@ -526,6 +556,7 @@ local function MTH_FEED_CoreDropItemOnUnitHook(unit)
|
||||
end
|
||||
|
||||
if target == "pet" and CursorHasItem and CursorHasItem() then
|
||||
MTH_FEED_EnsureTrackerFrame()
|
||||
local itemId, itemName, itemLink, itemIcon = MTH_FEED_GetCursorItemSnapshot()
|
||||
local pet = MTH_FEED_GetCurrentPetContext()
|
||||
if not itemId then
|
||||
@@ -608,8 +639,8 @@ function MTH_FEED_DebugStatus()
|
||||
end
|
||||
|
||||
function MTH_FEED_ReinstallCoreTracking()
|
||||
MTH_FEED_EnsureTrackerFrame()
|
||||
local ok = MTH_FEED_InstallCoreHooks()
|
||||
MTH_FEED_EnsureTrackerFrame()
|
||||
return ok and true or false
|
||||
end
|
||||
|
||||
@@ -617,7 +648,7 @@ local function MTH_FEED_EnsureBootstrapFrame()
|
||||
if MTH_FEED_BootstrapFrame ~= nil then
|
||||
return MTH_FEED_BootstrapFrame
|
||||
end
|
||||
local frame = CreateFrame("Frame", "MTH_FeedTrackingBootstrapFrame")
|
||||
local frame = CreateFrame("Frame", "MTH_FeedTrackingBoot")
|
||||
if not frame then
|
||||
return nil
|
||||
end
|
||||
@@ -626,6 +657,8 @@ local function MTH_FEED_EnsureBootstrapFrame()
|
||||
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
frame:SetScript("OnEvent", function()
|
||||
MTH_FEED_ReinstallCoreTracking()
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
end)
|
||||
MTH_FEED_BootstrapFrame = frame
|
||||
return frame
|
||||
@@ -822,7 +855,16 @@ local function MTH_FEED_NormalizeReason(reason, rawMessage)
|
||||
return "unknown"
|
||||
end
|
||||
|
||||
local MTH_FEED_DietMapCache = nil
|
||||
|
||||
MTH_FEED_InvalidateDietMapCache = function()
|
||||
MTH_FEED_DietMapCache = nil
|
||||
end
|
||||
|
||||
local function MTH_FEED_GetDietByItemIdMap()
|
||||
if MTH_FEED_DietMapCache then
|
||||
return MTH_FEED_DietMapCache
|
||||
end
|
||||
local map = {}
|
||||
if type(FOM_Foods) ~= "table" then
|
||||
return map
|
||||
@@ -837,6 +879,7 @@ local function MTH_FEED_GetDietByItemIdMap()
|
||||
end
|
||||
end
|
||||
end
|
||||
MTH_FEED_DietMapCache = map
|
||||
return map
|
||||
end
|
||||
|
||||
@@ -940,9 +983,6 @@ function MTH_FEED_BeginAttempt(payload)
|
||||
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,
|
||||
@@ -1113,7 +1153,7 @@ function MTH_FEED_IsFoodCompatible(petLevel, foodLevel)
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_FEED_IsExceptionReject(itemId)
|
||||
function MTH_FEED_IsExceptionReject(itemId, petLevel)
|
||||
local key = tonumber(itemId)
|
||||
if not key then
|
||||
return false
|
||||
@@ -1123,6 +1163,11 @@ function MTH_FEED_IsExceptionReject(itemId)
|
||||
if type(row) ~= "table" then
|
||||
return false
|
||||
end
|
||||
local numericPet = tonumber(petLevel)
|
||||
local blockedAtOrAbove = tonumber(row.blockAtOrAbovePetLevel)
|
||||
if blockedAtOrAbove and numericPet and numericPet >= blockedAtOrAbove then
|
||||
return true
|
||||
end
|
||||
if row.anomalyReject ~= true then
|
||||
return false
|
||||
end
|
||||
@@ -1137,6 +1182,17 @@ function MTH_FEED_IsExceptionReject(itemId)
|
||||
if not reasons then
|
||||
return false
|
||||
end
|
||||
local lowLevelCount = tonumber(reasons.lowLevel) or 0
|
||||
if lowLevelCount >= 1 then
|
||||
local maxPetLevel = tonumber(row.maxPetLevel)
|
||||
if numericPet and maxPetLevel then
|
||||
if numericPet > maxPetLevel then
|
||||
return true
|
||||
end
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
local wrongFoodCount = tonumber(reasons.wrongFood) or 0
|
||||
if wrongFoodCount < 2 then
|
||||
return false
|
||||
@@ -1144,6 +1200,49 @@ function MTH_FEED_IsExceptionReject(itemId)
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_FEED_BlockItemForPetLevel(itemId, petLevel, reason)
|
||||
local key = tonumber(itemId)
|
||||
local numericPet = tonumber(petLevel)
|
||||
if not key or not numericPet then
|
||||
return false
|
||||
end
|
||||
if numericPet < 1 then
|
||||
numericPet = 1
|
||||
end
|
||||
local store = MTH_FEED_EnsureStore()
|
||||
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
|
||||
row.anomalyReject = true
|
||||
row.lastObservedAt = MTH_FEED_Now()
|
||||
if tonumber(row.blockAtOrAbovePetLevel) == nil or numericPet < tonumber(row.blockAtOrAbovePetLevel) then
|
||||
row.blockAtOrAbovePetLevel = numericPet
|
||||
end
|
||||
local normalizedReason = MTH_FEED_NormalizeReason(reason, nil)
|
||||
if normalizedReason == "no-buff" then
|
||||
row.reasons.noBuff = (tonumber(row.reasons.noBuff) or 0) + 1
|
||||
elseif normalizedReason == "low-level" then
|
||||
row.reasons.lowLevel = (tonumber(row.reasons.lowLevel) or 0) + 1
|
||||
elseif normalizedReason == "wrong-food" then
|
||||
row.reasons.wrongFood = (tonumber(row.reasons.wrongFood) or 0) + 1
|
||||
else
|
||||
row.reasons.unknown = (tonumber(row.reasons.unknown) or 0) + 1
|
||||
end
|
||||
MTH_FEED_TouchStore(store)
|
||||
MTH_FEED_Trace("BlockItemForPetLevel itemId=" .. tostring(key)
|
||||
.. " blockAtOrAbove=" .. tostring(row.blockAtOrAbovePetLevel)
|
||||
.. " reason=" .. tostring(normalizedReason))
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_FEED_GetPetFeedStats(petId)
|
||||
local key = tostring(petId or "")
|
||||
if key == "" then
|
||||
@@ -1153,7 +1252,7 @@ function MTH_FEED_GetPetFeedStats(petId)
|
||||
return store.byPetId[key]
|
||||
end
|
||||
|
||||
local function MTH_FEED_UpsertException(store, itemId, reason)
|
||||
local function MTH_FEED_UpsertException(store, itemId, reason, petLevel)
|
||||
if not itemId then
|
||||
return
|
||||
end
|
||||
@@ -1175,6 +1274,16 @@ local function MTH_FEED_UpsertException(store, itemId, reason)
|
||||
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
|
||||
local numericPetLevel = tonumber(petLevel)
|
||||
if numericPetLevel and numericPetLevel > 1 then
|
||||
local derivedMaxPetLevel = numericPetLevel - 1
|
||||
if derivedMaxPetLevel < 1 then
|
||||
derivedMaxPetLevel = 1
|
||||
end
|
||||
if tonumber(row.maxPetLevel) == nil or derivedMaxPetLevel < tonumber(row.maxPetLevel) then
|
||||
row.maxPetLevel = derivedMaxPetLevel
|
||||
end
|
||||
end
|
||||
else
|
||||
row.reasons.unknown = (tonumber(row.reasons.unknown) or 0) + 1
|
||||
end
|
||||
@@ -1182,6 +1291,36 @@ local function MTH_FEED_UpsertException(store, itemId, reason)
|
||||
row.lastObservedAt = MTH_FEED_Now()
|
||||
end
|
||||
|
||||
local function MTH_FEED_ClearStaleExceptionOnAccept(store, itemId)
|
||||
if not itemId or type(store) ~= "table" or type(store.exceptions) ~= "table" or type(store.exceptions.byItemId) ~= "table" then
|
||||
return
|
||||
end
|
||||
local key = tonumber(itemId) or itemId
|
||||
local row = store.exceptions.byItemId[key]
|
||||
if type(row) ~= "table" then
|
||||
return
|
||||
end
|
||||
if type(row.reasons) ~= "table" then
|
||||
store.exceptions.byItemId[key] = nil
|
||||
return
|
||||
end
|
||||
row.reasons.noBuff = 0
|
||||
row.lastObservedAt = MTH_FEED_Now()
|
||||
|
||||
local noBuff = tonumber(row.reasons.noBuff) or 0
|
||||
local lowLevel = tonumber(row.reasons.lowLevel) or 0
|
||||
local wrongFood = tonumber(row.reasons.wrongFood) or 0
|
||||
local unknown = tonumber(row.reasons.unknown) or 0
|
||||
if noBuff <= 0
|
||||
and lowLevel <= 0
|
||||
and wrongFood <= 0
|
||||
and unknown <= 0
|
||||
and tonumber(row.blockAtOrAbovePetLevel) == nil
|
||||
and tonumber(row.maxPetLevel) == nil then
|
||||
store.exceptions.byItemId[key] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_FEED_FinalizeAttempt(attemptId, payload)
|
||||
local row = MTH_FEED_Runtime.attempts[attemptId]
|
||||
if type(row) ~= "table" then
|
||||
@@ -1241,10 +1380,18 @@ function MTH_FEED_FinalizeAttempt(attemptId, payload)
|
||||
if itemRow then
|
||||
itemRow.accepted = itemRow.accepted + 1
|
||||
end
|
||||
MTH_FEED_ClearStaleExceptionOnAccept(store, row.itemId)
|
||||
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
|
||||
if type(MTH_FX_Flash) == "function" and type(MTH_FX_IsEnabled) == "function"
|
||||
and MTH_FX_IsEnabled("pet.feed") then
|
||||
local target = getglobal("zButtonPet")
|
||||
if target then
|
||||
MTH_FX_Flash(target, 0.35, 1.00, 0.35, 0.5)
|
||||
end
|
||||
end
|
||||
else
|
||||
petRow.totals.rejected = petRow.totals.rejected + 1
|
||||
if reason == "no-buff" then
|
||||
@@ -1257,7 +1404,7 @@ function MTH_FEED_FinalizeAttempt(attemptId, payload)
|
||||
if itemRow then
|
||||
itemRow.rejected = itemRow.rejected + 1
|
||||
end
|
||||
MTH_FEED_UpsertException(store, row.itemId, reason)
|
||||
MTH_FEED_UpsertException(store, row.itemId, reason, row.petLevel)
|
||||
end
|
||||
|
||||
if itemRow then
|
||||
@@ -1280,9 +1427,6 @@ function MTH_FEED_FinalizeAttempt(attemptId, payload)
|
||||
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
|
||||
@@ -1325,6 +1469,30 @@ function MTH_FEED_GetSessionFeedCount(petId)
|
||||
return tonumber(MTH_FEED_Runtime.sessionFeedsByPetId[petKey]) or 0
|
||||
end
|
||||
|
||||
function MTH_FEED_DebugSessionFeeds()
|
||||
local pet = MTH_FEED_GetCurrentPetContext()
|
||||
local petId = pet and pet.petId or "(none)"
|
||||
local count = MTH_FEED_GetSessionFeedCount(petId)
|
||||
local hookOk = MTH_FEED_IsCoreHookInstalled()
|
||||
local frameOk = MTH_FEED_TrackerFrame ~= nil
|
||||
local activeId = tostring(MTH_FEED_ActiveCoreAttemptId or "nil")
|
||||
if type(DEFAULT_CHAT_FRAME) == "table" and DEFAULT_CHAT_FRAME.AddMessage then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff88aaff[MTH-FeedTrack]|r petId=" .. tostring(petId)
|
||||
.. " feeds=" .. tostring(count)
|
||||
.. " hookOk=" .. tostring(hookOk)
|
||||
.. " frameOk=" .. tostring(frameOk)
|
||||
.. " activeAttempt=" .. activeId)
|
||||
local anyFeeds = false
|
||||
for k, v in pairs(MTH_FEED_Runtime.sessionFeedsByPetId) do
|
||||
anyFeeds = true
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff88aaff[MTH-FeedTrack]|r session petId='" .. tostring(k) .. "' count=" .. tostring(v))
|
||||
end
|
||||
if not anyFeeds then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff88aaff[MTH-FeedTrack]|r (no session feeds recorded)")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_FEED_RecordUnknownFoodCandidate(payload)
|
||||
local store = MTH_FEED_EnsureStore()
|
||||
local itemId = payload and tonumber(payload.itemId) or nil
|
||||
@@ -1542,6 +1710,5 @@ function MTH_FEED_DebugDumpCurrentPet(limit)
|
||||
return true
|
||||
end
|
||||
|
||||
MTH_FEED_EnsureTrackerFrame()
|
||||
MTH_FEED_InstallCoreHooks()
|
||||
MTH_FEED_EnsureBootstrapFrame()
|
||||
|
||||
+337
-123
@@ -1,5 +1,5 @@
|
||||
MTH = MTH or {
|
||||
version = "1.0.3",
|
||||
version = "2.0.0",
|
||||
name = "MetaHunt",
|
||||
modules = {},
|
||||
config = {},
|
||||
@@ -7,7 +7,40 @@ MTH = MTH or {
|
||||
}
|
||||
|
||||
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 MTH_NON_HUNTER_BLOCK_MESSAGE = "You are not a hunter. Disable the add-on for this character."
|
||||
local MTH_REALM_BLOCK_MESSAGE = "Zero support for Ravencraft/Capybara mafia's realms. Consider joining Octowow."
|
||||
local MTH_BLOCKED_REALMS = {
|
||||
["Medivh"] = true,
|
||||
["\230\176\184\230\173\140\232\141\146\233\135\142"] = true,
|
||||
["\211\192\184\232\187\196\210\176"] = true,
|
||||
["\229\159\186\232\181\171\231\186\179\230\150\175"] = true,
|
||||
["\187\249\186\213\196\201\203\185"] = true,
|
||||
["\231\190\164\230\152\159\231\155\134\229\156\176"] = true,
|
||||
["\200\186\208\199\197\232\181\216"] = true,
|
||||
}
|
||||
|
||||
local MTH_MESSAGE_DEFAULTS = {
|
||||
initModulesLoaded = false,
|
||||
initSarcasticWelcome = true,
|
||||
petHungry = false,
|
||||
beastTrainingScan = true,
|
||||
spellbookScan = false,
|
||||
trainerScan = true,
|
||||
hunterLevelUp = true,
|
||||
hunterLevelQuiet = false,
|
||||
petLevelUp = true,
|
||||
petLoyaltyUp = true,
|
||||
petRanAway = true,
|
||||
mapMarkers = true,
|
||||
stableScan = true,
|
||||
}
|
||||
|
||||
local MTH_MODULE_DEFAULT_STATES = {
|
||||
autoquest = false,
|
||||
autobuy = false,
|
||||
feedomatic = false,
|
||||
icu = false,
|
||||
}
|
||||
|
||||
local function MTH_ClassGateTrace(_step, _detail)
|
||||
return
|
||||
@@ -27,7 +60,7 @@ local function MTH_ClassGateEnsureAnnounceFrame(self)
|
||||
if not self or self._classGateAnnounceFrame then
|
||||
return
|
||||
end
|
||||
local frame = CreateFrame("Frame", "MTH_ClassGateAnnounceFrame")
|
||||
local frame = CreateFrame("Frame", "MTH_ClassGateAnnounce")
|
||||
if not frame then
|
||||
return
|
||||
end
|
||||
@@ -38,12 +71,12 @@ local function MTH_ClassGateEnsureAnnounceFrame(self)
|
||||
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
|
||||
frame:SetScript("OnUpdate", function()
|
||||
this._mthElapsed = (this._mthElapsed or 0) + (arg1 or 0)
|
||||
if this._mthElapsed < 0.5 then
|
||||
return
|
||||
end
|
||||
self._mthElapsed = 0
|
||||
this._mthElapsed = 0
|
||||
if MTH and MTH._classGateAnnouncedChat then
|
||||
MTH_ClassGateClearAnnounceFrame(MTH)
|
||||
return
|
||||
@@ -172,24 +205,83 @@ function MTH:ApplyClassGate(_source)
|
||||
return blocked
|
||||
end
|
||||
|
||||
function MTH:GetCurrentRealmName()
|
||||
local realm = nil
|
||||
if type(GetRealmName) == "function" then
|
||||
realm = GetRealmName()
|
||||
end
|
||||
if (realm == nil or realm == "") and type(GetCVar) == "function" then
|
||||
realm = GetCVar("realmName")
|
||||
end
|
||||
return realm
|
||||
end
|
||||
|
||||
function MTH:IsRealmGateBlocked()
|
||||
return self and self._realmGateBlocked and true or false
|
||||
end
|
||||
|
||||
function MTH:CheckRealmGate()
|
||||
-- TEMP TESTING: allow Ravencraft client access while HunterBook layout is reviewed.
|
||||
if true then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Realm name is only reliable once logged in; an empty result never blocks.
|
||||
local realm = self:GetCurrentRealmName()
|
||||
if type(realm) == "string" and realm ~= "" then
|
||||
self._currentRealm = realm
|
||||
if MTH_BLOCKED_REALMS[realm] then
|
||||
self._realmGateBlocked = true
|
||||
end
|
||||
end
|
||||
return self:IsRealmGateBlocked()
|
||||
end
|
||||
|
||||
function MTH:AnnounceRealmGateBlocked()
|
||||
if self._realmGateAnnounced then
|
||||
return
|
||||
end
|
||||
local sink = MTH_TryOutputClassGateMessage(MTH_REALM_BLOCK_MESSAGE)
|
||||
if sink == "chat" or sink == "uierrors" or sink == "print" then
|
||||
self._realmGateAnnounced = true
|
||||
end
|
||||
end
|
||||
|
||||
function MTH:ApplyRealmGate(_source)
|
||||
if self:IsHardBlocked() then
|
||||
return true
|
||||
end
|
||||
if self:CheckRealmGate() then
|
||||
self:AnnounceRealmGateBlocked()
|
||||
if self.ShutdownForNonHunter then
|
||||
self:ShutdownForNonHunter(_source or "realm-gate")
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
MTH:ApplyClassGate("startup")
|
||||
|
||||
if not MTH._classGateLifecycleFrame then
|
||||
local classGateFrame = CreateFrame("Frame", "MTH_ClassGateLifecycleFrame")
|
||||
local classGateFrame = CreateFrame("Frame", "MTH_ClassGateLifecycle")
|
||||
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
|
||||
classGateFrame:SetScript("OnEvent", function()
|
||||
if MTH and event == "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)
|
||||
if MTH and MTH.ApplyRealmGate and MTH:ApplyRealmGate(event) then
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
return
|
||||
end
|
||||
if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate(event) and MTH._classGateAnnouncedChat then
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
end
|
||||
end)
|
||||
MTH._classGateLifecycleFrame = classGateFrame
|
||||
@@ -251,15 +343,6 @@ 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
|
||||
@@ -408,16 +491,57 @@ end
|
||||
|
||||
local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
|
||||
if type(name) ~= "string" or name == "" then
|
||||
MTH_ModuleStateDebug("read <invalid-name> 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_CharSavedVariables) ~= "table" then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.moduleStates) ~= "table" then
|
||||
MTH_CharSavedVariables.moduleStates = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.modules) ~= "table" then
|
||||
MTH_CharSavedVariables.modules = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.modules[resolvedName]) ~= "table" then
|
||||
MTH_CharSavedVariables.modules[resolvedName] = {}
|
||||
end
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
local hardDefault = MTH_MODULE_DEFAULT_STATES[resolvedName]
|
||||
if hardDefault ~= nil then
|
||||
local hasCharState = MTH_CharSavedVariables.moduleStates[resolvedName] ~= nil
|
||||
or MTH_CharSavedVariables.modules[resolvedName].enabled ~= nil
|
||||
if not hasCharState then
|
||||
local resolved = hardDefault and true or false
|
||||
MTH_CharSavedVariables.moduleStates[resolvedName] = resolved
|
||||
MTH_CharSavedVariables.modules[resolvedName].enabled = resolved
|
||||
return resolved
|
||||
end
|
||||
end
|
||||
|
||||
if 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_CharSavedVariables.moduleStates[resolvedName] = resolved
|
||||
return resolved
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if type(MTH_SavedVariables.moduleStates) == "table" then
|
||||
@@ -425,26 +549,11 @@ local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
|
||||
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
|
||||
if type(MTH_CharSavedVariables.modules[resolvedName]) ~= "table" then
|
||||
MTH_CharSavedVariables.modules[resolvedName] = {}
|
||||
end
|
||||
MTH_CharSavedVariables.modules[resolvedName].enabled = resolved
|
||||
return resolved
|
||||
end
|
||||
end
|
||||
@@ -455,10 +564,11 @@ local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
|
||||
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]
|
||||
MTH_CharSavedVariables.moduleStates[resolvedName] = resolved
|
||||
if type(MTH_CharSavedVariables.modules[resolvedName]) ~= "table" then
|
||||
MTH_CharSavedVariables.modules[resolvedName] = {}
|
||||
end
|
||||
MTH_CharSavedVariables.modules[resolvedName].enabled = resolved
|
||||
return resolved
|
||||
end
|
||||
end
|
||||
@@ -468,22 +578,15 @@ local function MTH_GetPersistedModuleEnabled(name, defaultEnabled)
|
||||
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 = {}
|
||||
MTH_CharSavedVariables.moduleStates[resolvedName] = resolved
|
||||
if type(MTH_CharSavedVariables.modules[resolvedName]) ~= "table" then
|
||||
MTH_CharSavedVariables.modules[resolvedName] = {}
|
||||
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
|
||||
MTH_CharSavedVariables.modules[resolvedName].enabled = 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
|
||||
|
||||
@@ -497,28 +600,22 @@ local function MTH_SetPersistedModuleEnabled(name, enabled)
|
||||
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
|
||||
if type(MTH_CharSavedVariables.modules) ~= "table" then
|
||||
MTH_CharSavedVariables.modules = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.modules[name]) ~= "table" then
|
||||
MTH_CharSavedVariables.modules[name] = {}
|
||||
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))
|
||||
MTH_CharSavedVariables.modules[name].enabled = enabled
|
||||
end
|
||||
|
||||
function MTH:InitEventRouter()
|
||||
@@ -526,10 +623,10 @@ function MTH:InitEventRouter()
|
||||
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)
|
||||
local frame = CreateFrame("Frame", "MTH_EventRouter")
|
||||
frame:SetScript("OnEvent", function()
|
||||
if MTH and MTH.DispatchEvent then
|
||||
MTH:DispatchEvent(evt, a1, a2, a3, a4, a5, a6, a7, a8, a9)
|
||||
MTH:DispatchEvent(event, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -589,7 +686,7 @@ function MTH:_SetModuleEventSubscriptions(name, enabled)
|
||||
end
|
||||
end
|
||||
|
||||
function MTH:DispatchEvent(evt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
|
||||
function MTH:DispatchEvent(evt, ea1, ea2, ea3, ea4, ea5, ea6, ea7, ea8, ea9)
|
||||
for name, module in pairs(self.modules) do
|
||||
if module.enabled and module.onEvent then
|
||||
local allow = true
|
||||
@@ -598,11 +695,10 @@ function MTH:DispatchEvent(evt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
|
||||
end
|
||||
if allow then
|
||||
local ok, err = pcall(function()
|
||||
module:onEvent(evt, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)
|
||||
module:onEvent(evt, ea1, ea2, ea3, ea4, ea5, ea6, ea7, ea8, ea9)
|
||||
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")
|
||||
MTH:Print("Module '" .. name .. "' event error on '" .. tostring(evt) .. "': " .. tostring(err), "error")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -633,7 +729,6 @@ function MTH:RegisterModule(name, module)
|
||||
end
|
||||
|
||||
if self.modules[name] then
|
||||
if self.debug then print("|cFFFFAA00MetaHunt|r: Module '" .. name .. "' already registered") end
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -648,8 +743,6 @@ function MTH:RegisterModule(name, module)
|
||||
|
||||
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)
|
||||
|
||||
@@ -673,7 +766,6 @@ function MTH:RegisterModule(name, module)
|
||||
self:_SetModuleEventSubscriptions(name, true)
|
||||
end
|
||||
|
||||
if self.debug then print("|cFF00AA00MetaHunt|r: Module '" .. name .. "' registered") end
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -738,7 +830,6 @@ function MTH:SetModuleEnabled(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
|
||||
|
||||
@@ -760,7 +851,6 @@ function MTH:SetModuleEnabled(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
|
||||
@@ -999,16 +1089,60 @@ function MTH:Log(msg, severity)
|
||||
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:Print(msg, severity)
|
||||
return self:Log(msg, severity)
|
||||
end
|
||||
|
||||
function MTH:GetMessageSettings()
|
||||
if not MTH_SavedVariables then
|
||||
self:InitSavedVariables()
|
||||
end
|
||||
if type(MTH_SavedVariables.messages) ~= "table" then
|
||||
MTH_SavedVariables.messages = {}
|
||||
end
|
||||
local settings = MTH_SavedVariables.messages
|
||||
for key, defaultValue in pairs(MTH_MESSAGE_DEFAULTS) do
|
||||
if settings[key] == nil then
|
||||
settings[key] = defaultValue and true or false
|
||||
else
|
||||
settings[key] = settings[key] and true or false
|
||||
end
|
||||
end
|
||||
return settings
|
||||
end
|
||||
|
||||
function MTH:IsMessageEnabled(key, defaultEnabled)
|
||||
local settings = self:GetMessageSettings()
|
||||
local value = settings and settings[key]
|
||||
if value == nil then
|
||||
if defaultEnabled ~= nil then
|
||||
value = defaultEnabled and true or false
|
||||
else
|
||||
value = true
|
||||
end
|
||||
settings[key] = value
|
||||
end
|
||||
return value and true or false
|
||||
end
|
||||
|
||||
function MTH:SetMessageEnabled(key, enabled)
|
||||
if type(key) ~= "string" or key == "" then
|
||||
return false
|
||||
end
|
||||
local settings = self:GetMessageSettings()
|
||||
if not settings then
|
||||
return false
|
||||
end
|
||||
settings[key] = enabled and true or false
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_Log(message, severity)
|
||||
if MTH and MTH.Log then
|
||||
return MTH:Log(message, severity)
|
||||
@@ -1063,8 +1197,6 @@ function MTH:InitSavedVariables()
|
||||
if not MTH_SavedVariables then
|
||||
MTH_SavedVariables = {
|
||||
modules = {},
|
||||
feedomatic = {},
|
||||
zhunter = {},
|
||||
}
|
||||
end
|
||||
|
||||
@@ -1074,16 +1206,17 @@ function MTH:InitSavedVariables()
|
||||
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 {}
|
||||
if type(MTH_SavedVariables.messages) ~= "table" then
|
||||
MTH_SavedVariables.messages = {}
|
||||
end
|
||||
if not MTH_SavedVariables.modules.zhunter then
|
||||
MTH_SavedVariables.modules.zhunter = MTH_SavedVariables.zhunter or {}
|
||||
for key, defaultValue in pairs(MTH_MESSAGE_DEFAULTS) do
|
||||
if MTH_SavedVariables.messages[key] == nil then
|
||||
MTH_SavedVariables.messages[key] = defaultValue and true or false
|
||||
else
|
||||
MTH_SavedVariables.messages[key] = MTH_SavedVariables.messages[key] and true or false
|
||||
end
|
||||
end
|
||||
|
||||
MTH_SavedVariables.feedomatic = MTH_SavedVariables.modules.feedomatic
|
||||
MTH_SavedVariables.zhunter = MTH_SavedVariables.modules.zhunter
|
||||
|
||||
if not MTH_CharSavedVariables then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
@@ -1137,6 +1270,24 @@ function MTH:InitSavedVariables()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- NOTE: the legacy/scattered SavedVariables consolidation (MTH_SV_EnsureSchema)
|
||||
-- is intentionally NOT called here. InitSavedVariables also runs at file-load time
|
||||
-- (before WoW has loaded the SV file from disk), when the roots are still empty
|
||||
-- defaults. Running the migration there operated on throwaway tables and made it
|
||||
-- re-announce every session. The migration is now driven from an ADDON_LOADED
|
||||
-- handler in api/savedvariables.lua, which fires once the real saved data exists.
|
||||
|
||||
-- Account-root module aliases must NEVER persist: the canonical stores live under
|
||||
-- .modules.<id>. feedomatic/zhunter used to be mirrored here (config.lua alias +
|
||||
-- InitSavedVariables seeds), which made WoW serialize a full DUPLICATE on disk.
|
||||
-- Strip them unconditionally on every load — even after the one-shot migration gate
|
||||
-- has closed — so any stray copy re-created by older code, or already sitting in an
|
||||
-- on-disk file from a previous version, is removed.
|
||||
if type(MTH_SavedVariables) == "table" then
|
||||
MTH_SavedVariables.feedomatic = nil
|
||||
MTH_SavedVariables.zhunter = nil
|
||||
end
|
||||
end
|
||||
|
||||
function MTH:GetModuleSavedVariables(name)
|
||||
@@ -1180,12 +1331,10 @@ function MTH:ApplyPersistedModuleStates(source)
|
||||
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)
|
||||
@@ -1202,24 +1351,22 @@ function MTH:EnsureModuleStateBootstrap()
|
||||
return self._moduleStateBootstrapFrame
|
||||
end
|
||||
|
||||
local frame = CreateFrame("Frame", "MTH_ModuleStateBootstrapFrame")
|
||||
local frame = CreateFrame("Frame", "MTH_ModuleBootstrap")
|
||||
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
|
||||
frame:SetScript("OnEvent", function()
|
||||
if event ~= "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)
|
||||
if not this then return end
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
end)
|
||||
|
||||
self._moduleStateBootstrapFrame = frame
|
||||
@@ -1248,8 +1395,20 @@ function MTH:AnnounceLoadComplete()
|
||||
moduleList = table.concat(enabled, ", ")
|
||||
end
|
||||
|
||||
self:Print("Check ignition: OK! Version " .. tostring(self.version or "unknown") .. " loaded.")
|
||||
--self:Print("Modules enabled : " .. moduleList .. ".")
|
||||
local namPowerStatus = ""
|
||||
if type(GetNampowerVersion) == "function" then
|
||||
local maj, min = GetNampowerVersion()
|
||||
maj = tonumber(maj) or 0
|
||||
min = tonumber(min) or 0
|
||||
if maj > 2 or (maj == 2 and min >= 40) then
|
||||
namPowerStatus = " | NamPower detected"
|
||||
self.nampower = true
|
||||
end
|
||||
end
|
||||
self:Print("Check Ignition.... OK! Version " .. tostring(self.version or "unknown") .. " loaded." .. namPowerStatus)
|
||||
if self:IsMessageEnabled("initModulesLoaded", false) then
|
||||
self:Print("Modules enabled : " .. moduleList .. ".")
|
||||
end
|
||||
|
||||
local petTrainingStore = nil
|
||||
if type(MTH_PETS_GetRootStore) == "function" then
|
||||
@@ -1284,8 +1443,10 @@ function MTH:AnnounceLoadComplete()
|
||||
end
|
||||
end
|
||||
if not hasCompletedPetScan then
|
||||
local playerLevel = (type(UnitLevel) == "function") and (tonumber(UnitLevel("player")) or 0) or 0
|
||||
if playerLevel >= 12 then
|
||||
local rawPlayerLevel = (type(UnitLevel) == "function") and UnitLevel("player") or nil
|
||||
local playerLevel = tonumber(rawPlayerLevel) or 0
|
||||
local shouldShowBeastTrainingReminder = (playerLevel >= 12 and playerLevel <= 60)
|
||||
if shouldShowBeastTrainingReminder then
|
||||
self:Print("You should open your Beast Training once to save your learned pet abilities.")
|
||||
self._loadCompleteAnnounced = true
|
||||
return
|
||||
@@ -1293,7 +1454,7 @@ function MTH:AnnounceLoadComplete()
|
||||
end
|
||||
|
||||
local welcomeMessages = {
|
||||
"You are now shipping the latest cutting edge technology.",
|
||||
"You are now shipping the latest cutting-edge Nelf technology.",
|
||||
"SHELLS ARE FOR PUSSIES.",
|
||||
"Sometimes, you just need a little less gun.",
|
||||
"Repeat 5 times : \"Un chasseur sachant chasser sans son chien\".",
|
||||
@@ -1380,6 +1541,59 @@ function MTH:AnnounceLoadComplete()
|
||||
"Your pet follows orders. Pathing follows dark magic.",
|
||||
"Today’s objective: hit crits and dodge responsibility.",
|
||||
"Go forth, badboy ranger — leave footprints, not explanations.",
|
||||
"MetaHunt loaded. Your plan has been forwarded to Quality Control for comedy review.",
|
||||
"Systems online. Tactical brilliance still listed as a future expansion feature.",
|
||||
"Hunter detected. Restraint not detected.",
|
||||
"Startup complete. Your pet is the adult in this relationship.",
|
||||
"All modules green. Your target selection remains performance art.",
|
||||
"Welcome back. The dungeon requested a waiver after seeing your login.",
|
||||
"Interface stable. Decision-making unstable. Proceed.",
|
||||
"MetaHunt armed. Your confidence has entered the area without supervision.",
|
||||
"Ready to hunt. Ready to kite. Not ready to explain any of it.",
|
||||
"Boot successful. Friendly fire is still technically ‘friendly.’",
|
||||
"Addon synced. Your threat meter just sent a stress signal.",
|
||||
"Loaded cleanly. Reputation with healers currently in freefall.",
|
||||
"Mission start: hit things from far away and deny everything in chat.",
|
||||
"Welcome back. Your pet missed you. Just kidding, it ran away.",
|
||||
"MetaHunt loaded. You're still a Huntard, but now you're an organized one.",
|
||||
"Aspects, traps, tracking... Don't worry, I'll handle the hard parts.",
|
||||
"Oh good, another hunter who needs an addon to remember their own spells.",
|
||||
"MetaHunt ready. Feign Death is not a personality trait.",
|
||||
"Loaded successfully. No, I can't fix your DPS.",
|
||||
"Welcome. Your pet's loyalty is higher than your raid attendance.",
|
||||
"MetaHunt online. Please don't Multishot the sheep.",
|
||||
"Greetings, hunter. I see you've chosen the 'easy' class and still need help.",
|
||||
"All systems go. Try not to pull the entire dungeon this time.",
|
||||
"MetaHunt activated. Reminder: Melee range is not your home.",
|
||||
"Welcome back. Your ammo count is almost as low as your situational awareness.",
|
||||
"Loaded. I'll manage your buttons since you clearly have too many.",
|
||||
"MetaHunt ready. Aspect of the Cheetah in dungeons is a choice, not a strategy.",
|
||||
"Oh look, a hunter that uses addons. There might be hope for you yet.",
|
||||
"Welcome. No, you cannot tame that. Or that. Stop asking.",
|
||||
"MetaHunt standing by. Unlike you during your last Feign Death.",
|
||||
"Initialized. Fun fact: your pet has more utility than you do.",
|
||||
"All buttons configured. Now if only I could configure your aim.",
|
||||
"MetaHunt loaded. Growl is off, right? ...RIGHT?",
|
||||
"MetaHunt loaded. Your Freezing Trap will still break early. That's on you.",
|
||||
"Welcome. Aspect of the Pack is ready. Your group is not.",
|
||||
"Initializing... unlike your pet, I actually come when called.",
|
||||
"MetaHunt online. Remember: Hunter loot is everything. Everything.",
|
||||
"Loaded. Concussive Shot the flag carrier, not the rogue vanishing.",
|
||||
"Welcome back. Eyes of the Beast is not a valid boss strategy.",
|
||||
"Ready. Your Aimed Shot takes longer to cast than this addon took to load.",
|
||||
"MetaHunt armed. Scatter Shot into Freezing Trap — you'll get it right someday.",
|
||||
"Online. Don't worry, I track your cooldowns since you clearly don't.",
|
||||
"Welcome. Wing Clip and run. It's what you do best.",
|
||||
"Loaded. Distracting Shot is for the boss, not for your ego.",
|
||||
"MetaHunt ready. Your quiver is full but your skill bar is empty.",
|
||||
"Initialized. Serpent Sting on every target doesn't make you a DoT class.",
|
||||
"Welcome, hunter. Viper Sting the healer. No, the OTHER healer.",
|
||||
"Ready to hunt. Mend Pet is not optional, it's a lifestyle.",
|
||||
"MetaHunt loaded. Tranquilizing Shot the enrage, not the warrior.",
|
||||
"Online. Rapid Fire is a cooldown, not your approach to pulling.",
|
||||
"Welcome. You have 28 arrows left. Should've checked before the raid.",
|
||||
"Loaded. Bestial Wrath does not make YOU immune. Learned that yet?",
|
||||
"MetaHunt standing by. Dead zone? What dead zone? Oh... that dead zone.",
|
||||
}
|
||||
|
||||
local welcomeIndex = 1
|
||||
@@ -1391,7 +1605,9 @@ function MTH:AnnounceLoadComplete()
|
||||
welcomeIndex = math.random(1, welcomeCount)
|
||||
end
|
||||
end
|
||||
self:Print(welcomeMessages[welcomeIndex])
|
||||
if self:IsMessageEnabled("initSarcasticWelcome", true) then
|
||||
self:Print(welcomeMessages[welcomeIndex])
|
||||
end
|
||||
self._loadCompleteAnnounced = true
|
||||
end
|
||||
|
||||
@@ -1408,24 +1624,22 @@ function MTH:EnsureLoadCompleteAnnouncement()
|
||||
return self._loadCompleteFrame
|
||||
end
|
||||
|
||||
local frame = CreateFrame("Frame", "MTH_LoadCompleteAnnounceFrame")
|
||||
local frame = CreateFrame("Frame", "MTH_LoadAnnounce")
|
||||
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
|
||||
frame:SetScript("OnEvent", function()
|
||||
if event ~= "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)
|
||||
if not this then return end
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
end)
|
||||
|
||||
self._loadCompleteFrame = frame
|
||||
|
||||
@@ -0,0 +1,386 @@
|
||||
-- MetaHunt level tracker. All training facts come from MTH_DS_TrainerCatalog
|
||||
-- through trainer-spell-planner.lua; this module only detects progression and
|
||||
-- presents the resulting facts through chat and animations.
|
||||
|
||||
local MTH_LT_Frame = nil
|
||||
local MTH_LT_PendingPetCheck = false
|
||||
|
||||
local MTH_LT_HunterLines = {
|
||||
[1] = "You can now track beasts. Revolutionary: look for the things you intend to shoot.",
|
||||
[2] = "The wilderness remains deeply unimpressed.",
|
||||
[3] = "Still mostly leather, arrows, and misplaced confidence.",
|
||||
[4] = "You have actual trainer lessons now. Try not to spend all your coin on repairs.",
|
||||
[5] = "Your pet is doing its share. Please consider doing yours.",
|
||||
[6] = "A hunter's mark: because shouting at the target was not precise enough.",
|
||||
[7] = "Every pull is a plan until it becomes cardio.",
|
||||
[8] = "Concussive Shot. Distance is now a tactical concept, not an accident.",
|
||||
[9] = "Your arrows have begun their long argument with gravity.",
|
||||
[10] = "You have a hawk aspect. The bird remains unavailable for comment.",
|
||||
[11] = "A fine age for discovering that pets also require paperwork.",
|
||||
[12] = "Mend Pet unlocked. Your pet has noticed this was overdue.",
|
||||
[13] = "The wilderness has upgraded its complaints department.",
|
||||
[14] = "Eagle Eye. Finally, a way to inspect trouble before walking into it.",
|
||||
[15] = "You have survived another level through skill, luck, or a pet-shaped shield.",
|
||||
[16] = "Traps and sharp things. Subtlety continues to be optional.",
|
||||
[17] = "Your pet is still carrying the emotional weight of this partnership.",
|
||||
[18] = "Multi-Shot. One target was apparently an unacceptable limitation.",
|
||||
[19] = "Soon you may run faster. The enemies will take this personally.",
|
||||
[20] = "Congratulations: escape now has a speed setting.",
|
||||
[21] = "Fast enough to arrive at the next questionable decision sooner.",
|
||||
[22] = "Your trainer has new bills and somehow they are all addressed to you.",
|
||||
[23] = "The pet remains employed despite the working conditions.",
|
||||
[24] = "Beast Lore. At last: research before violence. Briefly.",
|
||||
[25] = "Halfway to better excuses, nowhere near fewer mistakes.",
|
||||
[26] = "Rapid Fire. Your bow has entered its impatient phase.",
|
||||
[27] = "Things are going well enough to become dangerous.",
|
||||
[28] = "Your arrows now contain a measurable amount of intent.",
|
||||
[29] = "Prepare for the hunter's most respected tactical maneuver.",
|
||||
[30] = "Feign Death. Why win a fight when you can become administrative debris?",
|
||||
[31] = "You are alive, which validates absolutely nothing.",
|
||||
[32] = "Flare. Stealth has been downgraded to a suggestion.",
|
||||
[33] = "The next trap is definitely part of the plan.",
|
||||
[34] = "Explosive Trap. Negotiation has left the chat.",
|
||||
[35] = "Your pet thinks this is all normal. That is worrying.",
|
||||
[36] = "More damage, more maintenance, same heroic disregard for repair bills.",
|
||||
[37] = "Keep going. The next disaster is probably already pathing toward you.",
|
||||
[38] = "Your toolkit is becoming impressively hard to explain to civilized people.",
|
||||
[39] = "One level until armor finally admits you have been taking hits.",
|
||||
[40] = "Mail training. Leather got you this far; enemies kept putting holes in it.",
|
||||
[41] = "You are now visibly better protected from consequences.",
|
||||
[42] = "More Multi-Shot: because collateral damage needed a sequel.",
|
||||
[43] = "Your pet has grown used to the sound of avoidable danger.",
|
||||
[44] = "The trainer is delighted. Your coin pouch is less enthusiastic.",
|
||||
[45] = "You are a seasoned hunter now, which means the mistakes are expensive.",
|
||||
[46] = "Nature resistance: proof that the outdoors have been escalating.",
|
||||
[47] = "Still alive. Still pointing the dangerous end away from yourself. Usually.",
|
||||
[48] = "Your damage has matured into a fully funded public nuisance.",
|
||||
[49] = "One more level until the trainer starts charging executive prices.",
|
||||
[50] = "Your arrows have tenure now.",
|
||||
[51] = "The wilderness would like a word. It has a queue.",
|
||||
[52] = "Aimed Shot hits harder, which is your preferred form of diplomacy.",
|
||||
[53] = "Your pet remains your most reliable party member. Make of that what you will.",
|
||||
[54] = "Explosive Trap again. The floor continues to be a valid target.",
|
||||
[55] = "Almost there. Try not to die to something with a tutorial tooltip.",
|
||||
[56] = "Aspect of the Viper. Mana problems now have a bird-shaped solution.",
|
||||
[57] = "The finish line is visible. So are several elite mobs. Choose carefully.",
|
||||
[58] = "The final trainer bills are arriving with alarming confidence.",
|
||||
[59] = "One last level. Your pet has begun drafting a victory speech.",
|
||||
[60] = "You made it. The wildlife will remember this unfavorably.",
|
||||
}
|
||||
|
||||
local MTH_LT_Priority = {
|
||||
["Feign Death"] = 100,
|
||||
["Mail"] = 95,
|
||||
["Aspect of the Cheetah"] = 90,
|
||||
["Rapid Fire"] = 88,
|
||||
["Aspect of the Pack"] = 86,
|
||||
["Explosive Trap"] = 84,
|
||||
["Freezing Trap"] = 82,
|
||||
["Flare"] = 80,
|
||||
["Eagle Eye"] = 78,
|
||||
["Multi-Shot"] = 76,
|
||||
["Aimed Shot"] = 74,
|
||||
["Aspect of the Viper"] = 72,
|
||||
["Trueshot Aura"] = 70,
|
||||
["Volley"] = 68,
|
||||
["Track Hidden"] = 66,
|
||||
}
|
||||
|
||||
local MTH_LT_PetLines = {
|
||||
"It is now statistically better than your last pull.",
|
||||
"The food budget has noticed.",
|
||||
"Please do not celebrate by pulling three extra mobs.",
|
||||
"It remains committed to solving problems with teeth.",
|
||||
"The wilderness has filed no objection.",
|
||||
"Your dependable colleague is carrying this team again.",
|
||||
}
|
||||
|
||||
local MTH_LT_LoyaltyLines = {
|
||||
[2] = "It tolerates your leadership as long as the food arrives on time.",
|
||||
[3] = "Your authority has been acknowledged, reluctantly.",
|
||||
[4] = "It has decided your plans are survivable often enough.",
|
||||
[5] = "Please do not reward this trust with an elite pull.",
|
||||
[6] = "Against all evidence, it trusts you completely.",
|
||||
}
|
||||
|
||||
local function MTH_LT_Store()
|
||||
if not MTH_CharSavedVariables then MTH_CharSavedVariables = {} end
|
||||
if type(MTH_CharSavedVariables.levelTracker) ~= "table" then
|
||||
MTH_CharSavedVariables.levelTracker = { petLine = 0, loyaltyLine = 0 }
|
||||
end
|
||||
return MTH_CharSavedVariables.levelTracker
|
||||
end
|
||||
|
||||
local function MTH_LT_MessageEnabled(key, defaultValue)
|
||||
return MTH and type(MTH.IsMessageEnabled) == "function" and MTH:IsMessageEnabled(key, defaultValue)
|
||||
end
|
||||
|
||||
local function MTH_LT_FormatCost(copper)
|
||||
local total = tonumber(copper) or 0
|
||||
local gold = math.floor(total / 10000)
|
||||
local silver = math.floor((total - gold * 10000) / 100)
|
||||
local bronze = total - gold * 10000 - silver * 100
|
||||
local parts = {}
|
||||
if gold > 0 then table.insert(parts, "|cffffd100" .. gold .. "g|r") end
|
||||
if silver > 0 or gold > 0 then table.insert(parts, "|cffc7c7cf" .. silver .. "s|r") end
|
||||
table.insert(parts, "|cffeda55f" .. bronze .. "c|r")
|
||||
return table.concat(parts, " ")
|
||||
end
|
||||
|
||||
local function MTH_LT_NextLine(store, key, lines)
|
||||
store[key] = (tonumber(store[key]) or 0) + 1
|
||||
if store[key] > table.getn(lines) then store[key] = 1 end
|
||||
return lines[store[key]]
|
||||
end
|
||||
|
||||
local function MTH_LT_GetHeadline(plan)
|
||||
local best = nil
|
||||
local bestPriority = -1
|
||||
for _, entry in ipairs(plan.rows or {}) do
|
||||
local priority = MTH_LT_Priority[tostring(entry.row and entry.row.name or "")] or 0
|
||||
if priority > bestPriority then best, bestPriority = entry, priority end
|
||||
end
|
||||
return best
|
||||
end
|
||||
|
||||
local function MTH_LT_FormatRows(plan)
|
||||
local labels = {}
|
||||
for index, entry in ipairs(plan.rows or {}) do
|
||||
local row = entry.row or {}
|
||||
local label = tostring(row.name or "Unknown")
|
||||
if row.rank and row.rank ~= "" then label = label .. " " .. tostring(row.rank) end
|
||||
table.insert(labels, label)
|
||||
end
|
||||
return table.concat(labels, ", ")
|
||||
end
|
||||
|
||||
local function MTH_LT_GrowlRankForLevel(level)
|
||||
local rank = math.floor((tonumber(level) or 1) / 10) + 1
|
||||
if rank < 1 then rank = 1 end
|
||||
if rank > 7 then rank = 7 end
|
||||
return rank
|
||||
end
|
||||
|
||||
local function MTH_LT_BuildPetGrowlPlan(level, preview, honored)
|
||||
if type(MTH_TrainerPlan_BuildLevel) ~= "function" then return nil end
|
||||
local plan = MTH_TrainerPlan_BuildLevel("pet", level, { honored = honored and true or false, includeKnown = preview })
|
||||
local growlPlan = {
|
||||
rows = {},
|
||||
unlearned = { count = 0, cost = 0 },
|
||||
}
|
||||
for _, entry in ipairs(plan.rows or {}) do
|
||||
if tostring(entry.row and entry.row.name or "") == "Growl" then
|
||||
table.insert(growlPlan.rows, entry)
|
||||
growlPlan.unlearned.count = growlPlan.unlearned.count + 1
|
||||
growlPlan.unlearned.cost = growlPlan.unlearned.cost + (tonumber(entry.price) or 0)
|
||||
end
|
||||
end
|
||||
return growlPlan
|
||||
end
|
||||
|
||||
local function MTH_LT_HandlePetGrowlLevel(petName, level)
|
||||
if level < 10 or level > 60 or math.mod(level, 10) ~= 0 then return end
|
||||
local rank = MTH_LT_GrowlRankForLevel(level)
|
||||
local known = type(MTH_HT_GetSpellInfo) == "function" and MTH_HT_GetSpellInfo("Growl") or nil
|
||||
if known and (tonumber(known.rankNum) or 0) >= rank then
|
||||
if type(MTH_Growl_OfferForTamedPet) == "function" then
|
||||
MTH_Growl_OfferForTamedPet(petName, level)
|
||||
end
|
||||
elseif MTH_LT_MessageEnabled("petLevelUp", true) then
|
||||
MTH:Print(petName .. " can now learn Growl Rank " .. tostring(rank) .. ". Visit a Pet Trainer to learn it.")
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_LT_PrintSpellList(plan)
|
||||
local line = ""
|
||||
local isFirstLine = true
|
||||
for _, entry in ipairs(plan.rows or {}) do
|
||||
local row = entry.row or {}
|
||||
local label = tostring(row.name or "Unknown")
|
||||
if row.rank and row.rank ~= "" then label = label .. " " .. tostring(row.rank) end
|
||||
if line == "" then
|
||||
line = label
|
||||
elseif string.len(line) + string.len(label) + 2 > 92 then
|
||||
MTH:Print((isFirstLine and "New spells available: " or "And also: ") .. line)
|
||||
isFirstLine = false
|
||||
line = label
|
||||
else
|
||||
line = line .. ", " .. label
|
||||
end
|
||||
end
|
||||
if line ~= "" then MTH:Print((isFirstLine and "New spells available: " or "And also: ") .. line) end
|
||||
end
|
||||
|
||||
local function MTH_LT_AnnounceTraining(kind, level, eventKind, preview)
|
||||
if type(MTH_TrainerPlan_BuildLevel) ~= "function" then return end
|
||||
local base = MTH_TrainerPlan_BuildLevel(kind, level, { honored = false, includeKnown = preview })
|
||||
local honored = MTH_TrainerPlan_BuildLevel(kind, level, { honored = true, includeKnown = preview })
|
||||
if base.unlearned.count <= 0 then
|
||||
if kind == "hunter" and MTH_LT_MessageEnabled("hunterLevelUp", true) then
|
||||
MTH:Print("Congratz! You hit level " .. tostring(level) .. ".")
|
||||
end
|
||||
if preview then MTH:Print("Level test: no " .. kind .. " trainer spells in the 1.18.1 catalog at level " .. tostring(level) .. ".") end
|
||||
return
|
||||
end
|
||||
local headline = MTH_LT_GetHeadline(base)
|
||||
local headlineText = headline and headline.row and tostring(headline.row.name or "") or "New training"
|
||||
if headline and headline.row and headline.row.rank and headline.row.rank ~= "" then headlineText = headlineText .. " " .. headline.row.rank end
|
||||
local isLevelCap = kind == "hunter" and level == 60
|
||||
if isLevelCap then headlineText = "The hunt is yours" end
|
||||
local isPetGrowlMilestone = kind == "pet" and level >= 10 and level <= 60 and math.mod(level, 10) == 0
|
||||
if isPetGrowlMilestone then headlineText = "Growl Rank " .. tostring(MTH_LT_GrowlRankForLevel(level)) end
|
||||
if kind == "hunter" and not isLevelCap then headlineText = nil end
|
||||
if MTH_LT_MessageEnabled(eventKind == "hunter" and "hunterLevelUp" or "petLevelUp", true) then
|
||||
if kind == "hunter" then
|
||||
MTH:Print("Congratz! You hit level " .. tostring(level) .. ".")
|
||||
MTH:Print(MTH_LT_HunterLines[level] or "New training is available.")
|
||||
MTH:Print("The spells you can learn will cost you " .. MTH_LT_FormatCost(base.unlearned.cost)
|
||||
.. ", but only " .. MTH_LT_FormatCost(honored.unlearned.cost) .. " if you are Honored with the master.")
|
||||
MTH_LT_PrintSpellList(base)
|
||||
else
|
||||
local displayPlan = isPetGrowlMilestone and MTH_LT_BuildPetGrowlPlan(level, preview) or base
|
||||
local displayHonoredPlan = isPetGrowlMilestone and MTH_LT_BuildPetGrowlPlan(level, preview, true) or honored
|
||||
if displayPlan and displayPlan.unlearned.count > 0 then
|
||||
MTH:Print("It can learn " .. MTH_LT_FormatRows(displayPlan))
|
||||
MTH:Print("Trainer cost: Base " .. MTH_LT_FormatCost(displayPlan.unlearned.cost) .. " | Honored " .. MTH_LT_FormatCost(displayHonoredPlan.unlearned.cost))
|
||||
end
|
||||
end
|
||||
end
|
||||
local isMilestone = kind == "hunter" and (level == 10 or level == 20 or level == 30 or level == 40 or level == 50 or level == 60)
|
||||
local flag = isMilestone and "level.huntermilestone" or (kind == "hunter" and "level.hunter" or "level.pet")
|
||||
if type(MTH_FX_IsEnabled) == "function" and MTH_FX_IsEnabled(flag) and type(MTH_Celebrate) == "function" then
|
||||
local subtitle = kind == "hunter" and (MTH_LT_HunterLines[level] or "New training is available.") or "New pet training is available. The trainer wants coin."
|
||||
if isMilestone and type(MTH_CelebrateBlackHole) == "function" then
|
||||
MTH_CelebrateBlackHole(isLevelCap and headlineText or "Level " .. tostring(level), subtitle)
|
||||
else
|
||||
local title = headlineText and ("Level " .. tostring(level) .. " - " .. headlineText) or "Level " .. tostring(level)
|
||||
MTH_Celebrate(title, subtitle, "fanfare", 5)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_LT_HandleHunterLevel(level)
|
||||
local store = MTH_LT_Store()
|
||||
local previous = tonumber(store.hunterLevel)
|
||||
store.hunterLevel = level
|
||||
if previous and level > previous then
|
||||
MTH_LT_AnnounceTraining("hunter", level, "hunter")
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_LT_GetPetState()
|
||||
if type(UnitExists) ~= "function" or not UnitExists("pet") then return nil end
|
||||
local level = type(UnitLevel) == "function" and tonumber(UnitLevel("pet")) or nil
|
||||
local name = type(UnitName) == "function" and UnitName("pet") or "Your pet"
|
||||
if not level or level <= 0 then return nil end
|
||||
-- Loyalty is a localized name via GetPetLoyalty(); reuse the pet service which
|
||||
-- parses it (plus UI fallbacks) into a numeric 1-6 level.
|
||||
local loyalty = nil
|
||||
if type(MTH_GetCurrentPetInfo) == "function" then
|
||||
local info = MTH_GetCurrentPetInfo()
|
||||
if type(info) == "table" then loyalty = tonumber(info.loyaltyLevel) or nil end
|
||||
end
|
||||
return { key = tostring(name or "pet"), name = tostring(name or "Your pet"), level = level, loyalty = loyalty }
|
||||
end
|
||||
|
||||
local function MTH_LT_HandlePetState()
|
||||
MTH_LT_PendingPetCheck = false
|
||||
local state = MTH_LT_GetPetState()
|
||||
if not state then return end
|
||||
local store = MTH_LT_Store()
|
||||
local pet = store.pet
|
||||
if type(pet) ~= "table" or pet.key ~= state.key then
|
||||
store.pet = state
|
||||
return
|
||||
end
|
||||
if state.level > (tonumber(pet.level) or 0) then
|
||||
local line = MTH_LT_NextLine(store, "petLine", MTH_LT_PetLines)
|
||||
if MTH_LT_MessageEnabled("petLevelUp", true) then MTH:Print(state.name .. " reached level " .. state.level .. ". " .. line) end
|
||||
MTH_LT_AnnounceTraining("pet", state.level, "pet")
|
||||
MTH_LT_HandlePetGrowlLevel(state.name, state.level)
|
||||
end
|
||||
if state.loyalty and state.loyalty > 1 and state.loyalty > (tonumber(pet.loyalty) or 0) then
|
||||
local line = MTH_LT_LoyaltyLines[state.loyalty] or "The food arrangement appears to be working."
|
||||
if MTH_LT_MessageEnabled("petLoyaltyUp", true) then MTH:Print(state.name .. " reached Loyalty Level " .. state.loyalty .. ". " .. line) end
|
||||
if type(MTH_FX_IsEnabled) == "function" and MTH_FX_IsEnabled("level.loyalty") then
|
||||
local title = state.name .. " - Loyalty " .. state.loyalty
|
||||
if state.loyalty == 6 and type(MTH_CelebrateBlackHole) == "function" then
|
||||
MTH_CelebrateBlackHole(title, line)
|
||||
elseif type(MTH_Celebrate) == "function" then
|
||||
MTH_Celebrate(title, line, "fanfare")
|
||||
end
|
||||
end
|
||||
end
|
||||
store.pet = state
|
||||
end
|
||||
|
||||
local function MTH_LT_RequestPetCheck()
|
||||
if MTH_LT_PendingPetCheck or not MTH_LT_Frame then return end
|
||||
MTH_LT_PendingPetCheck = true
|
||||
MTH_LT_Frame.elapsed = 0
|
||||
MTH_LT_Frame:SetScript("OnUpdate", function()
|
||||
this.elapsed = (this.elapsed or 0) + (arg1 or 0)
|
||||
if this.elapsed >= 0.35 then
|
||||
this:SetScript("OnUpdate", nil)
|
||||
MTH_LT_HandlePetState()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function MTH_LevelTracker_Test(kind, level)
|
||||
if kind == "hunter" or kind == "pet" then
|
||||
local targetLevel = tonumber(level) or ((kind == "hunter") and (tonumber(UnitLevel("player")) or 1) or 1)
|
||||
MTH:Print("Level test: previewing " .. kind .. " trainer spells at level " .. tostring(targetLevel) .. ".")
|
||||
MTH_LT_AnnounceTraining(kind, targetLevel, kind, true)
|
||||
return true
|
||||
end
|
||||
if kind == "loyalty" then
|
||||
local state = MTH_LT_GetPetState()
|
||||
local loyalty = tonumber(level) or (state and state.loyalty) or 1
|
||||
if loyalty <= 1 then
|
||||
MTH:Print("Pet Loyalty starts at level 1 when you tame a pet. Test levels 2 through 6.")
|
||||
return false
|
||||
end
|
||||
local petName = state and state.name or "Your pet"
|
||||
local line = MTH_LT_LoyaltyLines[loyalty] or "The food arrangement appears to be working."
|
||||
MTH:Print(petName .. " reached Loyalty Level " .. tostring(loyalty) .. ". " .. line)
|
||||
local title = petName .. " - Loyalty " .. tostring(loyalty)
|
||||
if loyalty == 6 and type(MTH_CelebrateBlackHole) == "function" then
|
||||
MTH_CelebrateBlackHole(title, line)
|
||||
elseif type(MTH_Celebrate) == "function" then
|
||||
MTH_Celebrate(title, line, "fanfare")
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_LevelTracker_Initialize()
|
||||
if MTH_LT_Frame then return end
|
||||
MTH_LT_Frame = CreateFrame("Frame", "MTH_LevelTracker")
|
||||
MTH_LT_Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_LT_Frame:RegisterEvent("PLAYER_LEVEL_UP")
|
||||
MTH_LT_Frame:RegisterEvent("UNIT_LEVEL")
|
||||
MTH_LT_Frame:RegisterEvent("UNIT_PET")
|
||||
MTH_LT_Frame:RegisterEvent("PET_BAR_UPDATE")
|
||||
MTH_LT_Frame:RegisterEvent("UNIT_HAPPINESS")
|
||||
MTH_LT_Frame:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
local level = type(UnitLevel) == "function" and tonumber(UnitLevel("player")) or nil
|
||||
if level then MTH_LT_Store().hunterLevel = level end
|
||||
MTH_LT_RequestPetCheck()
|
||||
elseif event == "PLAYER_LEVEL_UP" then
|
||||
MTH_LT_HandleHunterLevel(tonumber(arg1) or tonumber(UnitLevel("player")) or 1)
|
||||
elseif event == "UNIT_LEVEL" and arg1 == "pet" then
|
||||
MTH_LT_RequestPetCheck()
|
||||
elseif event == "UNIT_PET" and arg1 == "player" then
|
||||
MTH_LT_RequestPetCheck()
|
||||
elseif event == "PET_BAR_UPDATE" then
|
||||
MTH_LT_RequestPetCheck()
|
||||
elseif event == "UNIT_HAPPINESS" then
|
||||
MTH_LT_RequestPetCheck()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
MTH_LevelTracker_Initialize()
|
||||
+149
-21
@@ -20,6 +20,7 @@ end
|
||||
local MTH_PT_ApplyScan
|
||||
local MTH_PT_AbilityHasPositiveRanks
|
||||
local MTH_PT_PromoteUnrankedKnownAliases
|
||||
local MTH_PT_BootstrapFrame = nil
|
||||
|
||||
local MTH_PT_VERBOSE_SCAN_LOGS = false
|
||||
|
||||
@@ -167,9 +168,17 @@ function MTH_PT_HasBeastTrainingRows()
|
||||
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")
|
||||
if line == nil then return end
|
||||
if MTH_DebugFrame and type(MTH_DebugFrame.AddInfo) == "function" then
|
||||
MTH_DebugFrame:AddInfo("[BeastTraining] " .. tostring(line))
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_PT_IsScanMessageEnabled()
|
||||
if not (MTH and MTH.IsMessageEnabled) then
|
||||
return true
|
||||
end
|
||||
return MTH:IsMessageEnabled("beastTrainingScan", true)
|
||||
end
|
||||
|
||||
local function MTH_PT_MakeTokenFromAbilityRank(abilityName, rankNumber)
|
||||
@@ -329,6 +338,39 @@ local function MTH_PT_IsHunterTokenKnown(token)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Rank-strict variant of MTH_PT_IsHunterTokenKnown: only the EXACT token counts, with
|
||||
-- no fall back to the base ability. Knowing Charge (Rank 1) must NOT make Charge (Rank 2)
|
||||
-- look already-learned, otherwise learning a new rank is never recorded or announced.
|
||||
local function MTH_PT_IsExactTokenKnown(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
|
||||
return false
|
||||
end
|
||||
|
||||
-- Authoritative, rank-strict check for whether the HUNTER has learned a pet ability at a
|
||||
-- given rank. Consults both hunterKnownMap and spellMap, so callers (e.g. the tooltip hint)
|
||||
-- stay correct even when a full Beast Training rescan has not yet rebuilt spellMap rows.
|
||||
function MTH_IsPetAbilityKnownByHunter(abilityName, rankNumber)
|
||||
local ability = MTH_PT_NormalizeName(abilityName)
|
||||
if ability == "" then
|
||||
return false
|
||||
end
|
||||
local token = MTH_PT_MakeTokenFromAbilityRank(ability, rankNumber)
|
||||
if token == "" then
|
||||
return false
|
||||
end
|
||||
return MTH_PT_IsExactTokenKnown(token)
|
||||
end
|
||||
|
||||
local function MTH_PT_ProcessLearnSystemMessage(rawMessage)
|
||||
local message = tostring(rawMessage or "")
|
||||
if message == "" then return false end
|
||||
@@ -342,6 +384,10 @@ local function MTH_PT_ProcessLearnSystemMessage(rawMessage)
|
||||
_, _, 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
|
||||
-- Only trace messages that look like a learn line, to avoid flooding the log.
|
||||
if MTH_PT_DebugLog and string.find(message, "[Ll]earned") then
|
||||
MTH_PT_DebugLog("learn-msg parse FAILED: '" .. tostring(message) .. "'")
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -362,7 +408,7 @@ local function MTH_PT_ProcessLearnSystemMessage(rawMessage)
|
||||
local token = MTH_PT_MakeTokenFromAbilityRank(abilityName, rankNumber)
|
||||
if token == "" then return false end
|
||||
|
||||
local alreadyKnown = MTH_PT_IsHunterTokenKnown(token)
|
||||
local alreadyKnown = MTH_PT_IsExactTokenKnown(token)
|
||||
local changed = false
|
||||
if not alreadyKnown then
|
||||
changed = MTH_PT_MarkHunterKnownToken(token)
|
||||
@@ -373,6 +419,12 @@ local function MTH_PT_ProcessLearnSystemMessage(rawMessage)
|
||||
petRecorded = MTH_PETS_RecordCurrentPetLearnedAbility(abilityName, rankNumber, "beasttraining:learn-msg") and true or false
|
||||
end
|
||||
|
||||
if MTH_PT_DebugLog then
|
||||
MTH_PT_DebugLog("learn-msg matched: ability='" .. tostring(abilityName) .. "' rank=" .. tostring(rankNumber)
|
||||
.. " token='" .. tostring(token) .. "' alreadyKnown=" .. tostring(alreadyKnown)
|
||||
.. " changed=" .. tostring(changed) .. " petRecorded=" .. tostring(petRecorded))
|
||||
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))
|
||||
@@ -382,15 +434,20 @@ local function MTH_PT_ProcessLearnSystemMessage(rawMessage)
|
||||
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 .. ".")
|
||||
if MTH:IsMessageEnabled("beastTrainingScan", true) 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
|
||||
end
|
||||
if changed then
|
||||
MTH_PT_ApplyScan("learn-msg")
|
||||
end
|
||||
if changed and type(MTH_CelebratePetAbility) == "function" then
|
||||
MTH_CelebratePetAbility(label)
|
||||
end
|
||||
if type(MTH_PSP_RequestScan) == "function" then
|
||||
MTH_PSP_RequestScan("train-learn-msg")
|
||||
end
|
||||
@@ -605,18 +662,22 @@ function MTH_PT_ApplyScan(triggerReason)
|
||||
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.")
|
||||
if MTH_PT_IsScanMessageEnabled() then
|
||||
MTH:Print("Initial train scan complete: " .. tostring(totalBaselines) .. " baselines / " .. tostring(totalRanks) .. " ranks recorded for this character.")
|
||||
end
|
||||
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")
|
||||
if MTH_PT_IsScanMessageEnabled() then
|
||||
MTH:Print(msg)
|
||||
end
|
||||
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")
|
||||
if MTH_PT_IsScanMessageEnabled() then
|
||||
MTH:Print(msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -644,6 +705,24 @@ MTH_PT_IsPetBookVisible = function()
|
||||
return false
|
||||
end
|
||||
|
||||
-- Cheap content signature of the trainer's craft rows, so repeated reads of the
|
||||
-- same list (multiple CRAFT_UPDATE / open events) don't each pay for a full,
|
||||
-- datastore-wide re-scan (that pile-up was freezing the client).
|
||||
local MTH_PT_LastScanSignature = nil
|
||||
local function MTH_PT_ComputeCraftSignature()
|
||||
local getNumCrafts = MTH_PT_GetGlobal("GetNumCrafts")
|
||||
local getCraftInfo = MTH_PT_GetGlobal("GetCraftInfo")
|
||||
if type(getNumCrafts) ~= "function" or type(getCraftInfo) ~= "function" then return nil end
|
||||
local total = tonumber(getNumCrafts()) or 0
|
||||
if total <= 0 then return nil end
|
||||
local parts = { tostring(total) }
|
||||
for i = 1, total do
|
||||
local name, sub, ctype = getCraftInfo(i)
|
||||
table.insert(parts, tostring(name or "") .. "~" .. tostring(sub or "") .. "~" .. tostring(ctype or ""))
|
||||
end
|
||||
return table.concat(parts, "|")
|
||||
end
|
||||
|
||||
local function MTH_PT_RunDoubleScan(triggerBase)
|
||||
local triggerText = tostring(triggerBase or "")
|
||||
local feedbackOnOpen = (
|
||||
@@ -653,24 +732,34 @@ local function MTH_PT_RunDoubleScan(triggerBase)
|
||||
or triggerText == "beast-training-rows-open"
|
||||
or triggerText == "petbook-visible-poll"
|
||||
)
|
||||
-- Stay quiet while Growl auto-teach is driving the trainer window.
|
||||
if feedbackOnOpen and type(MTH_Growl_IsTeaching) == "function" and MTH_Growl_IsTeaching() then
|
||||
feedbackOnOpen = false
|
||||
end
|
||||
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
|
||||
if feedbackOnOpen and MTH and MTH.Print and MTH_PT_IsScanMessageEnabled() then
|
||||
MTH:Print("Beast Training opened, but scan did not run yet (no scannable rows detected).")
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local signature = MTH_PT_ComputeCraftSignature()
|
||||
if signature and signature == MTH_PT_LastScanSignature then
|
||||
if MTH_PT_DebugLog then MTH_PT_DebugLog("scan skipped (unchanged signature): trigger=" .. triggerText) end
|
||||
return true
|
||||
end
|
||||
local t0 = time()
|
||||
local ok, added, baselines, ranks = MTH_PT_ApplyScan(triggerText .. ":single")
|
||||
local t1 = time()
|
||||
if ok and signature then MTH_PT_LastScanSignature = signature end
|
||||
|
||||
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 feedbackOnOpen and MTH and MTH.Print and MTH_PT_IsScanMessageEnabled() 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."
|
||||
@@ -711,16 +800,22 @@ local function MTH_PT_OnEvent(_, evt, eventArg1)
|
||||
end
|
||||
|
||||
if evt == "PLAYER_ENTERING_WORLD" then
|
||||
local playerLevel = 0
|
||||
if type(UnitLevel) == "function" then
|
||||
playerLevel = tonumber(UnitLevel("player")) or 0
|
||||
end
|
||||
if UnitClass then
|
||||
local _, classToken = UnitClass("player")
|
||||
if classToken ~= "HUNTER" then return end
|
||||
end
|
||||
if not MTH_PT_HasAnyLearnedSpells() then
|
||||
if playerLevel >= 12 and 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.")
|
||||
if MTH_PT_IsScanMessageEnabled() then
|
||||
MTH:Print("No Beast Training scan found for this character. Open Beast Training (right panel) to record pet spell ranks.")
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
@@ -782,7 +877,7 @@ function MTH_PT_InitService()
|
||||
if MTH_PetTrainingFrame then
|
||||
return
|
||||
end
|
||||
MTH_PetTrainingFrame = CreateFrame("Frame", "MTHPetTrainingScanFrame")
|
||||
MTH_PetTrainingFrame = CreateFrame("Frame", "MTH_PetTrainingScan")
|
||||
MTH_PetTrainingFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_PetTrainingFrame:RegisterEvent("LEARNED_SPELL_IN_TAB")
|
||||
MTH_PetTrainingFrame:RegisterEvent("PET_TRAINING_SHOW")
|
||||
@@ -793,9 +888,8 @@ function MTH_PT_InitService()
|
||||
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)
|
||||
MTH_PetTrainingFrame:SetScript("OnUpdate", function()
|
||||
MTH_PT_PetBookPollElapsed = (MTH_PT_PetBookPollElapsed or 0) + (arg1 or 0)
|
||||
if MTH_PT_PetBookPollElapsed < 0.20 then
|
||||
return
|
||||
end
|
||||
@@ -815,6 +909,10 @@ function MTH_PT_InitService()
|
||||
MTH_PT_RunDoubleScanThrottled("beast-training-rows-present", 8)
|
||||
end
|
||||
|
||||
if (not hasRows) and MTH_PT_TrainingRowsWerePresent then
|
||||
-- Trainer closed: allow one fresh scan the next time it opens.
|
||||
MTH_PT_LastScanSignature = nil
|
||||
end
|
||||
MTH_PT_TrainingRowsWerePresent = hasRows
|
||||
MTH_PT_LastTrainingRowCount = rowCount
|
||||
|
||||
@@ -827,6 +925,35 @@ function MTH_PT_InitService()
|
||||
end)
|
||||
end
|
||||
|
||||
function MTH_PT_InitBootstrap()
|
||||
if MTH and MTH.ApplyClassGate and MTH:ApplyClassGate("trainscan-bootstrap") then
|
||||
return
|
||||
end
|
||||
if MTH_PetTrainingFrame or MTH_PT_BootstrapFrame then
|
||||
return
|
||||
end
|
||||
|
||||
local frame = CreateFrame("Frame")
|
||||
if not frame then
|
||||
return
|
||||
end
|
||||
frame:RegisterEvent("PET_TRAINING_SHOW")
|
||||
frame:RegisterEvent("CRAFT_SHOW")
|
||||
frame:SetScript("OnEvent", function()
|
||||
if event ~= "PET_TRAINING_SHOW" and event ~= "CRAFT_SHOW" then
|
||||
return
|
||||
end
|
||||
MTH_PT_InitService()
|
||||
if type(MTH_PT_OnEvent) == "function" then
|
||||
MTH_PT_OnEvent(MTH_PetTrainingFrame, event)
|
||||
end
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
MTH_PT_BootstrapFrame = nil
|
||||
end)
|
||||
MTH_PT_BootstrapFrame = frame
|
||||
end
|
||||
|
||||
function MTH_PT_ShutdownService(_reason)
|
||||
if not MTH_PetTrainingFrame then
|
||||
return
|
||||
@@ -910,3 +1037,4 @@ end
|
||||
|
||||
MTH_TR_InitService = MTH_PT_InitService
|
||||
MTH_TR_ShutdownService = MTH_PT_ShutdownService
|
||||
MTH_TR_InitBootstrap = MTH_PT_InitBootstrap
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt: Hunter Talent & Spellbook Scanner
|
||||
-- Runs once on login and re-runs when spells/talents change.
|
||||
--
|
||||
-- Exposes:
|
||||
-- MTH_HT_GetTalentRank(talentName) → currentRank (0 if unlearned)
|
||||
-- MTH_HT_HasTalent(talentName) → true if rank >= 1
|
||||
-- MTH_HT_GetSpellInfo(spellName) → { slot, rank, rankNum } or nil
|
||||
-- MTH_HT_RescanTalents() → force re-scan of all talent tabs
|
||||
-- MTH_HT_RescanSpellbook() → force re-scan of spellbook
|
||||
--
|
||||
-- Results cached in:
|
||||
-- MTH_HT_Talents["TalentName"] = { tab, idx, rank, maxRank }
|
||||
-- MTH_HT_Spells["SpellName"] = { slot, rank, rankNum }
|
||||
-- (spell entry is the *highest rank* known for that name)
|
||||
------------------------------------------------------
|
||||
|
||||
MTH_HT_Talents = {}
|
||||
MTH_HT_Spells = {}
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Talent scanner
|
||||
-- Uses GetNumTalentTabs / GetNumTalents / GetTalentInfo (all vanilla 1.12)
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
function MTH_HT_RescanTalents()
|
||||
MTH_HT_Talents = {}
|
||||
|
||||
local numTabs = GetNumTalentTabs and GetNumTalentTabs() or 0
|
||||
for tabIdx = 1, numTabs do
|
||||
local numTalents = GetNumTalents and GetNumTalents(tabIdx) or 0
|
||||
for talIdx = 1, numTalents do
|
||||
local name, _, _, _, currentRank, maxRank = GetTalentInfo(tabIdx, talIdx)
|
||||
if name and name ~= "" then
|
||||
local existing = MTH_HT_Talents[name]
|
||||
-- If the same name appears in multiple tabs (unlikely), keep highest rank.
|
||||
if not existing or (tonumber(currentRank) or 0) > (existing.rank or 0) then
|
||||
MTH_HT_Talents[name] = {
|
||||
tab = tabIdx,
|
||||
idx = talIdx,
|
||||
rank = tonumber(currentRank) or 0,
|
||||
maxRank = tonumber(maxRank) or 0,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Spellbook scanner
|
||||
-- Iterates all player spell tabs and caches every known spell by name.
|
||||
-- Stores the highest rank found (so MTH_HT_Spells["Aimed Shot"].slot is
|
||||
-- always the actionable rank the player actually has).
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
local function MTH_HT_ParseRankNum(rankStr)
|
||||
if not rankStr or rankStr == "" then return 0 end
|
||||
local _, _, n = string.find(tostring(rankStr), "(%d+)")
|
||||
return tonumber(n) or 0
|
||||
end
|
||||
|
||||
function MTH_HT_RescanSpellbook()
|
||||
MTH_HT_Spells = {}
|
||||
|
||||
local numTabs = GetNumSpellTabs and GetNumSpellTabs() or 0
|
||||
for tabIdx = 1, numTabs do
|
||||
local _, _, offset, numSpells = GetSpellTabInfo(tabIdx)
|
||||
for i = 1, numSpells do
|
||||
local slot = offset + i
|
||||
local name, rank = GetSpellName(slot, BOOKTYPE_SPELL)
|
||||
if name and name ~= "" then
|
||||
local rankNum = MTH_HT_ParseRankNum(rank)
|
||||
local existing = MTH_HT_Spells[name]
|
||||
if not existing or rankNum > (existing.rankNum or 0) then
|
||||
MTH_HT_Spells[name] = {
|
||||
slot = slot,
|
||||
rank = rank or "",
|
||||
rankNum = rankNum,
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Public accessors
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
function MTH_HT_GetTalentRank(talentName)
|
||||
local entry = MTH_HT_Talents[tostring(talentName or "")]
|
||||
return entry and (entry.rank or 0) or 0
|
||||
end
|
||||
|
||||
function MTH_HT_HasTalent(talentName)
|
||||
return MTH_HT_GetTalentRank(talentName) >= 1
|
||||
end
|
||||
|
||||
function MTH_HT_GetSpellInfo(spellName)
|
||||
return MTH_HT_Spells[tostring(spellName or "")]
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Event-driven scanning
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
local MTH_HT_Frame = CreateFrame("Frame", "MTH_HunterTalentScanFrame")
|
||||
|
||||
local MTH_HT_NeedTalentRescan = false
|
||||
local MTH_HT_NeedSpellRescan = false
|
||||
local MTH_HT_RescanDelay = 0.6 -- seconds after event before scanning
|
||||
local MTH_HT_RescanElapsed = 0
|
||||
|
||||
MTH_HT_Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_HT_Frame:RegisterEvent("LEARNED_SPELL_IN_TAB")
|
||||
MTH_HT_Frame:RegisterEvent("SPELLS_CHANGED")
|
||||
MTH_HT_Frame:RegisterEvent("CHARACTER_POINTS_CHANGED")
|
||||
|
||||
MTH_HT_Frame:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
-- Immediate scan on login
|
||||
MTH_HT_RescanTalents()
|
||||
MTH_HT_RescanSpellbook()
|
||||
if type(MTH_HT_OnScanComplete) == "function" then
|
||||
MTH_HT_OnScanComplete()
|
||||
end
|
||||
-- Also schedule a deferred re-scan: GetTalentInfo can return stale
|
||||
-- data for a brief window after PLAYER_ENTERING_WORLD on login.
|
||||
MTH_HT_NeedTalentRescan = true
|
||||
MTH_HT_NeedSpellRescan = true
|
||||
MTH_HT_RescanElapsed = 0
|
||||
this:Show() -- enable OnUpdate for the deferred pass
|
||||
return
|
||||
end
|
||||
|
||||
-- For other events, schedule a deferred rescan so we don't fire
|
||||
-- multiple times if several events arrive in the same frame.
|
||||
if event == "CHARACTER_POINTS_CHANGED" then
|
||||
MTH_HT_NeedTalentRescan = true
|
||||
end
|
||||
if event == "LEARNED_SPELL_IN_TAB" or event == "SPELLS_CHANGED" then
|
||||
MTH_HT_NeedSpellRescan = true
|
||||
end
|
||||
MTH_HT_RescanElapsed = 0
|
||||
this:Show()
|
||||
end)
|
||||
|
||||
MTH_HT_Frame:SetScript("OnUpdate", function()
|
||||
MTH_HT_RescanElapsed = (MTH_HT_RescanElapsed or 0) + (arg1 or 0)
|
||||
if MTH_HT_RescanElapsed < MTH_HT_RescanDelay then return end
|
||||
|
||||
-- Batch-flush pending scans
|
||||
if MTH_HT_NeedTalentRescan then
|
||||
MTH_HT_RescanTalents()
|
||||
MTH_HT_NeedTalentRescan = false
|
||||
end
|
||||
if MTH_HT_NeedSpellRescan then
|
||||
MTH_HT_RescanSpellbook()
|
||||
MTH_HT_NeedSpellRescan = false
|
||||
end
|
||||
|
||||
-- Notify dependents
|
||||
if type(MTH_HT_OnScanComplete) == "function" then
|
||||
MTH_HT_OnScanComplete()
|
||||
end
|
||||
|
||||
this:Hide()
|
||||
MTH_HT_RescanElapsed = 0
|
||||
end)
|
||||
|
||||
MTH_HT_Frame:Hide()
|
||||
@@ -7,6 +7,25 @@ local MTH_PS_LastScanAt = 0
|
||||
local MTH_PS_VERBOSE_LOGS = false
|
||||
local MTH_PS_ForceScanUntil = 0
|
||||
local MTH_PS_ForceScanBudget = 0
|
||||
local MTH_PS_EventThrottle = {}
|
||||
local MTH_PS_LastNoPetScanAt = 0
|
||||
|
||||
local function MTH_PS_IsPlayerDeadOrGhost()
|
||||
if type(UnitIsDeadOrGhost) == "function" then
|
||||
return UnitIsDeadOrGhost("player") and true or false
|
||||
end
|
||||
if type(UnitIsDead) == "function" then
|
||||
return UnitIsDead("player") and true or false
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function MTH_PS_HasLivePet()
|
||||
if type(UnitExists) ~= "function" then
|
||||
return false
|
||||
end
|
||||
return UnitExists("pet") and true or false
|
||||
end
|
||||
|
||||
local function MTH_PS_ClearForceWindow()
|
||||
MTH_PS_ForceScanUntil = 0
|
||||
@@ -26,9 +45,25 @@ end
|
||||
local function MTH_PS_IsFollowupEventTrigger(trigger)
|
||||
local t = tostring(trigger or "")
|
||||
return t == "event:UNIT_PET"
|
||||
or t == "UNIT_PET"
|
||||
or t == "event:PET_BAR_UPDATE"
|
||||
or t == "PET_BAR_UPDATE"
|
||||
or t == "event:SPELLS_CHANGED"
|
||||
or t == "SPELLS_CHANGED"
|
||||
or t == "event:LEARNED_SPELL_IN_TAB"
|
||||
or t == "LEARNED_SPELL_IN_TAB"
|
||||
end
|
||||
|
||||
local function MTH_PS_ShouldHandleEvent(evt, minIntervalSeconds)
|
||||
local now = tonumber(GetTime and GetTime() or time()) or 0
|
||||
local key = tostring(evt or "")
|
||||
local minInterval = tonumber(minIntervalSeconds) or 0
|
||||
local last = tonumber(MTH_PS_EventThrottle[key] or 0) or 0
|
||||
if minInterval > 0 and (now - last) < minInterval then
|
||||
return false
|
||||
end
|
||||
MTH_PS_EventThrottle[key] = now
|
||||
return true
|
||||
end
|
||||
|
||||
local function MTH_PS_IsPriorityTrigger(trigger)
|
||||
@@ -190,6 +225,41 @@ function MTH_PSP_RequestScan(trigger, minIntervalSeconds)
|
||||
local minInterval = tonumber(minIntervalSeconds) or 1
|
||||
local bypassThrottle = false
|
||||
local bypassReason = ""
|
||||
local deadOrGhost = MTH_PS_IsPlayerDeadOrGhost()
|
||||
local hasLivePet = MTH_PS_HasLivePet()
|
||||
|
||||
if deadOrGhost then
|
||||
if triggerText == "PET_BAR_UPDATE" or triggerText == "event:PET_BAR_UPDATE"
|
||||
or triggerText == "SPELLS_CHANGED" or triggerText == "event:SPELLS_CHANGED"
|
||||
or triggerText == "LEARNED_SPELL_IN_TAB" or triggerText == "event:LEARNED_SPELL_IN_TAB"
|
||||
then
|
||||
MTH_PS_TraceTrigger(triggerText, false, "dead-skip")
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if not hasLivePet then
|
||||
if MTH_PS_IsFollowupEventTrigger(triggerText) then
|
||||
MTH_PS_TraceTrigger(triggerText, false, "no-pet-followup")
|
||||
return false
|
||||
end
|
||||
if triggerText ~= "manual"
|
||||
and triggerText ~= "PLAYER_ENTERING_WORLD"
|
||||
and triggerText ~= "event:PLAYER_ENTERING_WORLD"
|
||||
and triggerText ~= "UNIT_PET"
|
||||
and triggerText ~= "event:UNIT_PET"
|
||||
then
|
||||
MTH_PS_TraceTrigger(triggerText, false, "no-pet")
|
||||
return false
|
||||
end
|
||||
if (now - (tonumber(MTH_PS_LastNoPetScanAt) or 0)) < 5 then
|
||||
MTH_PS_TraceTrigger(triggerText, false, "no-pet-throttled")
|
||||
return false
|
||||
end
|
||||
MTH_PS_LastNoPetScanAt = now
|
||||
else
|
||||
MTH_PS_LastNoPetScanAt = 0
|
||||
end
|
||||
|
||||
if MTH_PS_IsPriorityTrigger(triggerText) then
|
||||
bypassThrottle = true
|
||||
@@ -237,7 +307,19 @@ local function MTH_PS_OnEvent(_, evt, eventArg1)
|
||||
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)
|
||||
local minInterval = 1
|
||||
if evt == "PET_BAR_UPDATE" then
|
||||
minInterval = 4
|
||||
elseif evt == "SPELLS_CHANGED" then
|
||||
minInterval = 4
|
||||
elseif evt == "LEARNED_SPELL_IN_TAB" then
|
||||
minInterval = 1.5
|
||||
elseif evt == "UNIT_PET" then
|
||||
minInterval = 1
|
||||
end
|
||||
if MTH_PS_ShouldHandleEvent(evt, minInterval) then
|
||||
MTH_PSP_RequestScan(evt, minInterval)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -249,7 +331,7 @@ function MTH_PS_InitService()
|
||||
return
|
||||
end
|
||||
|
||||
MTH_PS_Frame = CreateFrame("Frame", "MTHPetSpellScanFrame")
|
||||
MTH_PS_Frame = CreateFrame("Frame", "MTH_PetSpellbookEvent")
|
||||
MTH_PS_Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
MTH_PS_Frame:RegisterEvent("UNIT_PET")
|
||||
MTH_PS_Frame:RegisterEvent("PET_BAR_UPDATE")
|
||||
@@ -266,6 +348,7 @@ function MTH_PS_ShutdownService(_reason)
|
||||
MTH_PS_Frame:SetScript("OnEvent", nil)
|
||||
MTH_PS_Frame = nil
|
||||
MTH_PS_LastScanAt = 0
|
||||
MTH_PS_LastNoPetScanAt = 0
|
||||
MTH_PS_ClearForceWindow()
|
||||
end
|
||||
|
||||
@@ -284,11 +367,15 @@ function MTH_CommandPetSpellScan()
|
||||
|
||||
local ok, count, changed = MTH_PS_ScanNow("manual")
|
||||
if not ok then
|
||||
MTH:Print("Pet spellbook scan failed.")
|
||||
if not (MTH and MTH.IsMessageEnabled) or MTH:IsMessageEnabled("spellbookScan", false) then
|
||||
MTH:Print("Pet spellbook scan failed.")
|
||||
end
|
||||
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))
|
||||
if not (MTH and MTH.IsMessageEnabled) or MTH:IsMessageEnabled("spellbookScan", false) then
|
||||
MTH:Print("Pet spellbook scan: " .. tostring(count or 0) .. " spell(s). changed=" .. tostring(changed and true or false))
|
||||
end
|
||||
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
|
||||
|
||||
+1979
-208
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,251 @@
|
||||
-- MetaHunt: one-time live trainer catalog collector.
|
||||
-- Developer workflow: /mth trainerscan, then open a Hunter or Pet Trainer.
|
||||
|
||||
local MTH_TS_Frame
|
||||
local MTH_TS_Armed = false
|
||||
local MTH_TS_AutoArmed = false
|
||||
|
||||
local function MTH_TS_Log(text)
|
||||
if MTH_DebugFrame and type(MTH_DebugFrame.AddInfo) == "function" then
|
||||
MTH_DebugFrame:AddInfo("[TrainerScan] " .. tostring(text))
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_TS_Call(name, ...)
|
||||
local fn = getglobal and getglobal(name) or (_G and _G[name])
|
||||
if type(fn) ~= "function" then return nil, "missing" end
|
||||
local ok, a, b, c, d, e, f, g = pcall(fn, unpack(arg))
|
||||
if not ok then return nil, "error=" .. tostring(a) end
|
||||
return { a, b, c, d, e, f, g }, nil
|
||||
end
|
||||
|
||||
local function MTH_TS_Values(values)
|
||||
if not values then return "" end
|
||||
local parts = {}
|
||||
local i = 1
|
||||
while i <= table.getn(values) do
|
||||
if values[i] ~= nil then
|
||||
table.insert(parts, tostring(values[i]))
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return table.concat(parts, " | ")
|
||||
end
|
||||
|
||||
local function MTH_TS_TargetName()
|
||||
if type(UnitName) == "function" then
|
||||
local name = UnitName("target")
|
||||
if name and name ~= "" then return name end
|
||||
end
|
||||
local title = getglobal and getglobal("TrainerFrameTitleText") or nil
|
||||
if title and title.GetText then return title:GetText() or "?" end
|
||||
return "?"
|
||||
end
|
||||
|
||||
local function MTH_TS_GetLiveStatusStore()
|
||||
if type(MTH_CharSavedVariables) ~= "table" then MTH_CharSavedVariables = {} end
|
||||
if type(MTH_CharSavedVariables.trainerLiveStatus) ~= "table" then
|
||||
MTH_CharSavedVariables.trainerLiveStatus = {}
|
||||
end
|
||||
return MTH_CharSavedVariables.trainerLiveStatus
|
||||
end
|
||||
|
||||
local function MTH_TS_GetCatalogKind(name, rank)
|
||||
local catalog = MTH_DS_TrainerCatalog
|
||||
if type(catalog) ~= "table" or type(catalog.byNameRank) ~= "table" then return nil end
|
||||
local keySuffix = "\031" .. tostring(name or "") .. "\031" .. tostring(rank or "")
|
||||
if catalog.byNameRank["hunter" .. keySuffix] then return "hunter" end
|
||||
if catalog.byNameRank["pet" .. keySuffix] then return "pet" end
|
||||
return nil
|
||||
end
|
||||
|
||||
function MTH_GetTrainerLiveStatus(kind, name, rank)
|
||||
local store = type(MTH_CharSavedVariables) == "table" and MTH_CharSavedVariables.trainerLiveStatus or nil
|
||||
if type(store) ~= "table" then return nil end
|
||||
return store[tostring(kind or "") .. "\031" .. tostring(name or "") .. "\031" .. tostring(rank or "")]
|
||||
end
|
||||
|
||||
local function MTH_TS_CollectLiveStatuses()
|
||||
local countValues = MTH_TS_Call("GetNumTrainerServices")
|
||||
local total = countValues and tonumber(countValues[1]) or 0
|
||||
if total <= 0 then return false end
|
||||
|
||||
local trainerName = MTH_TS_TargetName()
|
||||
local store = MTH_TS_GetLiveStatusStore()
|
||||
local observed = 0
|
||||
for index = 1, total do
|
||||
local info = MTH_TS_Call("GetTrainerServiceInfo", index)
|
||||
local name = info and info[1] or nil
|
||||
local rank = info and info[2] or ""
|
||||
local status = info and info[3] or nil
|
||||
local kind = MTH_TS_GetCatalogKind(name, rank)
|
||||
if kind and (status == "available" or status == "unavailable" or status == "used") then
|
||||
local key = kind .. "\031" .. tostring(name) .. "\031" .. tostring(rank)
|
||||
store[key] = {
|
||||
status = status,
|
||||
observedAt = time(),
|
||||
trainer = trainerName,
|
||||
}
|
||||
observed = observed + 1
|
||||
end
|
||||
end
|
||||
|
||||
if observed > 0 then
|
||||
MTH_TS_Log("Live trainer overlay: " .. tostring(observed) .. " observed catalog row(s) from " .. tostring(trainerName) .. ".")
|
||||
if MTH and MTH.Print and (not MTH.IsMessageEnabled or MTH:IsMessageEnabled("trainerScan", true)) then
|
||||
MTH:Print("Trainer scan: " .. tostring(observed) .. " Hunter/Pet spell status" .. (observed == 1 and "" or "es") .. " observed.")
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function MTH_TS_Collect()
|
||||
local countValues, countError = MTH_TS_Call("GetNumTrainerServices")
|
||||
local total = countValues and tonumber(countValues[1]) or 0
|
||||
if not total or total <= 0 then
|
||||
MTH_TS_Log("No trainer services yet. GetNumTrainerServices=" .. tostring(MTH_TS_Values(countValues))
|
||||
.. " " .. tostring(countError or ""))
|
||||
return false
|
||||
end
|
||||
|
||||
local trainerName = MTH_TS_TargetName()
|
||||
local capture = {
|
||||
trainer = trainerName,
|
||||
capturedAt = time(),
|
||||
services = {},
|
||||
}
|
||||
MTH_TRAINER_SCAN_LAST = capture
|
||||
if type(MTH_CharSavedVariables) == "table" then
|
||||
if type(MTH_CharSavedVariables.trainerCatalogScans) ~= "table" then
|
||||
MTH_CharSavedVariables.trainerCatalogScans = {}
|
||||
end
|
||||
MTH_CharSavedVariables.trainerCatalogScans[trainerName] = capture
|
||||
end
|
||||
MTH_TS_Log("=== " .. tostring(capture.trainer) .. " | " .. tostring(total) .. " trainer services ===")
|
||||
|
||||
local index = 1
|
||||
while index <= total do
|
||||
local info, infoError = MTH_TS_Call("GetTrainerServiceInfo", index)
|
||||
local icon, iconError = MTH_TS_Call("GetTrainerServiceIcon", index)
|
||||
local cost, costError = MTH_TS_Call("GetTrainerServiceCost", index)
|
||||
local level, levelError = MTH_TS_Call("GetTrainerServiceLevelReq", index)
|
||||
local skill, skillError = MTH_TS_Call("GetTrainerServiceSkillReq", index)
|
||||
local description, descriptionError = MTH_TS_Call("GetTrainerServiceDescription", index)
|
||||
local reqCountValues = MTH_TS_Call("GetTrainerServiceNumAbilityReq", index)
|
||||
local reqCount = reqCountValues and tonumber(reqCountValues[1]) or 0
|
||||
local requirements = {}
|
||||
local reqIndex = 1
|
||||
while reqIndex <= reqCount do
|
||||
local requirement, reqError = MTH_TS_Call("GetTrainerServiceAbilityReq", index, reqIndex)
|
||||
table.insert(requirements, {
|
||||
name = requirement and requirement[1] or nil,
|
||||
flag = requirement and requirement[2] or nil,
|
||||
error = reqError,
|
||||
})
|
||||
reqIndex = reqIndex + 1
|
||||
end
|
||||
|
||||
local row = {
|
||||
index = index,
|
||||
name = info and info[1] or nil,
|
||||
rank = info and info[2] or nil,
|
||||
status = info and info[3] or nil,
|
||||
expanded = info and info[4] or nil,
|
||||
icon = icon and icon[1] or nil,
|
||||
cost = cost and cost[1] or nil,
|
||||
requiredLevel = level and level[1] or nil,
|
||||
skillRequirement = skill,
|
||||
description = description and description[1] or nil,
|
||||
abilityRequirements = requirements,
|
||||
}
|
||||
table.insert(capture.services, row)
|
||||
|
||||
MTH_TS_Log(string.format("%02d name=%s | rank=%s | status=%s | expanded=%s | level=%s | cost=%s | icon=%s",
|
||||
index, tostring(row.name), tostring(row.rank), tostring(row.status), tostring(row.expanded),
|
||||
tostring(row.requiredLevel), tostring(row.cost), tostring(row.icon)))
|
||||
if row.description and row.description ~= "" then
|
||||
MTH_TS_Log(" description=" .. tostring(row.description))
|
||||
end
|
||||
if table.getn(requirements) > 0 then
|
||||
local requirementText = {}
|
||||
local requirementIndex = 1
|
||||
while requirementIndex <= table.getn(requirements) do
|
||||
local requirement = requirements[requirementIndex]
|
||||
table.insert(requirementText, tostring(requirement.name)
|
||||
.. " [flag=" .. tostring(requirement.flag) .. "]")
|
||||
requirementIndex = requirementIndex + 1
|
||||
end
|
||||
MTH_TS_Log(" abilityRequirements=" .. table.concat(requirementText, ", "))
|
||||
end
|
||||
if skill and table.getn(skill) > 0 then
|
||||
MTH_TS_Log(" skillRequirement=" .. MTH_TS_Values(skill))
|
||||
end
|
||||
if infoError or iconError or costError or levelError or skillError or descriptionError then
|
||||
MTH_TS_Log(" API=" .. tostring(infoError or "ok") .. ", " .. tostring(iconError or "ok")
|
||||
.. ", " .. tostring(costError or "ok") .. ", " .. tostring(levelError or "ok")
|
||||
.. ", " .. tostring(skillError or "ok") .. ", " .. tostring(descriptionError or "ok"))
|
||||
end
|
||||
index = index + 1
|
||||
end
|
||||
|
||||
MTH_TS_Log("=== Capture complete. " .. tostring(total)
|
||||
.. " rows retained in MTH_TRAINER_SCAN_LAST and trainerCatalogScans. ===")
|
||||
return true
|
||||
end
|
||||
|
||||
local function MTH_TS_EnsureFrame()
|
||||
if not MTH_TS_Frame then
|
||||
MTH_TS_Frame = CreateFrame("Frame", "MTH_TrainerCatalogScanner")
|
||||
MTH_TS_Frame:SetScript("OnEvent", function()
|
||||
if event == "TRAINER_SHOW" then
|
||||
MTH_TS_AutoArmed = true
|
||||
this.elapsed = 0
|
||||
this:SetScript("OnUpdate", function()
|
||||
this.elapsed = (this.elapsed or 0) + (arg1 or 0)
|
||||
if this.elapsed < 0.25 then return end
|
||||
this:SetScript("OnUpdate", nil)
|
||||
MTH_TS_CollectLiveStatuses()
|
||||
end)
|
||||
return
|
||||
end
|
||||
if event ~= "TRAINER_LIST_UPDATE" or not (MTH_TS_Armed or MTH_TS_AutoArmed) then return end
|
||||
this.elapsed = 0
|
||||
this:SetScript("OnUpdate", function()
|
||||
this.elapsed = (this.elapsed or 0) + (arg1 or 0)
|
||||
if this.elapsed < 0.25 then return end
|
||||
this:SetScript("OnUpdate", nil)
|
||||
local manualArmed = MTH_TS_Armed
|
||||
MTH_TS_Armed = false
|
||||
MTH_TS_AutoArmed = false
|
||||
MTH_TS_CollectLiveStatuses()
|
||||
if manualArmed and not MTH_TS_Collect() and MTH and MTH.Print then
|
||||
MTH:Print("Trainer list was empty. Open the trainer again and run /mth trainerscan.")
|
||||
end
|
||||
end)
|
||||
end)
|
||||
MTH_TS_Frame:RegisterEvent("TRAINER_SHOW")
|
||||
MTH_TS_Frame:RegisterEvent("TRAINER_LIST_UPDATE")
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_CommandTrainerScan()
|
||||
if MTH_DebugFrame and type(MTH_DebugFrame.Show) == "function" then MTH_DebugFrame:Show() end
|
||||
local currentRows = MTH_TS_Call("GetNumTrainerServices")
|
||||
if currentRows and (tonumber(currentRows[1]) or 0) > 0 then
|
||||
MTH_TS_Collect()
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Trainer catalog captured from the open trainer window.")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
MTH_TS_EnsureFrame()
|
||||
|
||||
MTH_TS_Armed = true
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Trainer catalog scan armed. Open a Hunter Trainer or Pet Trainer; results will appear in the debug window.")
|
||||
end
|
||||
end
|
||||
|
||||
MTH_TS_EnsureFrame()
|
||||
+388
-16
@@ -12,7 +12,7 @@ local function MTH_PT_GetGlobal(name)
|
||||
return nil
|
||||
end
|
||||
|
||||
local MTH_ItemLinkCacheTooltip = nil
|
||||
local MTH_ItemLinkCache = nil
|
||||
|
||||
local function MTH_GetQualityHexColor(quality)
|
||||
local q = tonumber(quality)
|
||||
@@ -34,14 +34,14 @@ function MTH_PrimeItemCache(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")
|
||||
if not MTH_ItemLinkCache then
|
||||
MTH_ItemLinkCache = CreateFrame("GameTooltip", "MTH_ItemLinkCache", UIParent, "GameTooltipTemplate")
|
||||
if not MTH_ItemLinkCache then return false end
|
||||
MTH_ItemLinkCache:SetOwner(UIParent, "ANCHOR_NONE")
|
||||
end
|
||||
|
||||
MTH_ItemLinkCacheTooltip:ClearLines()
|
||||
MTH_ItemLinkCacheTooltip:SetHyperlink("item:" .. tostring(id) .. ":0:0:0")
|
||||
MTH_ItemLinkCache:ClearLines()
|
||||
MTH_ItemLinkCache:SetHyperlink("item:" .. tostring(id) .. ":0:0:0")
|
||||
return type(GetItemInfo) == "function" and (GetItemInfo(id) ~= nil) or false
|
||||
end
|
||||
|
||||
@@ -129,6 +129,49 @@ local function MTH_CommandOptions()
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_FormatAgeSeconds(seconds)
|
||||
local value = tonumber(seconds) or 0
|
||||
if value < 0 then value = 0 end
|
||||
if value < 60 then
|
||||
return tostring(math.floor(value)) .. "s"
|
||||
end
|
||||
if value < 3600 then
|
||||
return tostring(math.floor(value / 60)) .. "m"
|
||||
end
|
||||
if value < 86400 then
|
||||
return tostring(math.floor(value / 3600)) .. "h"
|
||||
end
|
||||
return tostring(math.floor(value / 86400)) .. "d"
|
||||
end
|
||||
|
||||
local function MTH_CommandPeers()
|
||||
if not (MTH and MTH.VersionCheck and type(MTH.VersionCheck.GetTrackedPeers) == "function") then
|
||||
MTH:Print("Peer tracker is not available yet")
|
||||
return
|
||||
end
|
||||
|
||||
local peers = MTH.VersionCheck:GetTrackedPeers()
|
||||
local count = table.getn(peers)
|
||||
if count <= 0 then
|
||||
MTH:Print("No MetaHunt broadcasters seen yet on LFT")
|
||||
return
|
||||
end
|
||||
|
||||
MTH:Print("MetaHunt broadcasters seen on LFT: " .. tostring(count))
|
||||
local now = (type(time) == "function" and time()) or (type(GetTime) == "function" and math.floor(GetTime())) or 0
|
||||
local maxRows = math.min(count, 20)
|
||||
for i = 1, maxRows do
|
||||
local peer = peers[i]
|
||||
if type(peer) == "table" then
|
||||
local age = MTH_FormatAgeSeconds((now or 0) - (tonumber(peer.lastSeenAt) or 0))
|
||||
MTH:Print("- " .. tostring(peer.name or "?") .. " v" .. tostring(peer.versionText or "?") .. " (seen " .. tostring(age) .. " ago)")
|
||||
end
|
||||
end
|
||||
if count > maxRows then
|
||||
MTH:Print("... and " .. tostring(count - maxRows) .. " more")
|
||||
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"])
|
||||
@@ -197,6 +240,34 @@ local function MTH_CommandPetSpellScanFallback()
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_CommandFoodData(limitText)
|
||||
if type(FOM_PrintCollectedFoodData) ~= "function" then
|
||||
MTH:Print("Food data command is not available yet")
|
||||
return
|
||||
end
|
||||
local trimmed = string.gsub(tostring(limitText or ""), "^%s+", "")
|
||||
trimmed = string.gsub(trimmed, "%s+$", "")
|
||||
if string.lower(trimmed) == "purge" then
|
||||
if type(FOM_PruneCollectedFoodData) ~= "function" then
|
||||
MTH:Print("Food data purge is not available yet")
|
||||
return
|
||||
end
|
||||
local removed = tonumber(FOM_PruneCollectedFoodData()) or 0
|
||||
MTH:Print("Food data purge removed rows: " .. tostring(removed))
|
||||
return
|
||||
end
|
||||
if trimmed == "" then
|
||||
FOM_PrintCollectedFoodData(nil)
|
||||
return
|
||||
end
|
||||
local limit = tonumber(trimmed)
|
||||
if not limit then
|
||||
MTH:Print("Usage: /mth food [limit|purge]")
|
||||
return
|
||||
end
|
||||
FOM_PrintCollectedFoodData(limit)
|
||||
end
|
||||
|
||||
if type(MTH_PS_ScanNow) ~= "function" then
|
||||
function MTH_PS_ScanNow(trigger)
|
||||
local ok, count = MTH_CommandPetSpellScanFallback()
|
||||
@@ -224,7 +295,9 @@ 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))
|
||||
if not (MTH and MTH.IsMessageEnabled) or MTH:IsMessageEnabled("spellbookScan", false) then
|
||||
MTH:Print("Pet spellbook scan: " .. tostring(count or 0) .. " spell(s). ok=" .. tostring(ok and true or false))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -237,27 +310,326 @@ function SlashCmdList.MTH(msg, editbox)
|
||||
msg = string.gsub(tostring(msg), "^%s+", "")
|
||||
msg = string.gsub(msg, "%s+$", "")
|
||||
local lowerMsg = string.lower(msg)
|
||||
local _, _, lowerCmd, lowerArg = string.find(lowerMsg, "^(%S+)%s*(.-)%s*$")
|
||||
if msg == "" then
|
||||
MTH:Print("Available: /mth options, /mth book")
|
||||
MTH:Print("Available: /mth options, /mth book, /mth food")
|
||||
elseif lowerMsg == "err" or lowerMsg == "errors" or lowerMsg == "debug" then
|
||||
if MTH_DebugFrame and MTH_DebugFrame.Toggle then
|
||||
MTH_DebugFrame:Toggle()
|
||||
else
|
||||
MTH:Print("Debug frame is not available.")
|
||||
end
|
||||
elseif lowerMsg == "options" then
|
||||
MTH_CommandOptions()
|
||||
elseif lowerMsg == "book" or lowerMsg == "hunterbook" then
|
||||
MTH_CommandBook()
|
||||
elseif lowerMsg == "peers" or lowerMsg == "who" then
|
||||
MTH_CommandPeers()
|
||||
elseif lowerCmd == "trainscan" or lowerCmd == "petscan" then
|
||||
if type(MTH_CommandTrainScan) == "function" then
|
||||
MTH_CommandTrainScan()
|
||||
else
|
||||
MTH:Print("Train scan is not available.")
|
||||
end
|
||||
elseif lowerCmd == "trainerscan" then
|
||||
if type(MTH_CommandTrainerScan) == "function" then
|
||||
MTH_CommandTrainerScan()
|
||||
else
|
||||
MTH:Print("Trainer catalog scanner is not available.")
|
||||
end
|
||||
elseif lowerCmd == "spellscan" then
|
||||
if type(MTH_HT_RescanSpellbook) ~= "function" then
|
||||
MTH:Print("Hunter spellbook scanner is not available.")
|
||||
return
|
||||
end
|
||||
MTH_HT_RescanSpellbook()
|
||||
local mail = type(MTH_HT_GetSpellInfo) == "function" and MTH_HT_GetSpellInfo("Mail") or nil
|
||||
if mail then
|
||||
MTH:Print("Hunter spellbook scan: Mail found (rank=" .. tostring(mail.rank or "") .. ", slot=" .. tostring(mail.slot or "?") .. ").")
|
||||
else
|
||||
MTH:Print("Hunter spellbook scan: Mail was not returned by the client spellbook API.")
|
||||
end
|
||||
elseif lowerCmd == "leveltest" then
|
||||
local _, _, kind, levelText = string.find(lowerArg or "", "^(%S+)%s*(%d*)")
|
||||
if type(MTH_LevelTracker_Test) ~= "function" then
|
||||
MTH:Print("Level tracker is not available.")
|
||||
elseif not MTH_LevelTracker_Test(kind, tonumber(levelText)) then
|
||||
MTH:Print("Use /mth leveltest hunter [level], pet [level], or loyalty [level]")
|
||||
end
|
||||
elseif lowerCmd == "fx" or lowerCmd == "fireworks" or lowerCmd == "celebrate" then
|
||||
if type(MTH_CelebratePreview) == "function" then
|
||||
local style = lowerArg or ""
|
||||
if style == "" then style = "firework" end
|
||||
if style == "help" or style == "list" then
|
||||
MTH:Print("Celebrations: firework, blackhole, shockwave, shatter, all")
|
||||
elseif not MTH_CelebratePreview(style) then
|
||||
MTH:Print("Unknown celebration: " .. tostring(style))
|
||||
MTH:Print("Use /mth fx help for: firework, blackhole, shockwave, shatter, all")
|
||||
end
|
||||
else
|
||||
-- Self-diagnosing: report which piece failed to load so we can tell
|
||||
-- a stale UI from a genuine load error.
|
||||
MTH:Print("Celebration effect is not available. Diagnostics:")
|
||||
MTH:Print(" PizzaSauce global: " .. type(PizzaSauce))
|
||||
MTH:Print(" MTH_Celebrate: " .. type(MTH_Celebrate))
|
||||
MTH:Print(" MTH_FX_IsEnabled: " .. type(MTH_FX_IsEnabled))
|
||||
MTH:Print(" MTH_SetupAnimationsOptions: " .. type(MTH_SetupAnimationsOptions))
|
||||
MTH:Print(" -> If these are 'nil', do /reload (your UI is running code from before the feature was added).")
|
||||
end
|
||||
elseif lowerMsg == "zoneinfo" then
|
||||
-- Dumps all available zone data for the current zone, for adding to ds-zones / ds-minimap-sizes
|
||||
pcall(SetMapToCurrentZone)
|
||||
local zoneName = (type(GetRealZoneText) == "function" and GetRealZoneText()) or ""
|
||||
local subZone = (type(GetSubZoneText) == "function" and GetSubZoneText()) or ""
|
||||
local zoneText = (type(GetZoneText) == "function" and GetZoneText()) or ""
|
||||
local areaId = nil
|
||||
if type(GetCurrentMapAreaID) == "function" then
|
||||
local ok, v = pcall(GetCurrentMapAreaID)
|
||||
if ok then areaId = v end
|
||||
end
|
||||
local cx, cy = nil, nil
|
||||
if type(GetPlayerMapPosition) == "function" then
|
||||
local ok, x, y = pcall(GetPlayerMapPosition, "player")
|
||||
if ok then cx, cy = x, y end
|
||||
end
|
||||
local contId = nil
|
||||
if type(GetCurrentMapContinent) == "function" then
|
||||
local ok, v = pcall(GetCurrentMapContinent)
|
||||
if ok then contId = v end
|
||||
end
|
||||
MTH:Print("|cffffff00=== Zone Info ===|r")
|
||||
MTH:Print("GetRealZoneText: |cffffaa00" .. tostring(zoneName) .. "|r")
|
||||
MTH:Print("GetZoneText: |cffffaa00" .. tostring(zoneText) .. "|r")
|
||||
MTH:Print("GetSubZoneText: |cffffaa00" .. tostring(subZone) .. "|r")
|
||||
MTH:Print("AreaID (GetCurrentMapAreaID): |cff88ffff" .. tostring(areaId) .. "|r")
|
||||
MTH:Print("ContinentID: |cff88ffff" .. tostring(contId) .. "|r")
|
||||
if cx and cy then
|
||||
MTH:Print(string.format("Player map pos: |cff88ffff%.4f, %.4f|r (display: %.1f, %.1f)", cx, cy, cx*100, cy*100))
|
||||
end
|
||||
-- Also check if zone is in our tables
|
||||
local inZones = MTH_DS_Zones and MTH_DS_Zones[tonumber(areaId)] and "YES" or "not in MTH_DS_Zones"
|
||||
local inSizes = MTH_DS_MinimapSizes and MTH_DS_MinimapSizes[tonumber(areaId)] and "YES" or "not in ds-minimap-sizes"
|
||||
MTH:Print("MTH_DS_Zones entry: " .. inZones)
|
||||
MTH:Print("MTH_DS_MinimapSizes entry: " .. inSizes)
|
||||
elseif lowerMsg == "npcid" then
|
||||
-- Print the creature entry ID of the current target, or arm a listener for next NPC interaction
|
||||
local function MTH_ParseCreatureEntryFromAnyGuid(guid)
|
||||
if type(guid) ~= "string" or guid == "" then return nil end
|
||||
-- Format 1: TBC-style "Creature-realm-map-inst-?-ENTRY-spawn"
|
||||
local _, _, id1 = string.find(guid, "^Creature%-%d+%-%d+%-%d+%-%d+%-(%d+)%-%d+$")
|
||||
if id1 then return tonumber(id1) end
|
||||
-- Format 2: vanilla hex "0xF130EEEEEECCCCCC" — entry is bits 24-47 = hex chars 7-12
|
||||
if string.sub(guid, 1, 2) == "0x" and string.len(guid) >= 14 then
|
||||
local entryHex = string.sub(guid, 7, 12) -- 24 bits = up to 16M
|
||||
local parsed = tonumber(entryHex, 16)
|
||||
if parsed and parsed > 0 then return parsed end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_PrintNpcId(unitToken)
|
||||
unitToken = unitToken or "target"
|
||||
local name = (type(UnitName) == "function") and UnitName(unitToken) or "?"
|
||||
local level = (type(UnitLevel) == "function") and UnitLevel(unitToken) or "?"
|
||||
-- Try UnitGUID first, then GetUnitGUID (TurtleWoW nameplate API)
|
||||
local guid = nil
|
||||
if type(UnitGUID) == "function" then
|
||||
local ok, g = pcall(UnitGUID, unitToken)
|
||||
if ok and g and g ~= "" then guid = g end
|
||||
end
|
||||
if (not guid or guid == "") and type(GetUnitGUID) == "function" then
|
||||
local ok, g = pcall(GetUnitGUID, unitToken)
|
||||
if ok and g and g ~= "" then guid = g end
|
||||
end
|
||||
local creatureId = MTH_ParseCreatureEntryFromAnyGuid(guid)
|
||||
if creatureId then
|
||||
MTH:Print(string.format("|cffffff00NPC:|r %s |cffffff00ID:|r |cff88ffff%s|r lvl: %s", tostring(name), tostring(creatureId), tostring(level)))
|
||||
else
|
||||
MTH:Print(string.format("|cffffff00NPC:|r %s lvl: %s |cffff8800GUID: %s|r", tostring(name), tostring(level), tostring(guid or "nil")))
|
||||
MTH:Print("|cffaaaaaa(Could not parse creature ID from GUID)|r")
|
||||
end
|
||||
end
|
||||
|
||||
-- Try current target first
|
||||
local hasTarget = (type(UnitExists) == "function") and UnitExists("target")
|
||||
if hasTarget then
|
||||
MTH_PrintNpcId("target")
|
||||
end
|
||||
|
||||
-- Arm a one-shot frame for MERCHANT_SHOW / TRAINER_LIST_UPDATE / GOSSIP_SHOW
|
||||
if not _G["MTH_NpcIdListenerFrame"] then
|
||||
local f = CreateFrame("Frame", "MTH_NpcIdListenerFrame")
|
||||
f:RegisterEvent("MERCHANT_SHOW")
|
||||
f:RegisterEvent("TRAINER_LIST_UPDATE")
|
||||
f:RegisterEvent("GOSSIP_SHOW")
|
||||
f:SetScript("OnEvent", function()
|
||||
MTH_PrintNpcId("target")
|
||||
this:UnregisterEvent("MERCHANT_SHOW")
|
||||
this:UnregisterEvent("TRAINER_LIST_UPDATE")
|
||||
this:UnregisterEvent("GOSSIP_SHOW")
|
||||
_G["MTH_NpcIdListenerFrame"] = nil
|
||||
end)
|
||||
if not hasTarget then
|
||||
MTH:Print("|cffaaffaaArmed.|r Open any vendor, trainer, or stable master to capture their ID.")
|
||||
end
|
||||
end
|
||||
elseif lowerCmd == "dev" then
|
||||
if lowerArg == "petmigrate" then
|
||||
if type(MTH_CommandPetsMigrate) == "function" then
|
||||
MTH_CommandPetsMigrate()
|
||||
else
|
||||
MTH:Print("Pet migration command unavailable.")
|
||||
end
|
||||
else
|
||||
MTH:Print("Dev commands: /mth dev petmigrate")
|
||||
end
|
||||
elseif lowerCmd == "growl" then
|
||||
if type(MTH_Growl_SetEnabled) ~= "function" or type(MTH_Growl_IsEnabled) ~= "function" then
|
||||
MTH:Print("Growl auto-teach is not available.")
|
||||
elseif lowerArg == "on" or lowerArg == "enable" then
|
||||
MTH_Growl_SetEnabled(true)
|
||||
MTH:Print("Growl auto-teach: |cff00ff00ON|r (offers Growl to newly tamed pets).")
|
||||
elseif lowerArg == "off" or lowerArg == "disable" then
|
||||
MTH_Growl_SetEnabled(false)
|
||||
MTH:Print("Growl auto-teach: |cffff0000OFF|r.")
|
||||
else
|
||||
local state = MTH_Growl_IsEnabled() and "|cff00ff00ON|r" or "|cffff0000OFF|r"
|
||||
MTH:Print("Growl auto-teach is " .. state .. ". Usage: /mth growl on | off")
|
||||
end
|
||||
elseif lowerCmd == "food" then
|
||||
local sub = lowerArg
|
||||
local function MTH_FoodItemLabel(itemId)
|
||||
local id = tonumber(itemId)
|
||||
if not id then return tostring(itemId) end
|
||||
if type(GetItemInfo) == "function" then
|
||||
local name = GetItemInfo(id)
|
||||
if name and name ~= "" then
|
||||
return "|cffffcc00" .. name .. "|r |cffaaaaaa(" .. id .. ")|r"
|
||||
end
|
||||
end
|
||||
return "|cffaaaaaa" .. tostring(id) .. "|r"
|
||||
end
|
||||
if sub == "" or sub == "status" then
|
||||
-- Show quarantine and exception state
|
||||
local feedStore = type(MTH_CharSavedVariables) == "table" and MTH_CharSavedVariables.feedTracking or nil
|
||||
local quarantine = feedStore and feedStore.fomQuarantine and feedStore.fomQuarantine.byFamily or nil
|
||||
local exceptions = feedStore and feedStore.exceptions and feedStore.exceptions.byItemId or nil
|
||||
MTH:Print("|cffffff00=== Feed-O-Matic Status ===|r")
|
||||
-- Quarantine
|
||||
local qCount = 0
|
||||
if type(quarantine) == "table" then
|
||||
for family, byItem in pairs(quarantine) do
|
||||
if type(byItem) == "table" then
|
||||
for itemId, _ in pairs(byItem) do
|
||||
qCount = qCount + 1
|
||||
MTH:Print(" |cffff8800Quarantine:|r " .. MTH_FoodItemLabel(itemId) .. " (family=" .. tostring(family) .. ")")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if qCount == 0 then MTH:Print(" Quarantine: |cff00ff00empty|r") end
|
||||
-- Core exceptions
|
||||
local eCount = 0
|
||||
if type(exceptions) == "table" then
|
||||
for itemId, _ in pairs(exceptions) do
|
||||
if type(itemId) == "number" then -- skip string-key dupes
|
||||
eCount = eCount + 1
|
||||
MTH:Print(" |cffff4444Exception block:|r " .. MTH_FoodItemLabel(itemId))
|
||||
end
|
||||
end
|
||||
end
|
||||
if eCount == 0 then MTH:Print(" Exception blocks: |cff00ff00none|r") end
|
||||
-- FOM_RemovedFoods
|
||||
local rCount = 0
|
||||
if type(FOM_RemovedFoods) == "table" then
|
||||
for diet, list in pairs(FOM_RemovedFoods) do
|
||||
if type(list) == "table" then
|
||||
for _, itemId in ipairs(list) do
|
||||
rCount = rCount + 1
|
||||
MTH:Print(" |cffaaaaaaRemoved food:|r " .. MTH_FoodItemLabel(itemId) .. " (diet=" .. tostring(diet) .. ")")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if rCount == 0 then MTH:Print(" Removed foods: |cff00ff00none|r") end
|
||||
MTH:Print("|cffaaaaaa/mth food unban <id>|r — unban one item |cffaaaaaa/mth food reset|r — clear all bans")
|
||||
elseif string.find(sub, "^unban%s") or sub == "unban" then
|
||||
local _, _, itemArg = string.find(lowerArg, "^unban%s+(%S+)")
|
||||
local itemId = tonumber(itemArg)
|
||||
if not itemId then
|
||||
MTH:Print("|cffff4444Usage:|r /mth food unban <itemId>")
|
||||
MTH:Print("Example: /mth food unban 8952")
|
||||
elseif type(FOM_ClearItemBans) ~= "function" then
|
||||
MTH:Print("|cffff4444Error:|r Feed-O-Matic not loaded.")
|
||||
else
|
||||
local label = MTH_FoodItemLabel(itemId)
|
||||
FOM_ClearItemBans(itemId)
|
||||
MTH:Print("|cff00ff00Unbanned " .. label .. "|r — removed from quarantine, exceptions and removed-foods list.")
|
||||
end
|
||||
elseif sub == "reset" then
|
||||
local feedStore = type(MTH_CharSavedVariables) == "table" and MTH_CharSavedVariables.feedTracking or nil
|
||||
if feedStore then
|
||||
if feedStore.fomQuarantine then feedStore.fomQuarantine = { byFamily = {} } end
|
||||
if feedStore.exceptions then feedStore.exceptions = { byItemId = {} } end
|
||||
end
|
||||
if type(FOM_RemovedFoods) == "table" then
|
||||
for diet, _ in pairs(FOM_RemovedFoods) do FOM_RemovedFoods[diet] = {} end
|
||||
end
|
||||
if type(FOM_FEED_SCAN_CACHE) ~= "nil" then FOM_FEED_SCAN_CACHE = nil end
|
||||
MTH:Print("|cff00ff00All Feed-O-Matic bans and quarantines cleared.|r")
|
||||
else
|
||||
MTH:Print("|cffffff00/mth food|r — show quarantine/ban status")
|
||||
MTH:Print("|cffffff00/mth food unban <itemId>|r — remove one item from all bans")
|
||||
MTH:Print("|cffffff00/mth food reset|r — clear ALL bans and quarantines")
|
||||
end
|
||||
elseif lowerCmd == "zbar" and lowerArg == "debug" then
|
||||
-- Dump the live state of every zBar button to chat so we can see which one is the ghost.
|
||||
local p = function(s) MTH:Print("[zBar] " .. tostring(s), "debug") end
|
||||
local root = (type(MTH_ZH_GetSavedRoot) == "function") and MTH_ZH_GetSavedRoot() or ZHunterMod_Saved
|
||||
local zs = root and root["_zbar"]
|
||||
p("=== zBar debug ===")
|
||||
p("enabled=" .. tostring(zs and zs.enabled) .. " anchor=" .. tostring(getglobal("MTH_ZBar_Anchor") ~= nil))
|
||||
p("direction=" .. tostring(zs and zs.direction) .. " childexpand=" .. tostring(zs and zs.childexpand) .. " childarrange=" .. tostring(zs and zs.childarrange))
|
||||
local allButtons = {
|
||||
"zButtonAspect","zButtonAmmo","zButtonTrack","zButtonTrap",
|
||||
"zButtonPet","zButtonRanged","zButtonMounts","zButtonCompanions","zButtonToys",
|
||||
}
|
||||
for _, bname in ipairs(allButtons) do
|
||||
local btn = getglobal(bname)
|
||||
local shown = btn and btn.IsShown and btn:IsShown()
|
||||
local bsaved = root and type(root[bname]) == "table" and root[bname]
|
||||
local enabled = bsaved and bsaved["enabled"]
|
||||
local zvis = zs and zs.visible and zs.visible[bname]
|
||||
local x, y, point = "?", "?", "?"
|
||||
if btn and btn.GetPoint then
|
||||
local pt, _, _, bx, by = btn:GetPoint()
|
||||
point = tostring(pt)
|
||||
x = tostring(bx and math.floor(bx + 0.5) or "?")
|
||||
y = tostring(by and math.floor(by + 0.5) or "?")
|
||||
end
|
||||
local bmtname = btn and tostring(btn.name) or "nil"
|
||||
p(bname .. ": shown=" .. tostring(shown) ..
|
||||
" enabled=" .. tostring(enabled) ..
|
||||
" zbarVisible=" .. tostring(zvis) ..
|
||||
" btn.name=" .. bmtname ..
|
||||
" anchor=(" .. point .. " " .. x .. "," .. y .. ")")
|
||||
end
|
||||
p("=== end ===")
|
||||
else
|
||||
MTH:Print("Unknown command: " .. tostring(msg))
|
||||
MTH:Print("Available: /mth options, /mth book")
|
||||
MTH:Print("Available: /mth options, /mth book, /mth npcid, /mth food")
|
||||
end
|
||||
end
|
||||
|
||||
-- Initialize on load
|
||||
MTH:InitSavedVariables()
|
||||
if type(MTH_ST_InitService) == "function" then
|
||||
MTH_ST_InitService()
|
||||
if type(MTH_ST_InitBootstrap) == "function" then
|
||||
MTH_ST_InitBootstrap()
|
||||
end
|
||||
if type(MTH_TR_InitService) == "function" then
|
||||
MTH_TR_InitService()
|
||||
elseif type(MTH_PT_InitService) == "function" then
|
||||
MTH_PT_InitService()
|
||||
if type(MTH_TR_InitBootstrap) == "function" then
|
||||
MTH_TR_InitBootstrap()
|
||||
elseif type(MTH_PT_InitBootstrap) == "function" then
|
||||
MTH_PT_InitBootstrap()
|
||||
end
|
||||
if type(MTH_PS_InitService) == "function" then
|
||||
MTH_PS_InitService()
|
||||
|
||||
+23
-19
@@ -15,6 +15,13 @@ MTH_DebugFrame = {
|
||||
infoCount = 0
|
||||
}
|
||||
|
||||
local function MTH_DF_L(key, default)
|
||||
if MTH and MTH.GetLocalization then
|
||||
return MTH:GetLocalization(key, default)
|
||||
end
|
||||
return default or key
|
||||
end
|
||||
|
||||
function MTH_DebugFrame:Initialize()
|
||||
if self.initialized then
|
||||
return
|
||||
@@ -22,7 +29,7 @@ function MTH_DebugFrame:Initialize()
|
||||
self.initialized = true
|
||||
|
||||
-- Create main frame
|
||||
self.frame = CreateFrame("Frame", "MTH_DebugFrameWindow", UIParent)
|
||||
self.frame = CreateFrame("Frame", "MTH_DebugWindow", UIParent)
|
||||
self.frame:SetWidth(800)
|
||||
self.frame:SetHeight(400)
|
||||
self.frame:SetPoint("CENTER", UIParent, "CENTER")
|
||||
@@ -44,27 +51,27 @@ function MTH_DebugFrame:Initialize()
|
||||
if UISpecialFrames then
|
||||
local already = false
|
||||
for i = 1, table.getn(UISpecialFrames) do
|
||||
if UISpecialFrames[i] == "MTH_DebugFrameWindow" then
|
||||
if UISpecialFrames[i] == "MTH_DebugWindow" then
|
||||
already = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not already then
|
||||
table.insert(UISpecialFrames, "MTH_DebugFrameWindow")
|
||||
table.insert(UISpecialFrames, "MTH_DebugWindow")
|
||||
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")
|
||||
title:SetText(MTH_DF_L("DEBUG_TITLE", "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:SetText(MTH_DF_L("COMMON_CLOSE_SHORT", "X"))
|
||||
closeButton:SetScript("OnClick", function() MTH_DebugFrame:Hide() end)
|
||||
|
||||
-- Clear button
|
||||
@@ -72,7 +79,7 @@ function MTH_DebugFrame:Initialize()
|
||||
clearButton:SetPoint("BOTTOMLEFT", self.frame, "BOTTOMLEFT", 10, 10)
|
||||
clearButton:SetWidth(100)
|
||||
clearButton:SetHeight(25)
|
||||
clearButton:SetText("Clear")
|
||||
clearButton:SetText(MTH_DF_L("COMMON_CLEAR", "Clear"))
|
||||
clearButton:SetScript("OnClick", function() MTH_DebugFrame:Clear() end)
|
||||
|
||||
-- Select-all button
|
||||
@@ -80,11 +87,11 @@ function MTH_DebugFrame:Initialize()
|
||||
copyButton:SetPoint("LEFT", clearButton, "RIGHT", 10, 0)
|
||||
copyButton:SetWidth(100)
|
||||
copyButton:SetHeight(25)
|
||||
copyButton:SetText("Select All")
|
||||
copyButton:SetText(MTH_DF_L("COMMON_SELECT_ALL", "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 = CreateFrame("ScrollFrame", "MTH_DebugScroll", self.frame, "UIPanelScrollFrameTemplate")
|
||||
self.scrollFrame:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 15, -50)
|
||||
self.scrollFrame:SetPoint("BOTTOMRIGHT", self.frame, "BOTTOMRIGHT", -35, 45)
|
||||
|
||||
@@ -179,9 +186,9 @@ function MTH_DebugFrame:UpdateDisplay()
|
||||
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"
|
||||
local text = string.format(MTH_DF_L("DEBUG_SUMMARY_ERRORS", "=== Errors Captured: %s ==="), tostring(self.captureCount)) .. "\n"
|
||||
text = text .. string.format(MTH_DF_L("DEBUG_SUMMARY_INFO", "=== Info Captured: %s ==="), tostring(self.infoCount or 0)) .. "\n"
|
||||
text = text .. string.format(MTH_DF_L("DEBUG_SUMMARY_QUEUE", "=== Total in queue: %s ==="), tostring(table.getn(self.errors))) .. "\n\n"
|
||||
|
||||
for i = 1, table.getn(self.errors) do
|
||||
local err = self.errors[i]
|
||||
@@ -238,7 +245,7 @@ function MTH_DebugFrame:Clear()
|
||||
self:UpdateDisplay()
|
||||
if self.initialized and self.isVisible then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Debug frame cleared")
|
||||
MTH:Print(MTH_DF_L("DEBUG_CLEARED", "Debug frame cleared"))
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -251,7 +258,7 @@ function MTH_DebugFrame:SelectAllText()
|
||||
self.scrollText:HighlightText(0, -1)
|
||||
self.scrollText:SetFocus()
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("All text selected - press Ctrl+C to copy")
|
||||
MTH:Print(MTH_DF_L("DEBUG_ALL_SELECTED", "All text selected - press Ctrl+C to copy"))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -359,16 +366,13 @@ 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
|
||||
eventFrame:SetScript("OnEvent", function()
|
||||
if event == "ADDON_LOADED" and arg1 == "MetaHunt" then
|
||||
if MTH and MTH.GetConfig then
|
||||
MTH_DF_SetGlobalCaptureEnabled(MTH:GetConfig("debug", "globalErrorCapture", false))
|
||||
end
|
||||
MTH_DF_InstallHooks()
|
||||
MTH_DebugFrame:Initialize()
|
||||
if MTH and MTH.Print then
|
||||
local mode = MTH_DF_IsGlobalCaptureEnabled() and "legacy-global" or "safe-local"
|
||||
MTH:Print("=== MetaHunt Debug Frame Initialized (" .. mode .. ") ===", "debug")
|
||||
end
|
||||
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -0,0 +1,818 @@
|
||||
-- MetaHunt: center-screen celebration effect (message + firework burst).
|
||||
-- Uses the embedded PizzaSauce animation library. Degrades to a silent no-op
|
||||
-- when PizzaSauce or the WoW frame API are unavailable (e.g. offline tests).
|
||||
|
||||
-- Capture our own PizzaSauce instance now, before another addon that ships its
|
||||
-- own copy can overwrite the global. This is the pattern the library requires.
|
||||
local Sauce = PizzaSauce
|
||||
|
||||
-- Localization helper: resolve a key through MetaHunt's loc system, falling back
|
||||
-- to the inline English default. Every user-facing string is keyed so the enUS
|
||||
-- strings can be harvested later; the inline default is the working English.
|
||||
local function MTH_FX_L(key, default)
|
||||
if MTH and type(MTH.GetLocalization) == "function" then
|
||||
return MTH:GetLocalization(key, default)
|
||||
end
|
||||
return default or key
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Animation settings (shared by the effect code and the options "Animations" tab)
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Sections shown, in order, in the options panel. `titleKey` localizes `title`.
|
||||
MTH_FX_ANIM_SECTIONS = {
|
||||
{ id = "pet", title = "Pet", titleKey = "ANIM_SECTION_PET" },
|
||||
{ id = "level", title = "Leveling", titleKey = "ANIM_SECTION_LEVEL" },
|
||||
{ id = "combat", title = "Combat", titleKey = "ANIM_SECTION_COMBAT" },
|
||||
{ id = "book", title = "Book", titleKey = "ANIM_SECTION_BOOK" },
|
||||
{ id = "ui", title = "Interface", titleKey = "ANIM_SECTION_UI" },
|
||||
}
|
||||
|
||||
-- Every individual animation the addon can play. Each has a stable `key`, the
|
||||
-- section it belongs to, a checkbox label, an optional tooltip, and a default.
|
||||
-- labelKey/tooltipKey localize label/tooltip (resolved at render time).
|
||||
-- Adding a new animation = one entry here + one MTH_FX_IsEnabled("key") guard.
|
||||
MTH_FX_ANIM_DEFS = {
|
||||
{ key = "level.hunter", section = "level", default = true,
|
||||
label = "Celebrate Hunter training levels",
|
||||
tooltip = "Show a banner and burst when a level unlocks Hunter trainer spells." },
|
||||
{ key = "level.huntermilestone", section = "level", default = true,
|
||||
label = "Celebrate Hunter milestones",
|
||||
tooltip = "Use a large celebration at levels 10, 20, 30, 40, 50, and 60." },
|
||||
{ key = "level.pet", section = "level", default = true,
|
||||
label = "Celebrate pet training levels",
|
||||
tooltip = "Show a banner when your current pet gains a level with Pet Trainer spells." },
|
||||
{ key = "level.loyalty", section = "level", default = true,
|
||||
label = "Celebrate pet loyalty gains",
|
||||
tooltip = "Show a golden banner when your current pet gains a loyalty level." },
|
||||
{ key = "pet.celebrate", section = "pet", default = true,
|
||||
label = "Celebrate new pet abilities", labelKey = "ANIM_PET_CELEBRATE",
|
||||
tooltip = "Play a firework and a banner in the middle of the screen when your pet learns a new ability or a new rank.",
|
||||
tooltipKey = "ANIM_PET_CELEBRATE_TIP" },
|
||||
{ key = "pet.runaway", section = "pet", default = true,
|
||||
label = "Warn when a pet runs away", labelKey = "ANIM_PET_RUNAWAY",
|
||||
tooltip = "Flash a red, shaking banner in the middle of the screen when one of your pets runs away for good.",
|
||||
tooltipKey = "ANIM_PET_RUNAWAY_TIP" },
|
||||
{ key = "pet.happiness", section = "pet", default = true,
|
||||
label = "Flash zPet on happiness change", labelKey = "ANIM_PET_HAPPINESS",
|
||||
tooltip = "Briefly flash the pet happiness area green when your pet cheers up, or red when it gets unhappy.",
|
||||
tooltipKey = "ANIM_PET_HAPPINESS_TIP" },
|
||||
{ key = "pet.loyaltyup", section = "pet", default = true,
|
||||
label = "Glow zPet on loyalty gain", labelKey = "ANIM_PET_LOYALTYUP",
|
||||
tooltip = "Give a short golden glow when your pet gains a loyalty level.",
|
||||
tooltipKey = "ANIM_PET_LOYALTYUP_TIP" },
|
||||
{ key = "pet.feed", section = "pet", default = true,
|
||||
label = "Flash zPet when feeding your pet", labelKey = "ANIM_PET_FEED",
|
||||
tooltip = "Give a short green flash when Feed-O-Matic feeds your pet.",
|
||||
tooltipKey = "ANIM_PET_FEED_TIP" },
|
||||
{ key = "combat.ammoswap", section = "combat", default = true,
|
||||
label = "Flash zAmmo on Smart Ammo swap", labelKey = "ANIM_COMBAT_AMMOSWAP",
|
||||
tooltip = "Briefly highlight the zAmmo button when Smart Ammo swaps your ammo.",
|
||||
tooltipKey = "ANIM_COMBAT_AMMOSWAP_TIP" },
|
||||
{ key = "combat.ammoout", section = "combat", default = true,
|
||||
label = "Warn when current ammo runs out",
|
||||
tooltip = "Show a red warning banner when Smart Ammo equips fallback ammunition." },
|
||||
{ key = "combat.lowammo", section = "combat", default = true,
|
||||
label = "Warn when total ammo is low",
|
||||
tooltip = "Show a warning banner when your total arrows or bullets reach the Smart Ammo threshold." },
|
||||
{ key = "combat.ammozero", section = "combat", default = true,
|
||||
label = "Warn when all ammo is gone",
|
||||
tooltip = "Show a sarcastic warning banner when you have no arrows or bullets left." },
|
||||
{ key = "combat.rangedswap", section = "combat", default = true,
|
||||
label = "Flash zRanged on weapon swap", labelKey = "ANIM_COMBAT_RANGEDSWAP",
|
||||
tooltip = "Briefly highlight the zRanged button when you hot-swap a ranged weapon.",
|
||||
tooltipKey = "ANIM_COMBAT_RANGEDSWAP_TIP" },
|
||||
{ key = "combat.mmprocs", section = "combat", default = true,
|
||||
label = "Pulse MM Widget procs", labelKey = "ANIM_COMBAT_MMPROCS",
|
||||
tooltip = "Pulse an MM Widget cell when a Lock and Load or Experimental ammo proc lights up.",
|
||||
tooltipKey = "ANIM_COMBAT_MMPROCS_TIP" },
|
||||
{ key = "book.open", section = "book", default = true,
|
||||
label = "Fade in the Hunter's Book", labelKey = "ANIM_BOOK_OPEN",
|
||||
tooltip = "Fade the Hunter's Book window in when you open it.",
|
||||
tooltipKey = "ANIM_BOOK_OPEN_TIP" },
|
||||
{ key = "book.tabs", section = "book", default = true,
|
||||
label = "Fade Hunter's Book results on tab changes", labelKey = "ANIM_BOOK_TABS",
|
||||
tooltip = "Fade the results list back in when you switch tabs inside the Hunter's Book.",
|
||||
tooltipKey = "ANIM_BOOK_TABS_TIP" },
|
||||
{ key = "book.model", section = "book", default = true,
|
||||
label = "Fade Hunter's Book beast models", labelKey = "ANIM_BOOK_MODEL",
|
||||
tooltip = "Fade the beast model viewer in when it loads a new model.",
|
||||
tooltipKey = "ANIM_BOOK_MODEL_TIP" },
|
||||
{ key = "book.search", section = "book", default = true,
|
||||
label = "Fade Hunter's Book search results", labelKey = "ANIM_BOOK_SEARCH",
|
||||
tooltip = "Fade the results list when your search filter changes.",
|
||||
tooltipKey = "ANIM_BOOK_SEARCH_TIP" },
|
||||
{ key = "ui.options", section = "ui", default = true,
|
||||
label = "Slide MetaHunt Options open", labelKey = "ANIM_UI_OPTIONS",
|
||||
tooltip = "Slide and fade the MetaHunt options window in when you open it.",
|
||||
tooltipKey = "ANIM_UI_OPTIONS_TIP" },
|
||||
{ key = "ui.optiontabs", section = "ui", default = true,
|
||||
label = "Fade MetaHunt Options tab changes", labelKey = "ANIM_UI_OPTIONTABS",
|
||||
tooltip = "Fade the settings panel when you switch tabs inside the options window.",
|
||||
tooltipKey = "ANIM_UI_OPTIONTABS_TIP" },
|
||||
{ key = "ui.optionsclose", section = "ui", default = true,
|
||||
label = "Fade MetaHunt windows closed", labelKey = "ANIM_UI_OPTIONSCLOSE",
|
||||
tooltip = "Fade the options and Hunter's Book windows out when you close them.",
|
||||
tooltipKey = "ANIM_UI_OPTIONSCLOSE_TIP" },
|
||||
{ key = "ui.minimap", section = "ui", default = true,
|
||||
label = "Pulse MetaHunt minimap button on click", labelKey = "ANIM_UI_MINIMAP",
|
||||
tooltip = "Give the minimap button a quick pulse when you click it.",
|
||||
tooltipKey = "ANIM_UI_MINIMAP_TIP" },
|
||||
{ key = "ui.minimapping", section = "ui", default = true,
|
||||
label = "Ping minimap when locating a target", labelKey = "ANIM_UI_MINIMAPPING",
|
||||
tooltip = "Send a short ping ripple over the minimap when you locate a tracked NPC.",
|
||||
tooltipKey = "ANIM_UI_MINIMAPPING_TIP" },
|
||||
{ key = "ui.zbuttons", section = "ui", default = true,
|
||||
label = "Fade zButton bars open", labelKey = "ANIM_UI_ZBUTTONS",
|
||||
tooltip = "Fade the child buttons in when a collapsible zButton bar (pet, tracking, aspects...) expands.",
|
||||
tooltipKey = "ANIM_UI_ZBUTTONS_TIP" },
|
||||
{ key = "ui.zbar", section = "ui", default = true,
|
||||
label = "Fade the zBar in", labelKey = "ANIM_UI_ZBAR",
|
||||
tooltip = "Fade the unified zBar buttons in when the bar becomes active.",
|
||||
tooltipKey = "ANIM_UI_ZBAR_TIP" },
|
||||
{ key = "ui.stable", section = "ui", default = true,
|
||||
label = "Flash Hunter's Book stable slots", labelKey = "ANIM_UI_STABLE",
|
||||
tooltip = "Flash a stable slot in the Hunter's Book when a pet is deposited or withdrawn.",
|
||||
tooltipKey = "ANIM_UI_STABLE_TIP" },
|
||||
{ key = "ui.mappins", section = "ui", default = true,
|
||||
label = "Fade World Map markers in", labelKey = "ANIM_UI_MAPPINS",
|
||||
tooltip = "Fade the world map markers in when you locate a beast, vendor or master.",
|
||||
tooltipKey = "ANIM_UI_MAPPINS_TIP" },
|
||||
}
|
||||
|
||||
local function MTH_FX_Store()
|
||||
if MTH and type(MTH.GetModuleSavedVariables) == "function" then
|
||||
return MTH:GetModuleSavedVariables("animations")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_FX_DefaultFor(key)
|
||||
local i = 1
|
||||
while i <= table.getn(MTH_FX_ANIM_DEFS) do
|
||||
if MTH_FX_ANIM_DEFS[i].key == key then
|
||||
return MTH_FX_ANIM_DEFS[i].default and true or false
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_FX_AnimationsEnabled()
|
||||
local store = MTH_FX_Store()
|
||||
if not store or store.enabled == nil then
|
||||
return true
|
||||
end
|
||||
return store.enabled and true or false
|
||||
end
|
||||
|
||||
function MTH_FX_SetAnimationsEnabled(value)
|
||||
local store = MTH_FX_Store()
|
||||
if not store then return end
|
||||
store.enabled = value and true or false
|
||||
end
|
||||
|
||||
function MTH_FX_GetFlag(key)
|
||||
local store = MTH_FX_Store()
|
||||
if store and store.flags and store.flags[key] ~= nil then
|
||||
return store.flags[key] and true or false
|
||||
end
|
||||
return MTH_FX_DefaultFor(key)
|
||||
end
|
||||
|
||||
function MTH_FX_SetFlag(key, value)
|
||||
local store = MTH_FX_Store()
|
||||
if not store then return end
|
||||
store.flags = store.flags or {}
|
||||
store.flags[key] = value and true or false
|
||||
end
|
||||
|
||||
-- Master gate every animation call site checks: false when animations are
|
||||
-- globally disabled, or when this specific animation is turned off.
|
||||
function MTH_FX_IsEnabled(key)
|
||||
if not MTH_FX_AnimationsEnabled() then
|
||||
return false
|
||||
end
|
||||
if key == nil then
|
||||
return true
|
||||
end
|
||||
return MTH_FX_GetFlag(key)
|
||||
end
|
||||
|
||||
-- Register a one-shot "ballistic" tween type: a spark that flies out from the
|
||||
-- center along a velocity vector while gravity pulls it back down.
|
||||
if Sauce and type(Sauce.RegisterType) == "function" and not Sauce.MTH_HasBallistic then
|
||||
local ok, err = pcall(function()
|
||||
Sauce.MTH_HasBallistic = true
|
||||
Sauce:RegisterType("mth_ballistic", {
|
||||
init = function(target, from)
|
||||
return from or { 0, 0 }
|
||||
end,
|
||||
apply = function(target, from, to, t, tween)
|
||||
local dur = tween.dur or 1
|
||||
local elapsed = t * dur
|
||||
local vx = tween.vx or 0
|
||||
local vy = tween.vy or 0
|
||||
local gravity = tween.gravity or 0
|
||||
local x = from[1] + vx * elapsed
|
||||
local y = from[2] + vy * elapsed - 0.5 * gravity * elapsed * elapsed
|
||||
local anchor = tween.anchor or UIParent
|
||||
target:ClearAllPoints()
|
||||
target:SetPoint("CENTER", anchor, "CENTER", x, y)
|
||||
end,
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
-- Register a decaying "shake" tween type: oscillates the frame around its own
|
||||
-- current anchor point, with the amplitude fading to zero over the tween so it
|
||||
-- always lands back exactly where it started. Used for negative events.
|
||||
if Sauce and type(Sauce.RegisterType) == "function" and not Sauce.MTH_HasShake then
|
||||
local ok, err = pcall(function()
|
||||
Sauce.MTH_HasShake = true
|
||||
Sauce:RegisterType("mth_shake", {
|
||||
init = function(target)
|
||||
local p, rel, rp, x, y = target:GetPoint(1)
|
||||
return { p or "CENTER", rel, rp or p or "CENTER", x or 0, y or 0 }
|
||||
end,
|
||||
apply = function(target, from, to, t, tween)
|
||||
local mag = tween.magnitude or 8
|
||||
local freq = tween.freq or 34
|
||||
local decay = 1 - t
|
||||
local offset = math.sin(t * freq) * mag * decay
|
||||
target:ClearAllPoints()
|
||||
target:SetPoint(from[1], from[2], from[3], (from[4] or 0) + offset, from[5] or 0)
|
||||
end,
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
if Sauce and type(Sauce.RegisterType) == "function" and not Sauce.MTH_HasSpiral then
|
||||
local ok, err = pcall(function()
|
||||
Sauce.MTH_HasSpiral = true
|
||||
Sauce:RegisterType("mth_spiral", {
|
||||
init = function(target, from)
|
||||
return from or 0
|
||||
end,
|
||||
apply = function(target, from, to, t, tween)
|
||||
local radius = (tween.radius or 0) * (1 - t)
|
||||
local angle = (tween.startAngle or 0) + (tween.rotations or 1) * 2 * math.pi * t
|
||||
target:ClearAllPoints()
|
||||
target:SetPoint("CENTER", tween.anchor or UIParent, "CENTER",
|
||||
(tween.cx or 0) + math.cos(angle) * radius,
|
||||
(tween.cy or 0) + math.sin(angle) * radius)
|
||||
end,
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
-- Reusable flash: overlays a short additive colour glow on any frame/button and
|
||||
-- fades it out. Safe on any frame that supports CreateTexture; silent no-op
|
||||
-- otherwise. The overlay texture is cached on the frame for reuse.
|
||||
function MTH_FX_Flash(frame, r, g, b, duration)
|
||||
if not Sauce or not frame then
|
||||
return
|
||||
end
|
||||
if type(frame.CreateTexture) ~= "function" then
|
||||
return
|
||||
end
|
||||
local tex = frame.mthFxFlash
|
||||
if not tex then
|
||||
tex = frame:CreateTexture(nil, "OVERLAY")
|
||||
tex:SetTexture("Interface\\Buttons\\CheckButtonHilight")
|
||||
tex:SetBlendMode("ADD")
|
||||
tex:SetAllPoints(frame)
|
||||
frame.mthFxFlash = tex
|
||||
end
|
||||
tex:SetVertexColor(r or 1, g or 1, b or 1)
|
||||
tex:SetAlpha(0)
|
||||
tex:Show()
|
||||
local dur = duration or 0.45
|
||||
Sauce:Stop(tex)
|
||||
Sauce:Sequence({
|
||||
Sauce:FadeTo(tex, 0.85, dur * 0.35),
|
||||
Sauce:FadeTo(tex, 0, dur * 0.65, "inQuad", {
|
||||
onFinish = function() tex:Hide() end,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
local MTH_FX_NUM_SPARKS = 22
|
||||
local MTH_FX_NUM_CONFETTI = 48
|
||||
local MTH_FX_NUM_BLACKHOLE = 40
|
||||
local MTH_FX_Container
|
||||
local MTH_FX_MsgFrame
|
||||
local MTH_FX_TitleFS
|
||||
local MTH_FX_SubFS
|
||||
local MTH_FX_Sparks = {}
|
||||
local MTH_FX_Confetti = {}
|
||||
local MTH_FX_BlackHole = {}
|
||||
|
||||
-- Palette for the firework sparks (r, g, b), warm celebratory tones.
|
||||
local MTH_FX_Colors = {
|
||||
{ 1.00, 0.82, 0.00 },
|
||||
{ 1.00, 0.45, 0.10 },
|
||||
{ 1.00, 0.20, 0.30 },
|
||||
{ 0.35, 0.70, 1.00 },
|
||||
{ 0.55, 1.00, 0.45 },
|
||||
{ 0.85, 0.50, 1.00 },
|
||||
}
|
||||
|
||||
local function MTH_FX_EnsureFrames()
|
||||
if type(CreateFrame) ~= "function" then
|
||||
return false
|
||||
end
|
||||
if MTH_FX_Container then
|
||||
return true
|
||||
end
|
||||
|
||||
MTH_FX_Container = CreateFrame("Frame", "MTH_FX_Container", UIParent)
|
||||
MTH_FX_Container:SetWidth(1)
|
||||
MTH_FX_Container:SetHeight(1)
|
||||
MTH_FX_Container:SetPoint("CENTER", UIParent, "CENTER", 0, 40)
|
||||
MTH_FX_Container:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
MTH_FX_Container:Hide()
|
||||
|
||||
MTH_FX_MsgFrame = CreateFrame("Frame", "MTH_FX_Message", UIParent)
|
||||
MTH_FX_MsgFrame:SetWidth(512)
|
||||
MTH_FX_MsgFrame:SetHeight(90)
|
||||
MTH_FX_MsgFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 80)
|
||||
MTH_FX_MsgFrame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
|
||||
MTH_FX_TitleFS = MTH_FX_MsgFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalHuge")
|
||||
MTH_FX_TitleFS:SetPoint("CENTER", MTH_FX_MsgFrame, "CENTER", 0, 14)
|
||||
MTH_FX_TitleFS:SetTextColor(1, 0.82, 0)
|
||||
|
||||
MTH_FX_SubFS = MTH_FX_MsgFrame:CreateFontString(nil, "OVERLAY", "GameFontHighlightLarge")
|
||||
MTH_FX_SubFS:SetPoint("TOP", MTH_FX_TitleFS, "BOTTOM", 0, -8)
|
||||
MTH_FX_SubFS:SetTextColor(1, 1, 1)
|
||||
|
||||
MTH_FX_MsgFrame:Hide()
|
||||
|
||||
local i = 1
|
||||
while i <= MTH_FX_NUM_SPARKS do
|
||||
local spark = MTH_FX_Container:CreateTexture(nil, "OVERLAY")
|
||||
spark:SetTexture("Interface\\Cooldown\\star4")
|
||||
spark:SetWidth(22)
|
||||
spark:SetHeight(22)
|
||||
spark:SetBlendMode("ADD")
|
||||
spark:SetPoint("CENTER", MTH_FX_Container, "CENTER", 0, 0)
|
||||
spark:Hide()
|
||||
MTH_FX_Sparks[i] = spark
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
i = 1
|
||||
while i <= MTH_FX_NUM_CONFETTI do
|
||||
local bit = MTH_FX_Container:CreateTexture(nil, "OVERLAY")
|
||||
bit:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
bit:SetWidth(5)
|
||||
bit:SetHeight(8)
|
||||
bit:SetPoint("CENTER", MTH_FX_Container, "CENTER", 0, 0)
|
||||
bit:Hide()
|
||||
MTH_FX_Confetti[i] = bit
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
i = 1
|
||||
while i <= MTH_FX_NUM_BLACKHOLE do
|
||||
local particle = MTH_FX_Container:CreateTexture(nil, "OVERLAY")
|
||||
particle:SetTexture("Interface\\Cooldown\\star4")
|
||||
particle:SetWidth(10)
|
||||
particle:SetHeight(10)
|
||||
particle:SetBlendMode("ADD")
|
||||
particle:SetPoint("CENTER", MTH_FX_Container, "CENTER", 0, 0)
|
||||
particle:Hide()
|
||||
MTH_FX_BlackHole[i] = particle
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Public entry point: play a full-screen celebration. `style` defaults to the
|
||||
-- original firework burst and may be firework, victory, starfall, fanfare, or comet.
|
||||
function MTH_Celebrate(title, subtitle, style, displaySeconds)
|
||||
if not Sauce then
|
||||
return
|
||||
end
|
||||
if not MTH_FX_EnsureFrames() then
|
||||
return
|
||||
end
|
||||
style = string.lower(tostring(style or "firework"))
|
||||
local fadeDelay = tonumber(displaySeconds) or 5
|
||||
if fadeDelay < 0.5 then fadeDelay = 0.5 end
|
||||
|
||||
if style == "starfall" then
|
||||
MTH_FX_TitleFS:SetTextColor(0.45, 0.80, 1.00)
|
||||
else
|
||||
MTH_FX_TitleFS:SetTextColor(1, 0.82, 0)
|
||||
end
|
||||
MTH_FX_TitleFS:SetText(title or "")
|
||||
MTH_FX_SubFS:SetText(subtitle or "")
|
||||
|
||||
Sauce:Stop(MTH_FX_MsgFrame)
|
||||
MTH_FX_MsgFrame:SetAlpha(0)
|
||||
MTH_FX_MsgFrame:Show()
|
||||
if style == "victory" or style == "fanfare" then
|
||||
Sauce:Sequence({
|
||||
Sauce:Group({
|
||||
Sauce:SlideIn(MTH_FX_MsgFrame, "UP", 80, 0.36, "outBack", { defer = true }),
|
||||
Sauce:Pulse(MTH_FX_MsgFrame, style == "victory" and 1.32 or 1.18, 0.52, { defer = true }),
|
||||
}),
|
||||
Sauce:FadeOut(MTH_FX_MsgFrame, 0.6, "inQuad", { delay = fadeDelay }),
|
||||
})
|
||||
else
|
||||
Sauce:Sequence({
|
||||
Sauce:Group({
|
||||
Sauce:FadeTo(MTH_FX_MsgFrame, 1, 0.25),
|
||||
Sauce:Pulse(MTH_FX_MsgFrame, 1.22, 0.5),
|
||||
}),
|
||||
Sauce:FadeOut(MTH_FX_MsgFrame, 0.6, "inQuad", { delay = fadeDelay }),
|
||||
})
|
||||
end
|
||||
|
||||
MTH_FX_Container:Show()
|
||||
local colorCount = table.getn(MTH_FX_Colors)
|
||||
local colorIdx = 0
|
||||
local i = 1
|
||||
while i <= MTH_FX_NUM_SPARKS do
|
||||
local spark = MTH_FX_Sparks[i]
|
||||
spark:SetTexture("Interface\\Cooldown\\star4")
|
||||
spark:SetTexCoord(0, 1, 0, 1)
|
||||
local startX, startY = 0, 0
|
||||
local delay, gravity = 0, 300
|
||||
local angle = (i / MTH_FX_NUM_SPARKS) * 2 * math.pi + (math.random() - 0.5) * 0.4
|
||||
local speed = 150 + math.random() * 130
|
||||
local vx = math.cos(angle) * speed
|
||||
local vy = math.sin(angle) * speed + 70
|
||||
local dur = 0.9 + math.random() * 0.4
|
||||
|
||||
if style == "victory" then
|
||||
startX = ((i - math.floor(i / 2) * 2) == 0) and -130 or 130
|
||||
speed = 100 + math.random() * 100
|
||||
vx = math.cos(angle) * speed
|
||||
vy = math.sin(angle) * speed + 105
|
||||
elseif style == "starfall" then
|
||||
startX = -230 + math.random() * 460
|
||||
startY = 200 + math.random() * 100
|
||||
vx = -35 + math.random() * 70
|
||||
vy = -10 + math.random() * 25
|
||||
gravity = 235
|
||||
dur = 1.1 + math.random() * 0.5
|
||||
elseif style == "fanfare" then
|
||||
delay = ((i - 1) - math.floor((i - 1) / 3) * 3) * 0.18
|
||||
speed = 110 + math.random() * 90
|
||||
vx = math.cos(angle) * speed
|
||||
vy = math.sin(angle) * speed + 90
|
||||
elseif style == "comet" then
|
||||
if i == 1 then
|
||||
startY = -180
|
||||
vx, vy, gravity, dur = 0, 430, 0, 0.52
|
||||
else
|
||||
delay = 0.5
|
||||
startY = 45
|
||||
speed = 130 + math.random() * 120
|
||||
vx = math.cos(angle) * speed
|
||||
vy = math.sin(angle) * speed + 70
|
||||
end
|
||||
end
|
||||
|
||||
colorIdx = colorIdx + 1
|
||||
if colorIdx > colorCount then colorIdx = 1 end
|
||||
local color = MTH_FX_Colors[colorIdx]
|
||||
spark:SetVertexColor(color[1], color[2], color[3])
|
||||
spark:SetAlpha(delay > 0 and 0 or 1)
|
||||
spark:Show()
|
||||
|
||||
Sauce:Stop(spark)
|
||||
Sauce:Group({
|
||||
Sauce:Tween(spark, {
|
||||
type = "mth_ballistic",
|
||||
from = { startX, startY },
|
||||
duration = dur,
|
||||
easing = "linear",
|
||||
delay = delay,
|
||||
anchor = MTH_FX_Container,
|
||||
dur = dur,
|
||||
vx = vx,
|
||||
vy = vy,
|
||||
gravity = gravity,
|
||||
}),
|
||||
Sauce:FadeTo(spark, 1, 0.05, "outQuad", { delay = delay }),
|
||||
Sauce:FadeTo(spark, 0, 0.45, "inQuad", {
|
||||
delay = delay + dur - 0.45,
|
||||
onFinish = function()
|
||||
spark:Hide()
|
||||
end,
|
||||
}),
|
||||
})
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_FX_ShowPreviewBanner(title, subtitle, r, g, b)
|
||||
MTH_FX_TitleFS:SetTextColor(r, g, b)
|
||||
MTH_FX_TitleFS:SetText(title)
|
||||
MTH_FX_SubFS:SetText(subtitle)
|
||||
Sauce:Stop(MTH_FX_MsgFrame)
|
||||
MTH_FX_MsgFrame:SetAlpha(0)
|
||||
MTH_FX_MsgFrame:Show()
|
||||
Sauce:Sequence({
|
||||
Sauce:Group({
|
||||
Sauce:FadeTo(MTH_FX_MsgFrame, 1, 0.18),
|
||||
Sauce:Pulse(MTH_FX_MsgFrame, 1.18, 0.45),
|
||||
}),
|
||||
Sauce:FadeOut(MTH_FX_MsgFrame, 0.55, "inQuad", { delay = 5 }),
|
||||
})
|
||||
end
|
||||
|
||||
function MTH_CelebrateConfetti(title, subtitle)
|
||||
if not Sauce or not MTH_FX_EnsureFrames() then return end
|
||||
MTH_FX_ShowPreviewBanner(title or "Celebration!", subtitle or "Confetti", 1, 0.82, 0.15)
|
||||
MTH_FX_Container:Show()
|
||||
local count = table.getn(MTH_FX_Colors)
|
||||
local i = 1
|
||||
while i <= MTH_FX_NUM_CONFETTI do
|
||||
local bit = MTH_FX_Confetti[i]
|
||||
local color = MTH_FX_Colors[((i - 1) - math.floor((i - 1) / count) * count) + 1]
|
||||
local size = 3 + math.random() * 5
|
||||
local angle = math.random() * 2 * math.pi
|
||||
local speed = 90 + math.random() * 210
|
||||
local dur = 1.25 + math.random() * 0.7
|
||||
bit:SetWidth(size)
|
||||
bit:SetHeight(size * 1.6)
|
||||
bit:SetVertexColor(color[1], color[2], color[3])
|
||||
bit:SetAlpha(1)
|
||||
bit:Show()
|
||||
Sauce:Stop(bit)
|
||||
Sauce:Group({
|
||||
Sauce:Tween(bit, {
|
||||
type = "mth_ballistic", from = { 0, 0 }, duration = dur,
|
||||
easing = "linear", anchor = MTH_FX_Container, dur = dur,
|
||||
vx = math.cos(angle) * speed, vy = math.sin(angle) * speed + 100, gravity = 255,
|
||||
}),
|
||||
Sauce:FadeTo(bit, 0, 0.35, "inQuad", {
|
||||
delay = dur - 0.35, onFinish = function() bit:Hide() end,
|
||||
}),
|
||||
})
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_CelebrateBlackHole(title, subtitle)
|
||||
if not Sauce or not MTH_FX_EnsureFrames() then return end
|
||||
MTH_FX_ShowPreviewBanner(title or "Gravity Well", subtitle or "Black Hole", 0.68, 0.45, 1.00)
|
||||
MTH_FX_Container:Show()
|
||||
local colors = { { 0.45, 0.25, 1 }, { 0.30, 0.50, 1 }, { 0.80, 0.55, 1 } }
|
||||
local i = 1
|
||||
while i <= MTH_FX_NUM_BLACKHOLE do
|
||||
local particle = MTH_FX_BlackHole[i]
|
||||
local color = colors[((i - 1) - math.floor((i - 1) / 3) * 3) + 1]
|
||||
local startAngle = math.random() * 2 * math.pi
|
||||
local radius = 95 + math.random() * 145
|
||||
local duration = 1.5 + math.random() * 1.0
|
||||
local size = 7 + math.random() * 8
|
||||
particle:SetWidth(size)
|
||||
particle:SetHeight(size)
|
||||
particle:SetVertexColor(color[1], color[2], color[3])
|
||||
particle:SetAlpha(1)
|
||||
particle:Show()
|
||||
Sauce:Stop(particle)
|
||||
Sauce:Group({
|
||||
Sauce:Tween(particle, {
|
||||
type = "mth_spiral", from = 0, to = 1, duration = duration,
|
||||
easing = "inQuad", anchor = MTH_FX_Container, cx = 0, cy = 0,
|
||||
radius = radius, startAngle = startAngle, rotations = 1.5 + math.random() * 2.0,
|
||||
}),
|
||||
Sauce:Size(particle, { size, size }, { 2, 2 }, duration, "inQuad"),
|
||||
Sauce:FadeTo(particle, 0, duration * 0.3, "inQuad", {
|
||||
delay = duration * 0.7, onFinish = function() particle:Hide() end,
|
||||
}),
|
||||
})
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_CelebrateShockwave(title, subtitle)
|
||||
if not Sauce or not MTH_FX_EnsureFrames() then return end
|
||||
MTH_FX_ShowPreviewBanner(title or "Impact!", subtitle or "Shockwave", 1, 0.62, 0.15)
|
||||
MTH_FX_Container:Show()
|
||||
local dotsPerWave = 16
|
||||
local i = 1
|
||||
while i <= MTH_FX_NUM_CONFETTI do
|
||||
local dot = MTH_FX_Confetti[i]
|
||||
local wave = math.floor((i - 1) / dotsPerWave) + 1
|
||||
local pos = (i - 1) - math.floor((i - 1) / dotsPerWave) * dotsPerWave
|
||||
local angle = pos / dotsPerWave * 2 * math.pi
|
||||
local duration = 1.1 + wave * 0.18
|
||||
local radius = 115 + wave * 52
|
||||
local delay = (wave - 1) * 0.18
|
||||
local intensity = 1 - (wave - 1) * 0.20
|
||||
dot:SetWidth(5)
|
||||
dot:SetHeight(5)
|
||||
dot:SetVertexColor(1 * intensity, 0.65 * intensity, 0.18 * intensity)
|
||||
dot:SetAlpha(0)
|
||||
dot:Show()
|
||||
Sauce:Stop(dot)
|
||||
Sauce:Group({
|
||||
Sauce:Tween(dot, {
|
||||
type = "mth_ballistic", from = { 0, 0 }, duration = duration,
|
||||
easing = "outQuad", delay = delay, anchor = MTH_FX_Container, dur = duration,
|
||||
vx = math.cos(angle) * radius / duration, vy = math.sin(angle) * radius / duration, gravity = 0,
|
||||
}),
|
||||
Sauce:FadeTo(dot, 1, 0.04, "outQuad", { delay = delay }),
|
||||
Sauce:FadeTo(dot, 0, 0.4, "inQuad", {
|
||||
delay = delay + duration - 0.4, onFinish = function() dot:Hide() end,
|
||||
}),
|
||||
})
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_CelebrateShatter(title, subtitle)
|
||||
if not Sauce or not MTH_FX_EnsureFrames() then return end
|
||||
MTH_FX_ShowPreviewBanner(title or "Shattered!", subtitle or "Shatter", 0.70, 0.85, 1.00)
|
||||
MTH_FX_Container:Show()
|
||||
local grid = 4
|
||||
local iconSize = 80
|
||||
local fragmentSize = iconSize / grid
|
||||
local i = 1
|
||||
while i <= MTH_FX_NUM_SPARKS do
|
||||
local spark = MTH_FX_Sparks[i]
|
||||
if i <= grid * grid then
|
||||
local row = math.floor((i - 1) / grid)
|
||||
local col = (i - 1) - row * grid
|
||||
local x = (col - grid / 2 + 0.5) * fragmentSize
|
||||
local y = ((grid - 1 - row) - grid / 2 + 0.5) * fragmentSize
|
||||
local duration = 1.1 + math.random() * 0.45
|
||||
spark:SetTexture("Interface\\Icons\\Ability_Hunter_SniperShot")
|
||||
spark:SetTexCoord(col / grid, (col + 1) / grid, row / grid, (row + 1) / grid)
|
||||
spark:SetWidth(fragmentSize)
|
||||
spark:SetHeight(fragmentSize)
|
||||
spark:SetVertexColor(1, 1, 1)
|
||||
spark:SetAlpha(1)
|
||||
spark:Show()
|
||||
Sauce:Stop(spark)
|
||||
Sauce:Group({
|
||||
Sauce:Tween(spark, {
|
||||
type = "mth_ballistic", from = { x, y }, duration = duration,
|
||||
easing = "linear", anchor = MTH_FX_Container, dur = duration,
|
||||
vx = x * 4 + (math.random() - 0.5) * 90,
|
||||
vy = y * 4 + 95 + math.random() * 80, gravity = 265,
|
||||
}),
|
||||
Sauce:FadeTo(spark, 0, 0.35, "inQuad", {
|
||||
delay = duration - 0.35, onFinish = function() spark:Hide() end,
|
||||
}),
|
||||
})
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_CelebratePreview(style)
|
||||
style = string.lower(tostring(style or "firework"))
|
||||
if style == "all" then
|
||||
if not Sauce then return end
|
||||
local styles = { "firework", "blackhole", "shockwave", "shatter" }
|
||||
local steps = {}
|
||||
local i = 1
|
||||
while i <= table.getn(styles) do
|
||||
local previewStyle = styles[i]
|
||||
table.insert(steps, Sauce:Run(function()
|
||||
MTH_CelebratePreview(previewStyle)
|
||||
end, { defer = true }))
|
||||
table.insert(steps, Sauce:Delay(5.7))
|
||||
i = i + 1
|
||||
end
|
||||
Sauce:Sequence(steps)
|
||||
return true
|
||||
end
|
||||
|
||||
if style == "blackhole" then
|
||||
MTH_CelebrateBlackHole("Celebration Preview", "Black Hole")
|
||||
return true
|
||||
elseif style == "shockwave" then
|
||||
MTH_CelebrateShockwave("Celebration Preview", "Shockwave")
|
||||
return true
|
||||
elseif style == "shatter" then
|
||||
MTH_CelebrateShatter("Celebration Preview", "Shatter")
|
||||
return true
|
||||
end
|
||||
|
||||
local labels = {
|
||||
firework = "Firework Burst",
|
||||
}
|
||||
if not labels[style] then return false end
|
||||
MTH_Celebrate("Celebration Preview", labels[style], style)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Convenience wrapper used when the hunter's pet learns a new ability/rank.
|
||||
function MTH_CelebratePetAbility(label)
|
||||
if not MTH_FX_IsEnabled("pet.celebrate") then
|
||||
return
|
||||
end
|
||||
MTH_Celebrate(MTH_FX_L("ANIM_PET_LEARNED_TITLE", "New Pet Ability!"), label)
|
||||
end
|
||||
|
||||
-- Dramatic negative banner: a red, shaking message shown when a pet runs away
|
||||
-- for good. Gated on pet.runaway. Reuses the shared message frame.
|
||||
function MTH_CelebrateRunaway(petName)
|
||||
if not MTH_FX_IsEnabled("pet.runaway") then
|
||||
return
|
||||
end
|
||||
if not Sauce then
|
||||
return
|
||||
end
|
||||
if not MTH_FX_EnsureFrames() then
|
||||
return
|
||||
end
|
||||
|
||||
MTH_FX_TitleFS:SetTextColor(1, 0.15, 0.15)
|
||||
MTH_FX_TitleFS:SetText(MTH_FX_L("ANIM_PET_RUNAWAY_TITLE", "Your pet ran away!"))
|
||||
MTH_FX_SubFS:SetText(petName or "")
|
||||
|
||||
Sauce:Stop(MTH_FX_MsgFrame)
|
||||
MTH_FX_MsgFrame:SetAlpha(0)
|
||||
MTH_FX_MsgFrame:Show()
|
||||
Sauce:Sequence({
|
||||
Sauce:FadeTo(MTH_FX_MsgFrame, 1, 0.18),
|
||||
Sauce:Tween(MTH_FX_MsgFrame, {
|
||||
type = "mth_shake",
|
||||
duration = 0.55,
|
||||
easing = "linear",
|
||||
magnitude = 11,
|
||||
freq = 42,
|
||||
}),
|
||||
Sauce:FadeOut(MTH_FX_MsgFrame, 0.7, "inQuad", { delay = 5 }),
|
||||
})
|
||||
end
|
||||
|
||||
function MTH_CelebrateAmmoFallback(previousAmmo, newAmmo)
|
||||
if not MTH_FX_IsEnabled("combat.ammoout") or not Sauce or not MTH_FX_EnsureFrames() then return end
|
||||
|
||||
MTH_FX_TitleFS:SetTextColor(1, 0.15, 0.15)
|
||||
MTH_FX_TitleFS:SetText("Out of ammo!")
|
||||
MTH_FX_SubFS:SetText("Equipped " .. tostring(newAmmo or "fallback ammunition"))
|
||||
|
||||
Sauce:Stop(MTH_FX_MsgFrame)
|
||||
MTH_FX_MsgFrame:SetAlpha(0)
|
||||
MTH_FX_MsgFrame:Show()
|
||||
Sauce:Sequence({
|
||||
Sauce:FadeTo(MTH_FX_MsgFrame, 1, 0.18),
|
||||
Sauce:Tween(MTH_FX_MsgFrame, {
|
||||
type = "mth_shake",
|
||||
duration = 0.55,
|
||||
easing = "linear",
|
||||
magnitude = 11,
|
||||
freq = 42,
|
||||
}),
|
||||
Sauce:FadeOut(MTH_FX_MsgFrame, 0.7, "inQuad", { delay = 5 }),
|
||||
})
|
||||
end
|
||||
|
||||
function MTH_CelebrateLowAmmo(ammoType, total)
|
||||
if not MTH_FX_IsEnabled("combat.lowammo") or not Sauce or not MTH_FX_EnsureFrames() then return end
|
||||
MTH_FX_TitleFS:SetTextColor(1, 0.55, 0.10)
|
||||
MTH_FX_TitleFS:SetText("Running low on " .. tostring(ammoType or "ammo") .. "!")
|
||||
MTH_FX_SubFS:SetText(tostring(total or 0) .. " left before you're reduced to harsh language.")
|
||||
|
||||
Sauce:Stop(MTH_FX_MsgFrame)
|
||||
MTH_FX_MsgFrame:SetAlpha(0)
|
||||
MTH_FX_MsgFrame:Show()
|
||||
Sauce:Sequence({
|
||||
Sauce:FadeTo(MTH_FX_MsgFrame, 1, 0.18),
|
||||
Sauce:Tween(MTH_FX_MsgFrame, {
|
||||
type = "mth_shake",
|
||||
duration = 0.55,
|
||||
easing = "linear",
|
||||
magnitude = 11,
|
||||
freq = 42,
|
||||
}),
|
||||
Sauce:FadeOut(MTH_FX_MsgFrame, 0.7, "inQuad", { delay = 5 }),
|
||||
})
|
||||
end
|
||||
|
||||
function MTH_CelebrateAmmoZero(ammoType)
|
||||
if not MTH_FX_IsEnabled("combat.ammozero") or not Sauce or not MTH_FX_EnsureFrames() then return end
|
||||
MTH_FX_TitleFS:SetTextColor(1, 0.15, 0.15)
|
||||
MTH_FX_TitleFS:SetText("Out of " .. tostring(ammoType or "ammo") .. "!")
|
||||
MTH_FX_SubFS:SetText("Your ranged weapon is now a very expensive club. Well done.")
|
||||
|
||||
Sauce:Stop(MTH_FX_MsgFrame)
|
||||
MTH_FX_MsgFrame:SetAlpha(0)
|
||||
MTH_FX_MsgFrame:Show()
|
||||
Sauce:Sequence({
|
||||
Sauce:FadeTo(MTH_FX_MsgFrame, 1, 0.18),
|
||||
Sauce:Tween(MTH_FX_MsgFrame, {
|
||||
type = "mth_shake",
|
||||
duration = 0.55,
|
||||
easing = "linear",
|
||||
magnitude = 11,
|
||||
freq = 42,
|
||||
}),
|
||||
Sauce:FadeOut(MTH_FX_MsgFrame, 0.7, "inQuad", { delay = 5 }),
|
||||
})
|
||||
end
|
||||
@@ -0,0 +1,418 @@
|
||||
-- hunterbook-model-viewer.lua
|
||||
-- Embeds a small 3D creature skin preview at the bottom of the Beast Lore /
|
||||
-- Abilities inspector. Clicking it opens a larger draggable popup with
|
||||
-- < / > navigation through all skin variants of the selected family.
|
||||
--
|
||||
-- Uses MTH_DS_BeastSkins[familyName] generated by .tools/generators/gen_beast_skins.py
|
||||
-- Skin rendering: PlayerModel:SetUnit(CreatureDisplayInfo.ID)
|
||||
|
||||
local MTH_MV_Sauce = PizzaSauce
|
||||
|
||||
-- Fade a model frame in when a new skin is displayed, unless disabled.
|
||||
local function MTH_MV_AnimateModel(model)
|
||||
if not MTH_MV_Sauce or not model then return end
|
||||
if type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("book.model") then return end
|
||||
if type(model.SetAlpha) ~= "function" then return end
|
||||
if not model:IsShown() then return end
|
||||
model:SetAlpha(0)
|
||||
MTH_MV_Sauce:FadeTo(model, 1, 0.30, "outQuad")
|
||||
end
|
||||
|
||||
local MTH_MV = {
|
||||
inited = false,
|
||||
family = nil,
|
||||
skins = {},
|
||||
index = 1,
|
||||
facing = 0.6,
|
||||
rotating = false,
|
||||
rotateLastX = 0,
|
||||
keepPopup = false,
|
||||
skinBeastRows = {},
|
||||
skinListEmpty = nil,
|
||||
-- small preview (parented to book frame, anchored below MTH_BOOK_DetailBackdrop)
|
||||
previewFrame = nil,
|
||||
previewModel = nil,
|
||||
-- popup
|
||||
popup = nil,
|
||||
popupModel = nil,
|
||||
popupTitle = nil,
|
||||
popupSkinLabel = nil,
|
||||
}
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Internal
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
local function MTH_MV_ApplyModel(model, skinId)
|
||||
if not model or not skinId then
|
||||
return
|
||||
end
|
||||
model:SetUnit(skinId)
|
||||
model:SetPosition(0, 0, 0)
|
||||
model:SetModelScale(1)
|
||||
local useFacing = (MTH_MV.popupModel ~= nil and model == MTH_MV.popupModel)
|
||||
model:SetFacing(useFacing and MTH_MV.facing or 0.6)
|
||||
end
|
||||
|
||||
local MTH_MV_BuildSkinBeastList -- forward declaration
|
||||
|
||||
local function MTH_MV_DisplayCurrent()
|
||||
local skin = MTH_MV.skins[MTH_MV.index]
|
||||
if not skin then return end
|
||||
local n = table.getn(MTH_MV.skins)
|
||||
|
||||
MTH_MV_ApplyModel(MTH_MV.previewModel, skin.id)
|
||||
MTH_MV_ApplyModel(MTH_MV.popupModel, skin.id)
|
||||
MTH_MV_AnimateModel(MTH_MV.previewModel)
|
||||
MTH_MV_AnimateModel(MTH_MV.popupModel)
|
||||
|
||||
local indexStr = tostring(MTH_MV.index) .. " / " .. tostring(n)
|
||||
if MTH_MV.popupSkinLabel then
|
||||
MTH_MV.popupSkinLabel:SetText(indexStr .. " · " .. tostring(skin.tex or ""))
|
||||
end
|
||||
MTH_MV_BuildSkinBeastList(skin.id)
|
||||
end
|
||||
|
||||
MTH_MV_BuildSkinBeastList = function(skinId)
|
||||
for i = 1, table.getn(MTH_MV.skinBeastRows) do
|
||||
MTH_MV.skinBeastRows[i]:Hide()
|
||||
end
|
||||
if MTH_MV.skinListEmpty then MTH_MV.skinListEmpty:Hide() end
|
||||
if not skinId or not MTH_DS_Beasts then return end
|
||||
|
||||
local matches = {}
|
||||
for npcId, beast in pairs(MTH_DS_Beasts) do
|
||||
if type(beast) == "table" and beast.skinId == skinId then
|
||||
local nm = (MTH and MTH.GetLocalizedBeastName and MTH:GetLocalizedBeastName(npcId, beast.name)) or beast.name
|
||||
table.insert(matches, { id = npcId, name = nm or "?", lvl = tostring(beast.lvl or "?") })
|
||||
end
|
||||
end
|
||||
table.sort(matches, function(a, b) return (a.name or "") < (b.name or "") end)
|
||||
|
||||
local maxRows = table.getn(MTH_MV.skinBeastRows)
|
||||
local shown = 0
|
||||
for i = 1, table.getn(matches) do
|
||||
if shown >= maxRows then break end
|
||||
shown = shown + 1
|
||||
local row = MTH_MV.skinBeastRows[shown]
|
||||
row._npcId = matches[i].id
|
||||
row.nameText:SetText(matches[i].name)
|
||||
row.lvlText:SetText(matches[i].lvl)
|
||||
row:Show()
|
||||
end
|
||||
if shown == 0 and MTH_MV.skinListEmpty then
|
||||
MTH_MV.skinListEmpty:Show()
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_MV_Init()
|
||||
if MTH_MV.inited then return end
|
||||
local backdrop = getglobal("MTH_BOOK_DetailBackdrop")
|
||||
if not backdrop then
|
||||
return
|
||||
end
|
||||
MTH_MV.inited = true
|
||||
|
||||
-- ----------------------------------------------------------------
|
||||
-- Model preview container (sibling of inspector, full column width)
|
||||
-- Hardcoded offset from book frame TOPLEFT: x=608 (right column),
|
||||
-- y = -(166 inspector_top + 178 inspector_h + 6 gap) = -350
|
||||
-- ----------------------------------------------------------------
|
||||
local bookFrame = backdrop:GetParent()
|
||||
MTH_MV.previewFrame = CreateFrame("Frame", "MTH_BOOK_SkinPreview", bookFrame)
|
||||
MTH_MV.previewFrame:SetWidth(136)
|
||||
MTH_MV.previewFrame:SetHeight(136)
|
||||
MTH_MV.previewFrame:SetPoint("TOPLEFT", bookFrame, "TOPLEFT", 608, -384)
|
||||
MTH_MV.previewFrame:SetBackdrop({
|
||||
bgFile = "Interface\\Buttons\\WHITE8X8",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true, tileSize = 16, edgeSize = 12,
|
||||
insets = {left = 3, right = 3, top = 3, bottom = 3},
|
||||
})
|
||||
MTH_MV.previewFrame:SetBackdropColor(0, 0, 0, 0.7)
|
||||
MTH_MV.previewFrame:SetBackdropBorderColor(0.35, 0.35, 0.35, 0.8)
|
||||
MTH_MV.previewFrame:Hide()
|
||||
|
||||
-- PlayerModel fills the inner area
|
||||
MTH_MV.previewModel = CreateFrame("PlayerModel", nil, MTH_MV.previewFrame)
|
||||
MTH_MV.previewModel:SetPoint("TOPLEFT", MTH_MV.previewFrame, "TOPLEFT", 3, -3)
|
||||
MTH_MV.previewModel:SetPoint("BOTTOMRIGHT", MTH_MV.previewFrame, "BOTTOMRIGHT", -3, 3)
|
||||
|
||||
-- Invisible click overlay → opens popup
|
||||
local previewBtn = CreateFrame("Button", nil, MTH_MV.previewFrame)
|
||||
previewBtn:SetAllPoints(MTH_MV.previewFrame)
|
||||
previewBtn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square")
|
||||
previewBtn:SetScript("OnClick", function()
|
||||
MTH_BOOK_ModelViewer_OpenPopup()
|
||||
end)
|
||||
previewBtn:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(MTH_MV.previewFrame, "ANCHOR_LEFT")
|
||||
GameTooltip:AddLine("Click to open skin viewer", 1, 1, 1)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
previewBtn:SetScript("OnLeave", function()
|
||||
GameTooltip:Hide()
|
||||
end)
|
||||
|
||||
-- ----------------------------------------------------------------
|
||||
-- Popup frame (wider: left side = model viewer, right = beast list)
|
||||
-- ----------------------------------------------------------------
|
||||
local popup = CreateFrame("Frame", "MTH_BOOK_ModelPopup", UIParent)
|
||||
popup:SetWidth(490)
|
||||
popup:SetHeight(320)
|
||||
popup:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
|
||||
popup:SetFrameStrata("TOOLTIP")
|
||||
popup:SetToplevel(true)
|
||||
popup:SetMovable(true)
|
||||
popup:EnableMouse(true)
|
||||
popup:RegisterForDrag("LeftButton")
|
||||
popup:SetScript("OnDragStart", function() this:StartMoving() end)
|
||||
popup:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
||||
popup:SetScript("OnHide", function() MTH_MV.rotating = false end)
|
||||
popup:Hide()
|
||||
popup:SetBackdrop({
|
||||
bgFile = "Interface\\Buttons\\WHITE8X8",
|
||||
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
||||
tile = true, tileSize = 32, edgeSize = 32,
|
||||
insets = {left = 11, right = 12, top = 12, bottom = 11},
|
||||
})
|
||||
popup:SetBackdropColor(0.05, 0.05, 0.08, 0.95)
|
||||
|
||||
-- Large model background (left side, x=20)
|
||||
local modelBg = CreateFrame("Frame", nil, popup)
|
||||
modelBg:SetWidth(224)
|
||||
modelBg:SetHeight(226)
|
||||
modelBg:SetPoint("TOPLEFT", popup, "TOPLEFT", 20, -42)
|
||||
modelBg:SetBackdrop({
|
||||
bgFile = "Interface\\Buttons\\WHITE8X8",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true, tileSize = 16, edgeSize = 12,
|
||||
insets = {left = 3, right = 3, top = 3, bottom = 3},
|
||||
})
|
||||
modelBg:SetBackdropColor(0, 0, 0, 0.85)
|
||||
modelBg:SetBackdropBorderColor(0.35, 0.35, 0.35, 0.9)
|
||||
|
||||
MTH_MV.popupModel = CreateFrame("PlayerModel", "MTH_BOOK_ModelPopupModel", modelBg)
|
||||
MTH_MV.popupModel:SetPoint("TOPLEFT", modelBg, "TOPLEFT", 3, -3)
|
||||
MTH_MV.popupModel:SetPoint("BOTTOMRIGHT", modelBg, "BOTTOMRIGHT", -3, 3)
|
||||
|
||||
-- Mouse rotation: drag on the black model area rotates; outside drags the popup
|
||||
MTH_MV.popupModel:EnableMouse(true)
|
||||
MTH_MV.popupModel:SetScript("OnMouseDown", function()
|
||||
MTH_MV.rotating = true
|
||||
MTH_MV.rotateLastX = GetCursorPosition()
|
||||
end)
|
||||
MTH_MV.popupModel:SetScript("OnMouseUp", function()
|
||||
MTH_MV.rotating = false
|
||||
end)
|
||||
MTH_MV.popupModel:SetScript("OnUpdate", function()
|
||||
if not MTH_MV.rotating then return end
|
||||
local cx = GetCursorPosition()
|
||||
local dx = cx - MTH_MV.rotateLastX
|
||||
MTH_MV.rotateLastX = cx
|
||||
if dx ~= 0 then
|
||||
MTH_MV.facing = MTH_MV.facing - dx * 0.03
|
||||
MTH_MV.popupModel:SetFacing(MTH_MV.facing)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Title (family name) anchored above the model
|
||||
MTH_MV.popupTitle = popup:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
MTH_MV.popupTitle:SetPoint("TOP", modelBg, "TOP", 0, 18)
|
||||
MTH_MV.popupTitle:SetTextColor(1, 0.82, 0)
|
||||
MTH_MV.popupTitle:SetText("Skin Viewer")
|
||||
|
||||
-- Close [X] button
|
||||
local closeBtn = CreateFrame("Button", nil, popup)
|
||||
closeBtn:SetWidth(22)
|
||||
closeBtn:SetHeight(22)
|
||||
closeBtn:SetPoint("TOPRIGHT", popup, "TOPRIGHT", -10, -10)
|
||||
closeBtn:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square")
|
||||
local closeLbl = closeBtn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
closeLbl:SetAllPoints(closeBtn)
|
||||
closeLbl:SetText("X")
|
||||
closeLbl:SetTextColor(0.9, 0.9, 0.9)
|
||||
closeBtn:SetScript("OnClick", function() this:GetParent():Hide() end)
|
||||
|
||||
-- "Unique Appearance" badge removed (inline texture syntax unsupported in this client)
|
||||
|
||||
-- Nav: ◀ label ▶ (kept within the left 264px area)
|
||||
local prevBtn = CreateFrame("Button", nil, popup, "UIPanelButtonTemplate")
|
||||
prevBtn:SetWidth(28)
|
||||
prevBtn:SetHeight(20)
|
||||
prevBtn:SetPoint("BOTTOMLEFT", popup, "BOTTOMLEFT", 14, 14)
|
||||
prevBtn:SetText("<")
|
||||
prevBtn:SetScript("OnClick", function()
|
||||
local n = table.getn(MTH_MV.skins)
|
||||
if n < 1 then return end
|
||||
MTH_MV.index = MTH_MV.index - 1
|
||||
if MTH_MV.index < 1 then MTH_MV.index = n end
|
||||
MTH_MV_DisplayCurrent()
|
||||
end)
|
||||
|
||||
local nextBtn = CreateFrame("Button", nil, popup, "UIPanelButtonTemplate")
|
||||
nextBtn:SetWidth(28)
|
||||
nextBtn:SetHeight(20)
|
||||
nextBtn:SetPoint("BOTTOMLEFT", popup, "BOTTOMLEFT", 222, 14)
|
||||
nextBtn:SetText(">")
|
||||
nextBtn:SetScript("OnClick", function()
|
||||
local n = table.getn(MTH_MV.skins)
|
||||
if n < 1 then return end
|
||||
MTH_MV.index = MTH_MV.index + 1
|
||||
if MTH_MV.index > n then MTH_MV.index = 1 end
|
||||
MTH_MV_DisplayCurrent()
|
||||
end)
|
||||
|
||||
MTH_MV.popupSkinLabel = popup:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
MTH_MV.popupSkinLabel:SetPoint("BOTTOMLEFT", prevBtn, "BOTTOMRIGHT", 4, 0)
|
||||
MTH_MV.popupSkinLabel:SetPoint("BOTTOMRIGHT", nextBtn, "BOTTOMLEFT", -4, 0)
|
||||
MTH_MV.popupSkinLabel:SetJustifyH("CENTER")
|
||||
MTH_MV.popupSkinLabel:SetTextColor(0.9, 0.9, 0.9)
|
||||
|
||||
-- ---------------------------------------------------------------
|
||||
-- Right panel: vertical divider + beast list
|
||||
-- ---------------------------------------------------------------
|
||||
local divider = popup:CreateTexture(nil, "ARTWORK")
|
||||
divider:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
divider:SetVertexColor(0.35, 0.35, 0.35, 0.8)
|
||||
divider:SetPoint("TOPLEFT", popup, "TOPLEFT", 270, -36)
|
||||
divider:SetPoint("BOTTOMLEFT", popup, "BOTTOMLEFT", 270, 12)
|
||||
divider:SetWidth(1)
|
||||
|
||||
local listHeader = popup:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
listHeader:SetPoint("TOPLEFT", popup, "TOPLEFT", 278, -18)
|
||||
listHeader:SetTextColor(1, 0.82, 0)
|
||||
listHeader:SetText("Beasts with this skin")
|
||||
|
||||
local listSep = popup:CreateTexture(nil, "ARTWORK")
|
||||
listSep:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
listSep:SetVertexColor(0.35, 0.35, 0.35, 0.6)
|
||||
listSep:SetPoint("TOPLEFT", popup, "TOPLEFT", 278, -30)
|
||||
listSep:SetPoint("TOPRIGHT", popup, "TOPRIGHT", -10, -30)
|
||||
listSep:SetHeight(1)
|
||||
|
||||
MTH_MV.skinListEmpty = popup:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
MTH_MV.skinListEmpty:SetPoint("TOPLEFT", popup, "TOPLEFT", 278, -38)
|
||||
MTH_MV.skinListEmpty:SetTextColor(0.5, 0.5, 0.5)
|
||||
MTH_MV.skinListEmpty:SetText("\226\128\148")
|
||||
MTH_MV.skinListEmpty:Hide()
|
||||
|
||||
-- Beast rows (14 max)
|
||||
local ROW_X = 278
|
||||
local ROW_START = -36
|
||||
local ROW_H = 17
|
||||
local MAX_ROWS = 14
|
||||
for i = 1, MAX_ROWS do
|
||||
local row = CreateFrame("Button", nil, popup)
|
||||
row:SetPoint("TOPLEFT", popup, "TOPLEFT", ROW_X, ROW_START - (i - 1) * ROW_H)
|
||||
row:SetWidth(200)
|
||||
row:SetHeight(ROW_H - 1)
|
||||
row._npcId = nil
|
||||
|
||||
local hl = row:CreateTexture(nil, "HIGHLIGHT")
|
||||
hl:SetAllPoints(row)
|
||||
hl:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
||||
hl:SetBlendMode("ADD")
|
||||
hl:SetAlpha(0.35)
|
||||
|
||||
row.nameText = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
|
||||
row.nameText:SetPoint("LEFT", row, "LEFT", 0, 0)
|
||||
row.nameText:SetWidth(158)
|
||||
row.nameText:SetJustifyH("LEFT")
|
||||
|
||||
row.lvlText = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
row.lvlText:SetPoint("LEFT", row, "LEFT", 160, 0)
|
||||
row.lvlText:SetWidth(38)
|
||||
row.lvlText:SetJustifyH("LEFT")
|
||||
row.lvlText:SetTextColor(0.7, 0.7, 0.7)
|
||||
|
||||
row:SetScript("OnClick", function()
|
||||
if this._npcId and type(MTH_BOOK_JumpToBeastById) == "function" then
|
||||
MTH_MV.keepPopup = true
|
||||
MTH_BOOK_JumpToBeastById(this._npcId)
|
||||
MTH_MV.keepPopup = false
|
||||
end
|
||||
end)
|
||||
row:Hide()
|
||||
MTH_MV.skinBeastRows[i] = row
|
||||
end
|
||||
|
||||
MTH_MV.popup = popup
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Public API
|
||||
-- ---------------------------------------------------------------------------
|
||||
|
||||
function MTH_BOOK_ModelViewer_SetFamily(family, skinId)
|
||||
MTH_MV_Init()
|
||||
if not MTH_MV.inited then return end
|
||||
local skins = MTH_DS_BeastSkins and type(MTH_DS_BeastSkins) == "table" and MTH_DS_BeastSkins[family]
|
||||
if not skins or table.getn(skins) == 0 then
|
||||
MTH_BOOK_ModelViewer_Clear()
|
||||
return
|
||||
end
|
||||
|
||||
-- Filter to only skins used by at least one tameable beast
|
||||
local usedSkinIds = {}
|
||||
if MTH_DS_Beasts then
|
||||
for _, beast in pairs(MTH_DS_Beasts) do
|
||||
if type(beast) == "table" and beast.skinId then
|
||||
usedSkinIds[beast.skinId] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
local filteredSkins = {}
|
||||
for i = 1, table.getn(skins) do
|
||||
if usedSkinIds[skins[i].id] then
|
||||
table.insert(filteredSkins, skins[i])
|
||||
end
|
||||
end
|
||||
if table.getn(filteredSkins) == 0 then
|
||||
MTH_BOOK_ModelViewer_Clear()
|
||||
return
|
||||
end
|
||||
|
||||
MTH_MV.family = family
|
||||
MTH_MV.skins = filteredSkins
|
||||
MTH_MV.facing = 0.6
|
||||
|
||||
-- If we have a specific skinId, jump to the matching skin entry.
|
||||
MTH_MV.index = 1
|
||||
if skinId then
|
||||
for i = 1, table.getn(filteredSkins) do
|
||||
if filteredSkins[i].id == skinId then
|
||||
MTH_MV.index = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if MTH_MV.popupTitle then
|
||||
MTH_MV.popupTitle:SetText(tostring(family) .. " — Skins")
|
||||
end
|
||||
|
||||
MTH_MV_DisplayCurrent()
|
||||
|
||||
if MTH_MV.previewFrame then MTH_MV.previewFrame:Show() end
|
||||
MTH_MV_DisplayCurrent()
|
||||
end
|
||||
|
||||
function MTH_BOOK_ModelViewer_Clear()
|
||||
if MTH_MV.previewFrame then MTH_MV.previewFrame:Hide() end
|
||||
if not MTH_MV.keepPopup then
|
||||
if MTH_MV.popup and MTH_MV.popup:IsShown() then MTH_MV.popup:Hide() end
|
||||
end
|
||||
MTH_MV.family = nil
|
||||
MTH_MV.skins = {}
|
||||
MTH_MV.index = 1
|
||||
end
|
||||
|
||||
function MTH_BOOK_ModelViewer_OpenPopup()
|
||||
MTH_MV_Init()
|
||||
if not MTH_MV.popup then return end
|
||||
if table.getn(MTH_MV.skins) == 0 then return end
|
||||
MTH_MV.popup:Show()
|
||||
MTH_MV.popup:Raise()
|
||||
MTH_MV_DisplayCurrent()
|
||||
end
|
||||
@@ -2,17 +2,18 @@ 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" },
|
||||
columnLabels = { "ID", "Lvl", "Fam", "Name", "Abilities", "Zone", "R", "E", "U", "AS" },
|
||||
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" },
|
||||
{ x = 68, width = 18, align = "LEFT" },
|
||||
{ x = 88, width = 130, align = "LEFT" },
|
||||
{ x = 220, width = 148, align = "LEFT" },
|
||||
{ x = 370, width = 80, align = "LEFT" },
|
||||
{ x = 452, width = 14, align = "CENTER" },
|
||||
{ x = 468, width = 14, align = "CENTER" },
|
||||
{ x = 484, width = 14, align = "CENTER" },
|
||||
{ x = 500, width = 54, align = "LEFT" },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -28,13 +29,31 @@ function MTH_BOOK_GetBeastZoneSummary(beast)
|
||||
return "-"
|
||||
end
|
||||
|
||||
local seen = {}
|
||||
local zoneNames = {}
|
||||
-- Count coords per WMA zone ID to find the dominant zone.
|
||||
local coordCount = {}
|
||||
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]))
|
||||
if c and c[3] then
|
||||
local zid = c[3]
|
||||
coordCount[zid] = (coordCount[zid] or 0) + 1
|
||||
end
|
||||
end
|
||||
local zids = {}
|
||||
for zid, _ in pairs(coordCount) do
|
||||
table.insert(zids, zid)
|
||||
end
|
||||
-- Sort by frequency descending so the zone with most spawns is listed first.
|
||||
table.sort(zids, function(a, b)
|
||||
return (coordCount[a] or 0) > (coordCount[b] or 0)
|
||||
end)
|
||||
-- Resolve to zone names, deduplicating by name (multiple WMA IDs can resolve to the same zone).
|
||||
local seenNames = {}
|
||||
local zoneNames = {}
|
||||
for i = 1, table.getn(zids) do
|
||||
local name = MTH_BOOK_GetZoneName(zids[i])
|
||||
if name and not seenNames[name] then
|
||||
seenNames[name] = true
|
||||
table.insert(zoneNames, name)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -67,6 +86,69 @@ function MTH_BOOK_SplitAbilities(text)
|
||||
return out
|
||||
end
|
||||
|
||||
local _subzoneChildCache = nil
|
||||
|
||||
local function MTH_BOOK_EnsureSubzoneCache()
|
||||
if _subzoneChildCache then return end
|
||||
_subzoneChildCache = {}
|
||||
if not MTH_DS_Zones then return end
|
||||
for zid, row in pairs(MTH_DS_Zones) do
|
||||
local parent = row.parent
|
||||
local x1 = row.x1
|
||||
local y1 = row.y1
|
||||
local w = row.width
|
||||
local h = row.height
|
||||
if parent and x1 and y1 and w and h then
|
||||
local names = row.names
|
||||
local name = names and (names.enUS or names[GetLocale and GetLocale() or "enUS"] or names.deDE)
|
||||
if name then
|
||||
if not _subzoneChildCache[parent] then
|
||||
_subzoneChildCache[parent] = {}
|
||||
end
|
||||
table.insert(_subzoneChildCache[parent], { x1, y1, x1 + w, y1 + h, name })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_BOOK_GetBeastSubZoneSummary(beast)
|
||||
if not beast or not beast.coords or table.getn(beast.coords) == 0 then return nil end
|
||||
MTH_BOOK_EnsureSubzoneCache()
|
||||
if not _subzoneChildCache then return nil end
|
||||
|
||||
local counts = {}
|
||||
for i = 1, table.getn(beast.coords) do
|
||||
local c = beast.coords[i]
|
||||
if c then
|
||||
local cx, cy, wmaId = c[1], c[2], c[3]
|
||||
if cx and cy and wmaId then
|
||||
local pfqId = wmaId
|
||||
if type(MTH_MAP_WMA_TO_PFQ) == "table" then
|
||||
pfqId = MTH_MAP_WMA_TO_PFQ[wmaId] or wmaId
|
||||
end
|
||||
local children = _subzoneChildCache[pfqId]
|
||||
if children then
|
||||
for _, sub in ipairs(children) do
|
||||
if cx >= sub[1] and cx <= sub[3] and cy >= sub[2] and cy <= sub[4] then
|
||||
counts[sub[5]] = (counts[sub[5]] or 0) + 1
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local bestName, bestCount = nil, 0
|
||||
for name, count in pairs(counts) do
|
||||
if count > bestCount then
|
||||
bestName = name
|
||||
bestCount = count
|
||||
end
|
||||
end
|
||||
return bestName
|
||||
end
|
||||
|
||||
function MTH_BOOK_HasUsableAbilities(beast)
|
||||
if not beast then return false end
|
||||
local abilities = MTH_BOOK_SplitAbilities(beast.abilities)
|
||||
@@ -231,14 +313,18 @@ function MTH_BOOK_BeastMatches(beastId, beast)
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.petInZoneOnly then
|
||||
local currentZoneId = MTH_BOOK_GetCurrentZoneId()
|
||||
if currentZoneId and not MTH_BOOK_BeastHasZoneId(beast, currentZoneId) then
|
||||
-- Use the zone ID cached once per BuildResults call (avoids N calls to
|
||||
-- GetCurrentZoneId which is expensive). If zone ID is nil (new/unknown
|
||||
-- zone like Moonwhisper Coast), no DB entry can match — exclude all.
|
||||
local currentZoneId = MTH_BOOK_STATE._cachedZoneId
|
||||
if not currentZoneId or 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 localizedName = (MTH and MTH.GetLocalizedBeastName and MTH:GetLocalizedBeastName(beastId, beast.name)) or beast.name
|
||||
local name = MTH_BOOK_SafeLower(localizedName)
|
||||
local family = MTH_BOOK_SafeLower(beast.family)
|
||||
local idText = tostring(beastId)
|
||||
if string.find(name, MTH_BOOK_STATE.search, 1, true) == nil
|
||||
@@ -249,6 +335,12 @@ function MTH_BOOK_BeastMatches(beastId, beast)
|
||||
end
|
||||
end
|
||||
|
||||
if MTH_BOOK_STATE.petZoneFilter and MTH_BOOK_STATE.petZoneFilter ~= "all" then
|
||||
if not MTH_BOOK_BeastHasZoneId(beast, MTH_BOOK_STATE.petZoneFilter) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -256,12 +348,12 @@ 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
|
||||
if not ba or not bb then return tostring(a) < tostring(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 ""
|
||||
local na = (MTH and MTH.GetLocalizedBeastName) and MTH:GetLocalizedBeastName(a, ba.name) or (ba.name or "")
|
||||
local nb = (MTH and MTH.GetLocalizedBeastName) and MTH:GetLocalizedBeastName(b, bb.name) or (bb.name or "")
|
||||
if na ~= nb then return na < nb end
|
||||
return a < b
|
||||
end
|
||||
|
||||
+243
-52
@@ -2,18 +2,20 @@ if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end
|
||||
|
||||
MTH_HUNTERBOOK_TABS.families = {
|
||||
headerLabel = "Families",
|
||||
columnLabels = { "Family", "Named", "Coords", "Abilities", "Diet" },
|
||||
columnLabels = { "Family", "Named", "Coords", "Abilities", "Diet", "HP", "dmg", "Arm" },
|
||||
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" },
|
||||
{ x = 10, width = 100, align = "LEFT" },
|
||||
{ x = 114, width = 38, align = "LEFT" },
|
||||
{ x = 154, width = 38, align = "LEFT" },
|
||||
{ x = 196, width = 364, align = "LEFT" },
|
||||
{ x = 496, width = 102, align = "LEFT" },
|
||||
{ x = 602, width = 30, align = "CENTER" },
|
||||
{ x = 636, width = 36, align = "CENTER" },
|
||||
{ x = 674, width = 38, align = "CENTER" },
|
||||
},
|
||||
}
|
||||
|
||||
local function MTH_BOOKTAB_FamiliesTrace(message)
|
||||
return
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_FamiliesSafeLower(value)
|
||||
@@ -27,6 +29,33 @@ local function MTH_BOOKTAB_FamiliesTrim(value)
|
||||
return text
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_FormatFamilyStat(value)
|
||||
local numeric = tonumber(value)
|
||||
if not numeric then
|
||||
return "-"
|
||||
end
|
||||
local colorPrefix = "|cFF4DA6FF"
|
||||
if numeric < 1 then
|
||||
colorPrefix = "|cFFFF4040"
|
||||
elseif numeric > 1 then
|
||||
colorPrefix = "|cFF40FF40"
|
||||
end
|
||||
local text = nil
|
||||
if MTH_BOOK_STATE and MTH_BOOK_STATE.familyStatsPercent == true then
|
||||
text = tostring(math.floor((numeric * 100) + 0.5)) .. "%"
|
||||
return colorPrefix .. text .. "|r"
|
||||
end
|
||||
text = string.format("%.3f", numeric)
|
||||
text = string.gsub(text, "(%..-)0+$", "%1")
|
||||
text = string.gsub(text, "%.$", "")
|
||||
return colorPrefix .. text .. "|r"
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_ShouldDisplayAllDiet(familyName)
|
||||
local token = MTH_BOOKTAB_FamiliesSafeLower(MTH_BOOKTAB_FamiliesTrim(familyName))
|
||||
return token == "bears" or token == "boars"
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_FindAbilityBundle(abilityName)
|
||||
if not (MTH_DS_PetSpells and type(MTH_DS_PetSpells.byAbility) == "table") then
|
||||
return nil
|
||||
@@ -47,12 +76,41 @@ end
|
||||
function MTH_BOOKTAB_BuildFamiliesRows()
|
||||
local results = {}
|
||||
local families = MTH_DS_Families
|
||||
local beasts = MTH_DS_Beasts
|
||||
if type(families) ~= "table" then
|
||||
return results
|
||||
end
|
||||
|
||||
local abilityCounts = {}
|
||||
local canonicalMap = {}
|
||||
local familyCounts = {}
|
||||
|
||||
if type(beasts) == "table" then
|
||||
for _, beastRow in pairs(beasts) do
|
||||
if type(beastRow) == "table" then
|
||||
local familyName = MTH_BOOKTAB_FamiliesTrim(beastRow.family)
|
||||
if familyName ~= "" then
|
||||
local counts = familyCounts[familyName]
|
||||
if not counts then
|
||||
counts = { named = 0, coords = 0 }
|
||||
familyCounts[familyName] = counts
|
||||
end
|
||||
counts.named = counts.named + 1
|
||||
|
||||
if type(beastRow.coords) == "table" then
|
||||
for coordIndex = 1, table.getn(beastRow.coords) do
|
||||
local coord = beastRow.coords[coordIndex]
|
||||
if type(coord) == "table" then
|
||||
if coord[1] ~= nil and coord[2] ~= nil then
|
||||
counts.coords = counts.coords + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, familyRow in pairs(families) do
|
||||
if type(familyRow) == "table" and type(familyRow.abilities) == "table" then
|
||||
@@ -78,6 +136,8 @@ function MTH_BOOKTAB_BuildFamiliesRows()
|
||||
|
||||
for familyName, familyRow in pairs(families) do
|
||||
if type(familyRow) == "table" then
|
||||
local beastCounts = familyCounts[familyName] or nil
|
||||
MTH_BOOKTAB_FamiliesTrace("BUILD " .. tostring(familyName) .. " icon=" .. tostring(familyRow.icon))
|
||||
local abilities = {}
|
||||
local seen = {}
|
||||
|
||||
@@ -95,25 +155,49 @@ function MTH_BOOKTAB_BuildFamiliesRows()
|
||||
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 firstAnySpell = nil
|
||||
local firstBeastSpell = nil
|
||||
local rankMin = nil
|
||||
local rankMax = nil
|
||||
local rankMap = {}
|
||||
local beastRankMin = nil
|
||||
local beastRankMax = nil
|
||||
local beastRankMap = {}
|
||||
local anyRankMin = nil
|
||||
local anyRankMax = nil
|
||||
local anyRankMap = {}
|
||||
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
|
||||
if type(spell) == "table" then
|
||||
if not firstAnySpell then
|
||||
firstAnySpell = 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
|
||||
anyRankMap[rank] = true
|
||||
if not anyRankMin or rank < anyRankMin then anyRankMin = rank end
|
||||
if not anyRankMax or rank > anyRankMax then anyRankMax = rank end
|
||||
end
|
||||
if MTH_BOOKTAB_FamiliesSafeLower(spell.learnMethod or "beast") ~= "trainer" then
|
||||
if not firstBeastSpell then
|
||||
firstBeastSpell = spell
|
||||
end
|
||||
if rank and rank > 0 then
|
||||
beastRankMap[rank] = true
|
||||
if not beastRankMin or rank < beastRankMin then beastRankMin = rank end
|
||||
if not beastRankMax or rank > beastRankMax then beastRankMax = rank end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local displaySpell = firstBeastSpell or firstAnySpell
|
||||
local rankMap = beastRankMap
|
||||
local rankMin = beastRankMin
|
||||
local rankMax = beastRankMax
|
||||
if not firstBeastSpell then
|
||||
rankMap = anyRankMap
|
||||
rankMin = anyRankMin
|
||||
rankMax = anyRankMax
|
||||
end
|
||||
|
||||
local rankCount = 0
|
||||
for _ in pairs(rankMap) do
|
||||
rankCount = rankCount + 1
|
||||
@@ -123,8 +207,8 @@ function MTH_BOOKTAB_BuildFamiliesRows()
|
||||
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 "",
|
||||
icon = displaySpell and displaySpell.icon or nil,
|
||||
description = displaySpell and displaySpell.description or "",
|
||||
rankMin = rankMin,
|
||||
rankMax = rankMax,
|
||||
rankCount = rankCount,
|
||||
@@ -141,25 +225,43 @@ function MTH_BOOKTAB_BuildFamiliesRows()
|
||||
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))
|
||||
local dietText = ""
|
||||
local stats = type(familyRow.stats) == "table" and familyRow.stats or nil
|
||||
local healthStat = stats and tonumber(stats.health) or nil
|
||||
local damageStat = stats and tonumber(stats.damage) or nil
|
||||
local armorStat = stats and tonumber(stats.armor) or nil
|
||||
if MTH_BOOKTAB_ShouldDisplayAllDiet(familyName) then
|
||||
dietText = "ALL"
|
||||
else
|
||||
local dietItems = {}
|
||||
if type(familyRow.food) == "table" then
|
||||
for i = 1, table.getn(familyRow.food) do
|
||||
local food = MTH_BOOKTAB_FamiliesTrim(familyRow.food[i])
|
||||
local token = MTH_BOOKTAB_FamiliesSafeLower(food)
|
||||
if food ~= "" and token ~= "raw fish" and token ~= "raw meat" 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.sort(dietItems, function(a, b)
|
||||
return MTH_BOOKTAB_FamiliesSafeLower(a) < MTH_BOOKTAB_FamiliesSafeLower(b)
|
||||
end)
|
||||
dietText = table.concat(dietItems, "|")
|
||||
end
|
||||
|
||||
table.insert(results, {
|
||||
family = tostring(familyName),
|
||||
named = tonumber(familyRow.named) or 0,
|
||||
coords = tonumber(familyRow.coords) or 0,
|
||||
icon = familyRow.icon or nil,
|
||||
named = beastCounts and (tonumber(beastCounts.named) or 0) or 0,
|
||||
coords = beastCounts and (tonumber(beastCounts.coords) or 0) or 0,
|
||||
abilities = abilities,
|
||||
dietText = table.concat(dietItems, ", "),
|
||||
dietText = dietText,
|
||||
healthStat = healthStat,
|
||||
healthText = MTH_BOOKTAB_FormatFamilyStat(healthStat),
|
||||
damageStat = damageStat,
|
||||
damageText = MTH_BOOKTAB_FormatFamilyStat(damageStat),
|
||||
armorStat = armorStat,
|
||||
armorText = MTH_BOOKTAB_FormatFamilyStat(armorStat),
|
||||
})
|
||||
end
|
||||
end
|
||||
@@ -217,31 +319,76 @@ function MTH_BOOKTAB_EnsureFamiliesUI()
|
||||
ui.frame:SetPoint("BOTTOMRIGHT", listParent, "BOTTOMRIGHT", -6, 6)
|
||||
ui.frame:Hide()
|
||||
|
||||
local statsText = getglobal("MTH_BOOK_StatsText")
|
||||
local statsParent = statsText and statsText:GetParent() or nil
|
||||
if statsParent then
|
||||
ui.percentCheckbox = CreateFrame("CheckButton", "MTH_BOOK_FamiliesPercentCheckbox", statsParent, "UICheckButtonTemplate")
|
||||
ui.percentCheckbox:SetFrameStrata(statsParent:GetFrameStrata())
|
||||
ui.percentCheckbox:SetFrameLevel((statsParent:GetFrameLevel() or 0) + 20)
|
||||
ui.percentCheckbox:ClearAllPoints()
|
||||
if statsText then
|
||||
ui.percentCheckbox:SetPoint("LEFT", statsText, "RIGHT", 260, 0)
|
||||
else
|
||||
ui.percentCheckbox:SetPoint("TOPLEFT", statsParent, "TOPLEFT", 538, -526)
|
||||
end
|
||||
ui.percentCheckbox.label = ui.percentCheckbox:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.percentCheckbox.label:SetPoint("LEFT", ui.percentCheckbox, "RIGHT", 4, 0)
|
||||
ui.percentCheckbox.label:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.percentCheckbox.label:SetText("Show modifiers in %")
|
||||
ui.percentCheckbox:SetScript("OnClick", function()
|
||||
if not this then return end
|
||||
MTH_BOOK_STATE.familyStatsPercent = this:GetChecked() == 1
|
||||
MTH_BOOKTAB_RenderFamiliesList()
|
||||
end)
|
||||
ui.percentCheckbox:Hide()
|
||||
end
|
||||
|
||||
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:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 114, -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:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 154, -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:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 196, -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:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 496, -2)
|
||||
ui.headerDiet:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerDiet:SetText("Diet")
|
||||
|
||||
ui.headerHP = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerHP:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 602, -2)
|
||||
ui.headerHP:SetWidth(30)
|
||||
ui.headerHP:SetJustifyH("CENTER")
|
||||
ui.headerHP:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerHP:SetText("HP")
|
||||
|
||||
ui.headerDmg = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerDmg:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 636, -2)
|
||||
ui.headerDmg:SetWidth(36)
|
||||
ui.headerDmg:SetJustifyH("CENTER")
|
||||
ui.headerDmg:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerDmg:SetText("dmg")
|
||||
|
||||
ui.headerArm = ui.frame:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
ui.headerArm:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 674, -2)
|
||||
ui.headerArm:SetWidth(38)
|
||||
ui.headerArm:SetJustifyH("CENTER")
|
||||
ui.headerArm:SetTextColor(1.00, 0.82, 0.00)
|
||||
ui.headerArm:SetText("Arm")
|
||||
|
||||
ui.rows = {}
|
||||
for i = 1, 28 do
|
||||
local row = CreateFrame("Frame", nil, ui.frame)
|
||||
@@ -254,26 +401,47 @@ function MTH_BOOKTAB_EnsureFamiliesUI()
|
||||
row:SetPoint("TOPRIGHT", ui.rows[i - 1], "BOTTOMRIGHT", 0, -1)
|
||||
end
|
||||
|
||||
row.familyIcon = row:CreateTexture(nil, "ARTWORK")
|
||||
row.familyIcon:SetPoint("LEFT", row, "LEFT", 4, 0)
|
||||
row.familyIcon:SetWidth(16)
|
||||
row.familyIcon:SetHeight(16)
|
||||
row.familyIcon:Hide()
|
||||
|
||||
row.family = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.family:SetPoint("LEFT", row, "LEFT", 4, 0)
|
||||
row.family:SetWidth(106)
|
||||
row.family:SetPoint("LEFT", row, "LEFT", 24, 0)
|
||||
row.family:SetWidth(82)
|
||||
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:SetPoint("LEFT", row, "LEFT", 114, 0)
|
||||
row.named:SetWidth(38)
|
||||
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:SetPoint("LEFT", row, "LEFT", 154, 0)
|
||||
row.coords:SetWidth(38)
|
||||
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:SetPoint("LEFT", row, "LEFT", 496, 0)
|
||||
row.diet:SetWidth(102)
|
||||
row.diet:SetJustifyH("LEFT")
|
||||
|
||||
row.hp = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.hp:SetPoint("LEFT", row, "LEFT", 602, 0)
|
||||
row.hp:SetWidth(30)
|
||||
row.hp:SetJustifyH("CENTER")
|
||||
|
||||
row.dmg = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.dmg:SetPoint("LEFT", row, "LEFT", 636, 0)
|
||||
row.dmg:SetWidth(36)
|
||||
row.dmg:SetJustifyH("CENTER")
|
||||
|
||||
row.arm = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
row.arm:SetPoint("LEFT", row, "LEFT", 674, 0)
|
||||
row.arm:SetWidth(38)
|
||||
row.arm:SetJustifyH("CENTER")
|
||||
|
||||
row.abilityButtons = {}
|
||||
for b = 1, 16 do
|
||||
local button = CreateFrame("Button", nil, row)
|
||||
@@ -337,9 +505,14 @@ function MTH_BOOKTAB_SetFamiliesUIVisible(visible)
|
||||
if detailParent then detailParent:Hide() end
|
||||
if detailText then detailText:Hide() end
|
||||
if openMapButton then openMapButton:Hide() end
|
||||
if ui.percentCheckbox then
|
||||
ui.percentCheckbox:SetChecked(MTH_BOOK_STATE and MTH_BOOK_STATE.familyStatsPercent == true and 1 or nil)
|
||||
ui.percentCheckbox:Show()
|
||||
end
|
||||
ui.frame:Show()
|
||||
else
|
||||
ui.frame:Hide()
|
||||
if ui.percentCheckbox then ui.percentCheckbox:Hide() end
|
||||
if listParent then listParent:Show() end
|
||||
if detailParent then detailParent:Show() end
|
||||
if detailText then detailText:Show() end
|
||||
@@ -358,11 +531,23 @@ function MTH_BOOKTAB_RenderFamiliesList()
|
||||
if row then
|
||||
rowFrame:Show()
|
||||
rowFrame.family:SetText(tostring(row.family or "-"))
|
||||
local familyIconPath = row.icon and MTH_BOOK_ResolveIconPath and MTH_BOOK_ResolveIconPath(row.icon) or nil
|
||||
MTH_BOOKTAB_FamiliesTrace("RENDER " .. tostring(row.family) .. " icon=" .. tostring(row.icon) .. " path=" .. tostring(familyIconPath))
|
||||
if familyIconPath then
|
||||
rowFrame.familyIcon:SetTexture(familyIconPath)
|
||||
rowFrame.familyIcon:Show()
|
||||
else
|
||||
rowFrame.familyIcon:Hide()
|
||||
end
|
||||
rowFrame.named:SetText(tostring(row.named or 0))
|
||||
rowFrame.coords:SetText(tostring(row.coords or 0))
|
||||
rowFrame.diet:SetText(tostring(row.dietText or "-"))
|
||||
rowFrame.hp:SetText(MTH_BOOKTAB_FormatFamilyStat(row.healthStat))
|
||||
rowFrame.dmg:SetText(MTH_BOOKTAB_FormatFamilyStat(row.damageStat))
|
||||
rowFrame.arm:SetText(MTH_BOOKTAB_FormatFamilyStat(row.armorStat))
|
||||
|
||||
local offsetX = 232
|
||||
local offsetX = 196
|
||||
local abilityMaxRight = 490
|
||||
for b = 1, table.getn(rowFrame.abilityButtons) do
|
||||
local button = rowFrame.abilityButtons[b]
|
||||
local ability = row.abilities and row.abilities[b] or nil
|
||||
@@ -375,17 +560,23 @@ function MTH_BOOKTAB_RenderFamiliesList()
|
||||
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)
|
||||
local buttonWidth = 14 + textW
|
||||
if (offsetX + buttonWidth) > abilityMaxRight then
|
||||
button.entry = nil
|
||||
button:Hide()
|
||||
else
|
||||
button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
|
||||
button:SetWidth(buttonWidth)
|
||||
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
|
||||
end
|
||||
button:Show()
|
||||
offsetX = offsetX + button:GetWidth() + 6
|
||||
else
|
||||
button.entry = nil
|
||||
button:Hide()
|
||||
|
||||
@@ -69,8 +69,14 @@ 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
|
||||
-- Coord zone IDs are WMA; MTH_DS_Zones is keyed by pfQuest area IDs. Translate
|
||||
-- first, or WMA 121 (Feralas) collides with pfQ 121 (Raven Hill -> parent Duskwood).
|
||||
local pfqId = zid
|
||||
if type(MTH_MAP_WMA_TO_PFQ) == "table" then
|
||||
pfqId = MTH_MAP_WMA_TO_PFQ[zid] or zid
|
||||
end
|
||||
local row = MTH_DS_Zones and (MTH_DS_Zones[pfqId] or MTH_DS_Zones[tostring(pfqId)])
|
||||
if row and row.parent and tonumber(row.parent) and tonumber(row.parent) ~= pfqId then
|
||||
local parentName = MTH_BOOK_GetZoneName(row.parent)
|
||||
if parentName and string.sub(parentName, 1, 5) ~= "Zone " then
|
||||
return parentName, zoneName
|
||||
@@ -105,6 +111,47 @@ function MTH_BOOKTAB_GetNPCZoneSummary(vendor)
|
||||
end
|
||||
MTH_BOOK_GetNPCZoneSummary = MTH_BOOKTAB_GetNPCZoneSummary
|
||||
|
||||
-- Zone column text: every distinct zone the NPC sits in, most-specific (smallest
|
||||
-- map) first, so multi-location NPCs read e.g. "Darnassus, Teldrassil".
|
||||
function MTH_BOOKTAB_GetNPCZoneDisplay(vendor)
|
||||
if not vendor or not vendor.coords or table.getn(vendor.coords) == 0 then
|
||||
return "-"
|
||||
end
|
||||
local seen = {}
|
||||
local list = {}
|
||||
for i = 1, table.getn(vendor.coords) do
|
||||
local c = vendor.coords[i]
|
||||
if c and c[3] then
|
||||
local zoneName = MTH_BOOK_GetZoneName(c[3])
|
||||
if zoneName and zoneName ~= "-" and not seen[zoneName] then
|
||||
seen[zoneName] = true
|
||||
local area = nil
|
||||
local size = MTH_DS_MinimapSizes and (MTH_DS_MinimapSizes[tonumber(c[3])] or MTH_DS_MinimapSizes[c[3]])
|
||||
if type(size) == "table" and tonumber(size[1]) and tonumber(size[2]) then
|
||||
area = tonumber(size[1]) * tonumber(size[2])
|
||||
end
|
||||
table.insert(list, { name = zoneName, area = area, order = i })
|
||||
end
|
||||
end
|
||||
end
|
||||
local count = table.getn(list)
|
||||
if count == 0 then return "-" end
|
||||
-- Smallest map first; zones with no known size keep data order, at the end.
|
||||
table.sort(list, function(x, y)
|
||||
if x.area and y.area then
|
||||
if x.area ~= y.area then return x.area < y.area end
|
||||
return x.order < y.order
|
||||
end
|
||||
if x.area then return true end
|
||||
if y.area then return false end
|
||||
return x.order < y.order
|
||||
end)
|
||||
local names = {}
|
||||
for i = 1, count do table.insert(names, list[i].name) end
|
||||
return table.concat(names, ", ")
|
||||
end
|
||||
MTH_BOOK_GetNPCZoneDisplay = MTH_BOOKTAB_GetNPCZoneDisplay
|
||||
|
||||
function MTH_BOOK_NPCHasZoneId(vendor, zoneId)
|
||||
local zid = tonumber(zoneId)
|
||||
if not zid or not vendor or not vendor.coords then
|
||||
@@ -168,7 +215,8 @@ function MTH_BOOK_NPCMatches(npcId, vendor)
|
||||
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))
|
||||
local displayName = (MTH and MTH.GetLocalizedNPCNameById and MTH:GetLocalizedNPCNameById(npcId, vendor.name)) or vendor.name
|
||||
local hay = MTH_BOOK_SafeLower(tostring(displayName 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
|
||||
@@ -182,8 +230,10 @@ function MTH_BOOK_NPCSort(a, b)
|
||||
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)
|
||||
local vaName = (MTH and MTH.GetLocalizedNPCNameById and MTH:GetLocalizedNPCNameById(a, va.name)) or va.name
|
||||
local vbName = (MTH and MTH.GetLocalizedNPCNameById and MTH:GetLocalizedNPCNameById(b, vb.name)) or vb.name
|
||||
local na = MTH_BOOK_SafeLower(vaName)
|
||||
local nb = MTH_BOOK_SafeLower(vbName)
|
||||
if na ~= nb then return na < nb end
|
||||
return a < b
|
||||
end
|
||||
|
||||
+111
-81
@@ -140,7 +140,7 @@ function MTH_BOOK_GetResistanceShortName(nameText)
|
||||
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, "frost", 1, true) then return "FrR" end
|
||||
if string.find(lowerName, "shadow", 1, true) then return "SR" end
|
||||
return tostring(nameText or "-")
|
||||
end
|
||||
@@ -547,7 +547,75 @@ MTH_BOOK_NormalizeStableFoodLink = function(itemId, itemLink, itemName)
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_BOOK_FindStableFoodBagIcon(itemId, itemName)
|
||||
local getContainerNumSlots = (type(getglobal) == "function" and getglobal("GetContainerNumSlots")) or (_G and _G["GetContainerNumSlots"])
|
||||
local getContainerItemLink = (type(getglobal) == "function" and getglobal("GetContainerItemLink")) or (_G and _G["GetContainerItemLink"])
|
||||
local getContainerItemInfo = (type(getglobal) == "function" and getglobal("GetContainerItemInfo")) or (_G and _G["GetContainerItemInfo"])
|
||||
if type(getContainerNumSlots) ~= "function" or type(getContainerItemLink) ~= "function" or type(getContainerItemInfo) ~= "function" then
|
||||
return nil
|
||||
end
|
||||
local wantedItemId = tonumber(itemId)
|
||||
local wantedName = string.lower(tostring(itemName or ""))
|
||||
for bag = 0, 4 do
|
||||
local slotCount = tonumber(getContainerNumSlots(bag)) or 0
|
||||
for slot = 1, slotCount do
|
||||
local link = getContainerItemLink(bag, slot)
|
||||
if link then
|
||||
local matched = false
|
||||
if wantedItemId then
|
||||
local _, _, parsedId = string.find(tostring(link), "item:(%d+)")
|
||||
if tonumber(parsedId) == wantedItemId then
|
||||
matched = true
|
||||
end
|
||||
end
|
||||
if not matched and wantedName ~= "" then
|
||||
local _, _, parsedName = string.find(tostring(link), "|h%[([^%]]+)%]|h")
|
||||
if parsedName and string.lower(tostring(parsedName)) == wantedName then
|
||||
matched = true
|
||||
end
|
||||
end
|
||||
if matched then
|
||||
local bagTexture = getContainerItemInfo(bag, slot)
|
||||
if bagTexture and bagTexture ~= "" then
|
||||
if type(bagTexture) == "string" and string.find(bagTexture, "\\", 1, true) then
|
||||
return bagTexture
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_BOOK_ResolveStableFoodIcon(itemId, iconHint, itemName, itemLink)
|
||||
local function resolveTextureCandidate(value)
|
||||
if value == nil or value == "" then
|
||||
return nil
|
||||
end
|
||||
if type(value) == "number" then
|
||||
return nil
|
||||
end
|
||||
local text = tostring(value)
|
||||
if text == "" then
|
||||
return nil
|
||||
end
|
||||
if string.find(text, "|Hitem:", 1, true) or string.find(text, "^item:%d+") then
|
||||
return nil
|
||||
end
|
||||
if string.find(text, "^table:", 1, true) then
|
||||
return nil
|
||||
end
|
||||
if string.find(text, "\\", 1, true) then
|
||||
return text
|
||||
end
|
||||
local asNumber = tonumber(text)
|
||||
if asNumber then
|
||||
return nil
|
||||
end
|
||||
return "Interface\\Icons\\" .. text
|
||||
end
|
||||
|
||||
local linkText = tostring(itemLink or "")
|
||||
local linkItemId = nil
|
||||
if linkText ~= "" then
|
||||
@@ -559,104 +627,55 @@ local function MTH_BOOK_ResolveStableFoodIcon(itemId, iconHint, itemName, itemLi
|
||||
|
||||
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)
|
||||
local resolvedInfoTexture = resolveTextureCandidate(infoTexture)
|
||||
if resolvedInfoTexture then
|
||||
return resolvedInfoTexture
|
||||
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)
|
||||
local resolvedDirectIcon = resolveTextureCandidate(directIcon)
|
||||
if resolvedDirectIcon then
|
||||
return resolvedDirectIcon
|
||||
end
|
||||
end
|
||||
|
||||
local resolvedHintIcon = resolveTextureCandidate(iconHint)
|
||||
if resolvedHintIcon then
|
||||
return resolvedHintIcon
|
||||
end
|
||||
|
||||
if linkText ~= "" and type(GetItemInfo) == "function" then
|
||||
local _, _, _, _, _, _, _, _, _, linkTexture = GetItemInfo(linkText)
|
||||
local resolvedLinkTexture = resolveTextureCandidate(linkTexture)
|
||||
if resolvedLinkTexture then
|
||||
return resolvedLinkTexture
|
||||
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)
|
||||
local resolvedNamedIcon = resolveTextureCandidate(namedIcon)
|
||||
if resolvedNamedIcon then
|
||||
return resolvedNamedIcon
|
||||
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
|
||||
local resolvedDsIcon = resolveTextureCandidate(MTH_DS_Items[numericItemId].icon)
|
||||
if resolvedDsIcon then
|
||||
return resolvedDsIcon
|
||||
end
|
||||
end
|
||||
|
||||
local bagIcon = MTH_BOOK_FindStableFoodBagIcon(numericItemId, itemName)
|
||||
if bagIcon then
|
||||
return bagIcon
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -1226,6 +1245,17 @@ function MTH_BOOKTAB_RenderStableCards()
|
||||
local row = slotEntry and slotEntry.row or nil
|
||||
MTH_BOOK_DebugTrace("Render card#" .. tostring(i) .. " petId=" .. tostring(petId) .. " hasRow=" .. tostring(type(row) == "table"))
|
||||
|
||||
-- Animations: flash the slot when its occupant changes (deposit /
|
||||
-- withdraw / swap), but never on the first render of this card.
|
||||
if card._stableInit and card._lastStablePetId ~= petId then
|
||||
if type(MTH_FX_Flash) == "function" and type(MTH_FX_IsEnabled) == "function"
|
||||
and MTH_FX_IsEnabled("ui.stable") then
|
||||
MTH_FX_Flash(card, 1.00, 0.82, 0.20, 0.55)
|
||||
end
|
||||
end
|
||||
card._lastStablePetId = petId
|
||||
card._stableInit = true
|
||||
|
||||
card:ClearAllPoints()
|
||||
card:SetPoint("TOPLEFT", ui.frame, "TOPLEFT", 8, y)
|
||||
card:SetPoint("TOPRIGHT", ui.frame, "TOPRIGHT", -8, y)
|
||||
@@ -1566,7 +1596,7 @@ function MTH_BOOKTAB_RenderStableCards()
|
||||
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
|
||||
local hasRecordedTame = type(MTH_PETS_HasVerifiedTameRecord) == "function" and MTH_PETS_HasVerifiedTameRecord(row) or (row.tameRecorded == true)
|
||||
if not hasRecordedTame and legacyTameContext then
|
||||
if legacyTameContext.name or legacyTameContext.zone or legacyTameContext.timestamp then
|
||||
hasRecordedTame = true
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
if type(MTH_HUNTERBOOK_TABS) ~= "table" then MTH_HUNTERBOOK_TABS = {} end
|
||||
|
||||
local trainerColumns = {
|
||||
{ x = 28, width = 150, align = "LEFT" },
|
||||
{ x = 180, width = 62, align = "LEFT" },
|
||||
{ x = 244, width = 43, align = "LEFT" },
|
||||
{ x = 289, width = 80, align = "LEFT" },
|
||||
{ x = 371, width = 105, align = "LEFT" },
|
||||
{ x = 478, width = 74, align = "LEFT" },
|
||||
}
|
||||
|
||||
MTH_HUNTERBOOK_TABS.hunterspells = {
|
||||
headerLabel = "Hunter Spells",
|
||||
columnLabels = { "Spell", "Rank", "Level", "Price", "Branch", "Status" },
|
||||
columnLayout = trainerColumns,
|
||||
}
|
||||
|
||||
MTH_HUNTERBOOK_TABS.petspells = {
|
||||
headerLabel = "Pet Spells",
|
||||
columnLabels = { "Spell", "Rank", "Level", "Price", "Branch", "Status" },
|
||||
columnLayout = trainerColumns,
|
||||
}
|
||||
|
||||
function MTH_BOOKTAB_IsTrainerSpellMode(mode)
|
||||
return mode == "hunterspells" or mode == "petspells"
|
||||
end
|
||||
|
||||
local function MTH_BOOKTAB_GetTrainerKind()
|
||||
if MTH_BOOK_STATE and MTH_BOOK_STATE.mode == "petspells" then return "pet" end
|
||||
return "hunter"
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_FormatTrainerCost(copper)
|
||||
local value = tonumber(copper) or 0
|
||||
local gold = math.floor(value / 10000)
|
||||
value = value - gold * 10000
|
||||
local silver = math.floor(value / 100)
|
||||
local bronze = value - silver * 100
|
||||
local parts = {}
|
||||
if gold > 0 then table.insert(parts, "|cffffd100" .. tostring(gold) .. "g|r") end
|
||||
if silver > 0 or gold > 0 then table.insert(parts, "|cffc7c7cf" .. tostring(silver) .. "s|r") end
|
||||
table.insert(parts, "|cffeda55f" .. tostring(bronze) .. "c|r")
|
||||
return table.concat(parts, " ")
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_GetTrainerDisplayCost(copper)
|
||||
if type(MTH_TrainerPlan_GetPrice) == "function" then
|
||||
return MTH_TrainerPlan_GetPrice({ trainerCost = copper }, MTH_BOOK_STATE and MTH_BOOK_STATE.trainerHonoredDiscount)
|
||||
end
|
||||
return tonumber(copper) or 0
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_GetTrainerSpellStatus(row)
|
||||
local kind = MTH_BOOKTAB_GetTrainerKind()
|
||||
local playerLevel = MTH_BOOK_GetPlayerLevelValue and MTH_BOOK_GetPlayerLevelValue() or nil
|
||||
local status = type(MTH_TrainerPlan_GetStatus) == "function" and MTH_TrainerPlan_GetStatus(kind, row, playerLevel) or "notyet"
|
||||
if status == "known" then return "Known", "|cff40ff40" end
|
||||
if status == "notlearned" then return "Not learned", "|cffffa500" end
|
||||
return "Not yet learnable", "|cffff4040"
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_BuildTrainerSpellResults()
|
||||
local results = {}
|
||||
local kind = MTH_BOOKTAB_GetTrainerKind()
|
||||
local plan = type(MTH_TrainerPlan_Build) == "function" and MTH_TrainerPlan_Build(kind, {
|
||||
playerLevel = MTH_BOOK_GetPlayerLevelValue and MTH_BOOK_GetPlayerLevelValue() or nil,
|
||||
honored = MTH_BOOK_STATE and MTH_BOOK_STATE.trainerHonoredDiscount,
|
||||
}) or nil
|
||||
if type(plan) ~= "table" or type(plan.rows) ~= "table" then return results end
|
||||
local search = MTH_BOOK_SafeLower(MTH_BOOK_STATE.search or "")
|
||||
for _, planned in ipairs(plan.rows) do
|
||||
local row = planned.row
|
||||
local level = tonumber(row.requiredLevel) or 0
|
||||
local haystack = tostring(row.name or "") .. " " .. tostring(row.rank or "") .. " " .. tostring(row.section or "") .. " " .. tostring(row.description or "")
|
||||
if type(row.prerequisites) == "table" then
|
||||
for _, prerequisite in ipairs(row.prerequisites) do
|
||||
haystack = haystack .. " " .. tostring(prerequisite)
|
||||
end
|
||||
end
|
||||
haystack = MTH_BOOK_SafeLower(haystack)
|
||||
if (search == "" or string.find(haystack, search, 1, true) ~= nil)
|
||||
and (not MTH_BOOK_STATE.minLevel or level >= MTH_BOOK_STATE.minLevel)
|
||||
and (not MTH_BOOK_STATE.maxLevel or level <= MTH_BOOK_STATE.maxLevel)
|
||||
and (type(MTH_TrainerPlan_MatchesQuick) ~= "function" or MTH_TrainerPlan_MatchesQuick(planned.status, MTH_BOOK_STATE.quick))
|
||||
then
|
||||
table.insert(results, row)
|
||||
end
|
||||
end
|
||||
return results
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_UpdateTrainerPlanPanel(detail)
|
||||
local parent = detail and detail:GetParent()
|
||||
if not parent then return end
|
||||
if not parent.mthTrainerPlan then
|
||||
local panel = CreateFrame("Frame", nil, parent)
|
||||
panel:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, -238)
|
||||
panel:SetWidth(136)
|
||||
panel:SetHeight(128)
|
||||
panel:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 12, insets = { left = 3, right = 3, top = 3, bottom = 3 } })
|
||||
panel:SetBackdropColor(0.06, 0.06, 0.06, 1)
|
||||
panel:SetBackdropBorderColor(0.28, 0.28, 0.28, 1)
|
||||
panel.text = panel:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
panel.text:SetPoint("TOPLEFT", panel, "TOPLEFT", 7, -7)
|
||||
panel.text:SetWidth(122)
|
||||
panel.text:SetJustifyH("LEFT")
|
||||
panel.text:SetJustifyV("TOP")
|
||||
parent.mthTrainerPlan = panel
|
||||
end
|
||||
local plan = type(MTH_TrainerPlan_Build) == "function" and MTH_TrainerPlan_Build(MTH_BOOKTAB_GetTrainerKind(), {
|
||||
playerLevel = MTH_BOOK_GetPlayerLevelValue and MTH_BOOK_GetPlayerLevelValue() or nil,
|
||||
honored = MTH_BOOK_STATE.trainerHonoredDiscount,
|
||||
}) or nil
|
||||
if not plan then return end
|
||||
local nextLevel = plan.nextLevel.level
|
||||
local nextLabel = "Next"
|
||||
if nextLevel then nextLabel = "At level " .. tostring(nextLevel) end
|
||||
local function formatBlock(label, summary)
|
||||
return "|cffffffff" .. label .. "|r\n"
|
||||
.. "|cff4da6ff" .. tostring(summary.count) .. "|r |cff9a9a9aspells|r |cffffffff| |r"
|
||||
.. MTH_BOOKTAB_FormatTrainerCost(summary.cost)
|
||||
end
|
||||
parent.mthTrainerPlan.text:SetText("|cffffd100Training Plan|r\n"
|
||||
.. formatBlock("Now", plan.now)
|
||||
.. "\n\n" .. formatBlock(nextLabel, plan.nextLevel)
|
||||
.. "\n\n" .. formatBlock("Remaining", plan.remaining))
|
||||
parent.mthTrainerPlan:Show()
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_GetTrainerSpellTotal()
|
||||
local catalog = MTH_DS_TrainerCatalog
|
||||
local rows = catalog and catalog[MTH_BOOKTAB_GetTrainerKind()] or nil
|
||||
return type(rows) == "table" and table.getn(rows) or 0
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_TrainerSpellSort(a, b)
|
||||
local al = tonumber(a and a.requiredLevel) or 0
|
||||
local bl = tonumber(b and b.requiredLevel) or 0
|
||||
if al ~= bl then return al < bl end
|
||||
local an = MTH_BOOK_SafeLower(a and a.name)
|
||||
local bn = MTH_BOOK_SafeLower(b and b.name)
|
||||
if an ~= bn then return an < bn end
|
||||
return MTH_BOOK_SafeLower(a and a.rank) < MTH_BOOK_SafeLower(b and b.rank)
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_GetTrainerSpellSortKey(row, col)
|
||||
if col == 1 then return MTH_BOOK_SafeLower(row and row.name) end
|
||||
if col == 2 then return MTH_BOOK_SafeLower(row and row.rank) end
|
||||
if col == 3 then return tonumber(row and row.requiredLevel) or 0 end
|
||||
if col == 4 then return tonumber(row and row.trainerCost) or 0 end
|
||||
if col == 5 then return MTH_BOOK_SafeLower(row and row.section) end
|
||||
if col == 6 then
|
||||
local status = MTH_BOOKTAB_GetTrainerSpellStatus(row)
|
||||
return MTH_BOOK_SafeLower(status)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_GetTrainerSpellRowValues(row)
|
||||
local requiredLevel = tonumber(row and row.requiredLevel) or 0
|
||||
local playerLevel = MTH_BOOK_GetPlayerLevelValue and MTH_BOOK_GetPlayerLevelValue() or nil
|
||||
local levelColor = (playerLevel and playerLevel >= requiredLevel) and "|cff40ff40" or "|cffff4040"
|
||||
local status, statusColor = MTH_BOOKTAB_GetTrainerSpellStatus(row)
|
||||
return {
|
||||
tostring(row and row.name or "-"),
|
||||
tostring(row and row.rank or "-"),
|
||||
levelColor .. tostring(row and row.requiredLevel or "-") .. "|r",
|
||||
MTH_BOOKTAB_FormatTrainerCost(MTH_BOOKTAB_GetTrainerDisplayCost(row and row.trainerCost)),
|
||||
tostring(row and row.section or "-"),
|
||||
statusColor .. status .. "|r",
|
||||
}
|
||||
end
|
||||
|
||||
function MTH_BOOKTAB_UpdateTrainerSpellDetail(detail, selected)
|
||||
if not MTH_BOOKTAB_IsTrainerSpellMode(MTH_BOOK_STATE and MTH_BOOK_STATE.mode) then return false end
|
||||
MTH_BOOKTAB_UpdateTrainerPlanPanel(detail)
|
||||
if not selected then
|
||||
MTH_BOOK_SetDetailText(detail, "|cFFFFD100Trainer Spells|r\n\nSelect a spell rank to inspect its trainer level, undiscounted cost, and prerequisites.")
|
||||
return true
|
||||
end
|
||||
local lines = {}
|
||||
table.insert(lines, "|cffffff00" .. tostring(selected.name or "Unknown") .. "|r")
|
||||
if selected.rank and selected.rank ~= "" then table.insert(lines, "|cffaaaaaa" .. tostring(selected.rank) .. "|r") end
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, "|cff9a9a9aBranch:|r |cffffffff" .. tostring(selected.section or "-") .. "|r")
|
||||
local requiredLevel = tonumber(selected.requiredLevel) or 0
|
||||
local playerLevel = MTH_BOOK_GetPlayerLevelValue and MTH_BOOK_GetPlayerLevelValue() or nil
|
||||
local levelColor = (playerLevel and playerLevel >= requiredLevel) and "|cff40ff40" or "|cffff4040"
|
||||
table.insert(lines, "|cff9a9a9aRequired Level:|r " .. levelColor .. tostring(selected.requiredLevel or "-") .. "|r")
|
||||
local status, statusColor = MTH_BOOKTAB_GetTrainerSpellStatus(selected)
|
||||
table.insert(lines, "|cff9a9a9aStatus:|r " .. statusColor .. status .. "|r")
|
||||
table.insert(lines, "|cff9a9a9aBase Price:|r " .. MTH_BOOKTAB_FormatTrainerCost(selected.trainerCost))
|
||||
table.insert(lines, "|cff9a9a9aHonored Price:|r " .. MTH_BOOKTAB_FormatTrainerCost(math.floor((tonumber(selected.trainerCost) or 0) * 0.90)))
|
||||
if type(selected.prerequisites) == "table" and table.getn(selected.prerequisites) > 0 then
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, "|cffffd100Prerequisites|r")
|
||||
for _, prerequisite in ipairs(selected.prerequisites) do
|
||||
table.insert(lines, " " .. tostring(prerequisite))
|
||||
end
|
||||
end
|
||||
if selected.description and selected.description ~= "" then
|
||||
table.insert(lines, "")
|
||||
table.insert(lines, "|cffffd100Description|r")
|
||||
table.insert(lines, "|cffffffff " .. tostring(selected.description) .. "|r")
|
||||
end
|
||||
local detailFrame = detail.mthTrainerDetailParent or detail:GetParent()
|
||||
if not detail.mthTrainerIcon then
|
||||
detail.mthTrainerIcon = detail:CreateTexture(nil, "ARTWORK")
|
||||
detail.mthTrainerIcon:SetWidth(28)
|
||||
detail.mthTrainerIcon:SetHeight(28)
|
||||
end
|
||||
detail.mthTrainerIcon:SetParent(detail)
|
||||
detail.mthTrainerIcon:ClearAllPoints()
|
||||
detail.mthTrainerIcon:SetPoint("TOPLEFT", detail, "TOPLEFT", 0, 0)
|
||||
detail.mthTrainerIcon:SetTexture(MTH_BOOK_ResolveIconPath(selected.icon) or "Interface\\Icons\\INV_Misc_QuestionMark")
|
||||
detail.mthTrainerIcon:Show()
|
||||
if detail.SetTextInsets then detail:SetTextInsets(4, 4, 36, 4) end
|
||||
if not detail.mthTrainerDetailScroll then
|
||||
detail:ClearAllPoints()
|
||||
detail:SetPoint("TOPLEFT", detailFrame, "TOPLEFT", 8, -42)
|
||||
detail:SetWidth(detailFrame:GetWidth() - 16)
|
||||
end
|
||||
MTH_BOOK_SetDetailText(detail, table.concat(lines, "\n"))
|
||||
return true
|
||||
end
|
||||
@@ -15,10 +15,42 @@ MTH_HUNTERBOOK_TABS.items = {
|
||||
},
|
||||
}
|
||||
|
||||
MTH_HUNTERBOOK_TABS.projectiles = {
|
||||
headerLabel = "Projectiles",
|
||||
columnLabels = { "ID", "Req", "Lvl", "Type", "Name", "DPS", "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 = 228, align = "LEFT" },
|
||||
{ x = 396, width = 44, align = "LEFT" },
|
||||
{ x = 442, width = 110, align = "LEFT" },
|
||||
},
|
||||
}
|
||||
|
||||
MTH_HUNTERBOOK_TABS.ammobags = {
|
||||
headerLabel = "Ammo Bags",
|
||||
columnLabels = { "ID", "Req", "Lvl", "Slots", "Type", "Name", "Source" },
|
||||
columnLayout = {
|
||||
{ x = 8, width = 40, align = "LEFT" },
|
||||
{ x = 50, width = 36, align = "LEFT" },
|
||||
{ x = 88, width = 34, align = "LEFT" },
|
||||
{ x = 124, width = 42, align = "LEFT" },
|
||||
{ x = 168, width = 80, align = "LEFT" },
|
||||
{ x = 250, width = 192, align = "LEFT" },
|
||||
{ x = 444, width = 108, align = "LEFT" },
|
||||
},
|
||||
}
|
||||
|
||||
function MTH_BOOK_NormalizeItemSubtype(value)
|
||||
local subtype = MTH_BOOK_SafeLower(value)
|
||||
subtype = string.gsub(subtype, "^%s+", "")
|
||||
subtype = string.gsub(subtype, "%s+$", "")
|
||||
if subtype == "arrows" then return "arrow" end
|
||||
if subtype == "bullets" then return "bullet" end
|
||||
if subtype == "ammo pouch" or subtype == "ammo pouches" then return "ammopouch" end
|
||||
if subtype == "quivers" then return "quiver" 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
|
||||
@@ -29,11 +61,36 @@ function MTH_BOOK_IsRangedWeaponSubtype(subtype)
|
||||
return subtype == "bow" or subtype == "gun" or subtype == "crossbow"
|
||||
end
|
||||
|
||||
local function MTH_BOOK_IsProjectileSubtype(subtype)
|
||||
return subtype == "arrow" or subtype == "bullet"
|
||||
end
|
||||
|
||||
local function MTH_BOOK_IsAmmoBagSubtype(subtype)
|
||||
return subtype == "quiver" or subtype == "ammopouch"
|
||||
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 rawSubtype = MTH_BOOK_SafeLower(item.subtype)
|
||||
rawSubtype = string.gsub(rawSubtype, "^%s+", "")
|
||||
rawSubtype = string.gsub(rawSubtype, "%s+$", "")
|
||||
local mode = tostring(MTH_BOOK_STATE and MTH_BOOK_STATE.mode or "items")
|
||||
if mode == "projectiles" then
|
||||
if not MTH_BOOK_IsProjectileSubtype(itemSubtype) then return false end
|
||||
elseif mode == "ammobags" then
|
||||
local hasSlots = tonumber(item.slots) and tonumber(item.slots) > 0
|
||||
local looksLikeAmmoBag = MTH_BOOK_IsAmmoBagSubtype(itemSubtype)
|
||||
or rawSubtype == "ammo pouch"
|
||||
or rawSubtype == "ammo pouches"
|
||||
or rawSubtype == "quiver"
|
||||
or rawSubtype == "quivers"
|
||||
or hasSlots
|
||||
if not looksLikeAmmoBag then return false end
|
||||
else
|
||||
if not MTH_BOOK_IsRangedWeaponSubtype(itemSubtype) then return false end
|
||||
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
|
||||
@@ -48,11 +105,12 @@ function MTH_BOOK_ItemMatches(itemId, item)
|
||||
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 localizedName = (MTH and MTH.GetLocalizedItemName)
|
||||
and MTH:GetLocalizedItemName(itemId, item.name)
|
||||
or item.name
|
||||
local name = MTH_BOOK_SafeLower(localizedName)
|
||||
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
|
||||
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
|
||||
@@ -66,8 +124,8 @@ function MTH_BOOK_ItemSort(a, b)
|
||||
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 ""
|
||||
local na = (MTH and MTH.GetLocalizedItemName) and MTH:GetLocalizedItemName(a, ia.name) or (ia.name or "")
|
||||
local nb = (MTH and MTH.GetLocalizedItemName) and MTH:GetLocalizedItemName(b, ib.name) or (ib.name or "")
|
||||
if na ~= nb then return na < nb end
|
||||
return a < b
|
||||
end
|
||||
|
||||
+1436
-271
File diff suppressed because it is too large
Load Diff
+64
-15
@@ -2,7 +2,7 @@
|
||||
..\FrameXML\UI.xsd">
|
||||
<Frame name="MTH_BOOK_Template" toplevel="true" frameStrata="DIALOG" movable="true" enableMouse="true" hidden="true" parent="UIParent" virtual="true">
|
||||
<Size>
|
||||
<AbsDimension x="760" y="560"/>
|
||||
<AbsDimension x="880" y="560"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER"/>
|
||||
@@ -57,6 +57,14 @@
|
||||
</Anchors>
|
||||
</FontString>
|
||||
|
||||
<FontString name="$parentBeastZoneLabel" inherits="GameFontNormalSmall" text="Zone:" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset><AbsDimension x="464" y="-142"/></Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
|
||||
</Layer>
|
||||
</Layers>
|
||||
|
||||
@@ -74,7 +82,7 @@
|
||||
</Frame>
|
||||
|
||||
<Frame name="$parentDetailBackdrop">
|
||||
<Size><AbsDimension x="136" y="324"/></Size>
|
||||
<Size><AbsDimension x="136" y="155"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="608" y="-166"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
@@ -85,7 +93,7 @@
|
||||
</Backdrop>
|
||||
<Frames>
|
||||
<EditBox name="$parentDetailText" autoFocus="false" multiLine="true" text="Select an entry to view details.">
|
||||
<Size><AbsDimension x="120" y="306"/></Size>
|
||||
<Size><AbsDimension x="120" y="139"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="8" y="-8"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
@@ -96,7 +104,7 @@
|
||||
<Button name="$parentOpenMapButton" inherits="UIPanelButtonTemplate" text="Open Map" hidden="true">
|
||||
<Size><AbsDimension x="136" y="22"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="608" y="-496"/></Offset></Anchor>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="608" y="-524"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
|
||||
@@ -120,54 +128,78 @@
|
||||
</Frame>
|
||||
|
||||
<Frame name="$parentTabBar">
|
||||
<Size><AbsDimension x="620" y="24"/></Size>
|
||||
<Size><AbsDimension x="96" y="472"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="18" y="-44"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Frame>
|
||||
|
||||
<Button name="$parentSectionStable" inherits="TabButtonTemplate" text="Stable">
|
||||
<Size><AbsDimension x="86" y="24"/></Size>
|
||||
<Size><AbsDimension x="64" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentTabBar"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionPetAbilities" inherits="TabButtonTemplate" text="Pet Abilities">
|
||||
<Size><AbsDimension x="108" y="24"/></Size>
|
||||
<Size><AbsDimension x="84" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionStable" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionPets" inherits="TabButtonTemplate" text="Beast Lore">
|
||||
<Size><AbsDimension x="98" y="24"/></Size>
|
||||
<Size><AbsDimension x="74" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionPetAbilities" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionFamilies" inherits="TabButtonTemplate" text="Families">
|
||||
<Size><AbsDimension x="92" y="24"/></Size>
|
||||
<Size><AbsDimension x="68" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionPets" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionNPCs" inherits="TabButtonTemplate" text="NPC Finder">
|
||||
<Size><AbsDimension x="110" y="24"/></Size>
|
||||
<Size><AbsDimension x="76" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionFamilies" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionItems" inherits="TabButtonTemplate" text="Weapons">
|
||||
<Size><AbsDimension x="104" y="24"/></Size>
|
||||
<Size><AbsDimension x="70" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionNPCs" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionPetHistory" inherits="TabButtonTemplate" text="Pet History">
|
||||
<Size><AbsDimension x="104" y="24"/></Size>
|
||||
<Button name="$parentSectionProjectiles" inherits="TabButtonTemplate" text="Projectiles">
|
||||
<Size><AbsDimension x="80" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionItems" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionAmmoBags" inherits="TabButtonTemplate" text="Ammo Bags">
|
||||
<Size><AbsDimension x="78" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionProjectiles" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionPetHistory" inherits="TabButtonTemplate" text="Pet History">
|
||||
<Size><AbsDimension x="82" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionAmmoBags" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionHunterSpells" inherits="TabButtonTemplate" text="Hunter Spells">
|
||||
<Size><AbsDimension x="82" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionPetHistory" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
<Button name="$parentSectionPetSpells" inherits="TabButtonTemplate" text="Pet Spells">
|
||||
<Size><AbsDimension x="82" y="24"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="LEFT" relativeTo="$parentSectionHunterSpells" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Button>
|
||||
|
||||
<EditBox name="$parentSearch" inherits="InputBoxTemplate">
|
||||
<Size><AbsDimension x="180" y="20"/></Size>
|
||||
@@ -215,6 +247,12 @@
|
||||
</Anchors>
|
||||
</CheckButton>
|
||||
|
||||
<CheckButton name="$parentTrainerHonoredDiscount" inherits="UICheckButtonTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="618" y="-80"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</CheckButton>
|
||||
|
||||
<Button name="$parentPetBookScanButton" inherits="UIPanelButtonTemplate" text="Scan from Pet Book" hidden="true">
|
||||
<Size><AbsDimension x="130" y="20"/></Size>
|
||||
<Anchors>
|
||||
@@ -253,7 +291,7 @@
|
||||
</Frame>
|
||||
<CheckButton name="$parentPetInZoneOnly" inherits="UICheckButtonTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="488" y="-112"/></Offset></Anchor>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="526" y="-106"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</CheckButton>
|
||||
<Button name="$parentShowAllOnMapButton" inherits="UIPanelButtonTemplate" text="Show all on map" hidden="true">
|
||||
@@ -286,8 +324,19 @@
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="232" y="-140"/></Offset></Anchor></Anchors>
|
||||
</CheckButton>
|
||||
<CheckButton name="$parentHideUnknown" inherits="UICheckButtonTemplate">
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="372" y="-140"/></Offset></Anchor></Anchors>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="290" y="-140"/></Offset></Anchor></Anchors>
|
||||
</CheckButton>
|
||||
<Frame name="$parentBeastContinentDropdown" inherits="UIDropDownMenuTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="482" y="-136"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Frame>
|
||||
|
||||
<Frame name="$parentBeastZoneDropdown" inherits="UIDropDownMenuTemplate" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="592" y="-136"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</Frame>
|
||||
|
||||
<Frame name="$parentListSliderBackdrop">
|
||||
<Size><AbsDimension x="22" y="322"/></Size>
|
||||
|
||||
+291
-130
@@ -2,103 +2,175 @@
|
||||
-- MetaHunt Localization System
|
||||
------------------------------------------------------
|
||||
|
||||
MTH.localization = {}
|
||||
MTH.localization = MTH.localization or {}
|
||||
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"
|
||||
local MTH_LOCALE_ALIASES = {
|
||||
enGB = "enUS",
|
||||
esMX = "esES",
|
||||
zhTW = "zhCN",
|
||||
}
|
||||
|
||||
-- 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."
|
||||
local MTH_SUPPORTED_LOCALES = {
|
||||
enUS = true,
|
||||
deDE = true,
|
||||
esES = true,
|
||||
ptBR = true,
|
||||
ruRU = true,
|
||||
zhCN = true,
|
||||
}
|
||||
|
||||
local function MTH_ResolveLocale(locale)
|
||||
local resolved = tostring(locale or "enUS")
|
||||
if MTH_LOCALE_ALIASES[resolved] then
|
||||
resolved = MTH_LOCALE_ALIASES[resolved]
|
||||
end
|
||||
if not MTH_SUPPORTED_LOCALES[resolved] then
|
||||
resolved = "enUS"
|
||||
end
|
||||
return resolved
|
||||
end
|
||||
|
||||
MTH.localization[MTH.currentLocale] = L
|
||||
local activeLocale = MTH_ResolveLocale(MTH.currentLocale)
|
||||
local localeData = MTH_LocaleData or {}
|
||||
local uiLocales = localeData.ui or {}
|
||||
local uiDefault = uiLocales.enUS or {}
|
||||
local uiActive = uiLocales[activeLocale] or {}
|
||||
|
||||
-- Get localized string
|
||||
function MTH:GetLocalization(key, default)
|
||||
if L[key] then
|
||||
return L[key]
|
||||
local function MTH_L(key, default)
|
||||
if uiActive[key] ~= nil then
|
||||
return uiActive[key]
|
||||
end
|
||||
if uiDefault[key] ~= nil then
|
||||
return uiDefault[key]
|
||||
end
|
||||
return default or key
|
||||
end
|
||||
|
||||
MTH.localization[activeLocale] = uiActive
|
||||
MTH.localization.enUS = uiDefault
|
||||
|
||||
BINDING_HEADER_FEEDOMATIC = MTH_L("BINDING_HEADER_FEEDOMATIC", "[MetaHunt] Feed-O-Matic")
|
||||
BINDING_HEADER_ZAspectHeader = MTH_L("BINDING_HEADER_ZAspectHeader", "[MetaHunt] ZAspect Buttons")
|
||||
BINDING_HEADER_ZTrackHeader = MTH_L("BINDING_HEADER_ZTrackHeader", "[MetaHunt] ZTrack Buttons")
|
||||
BINDING_HEADER_ZTrapHeader = MTH_L("BINDING_HEADER_ZTrapHeader", "[MetaHunt] ZTrap Buttons")
|
||||
BINDING_HEADER_ZPetHeader = MTH_L("BINDING_HEADER_ZPetHeader", "[MetaHunt] ZPet Buttons")
|
||||
BINDING_HEADER_ZAmmoHeader = MTH_L("BINDING_HEADER_ZAmmoHeader", "[MetaHunt] ZAmmo Buttons")
|
||||
BINDING_HEADER_ExpAmmoHeader = MTH_L("BINDING_HEADER_ExpAmmoHeader", "[MetaHunt] MM Widget")
|
||||
BINDING_NAME_EXPAMMOACTION = MTH_L("BINDING_NAME_EXPAMMOACTION", "MM Widget Action")
|
||||
|
||||
function MTH:GetLocalization(key, default)
|
||||
return MTH_L(key, default)
|
||||
end
|
||||
|
||||
local function MTH_GetLocaleVendorNameMap(locale)
|
||||
local dataLocales = (MTH_LocaleData and MTH_LocaleData.data) or nil
|
||||
local vendorLocales = dataLocales and dataLocales.vendors or nil
|
||||
if type(vendorLocales) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
return vendorLocales[locale]
|
||||
end
|
||||
|
||||
local function MTH_GetLocaleItemNameMap(locale)
|
||||
local dataLocales = (MTH_LocaleData and MTH_LocaleData.data) or nil
|
||||
local itemLocales = dataLocales and dataLocales.items or nil
|
||||
if type(itemLocales) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
return itemLocales[locale]
|
||||
end
|
||||
|
||||
local function MTH_GetLocaleBeastNameMap(locale)
|
||||
local dataLocales = (MTH_LocaleData and MTH_LocaleData.data) or nil
|
||||
if type(dataLocales) ~= "table" then
|
||||
MTH_LocaleData = MTH_LocaleData or {}
|
||||
MTH_LocaleData.data = MTH_LocaleData.data or {}
|
||||
dataLocales = MTH_LocaleData.data
|
||||
end
|
||||
local beastLocales = dataLocales and dataLocales.beasts or nil
|
||||
if type(beastLocales) ~= "table" then
|
||||
dataLocales.beasts = {}
|
||||
beastLocales = dataLocales.beasts
|
||||
end
|
||||
|
||||
if locale == "enUS" then
|
||||
local enMap = beastLocales.enUS
|
||||
if type(enMap) ~= "table" then
|
||||
enMap = {}
|
||||
beastLocales.enUS = enMap
|
||||
end
|
||||
if next(enMap) == nil then
|
||||
local beasts = _G and _G.MTH_DS_Beasts or MTH_DS_Beasts
|
||||
if type(beasts) == "table" then
|
||||
for beastId, beast in pairs(beasts) do
|
||||
local id = tonumber(beastId)
|
||||
local name = type(beast) == "table" and tostring(beast.name or "") or ""
|
||||
if id and name ~= "" and enMap[id] == nil then
|
||||
enMap[id] = name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return beastLocales[locale]
|
||||
end
|
||||
|
||||
function MTH:GetLocalizedVendorName(vendorId, defaultName)
|
||||
local id = tonumber(vendorId)
|
||||
if not id then
|
||||
return defaultName or "Unknown"
|
||||
end
|
||||
local locale = MTH_ResolveLocale(self.currentLocale or GetLocale() or "enUS")
|
||||
local localeMap = MTH_GetLocaleVendorNameMap(locale)
|
||||
if localeMap and localeMap[id] and localeMap[id] ~= "" then
|
||||
return localeMap[id]
|
||||
end
|
||||
local fallbackMap = MTH_GetLocaleVendorNameMap("enUS")
|
||||
if fallbackMap and fallbackMap[id] and fallbackMap[id] ~= "" then
|
||||
return fallbackMap[id]
|
||||
end
|
||||
return defaultName or "Unknown"
|
||||
end
|
||||
|
||||
function MTH:GetLocalizedNPCNameById(npcId, defaultName)
|
||||
return self:GetLocalizedVendorName(npcId, defaultName)
|
||||
end
|
||||
|
||||
function MTH:GetLocalizedItemName(itemId, defaultName)
|
||||
local id = tonumber(itemId)
|
||||
if not id then
|
||||
return defaultName or "Unknown"
|
||||
end
|
||||
local locale = MTH_ResolveLocale(self.currentLocale or GetLocale() or "enUS")
|
||||
local localeMap = MTH_GetLocaleItemNameMap(locale)
|
||||
if localeMap and localeMap[id] and localeMap[id] ~= "" then
|
||||
return localeMap[id]
|
||||
end
|
||||
local fallbackMap = MTH_GetLocaleItemNameMap("enUS")
|
||||
if fallbackMap and fallbackMap[id] and fallbackMap[id] ~= "" then
|
||||
return fallbackMap[id]
|
||||
end
|
||||
return defaultName or "Unknown"
|
||||
end
|
||||
|
||||
function MTH:GetLocalizedBeastName(beastId, defaultName)
|
||||
local id = tonumber(beastId)
|
||||
if not id then
|
||||
return defaultName or "Unknown"
|
||||
end
|
||||
local locale = MTH_ResolveLocale(self.currentLocale or GetLocale() or "enUS")
|
||||
local localeMap = MTH_GetLocaleBeastNameMap(locale)
|
||||
if localeMap and localeMap[id] and localeMap[id] ~= "" then
|
||||
return localeMap[id]
|
||||
end
|
||||
local fallbackMap = MTH_GetLocaleBeastNameMap("enUS")
|
||||
if fallbackMap and fallbackMap[id] and fallbackMap[id] ~= "" then
|
||||
return fallbackMap[id]
|
||||
end
|
||||
return defaultName or "Unknown"
|
||||
end
|
||||
|
||||
-- Module localization helper
|
||||
function MTH:RegisterModuleLocalization(module_name, localizationTable)
|
||||
if not self.localization.modules then
|
||||
@@ -118,60 +190,149 @@ function MTH:GetModuleLocalization(module_name, key, default)
|
||||
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",
|
||||
},
|
||||
}
|
||||
|
||||
local MTH_SPELL_ID_BY_TOKEN = {
|
||||
["Banish"] = 710,
|
||||
["Bestial Wrath"] = 19574,
|
||||
["Concussive Shot"] = 5116,
|
||||
["Counterattack"] = 19306,
|
||||
["Clever Traps"] = 19239,
|
||||
["Deterrence"] = 19263,
|
||||
["Entrapment"] = 19184,
|
||||
["Entrapment (Rank 2)"] = 19387,
|
||||
["Entrapment (Rank 3)"] = 19388,
|
||||
["Explosive Trap"] = 13813,
|
||||
["Explosive Trap Effect"] = 13812,
|
||||
["Feed Pet"] = 6991,
|
||||
["Flare"] = 1543,
|
||||
["Freezing Trap"] = 1499,
|
||||
["Freezing Trap Effect"] = 3355,
|
||||
["Frost Trap"] = 13809,
|
||||
["Frost Trap Aura"] = 13810,
|
||||
["Hunter's Mark"] = 1130,
|
||||
["Immolation Trap"] = 13795,
|
||||
["Immolation Trap Effect"] = 13797,
|
||||
["Improved Concussive Shot"] = 19407,
|
||||
["Improved Wing Clip"] = 19229,
|
||||
["Perception"] = 20600,
|
||||
["Rapid Fire"] = 3045,
|
||||
["Scare Beast"] = 1513,
|
||||
["Scatter Shot"] = 19503,
|
||||
["Scorpid Sting"] = 3043,
|
||||
["Serpent Sting"] = 1978,
|
||||
["Stoneform"] = 20594,
|
||||
["Viper Sting"] = 3034,
|
||||
["War Stomp"] = 20549,
|
||||
["Will of the Forsaken"] = 7744,
|
||||
["Wing Clip"] = 2974,
|
||||
["Wyvern Sting"] = 19386,
|
||||
["Blood Fury"] = 20572,
|
||||
["Berserking"] = 26297,
|
||||
}
|
||||
|
||||
do
|
||||
local spellLocales = (MTH_LocaleData and MTH_LocaleData.spells) or nil
|
||||
if type(spellLocales) == "table" then
|
||||
for localeKey, localeMap in pairs(spellLocales) do
|
||||
if type(localeMap) == "table" then
|
||||
MTH_SPELL_LOCALE[localeKey] = localeMap
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local extras = {
|
||||
enUS = {
|
||||
["Devilsaur Eye"] = "Devilsaur Eye",
|
||||
["Devilsaur Fury"] = "Devilsaur Fury",
|
||||
["Zandalarian Hero Medallion"] = "Zandalarian Hero Medallion",
|
||||
["Restless Strength"] = "Restless Strength",
|
||||
["Earthstrike"] = "Earthstrike",
|
||||
["Badge of the Swarmguard"] = "Badge of the Swarmguard",
|
||||
["Jom Gabbar"] = "Jom Gabbar",
|
||||
["Kiss of the Spider"] = "Kiss of the Spider",
|
||||
["Slayer's Crest"] = "Slayer's Crest",
|
||||
},
|
||||
deDE = {
|
||||
["Devilsaur Eye"] = "Auge eines Teufelssauriers",
|
||||
["Devilsaur Fury"] = "Zorn des Teufelssauriers",
|
||||
["Zandalarian Hero Medallion"] = "Zandalarianisches Heldenmedallion",
|
||||
["Restless Strength"] = "Ruhelose Stärke",
|
||||
["Earthstrike"] = "Erdschlag",
|
||||
["Badge of the Swarmguard"] = "Abzeichen der Schwarmwache",
|
||||
["Jom Gabbar"] = "Jom Gabbar",
|
||||
["Kiss of the Spider"] = "Kuss der Spinne",
|
||||
["Slayer's Crest"] = "Jägerspitze",
|
||||
},
|
||||
esES = {
|
||||
["Devilsaur Eye"] = "Ojo de Demosaurio",
|
||||
["Devilsaur Fury"] = "Furia del devilsaurio",
|
||||
["Zandalarian Hero Medallion"] = "Zandalarian Hero Medallion",
|
||||
["Restless Strength"] = "Fuerza agitada",
|
||||
["Earthstrike"] = "Golpeterra",
|
||||
["Badge of the Swarmguard"] = "Badge of the Swarmguard",
|
||||
["Jom Gabbar"] = "Jom Gabbar",
|
||||
["Kiss of the Spider"] = "Kiss of the Spider",
|
||||
["Slayer's Crest"] = "Cresta de Matador",
|
||||
},
|
||||
frFR = {
|
||||
["Devilsaur Eye"] = "Oeil de diablosaure",
|
||||
["Devilsaur Fury"] = "Fureur du diablosaure",
|
||||
["Zandalarian Hero Medallion"] = "Médaillon de héros zandalarien",
|
||||
["Restless Strength"] = "Force inconstante",
|
||||
["Earthstrike"] = "Choc de terre",
|
||||
["Badge of the Swarmguard"] = "Insigne de garde-essaim",
|
||||
["Jom Gabbar"] = "Jom Gabbar",
|
||||
["Kiss of the Spider"] = "Baiser de l'araignée",
|
||||
["Slayer's Crest"] = "Ecusson de tueur",
|
||||
},
|
||||
zhCN = {
|
||||
["Devilsaur Eye"] = "魔暴龙眼",
|
||||
["Devilsaur Fury"] = "魔暴龙之怒",
|
||||
["Zandalarian Hero Medallion"] = "赞达拉英雄徽记",
|
||||
["Restless Strength"] = "充沛之力",
|
||||
["Earthstrike"] = "大地之击",
|
||||
["Badge of the Swarmguard"] = "虫群守卫徽章",
|
||||
["Jom Gabbar"] = "Jom Gabbar",
|
||||
["Kiss of the Spider"] = "蜘蛛之吻",
|
||||
["Slayer's Crest"] = "屠戮者徽记",
|
||||
},
|
||||
}
|
||||
|
||||
for localeKey, localeMap in pairs(extras) do
|
||||
if type(MTH_SPELL_LOCALE[localeKey]) ~= "table" then
|
||||
MTH_SPELL_LOCALE[localeKey] = {}
|
||||
end
|
||||
for token, localized in pairs(localeMap) do
|
||||
if MTH_SPELL_LOCALE[localeKey][token] == nil then
|
||||
MTH_SPELL_LOCALE[localeKey][token] = localized
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
MTH.Const = MTH.Const or {}
|
||||
MTH.Const.SpellLocaleMap = MTH_SPELL_LOCALE
|
||||
MTH.Const.NPCLocaleMap = (MTH_LocaleData and MTH_LocaleData.data and MTH_LocaleData.data.vendors) or {}
|
||||
MTH.Const.ItemLocaleMap = (MTH_LocaleData and MTH_LocaleData.data and MTH_LocaleData.data.items) or {}
|
||||
MTH.Const.BeastLocaleMap = (MTH_LocaleData and MTH_LocaleData.data and MTH_LocaleData.data.beasts) or {}
|
||||
|
||||
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 getSpellInfo = _G and _G["GetSpellInfo"] or nil
|
||||
local spellId = MTH_SPELL_ID_BY_TOKEN[key]
|
||||
if spellId and getSpellInfo then
|
||||
local spellName = getSpellInfo(spellId)
|
||||
if spellName and spellName ~= "" then
|
||||
return spellName
|
||||
end
|
||||
end
|
||||
local locale = MTH_ResolveLocale(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]
|
||||
|
||||
+281
-88
@@ -2,9 +2,10 @@ MTH_Map = {
|
||||
enabled = true,
|
||||
showWorld = true,
|
||||
showMinimap = true,
|
||||
activeSource = "beasts",
|
||||
activeSource = "focus",
|
||||
providers = {},
|
||||
nodesByZone = {},
|
||||
minimapNodesByZone = {},
|
||||
worldPins = {},
|
||||
minimapPins = {},
|
||||
zoneNameToId = {},
|
||||
@@ -12,6 +13,7 @@ MTH_Map = {
|
||||
mapZoneCache = {},
|
||||
focusNodes = {},
|
||||
pendingZoneOpenId = nil,
|
||||
pendingZoneOpenPfqId = nil,
|
||||
pendingZoneOpenSourceId = nil,
|
||||
pendingZoneOpenAttempts = 0,
|
||||
pendingZoneOpenNextAttemptAt = 0,
|
||||
@@ -21,8 +23,39 @@ MTH_Map = {
|
||||
lastWorldReason = "n/a",
|
||||
lastMiniReason = "n/a",
|
||||
nodeRevision = 0,
|
||||
minimapForceRefreshInterval = 1.0,
|
||||
verboseMiniReasons = false,
|
||||
minimapTickActive = 0.05,
|
||||
minimapTickIdle = 0.75,
|
||||
}
|
||||
|
||||
-- Capture our PizzaSauce instance for the world map pin drop-in (guarded; may be nil).
|
||||
local MTH_MAP_Sauce = PizzaSauce
|
||||
local function MTH_Map_AnimatePin(pin)
|
||||
if not MTH_MAP_Sauce or not pin then return end
|
||||
if type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("ui.mappins") then return end
|
||||
if type(pin.SetAlpha) == "function" then pin:SetAlpha(0) end
|
||||
-- Bouncy scale "pop": the pin overshoots its size and springs back while
|
||||
-- fading in. Far more eye-catching than a plain alpha fade.
|
||||
MTH_MAP_Sauce:Group({
|
||||
MTH_MAP_Sauce:FadeIn(pin, 0.20, "outQuad", { defer = true }),
|
||||
MTH_MAP_Sauce:Rubber(pin, 0.35, 0.55, { defer = true }),
|
||||
})
|
||||
end
|
||||
|
||||
-- Ping a minimap pin that just came into range. Only called for genuinely new
|
||||
-- nodes (see UpdateMinimap), never every tick, to avoid strobing.
|
||||
local function MTH_Map_PingMinimapPin(pin)
|
||||
if not MTH_MAP_Sauce or not pin then return end
|
||||
if type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("ui.minimapping") then return end
|
||||
if type(pin.SetAlpha) == "function" then pin:SetAlpha(0) end
|
||||
-- Springy grow-in so a freshly-discovered node clearly "pops" onto the minimap.
|
||||
MTH_MAP_Sauce:Group({
|
||||
MTH_MAP_Sauce:FadeIn(pin, 0.20, "outQuad", { defer = true }),
|
||||
MTH_MAP_Sauce:Rubber(pin, 0.30, 0.55, { defer = true }),
|
||||
})
|
||||
end
|
||||
|
||||
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 },
|
||||
@@ -56,11 +89,6 @@ local function MTH_Map_Mod(a, b)
|
||||
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
|
||||
@@ -122,16 +150,54 @@ local function MTH_Map_StripThePrefix(name)
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTH_Map_WmaToPfq(zoneId)
|
||||
if type(MTH_MAP_WMA_TO_PFQ) == "table" then
|
||||
return MTH_MAP_WMA_TO_PFQ[zoneId] or zoneId
|
||||
end
|
||||
return zoneId
|
||||
end
|
||||
|
||||
local function MTH_Map_PfqToWma(pfqId)
|
||||
if type(MTH_MAP_PFQ_TO_WMA) == "table" then
|
||||
return MTH_MAP_PFQ_TO_WMA[pfqId] or pfqId
|
||||
end
|
||||
return pfqId
|
||||
end
|
||||
|
||||
local function MTH_Map_GetMinimapSizes()
|
||||
if pfDB and pfDB["minimap"] and MTH_DS_MinimapSizes then
|
||||
if not MTH_Map._mergedMinimapSizes then
|
||||
local merged = {}
|
||||
for zoneId, size in pairs(pfDB["minimap"]) do
|
||||
merged[zoneId] = size
|
||||
end
|
||||
for zoneId, size in pairs(MTH_DS_MinimapSizes) do
|
||||
if merged[zoneId] == nil then
|
||||
merged[zoneId] = size
|
||||
end
|
||||
end
|
||||
MTH_Map._mergedMinimapSizes = merged
|
||||
end
|
||||
return MTH_Map._mergedMinimapSizes
|
||||
elseif pfDB and pfDB["minimap"] then
|
||||
return pfDB["minimap"]
|
||||
end
|
||||
return MTH_DS_MinimapSizes or {}
|
||||
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]
|
||||
-- Translate WMA→pfQ first so WMA IDs don't accidentally hit wrong pfQ entries.
|
||||
local pfqId = MTH_Map_WmaToPfq(normalizedId)
|
||||
|
||||
if MTH_DS_ZoneNamesFallback and MTH_DS_ZoneNamesFallback[pfqId] then
|
||||
return MTH_DS_ZoneNamesFallback[pfqId]
|
||||
end
|
||||
|
||||
if not MTH_DS_Zones then return nil end
|
||||
local row = MTH_DS_Zones[normalizedId] or MTH_DS_Zones[tostring(normalizedId)]
|
||||
local row = MTH_DS_Zones[pfqId] or MTH_DS_Zones[tostring(pfqId)]
|
||||
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
|
||||
@@ -323,7 +389,6 @@ function MTH_Map:BuildZoneLookup()
|
||||
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)
|
||||
@@ -359,6 +424,8 @@ end
|
||||
|
||||
function MTH_Map:ResolveZoneHierarchy(zoneId)
|
||||
local inputId = tonumber(zoneId)
|
||||
local pfqInputId = MTH_Map_WmaToPfq(inputId)
|
||||
local inputIsWma = inputId and pfqInputId ~= inputId or false
|
||||
local resolved = {
|
||||
inputId = inputId,
|
||||
zoneId = inputId,
|
||||
@@ -372,9 +439,9 @@ function MTH_Map:ResolveZoneHierarchy(zoneId)
|
||||
return resolved
|
||||
end
|
||||
|
||||
local sizes = MTH_DS_MinimapSizes or (pfDB and pfDB["minimap"]) or {}
|
||||
local sizes = MTH_Map_GetMinimapSizes()
|
||||
local visited = {}
|
||||
local cursor = inputId
|
||||
local cursor = pfqInputId
|
||||
local depth = 0
|
||||
|
||||
while cursor and not visited[cursor] and depth < 8 do
|
||||
@@ -385,15 +452,17 @@ function MTH_Map:ResolveZoneHierarchy(zoneId)
|
||||
end
|
||||
|
||||
local parent = tonumber(row.parent or row.continent)
|
||||
local outputCursor = inputIsWma and MTH_Map_PfqToWma(cursor) or cursor
|
||||
local outputParent = parent and (inputIsWma and MTH_Map_PfqToWma(parent) or parent) or nil
|
||||
if parent and not resolved.continentId then
|
||||
resolved.continentId = parent
|
||||
resolved.continentId = outputParent
|
||||
end
|
||||
|
||||
if parent and parent > 0 and parent ~= cursor then
|
||||
resolved.subzoneId = resolved.subzoneId or cursor
|
||||
resolved.parentId = parent
|
||||
resolved.subzoneId = resolved.subzoneId or outputCursor
|
||||
resolved.parentId = outputParent
|
||||
cursor = parent
|
||||
resolved.zoneId = cursor
|
||||
resolved.zoneId = inputIsWma and MTH_Map_PfqToWma(cursor) or cursor
|
||||
depth = depth + 1
|
||||
else
|
||||
break
|
||||
@@ -401,11 +470,17 @@ function MTH_Map:ResolveZoneHierarchy(zoneId)
|
||||
end
|
||||
|
||||
local renderZoneId = resolved.zoneId
|
||||
local pfqRenderId = tonumber(cursor or pfqInputId)
|
||||
local pfqParentId = resolved.parentId and MTH_Map_WmaToPfq(resolved.parentId) or nil
|
||||
if not sizes[renderZoneId] then
|
||||
if sizes[inputId] then
|
||||
renderZoneId = inputId
|
||||
elseif resolved.parentId and sizes[resolved.parentId] then
|
||||
renderZoneId = resolved.parentId
|
||||
elseif pfqRenderId and sizes[pfqRenderId] then
|
||||
renderZoneId = pfqRenderId
|
||||
elseif pfqParentId and sizes[pfqParentId] then
|
||||
renderZoneId = pfqParentId
|
||||
end
|
||||
end
|
||||
|
||||
@@ -414,12 +489,13 @@ function MTH_Map:ResolveZoneHierarchy(zoneId)
|
||||
return resolved
|
||||
end
|
||||
|
||||
function MTH_Map:GetCurrentMapID()
|
||||
function MTH_Map:GetCurrentMapID(space)
|
||||
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
|
||||
local preferPfq = space == "pfq"
|
||||
|
||||
if continent and zone and continent > 0 and zone > 0 and GetMapZones then
|
||||
if not self.mapZoneCache[continent] then
|
||||
@@ -430,6 +506,9 @@ function MTH_Map:GetCurrentMapID()
|
||||
if mapId then
|
||||
local resolved = self:ResolveZoneHierarchy(mapId)
|
||||
local normalized = resolved.zoneId or mapId
|
||||
if not preferPfq then
|
||||
normalized = MTH_Map_PfqToWma(normalized)
|
||||
end
|
||||
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
|
||||
@@ -445,6 +524,9 @@ function MTH_Map:GetCurrentMapID()
|
||||
mapId = self:GetMapIDByName(real)
|
||||
local resolved = self:ResolveZoneHierarchy(mapId)
|
||||
local normalized = resolved.zoneId or mapId
|
||||
if not preferPfq then
|
||||
normalized = MTH_Map_PfqToWma(normalized)
|
||||
end
|
||||
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
|
||||
@@ -454,6 +536,30 @@ function MTH_Map:GetCurrentMapID()
|
||||
return nil
|
||||
end
|
||||
|
||||
function MTH_Map:GetCurrentMinimapMapID()
|
||||
local realZoneName = GetRealZoneText and GetRealZoneText() or nil
|
||||
if realZoneName and realZoneName ~= "" then
|
||||
local mapId = self:GetMapIDByName(realZoneName)
|
||||
if mapId then
|
||||
return mapId
|
||||
end
|
||||
end
|
||||
|
||||
local continent = GetCurrentMapContinent and GetCurrentMapContinent() or nil
|
||||
local zone = GetCurrentMapZone and GetCurrentMapZone() or 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
|
||||
local zoneName = self.mapZoneCache[continent][zone]
|
||||
if zoneName and zoneName ~= "" then
|
||||
return self:GetMapIDByName(zoneName)
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
@@ -463,12 +569,24 @@ end
|
||||
|
||||
local function MTH_Map_ClearPendingZoneOpen()
|
||||
MTH_Map.pendingZoneOpenId = nil
|
||||
MTH_Map.pendingZoneOpenPfqId = nil
|
||||
MTH_Map.pendingZoneOpenSourceId = nil
|
||||
MTH_Map.pendingZoneOpenAttempts = 0
|
||||
MTH_Map.pendingZoneOpenNextAttemptAt = 0
|
||||
end
|
||||
|
||||
function MTH_Map:EnsureInitialized()
|
||||
if self.controller then
|
||||
return true
|
||||
end
|
||||
self:Init()
|
||||
return self.controller ~= nil
|
||||
end
|
||||
|
||||
function MTH_Map:SetSource(key)
|
||||
if not self:EnsureInitialized() then
|
||||
return false
|
||||
end
|
||||
if not self.providers[key] then
|
||||
return false
|
||||
end
|
||||
@@ -517,8 +635,12 @@ 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
|
||||
-- Translate WMA ID → pfQ so game map APIs (pfMap, SetMapZoom) work correctly.
|
||||
local pfqZid = (type(MTH_MAP_WMA_TO_PFQ) == "table" and MTH_MAP_WMA_TO_PFQ[zid]) or zid
|
||||
local resolved = self:ResolveZoneHierarchy(pfqZid)
|
||||
local targetZoneId = tonumber(resolved.zoneId or pfqZid) or pfqZid
|
||||
-- WMA ID of this zone, for comparing against GetCurrentMapID() which returns WMA IDs.
|
||||
local targetWmaId = (type(MTH_MAP_PFQ_TO_WMA) == "table" and MTH_MAP_PFQ_TO_WMA[targetZoneId]) or zid
|
||||
|
||||
if ToggleWorldMap then
|
||||
if not WorldMapFrame or not WorldMapFrame:IsShown() then
|
||||
@@ -531,34 +653,39 @@ function MTH_Map:OpenWorldMapForZone(zoneId)
|
||||
if pfMap and pfMap.SetMapByID then
|
||||
pfMap:SetMapByID(targetZoneId)
|
||||
local currentAfterPF = self:GetCurrentMapID()
|
||||
if tonumber(currentAfterPF) == tonumber(targetZoneId) then
|
||||
if tonumber(currentAfterPF) == tonumber(targetWmaId) then
|
||||
MTH_Map_ClearPendingZoneOpen()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local zoomApplied = MTH_Map_ApplyWorldMapZone(targetZoneId, zid)
|
||||
local zoomApplied = MTH_Map_ApplyWorldMapZone(zid, zid)
|
||||
if zoomApplied then
|
||||
local currentAfterZoom = self:GetCurrentMapID()
|
||||
if tonumber(currentAfterZoom) == tonumber(targetZoneId) then
|
||||
if tonumber(currentAfterZoom) == tonumber(targetWmaId) then
|
||||
MTH_Map_ClearPendingZoneOpen()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local nowMapId = self:GetCurrentMapID()
|
||||
if tonumber(nowMapId) == tonumber(targetZoneId) then
|
||||
if tonumber(nowMapId) == tonumber(targetWmaId) then
|
||||
MTH_Map_ClearPendingZoneOpen()
|
||||
return true
|
||||
end
|
||||
|
||||
self.pendingZoneOpenId = targetZoneId
|
||||
self.pendingZoneOpenSourceId = zid
|
||||
self.pendingZoneOpenId = targetWmaId
|
||||
self.pendingZoneOpenPfqId = targetZoneId
|
||||
self.pendingZoneOpenSourceId = pfqZid
|
||||
self.pendingZoneOpenAttempts = 8
|
||||
self.pendingZoneOpenNextAttemptAt = (GetTime and GetTime() or 0) + 0.25
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_Map:GetFamilyColor(family)
|
||||
return MTH_Map_GetFamilyColor(family)
|
||||
end
|
||||
|
||||
function MTH_Map:FocusBeast(beastId, beast)
|
||||
local id = tonumber(beastId)
|
||||
if not id then return false end
|
||||
@@ -572,6 +699,7 @@ function MTH_Map:FocusBeast(beastId, beast)
|
||||
local cr, cg, cb = MTH_Map_GetFamilyColor(row.family)
|
||||
local nodes = {}
|
||||
local firstZoneId = nil
|
||||
local displayName = (MTH and MTH.GetLocalizedBeastName and MTH:GetLocalizedBeastName(id, row.name)) or row.name or "Unknown"
|
||||
|
||||
for i = 1, table.getn(coords) do
|
||||
local c = coords[i]
|
||||
@@ -581,7 +709,7 @@ function MTH_Map:FocusBeast(beastId, beast)
|
||||
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 })
|
||||
MTH_Map_AddNode(nodes, c[3], c[1], c[2], string.format("%s (%d)", displayName, id), details, cr, cg, cb, { kind = "beast", beastId = id })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -637,6 +765,7 @@ function MTH_Map:FocusVendor(vendorId, vendor)
|
||||
|
||||
local nodes = {}
|
||||
local firstZoneId = nil
|
||||
local displayName = (MTH and MTH.GetLocalizedVendorName and MTH:GetLocalizedVendorName(id, row.name)) or row.name or "Unknown"
|
||||
for i = 1, table.getn(coords) do
|
||||
local c = coords[i]
|
||||
if c and c[1] and c[2] and c[3] then
|
||||
@@ -645,7 +774,7 @@ function MTH_Map:FocusVendor(vendorId, vendor)
|
||||
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 })
|
||||
MTH_Map_AddNode(nodes, c[3], c[1], c[2], string.format("%s (%d)", displayName, id), details, cr, cg, cb, { kind = "vendor", vendorId = id })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -670,6 +799,9 @@ function MTH_Map:SetEnabled(enabled)
|
||||
if not self.enabled then
|
||||
self:HideAllPins()
|
||||
else
|
||||
if not self:EnsureInitialized() then
|
||||
return
|
||||
end
|
||||
self:UpdateWorldMap()
|
||||
self:UpdateMinimap()
|
||||
end
|
||||
@@ -680,6 +812,9 @@ function MTH_Map:SetWorldEnabled(enabled)
|
||||
if not self.showWorld then
|
||||
self:HideWorldPins()
|
||||
else
|
||||
if not self:EnsureInitialized() then
|
||||
return
|
||||
end
|
||||
self:UpdateWorldMap()
|
||||
end
|
||||
end
|
||||
@@ -689,6 +824,9 @@ function MTH_Map:SetMinimapEnabled(enabled)
|
||||
if not self.showMinimap then
|
||||
self:HideMinimapPins()
|
||||
else
|
||||
if not self:EnsureInitialized() then
|
||||
return
|
||||
end
|
||||
self:UpdateMinimap()
|
||||
end
|
||||
end
|
||||
@@ -828,6 +966,7 @@ function MTH_Map:HideMinimapPins()
|
||||
for i = 1, table.getn(self.minimapPins) do
|
||||
if self.minimapPins[i] then self.minimapPins[i]:Hide() end
|
||||
end
|
||||
self._miniShownSet = nil
|
||||
end
|
||||
|
||||
function MTH_Map:HideAllPins()
|
||||
@@ -838,6 +977,7 @@ end
|
||||
function MTH_Map:RebuildNodes()
|
||||
self:BuildZoneLookup()
|
||||
self.nodesByZone = {}
|
||||
self.minimapNodesByZone = {}
|
||||
self.nodeRevision = (tonumber(self.nodeRevision) or 0) + 1
|
||||
self._lastWorldRenderKey = nil
|
||||
self._lastMiniState = nil
|
||||
@@ -851,20 +991,26 @@ function MTH_Map:RebuildNodes()
|
||||
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
|
||||
local worldResolved = self:ResolveZoneHierarchy(sourceZoneId)
|
||||
local worldZoneId = tonumber(worldResolved.zoneId or sourceZoneId)
|
||||
local pfqSourceZoneId = MTH_Map_WmaToPfq(sourceZoneId)
|
||||
local minimapZoneId = tonumber(pfqSourceZoneId)
|
||||
if worldZoneId 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)
|
||||
node.zoneId = worldZoneId
|
||||
node.parentZoneId = worldResolved.parentId
|
||||
node.subzoneId = worldResolved.subzoneId
|
||||
node.minimapZoneId = minimapZoneId
|
||||
if not self.nodesByZone[worldZoneId] then self.nodesByZone[worldZoneId] = {} end
|
||||
table.insert(self.nodesByZone[worldZoneId], node)
|
||||
if minimapZoneId then
|
||||
if not self.minimapNodesByZone[minimapZoneId] then self.minimapNodesByZone[minimapZoneId] = {} end
|
||||
table.insert(self.minimapNodesByZone[minimapZoneId], node)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
MTH_Map_Log("Rebuilt nodes for source '" .. tostring(self.activeSource) .. "'")
|
||||
end
|
||||
|
||||
function MTH_Map:UpdateWorldMap()
|
||||
@@ -923,6 +1069,7 @@ function MTH_Map:UpdateWorldMap()
|
||||
pin:ClearAllPoints()
|
||||
pin:SetPoint("CENTER", worldButton, "TOPLEFT", x, -y)
|
||||
pin:Show()
|
||||
MTH_Map_AnimatePin(pin)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -949,23 +1096,22 @@ function MTH_Map:UpdateMinimap()
|
||||
return
|
||||
end
|
||||
|
||||
local mapId = self:GetCurrentMapID()
|
||||
if not mapId or not self.nodesByZone[mapId] then
|
||||
local mapId = self:GetCurrentMinimapMapID()
|
||||
local nodes = mapId and self.minimapNodesByZone[mapId] or nil
|
||||
if not mapId or not nodes 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"
|
||||
if table.getn(nodes) <= 0 then
|
||||
self.lastMiniReason = "no-nodes-in-zone"
|
||||
self._lastMiniState = nil
|
||||
self:HideMinimapPins()
|
||||
return
|
||||
end
|
||||
|
||||
local now = GetTime() or 0
|
||||
local xPlayer, yPlayer = GetPlayerMapPosition("player")
|
||||
if not xPlayer or not yPlayer or (xPlayer == 0 and yPlayer == 0) then
|
||||
self.lastMiniReason = "player-position-unavailable"
|
||||
@@ -978,10 +1124,35 @@ function MTH_Map:UpdateMinimap()
|
||||
yPlayer = yPlayer * 100
|
||||
|
||||
local zoom = Minimap:GetZoom()
|
||||
local miniState = self._lastMiniState
|
||||
if miniState
|
||||
and miniState.mapId == mapId
|
||||
and miniState.zoom == zoom
|
||||
and miniState.nodeRevision == (self.nodeRevision or 0)
|
||||
then
|
||||
if xPlayer == miniState.xPlayer and yPlayer == miniState.yPlayer and now < (miniState.nextForceAt or 0) then
|
||||
self.lastMiniReason = self.verboseMiniReasons and "throttled-still" or "throttle"
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local minimapSizes = MTH_Map_GetMinimapSizes()
|
||||
local mapSize = minimapSizes and minimapSizes[mapId]
|
||||
if not mapSize then
|
||||
local wmaId = MTH_Map_PfqToWma(mapId)
|
||||
mapSize = wmaId and minimapSizes and minimapSizes[wmaId]
|
||||
end
|
||||
if not mapSize then
|
||||
self.lastMiniReason = self.verboseMiniReasons and "missing-minimap-size" or "missing-size"
|
||||
self._lastMiniState = nil
|
||||
self:HideMinimapPins()
|
||||
return
|
||||
end
|
||||
|
||||
local indoor = MTH_Map_MinimapIndoorState()
|
||||
local zoomSet = MTH_MAP_MINIMAP_ZOOM[indoor]
|
||||
if not zoomSet then
|
||||
self.lastMiniReason = "missing-zoomset"
|
||||
self.lastMiniReason = self.verboseMiniReasons and "missing-zoomset" or "missing-zoomset"
|
||||
self._lastMiniState = nil
|
||||
self:HideMinimapPins()
|
||||
return
|
||||
@@ -989,7 +1160,7 @@ function MTH_Map:UpdateMinimap()
|
||||
|
||||
local mapZoom = zoomSet[zoom]
|
||||
if not mapZoom then
|
||||
self.lastMiniReason = "missing-mapzoom"
|
||||
self.lastMiniReason = self.verboseMiniReasons and "missing-mapzoom" or "missing-zoom"
|
||||
self._lastMiniState = nil
|
||||
self:HideMinimapPins()
|
||||
return
|
||||
@@ -998,34 +1169,19 @@ function MTH_Map:UpdateMinimap()
|
||||
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.lastMiniReason = self.verboseMiniReasons and "invalid-map-size" or "invalid-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
|
||||
local prevShown = self._miniShownSet or {}
|
||||
local nowShown = {}
|
||||
|
||||
for i = 1, table.getn(nodes) do
|
||||
local node = nodes[i]
|
||||
@@ -1047,24 +1203,36 @@ function MTH_Map:UpdateMinimap()
|
||||
pin:ClearAllPoints()
|
||||
pin:SetPoint("CENTER", Minimap, "CENTER", xPos, -yPos)
|
||||
pin:Show()
|
||||
|
||||
local nodeKey = tostring(node.name or "") .. "@" .. tostring(node.x) .. "," .. tostring(node.y)
|
||||
nowShown[nodeKey] = true
|
||||
if not prevShown[nodeKey] then
|
||||
MTH_Map_PingMinimapPin(pin)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self._miniShownSet = nowShown
|
||||
|
||||
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,
|
||||
}
|
||||
local lastState = self._lastMiniState or {}
|
||||
lastState.mapId = mapId
|
||||
lastState.zoom = zoom
|
||||
lastState.indoor = indoor
|
||||
lastState.xPlayer = xPlayer
|
||||
lastState.yPlayer = yPlayer
|
||||
lastState.nodeRevision = self.nodeRevision or 0
|
||||
lastState.nextForceAt = now + (tonumber(self.minimapForceRefreshInterval) or 1.0)
|
||||
self._lastMiniState = lastState
|
||||
|
||||
self.lastMiniReason = "ok(map=" .. tostring(mapId) .. ",shown=" .. tostring(shown) .. ")"
|
||||
if self.verboseMiniReasons then
|
||||
self.lastMiniReason = "ok(map=" .. tostring(mapId) .. ",shown=" .. tostring(shown) .. ")"
|
||||
else
|
||||
self.lastMiniReason = "ok"
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_Map:RegisterDefaultProviders()
|
||||
@@ -1087,12 +1255,13 @@ function MTH_Map:RegisterDefaultProviders()
|
||||
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 displayName = (MTH and MTH.GetLocalizedBeastName and MTH:GetLocalizedBeastName(beastId, beast.name)) or beast.name or "Unknown"
|
||||
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 })
|
||||
MTH_Map_AddNode(nodes, c[3], c[1], c[2], string.format("%s (%d)", displayName, beastId), details, cr, cg, cb, { kind = "beast", beastId = beastId })
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1111,11 +1280,12 @@ function MTH_Map:RegisterDefaultProviders()
|
||||
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
|
||||
local displayName = (MTH and MTH.GetLocalizedVendorName and MTH:GetLocalizedVendorName(vendorId, vendor.name)) or vendor.name or "Unknown"
|
||||
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 })
|
||||
MTH_Map_AddNode(nodes, c[3], c[1], c[2], string.format("%s (%d)", displayName, vendorId), details, 0.20, 0.90, 1.00, { kind = "vendor", vendorId = vendorId })
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1136,15 +1306,19 @@ function MTH_Map:RegisterDefaultProviders()
|
||||
|
||||
for itemId, item in pairs(items) do
|
||||
if item and item.drops then
|
||||
local itemDefaultName = item.name or "Unknown"
|
||||
local itemDisplayName = (MTH and MTH.GetLocalizedItemName)
|
||||
and MTH:GetLocalizedItemName(itemId, itemDefaultName)
|
||||
or itemDefaultName
|
||||
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)
|
||||
creatureName = (MTH_DS_CreatureNames and MTH_DS_CreatureNames[creatureId]) or ((MTH and MTH.GetLocalizedBeastName and creature and MTH:GetLocalizedBeastName(creatureId, creature.name)) or (creature and creature.name))
|
||||
elseif creature and creature.coords and table.getn(creature.coords) > 0 then
|
||||
creatureName = creature.name
|
||||
creatureName = (MTH and MTH.GetLocalizedBeastName and MTH:GetLocalizedBeastName(creatureId, creature.name)) or creature.name
|
||||
end
|
||||
|
||||
if coords and table.getn(coords) > 0 then
|
||||
@@ -1155,7 +1329,7 @@ function MTH_Map:RegisterDefaultProviders()
|
||||
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",
|
||||
itemDisplayName or "Unknown",
|
||||
tonumber(itemId) or 0,
|
||||
creatureName or "Unknown",
|
||||
tonumber(creatureId) or 0,
|
||||
@@ -1166,7 +1340,7 @@ function MTH_Map:RegisterDefaultProviders()
|
||||
c[3],
|
||||
c[1],
|
||||
c[2],
|
||||
string.format("%s ← %s", item.name or "Unknown Item", creatureName or "Unknown Creature"),
|
||||
string.format("%s ← %s", itemDisplayName or "Unknown Item", creatureName or "Unknown Creature"),
|
||||
details,
|
||||
1.00,
|
||||
0.35,
|
||||
@@ -1185,7 +1359,6 @@ function MTH_Map:RegisterDefaultProviders()
|
||||
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
|
||||
@@ -1194,12 +1367,15 @@ function MTH_Map:RegisterDefaultProviders()
|
||||
end
|
||||
|
||||
function MTH_Map:Init()
|
||||
if self.controller then
|
||||
return
|
||||
end
|
||||
self:BuildZoneLookup()
|
||||
self:RegisterDefaultProviders()
|
||||
self:RebuildNodes()
|
||||
self.nodesByZone = self.nodesByZone or {}
|
||||
|
||||
if not self.controller then
|
||||
self.controller = CreateFrame("Frame", "MTHMapController", UIParent)
|
||||
self.controller = CreateFrame("Frame", "MTH_MapController", UIParent)
|
||||
self.controller:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
self.controller:RegisterEvent("ZONE_CHANGED")
|
||||
self.controller:RegisterEvent("ZONE_CHANGED_NEW_AREA")
|
||||
@@ -1208,6 +1384,9 @@ function MTH_Map:Init()
|
||||
|
||||
self.controller:SetScript("OnEvent", function(frame, eventName)
|
||||
eventName = eventName or event
|
||||
if eventName == "WORLD_MAP_UPDATE" and MTH_Map._syncingMapContext then
|
||||
return
|
||||
end
|
||||
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()
|
||||
@@ -1222,7 +1401,6 @@ function MTH_Map:Init()
|
||||
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
|
||||
@@ -1230,9 +1408,9 @@ function MTH_Map:Init()
|
||||
|
||||
if WorldMapFrame and WorldMapFrame:IsShown() then
|
||||
if pfMap and pfMap.SetMapByID then
|
||||
pfMap:SetMapByID(MTH_Map.pendingZoneOpenId)
|
||||
pfMap:SetMapByID(MTH_Map.pendingZoneOpenPfqId or MTH_Map.pendingZoneOpenId)
|
||||
end
|
||||
MTH_Map_ApplyWorldMapZone(MTH_Map.pendingZoneOpenId, MTH_Map.pendingZoneOpenSourceId)
|
||||
MTH_Map_ApplyWorldMapZone(MTH_Map.pendingZoneOpenId or MTH_Map.pendingZoneOpenPfqId, MTH_Map.pendingZoneOpenId or MTH_Map.pendingZoneOpenPfqId)
|
||||
local nowMapId = MTH_Map:GetCurrentMapID()
|
||||
if tonumber(nowMapId) == tonumber(MTH_Map.pendingZoneOpenId) then
|
||||
MTH_Map_ClearPendingZoneOpen()
|
||||
@@ -1251,12 +1429,27 @@ function MTH_Map:Init()
|
||||
end
|
||||
|
||||
if MTH_Map.enabled and MTH_Map.showMinimap then
|
||||
MTH_Map:UpdateMinimap()
|
||||
local mapId = MTH_Map:GetCurrentMinimapMapID()
|
||||
if not mapId and (frame._mthMapRecoverAt or 0) <= now then
|
||||
frame._mthMapRecoverAt = now + 2.0
|
||||
if SetMapToCurrentZone and (not WorldMapFrame or not WorldMapFrame:IsShown()) then
|
||||
SetMapToCurrentZone()
|
||||
mapId = MTH_Map:GetCurrentMinimapMapID()
|
||||
end
|
||||
end
|
||||
local nodes = mapId and MTH_Map.minimapNodesByZone and MTH_Map.minimapNodesByZone[mapId] or nil
|
||||
local hasNodes = nodes and table.getn(nodes) > 0
|
||||
frame._mthTick = now + (hasNodes and (tonumber(MTH_Map.minimapTickActive) or 0.20) or (tonumber(MTH_Map.minimapTickIdle) or 0.75))
|
||||
if hasNodes then
|
||||
MTH_Map:UpdateMinimap()
|
||||
elseif (frame._mthNoNodeTick or 0) <= now then
|
||||
frame._mthNoNodeTick = now + 1.0
|
||||
MTH_Map:UpdateMinimap()
|
||||
end
|
||||
else
|
||||
frame._mthTick = now + (tonumber(MTH_Map.minimapTickIdle) or 0.75)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
MTH_Map_Log("Map system initialized")
|
||||
end
|
||||
|
||||
MTH_Map:Init()
|
||||
|
||||
+66
-29
@@ -1,19 +1,56 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt Minimap Button
|
||||
--
|
||||
-- NOTE: The namespace table is MTH_MinimapButton (a plain Lua table).
|
||||
-- The actual UI frame is named "MTH_MinimapBtn" to avoid a global
|
||||
-- collision — CreateFrame("...", "MTH_MinimapButton", ...) would
|
||||
-- OVERWRITE the namespace table with the frame object, orphaning
|
||||
-- all methods and .frame/.icon references. pfUI iterates Minimap
|
||||
-- children by name and calls _G[name]:GetHeight(); if the global is
|
||||
-- a plain {} table instead of a frame, that call crashes.
|
||||
------------------------------------------------------
|
||||
|
||||
if not MTH_MinimapButton then
|
||||
MTH_MinimapButton = {}
|
||||
end
|
||||
|
||||
-- Capture our PizzaSauce instance for the click pulse (guarded; may be nil).
|
||||
local MTH_MB_Sauce = PizzaSauce
|
||||
local function MTH_MB_AnimateClick()
|
||||
if not MTH_MB_Sauce then return end
|
||||
if type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("ui.minimap") then return end
|
||||
local icon = MTH_MinimapButton and MTH_MinimapButton.icon
|
||||
if not icon or type(icon.SetWidth) ~= "function" then return end
|
||||
-- Reset to base size first so rapid clicks can't make the icon drift in size.
|
||||
icon:SetWidth(20)
|
||||
icon:SetHeight(20)
|
||||
MTH_MB_Sauce:Pulse(icon, 1.35, 0.28)
|
||||
end
|
||||
|
||||
local function MTH_MB_L(key, default)
|
||||
if MTH and MTH.GetLocalization then
|
||||
return MTH:GetLocalization(key, default)
|
||||
end
|
||||
return default or key
|
||||
end
|
||||
|
||||
local function MTH_MB_GetStore()
|
||||
if not MTH_CharSavedVariables then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.minimapButton) ~= "table" then
|
||||
MTH_CharSavedVariables.minimapButton = {}
|
||||
local store
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
store = MTH:GetModuleCharSavedVariables("minimapbutton")
|
||||
end
|
||||
if type(store) ~= "table" then
|
||||
if type(MTH_CharSavedVariables.modules) ~= "table" then
|
||||
MTH_CharSavedVariables.modules = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.modules.minimapbutton) ~= "table" then
|
||||
MTH_CharSavedVariables.modules.minimapbutton = {}
|
||||
end
|
||||
store = MTH_CharSavedVariables.modules.minimapbutton
|
||||
end
|
||||
local store = MTH_CharSavedVariables.minimapButton
|
||||
if store.angle == nil then
|
||||
store.angle = 220
|
||||
end
|
||||
@@ -77,7 +114,9 @@ function MTH_MinimapButton:Initialize()
|
||||
return
|
||||
end
|
||||
|
||||
local button = CreateFrame("Button", "MTH_MinimapButtonFrame", Minimap)
|
||||
-- Frame name is MTH_MinimapBtn (NOT "MTH_MinimapButton") to avoid
|
||||
-- overwriting the namespace table in _G.
|
||||
local button = CreateFrame("Button", "MTH_MinimapBtn", Minimap)
|
||||
if not button then
|
||||
return
|
||||
end
|
||||
@@ -107,14 +146,13 @@ function MTH_MinimapButton:Initialize()
|
||||
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)
|
||||
button:SetScript("OnEnter", function()
|
||||
if not this then return end
|
||||
GameTooltip:SetOwner(this, "ANCHOR_LEFT")
|
||||
GameTooltip:SetText(MTH_MB_L("MINIMAP_TITLE", "MetaHunt"), 1, 0.82, 0)
|
||||
GameTooltip:AddLine(MTH_MB_L("MINIMAP_HINT_LEFT_CLICK", "Left-Click: Open Hunter Book"), 1, 1, 1)
|
||||
GameTooltip:AddLine(MTH_MB_L("MINIMAP_HINT_RIGHT_CLICK", "Right-Click: Open MetaHunt Options"), 1, 1, 1)
|
||||
GameTooltip:AddLine(MTH_MB_L("MINIMAP_HINT_DRAG", "Drag: Move button"), 0.8, 0.8, 0.8)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
|
||||
@@ -122,25 +160,24 @@ function MTH_MinimapButton:Initialize()
|
||||
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()
|
||||
button:SetScript("OnDragStart", function()
|
||||
if not this then return end
|
||||
this.dragging = 1
|
||||
this: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)
|
||||
button:SetScript("OnDragStop", function()
|
||||
if not this then return end
|
||||
this.dragging = nil
|
||||
this:SetScript("OnUpdate", nil)
|
||||
MTH_MB_UpdateDragPosition()
|
||||
end)
|
||||
|
||||
button:SetScript("OnClick", function(_, mouseButton)
|
||||
mouseButton = mouseButton or arg1
|
||||
button:SetScript("OnClick", function()
|
||||
local mouseButton = arg1
|
||||
MTH_MB_AnimateClick()
|
||||
if mouseButton == "RightButton" then
|
||||
if type(MTH_OpenOptions) == "function" then
|
||||
MTH_OpenOptions("General")
|
||||
@@ -160,12 +197,12 @@ function MTH_MinimapButton:Initialize()
|
||||
MTH_MB_UpdatePosition()
|
||||
end
|
||||
|
||||
local loader = CreateFrame("Frame", "MTH_MinimapButtonLoader")
|
||||
local loader = CreateFrame("Frame", "MTH_MinimapLoader")
|
||||
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
|
||||
loader:SetScript("OnEvent", function()
|
||||
if event == "ADDON_LOADED" and arg1 == "MetaHunt" then
|
||||
MTH_MinimapButton:Initialize()
|
||||
this:UnregisterAllEvents()
|
||||
this:SetScript("OnEvent", nil)
|
||||
end
|
||||
end)
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
-- MetaHunt: "Animations" options panel.
|
||||
-- Global enable/disable plus a per-animation checkbox for each effect, grouped
|
||||
-- into sections (Pet, Book, ...). The animation registry lives in fx-celebrate.lua
|
||||
-- (MTH_FX_ANIM_SECTIONS / MTH_FX_ANIM_DEFS) so both the effects and this panel
|
||||
-- stay in sync.
|
||||
|
||||
local MTH_ANIM_READY = MTH_OptionsRequire and MTH_OptionsRequire("options-animations", {
|
||||
"MTH_GetFrame",
|
||||
"MTH_ClearContainer",
|
||||
"MTH_CreateCheckbox",
|
||||
})
|
||||
|
||||
local MTH_ANIM_LINK = "https://codeberg.org/Pizzahawaii/PizzaSauce"
|
||||
local MTH_ANIM_CTRL = {}
|
||||
|
||||
local function MTH_ANIM_L(key, default)
|
||||
if MTH and MTH.GetLocalization then
|
||||
return MTH:GetLocalization(key, default)
|
||||
end
|
||||
return default or key
|
||||
end
|
||||
|
||||
local function MTH_ANIM_InsertLink(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(MTH_ANIM_L("ANIM_LINK_INSERTED", "Link inserted in chat: ") .. link)
|
||||
end
|
||||
return
|
||||
end
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print(MTH_ANIM_L("ANIM_LINK_FALLBACK", "Link: ") .. link)
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_ANIM_UpdateChildStates()
|
||||
local master = MTH_FX_AnimationsEnabled()
|
||||
local i = 1
|
||||
while i <= table.getn(MTH_ANIM_CTRL) do
|
||||
local cb = MTH_ANIM_CTRL[i]
|
||||
if cb then
|
||||
cb:SetChecked(MTH_FX_GetFlag(cb.mthKey))
|
||||
if master then
|
||||
cb:Enable()
|
||||
else
|
||||
cb:Disable()
|
||||
end
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_SetupAnimationsOptions()
|
||||
if not MTH_ANIM_READY then
|
||||
return
|
||||
end
|
||||
if type(MTH_FX_AnimationsEnabled) ~= "function" then
|
||||
return
|
||||
end
|
||||
|
||||
local container = MTH_GetFrame("MetaHuntOptionsAnimations")
|
||||
if not container then
|
||||
return
|
||||
end
|
||||
|
||||
local panel = MTH_Layout.Panel(container)
|
||||
panel:Title(MTH_ANIM_L("ANIM_TITLE", "Animations"))
|
||||
panel:Note(MTH_ANIM_L("ANIM_CREDIT",
|
||||
"Animations are powered by PizzaSauce, a WoW 1.12 animation library kindly made and shared by Pizzahawaii. Thank you!"),
|
||||
{ color = MTH_Layout.Theme.colText, lines = 2 })
|
||||
panel:Link("MetaHuntOptionsAnimLink", MTH_ANIM_LINK, function() MTH_ANIM_InsertLink(MTH_ANIM_LINK) end)
|
||||
|
||||
MTH_ANIM_CTRL = {}
|
||||
|
||||
-- Global master toggle (full width, above the columns).
|
||||
panel:TopCheckbox("MetaHuntOptionsAnimMaster",
|
||||
MTH_ANIM_L("ANIM_ENABLE_ALL", "Enable animations"), {
|
||||
checked = MTH_FX_AnimationsEnabled,
|
||||
onClick = function()
|
||||
local cb = getglobal("MetaHuntOptionsAnimMaster")
|
||||
MTH_FX_SetAnimationsEnabled(cb:GetChecked())
|
||||
MTH_ANIM_UpdateChildStates()
|
||||
end,
|
||||
})
|
||||
|
||||
-- Keep the two columns balanced at standard Options-window sizes. The
|
||||
-- Interface section is intentionally paired with Pet instead of stacked
|
||||
-- below Leveling and Combat, where it would run below the window.
|
||||
local s = 1
|
||||
while MTH_FX_ANIM_SECTIONS and s <= table.getn(MTH_FX_ANIM_SECTIONS) do
|
||||
local sec = MTH_FX_ANIM_SECTIONS[s]
|
||||
|
||||
-- Collect this section's defs in order.
|
||||
local secDefs = {}
|
||||
local d = 1
|
||||
while MTH_FX_ANIM_DEFS and d <= table.getn(MTH_FX_ANIM_DEFS) do
|
||||
local def = MTH_FX_ANIM_DEFS[d]
|
||||
if def.section == sec.id then
|
||||
table.insert(secDefs, def)
|
||||
end
|
||||
d = d + 1
|
||||
end
|
||||
local count = table.getn(secDefs)
|
||||
|
||||
if count > 0 then
|
||||
local side = (sec.id == "pet" or sec.id == "ui") and "left" or "right"
|
||||
local box = panel:Box(side,
|
||||
"MetaHuntOptionsAnimBox_" .. tostring(sec.id),
|
||||
MTH_ANIM_L(sec.titleKey, sec.title))
|
||||
|
||||
local di = 1
|
||||
while di <= count do
|
||||
local def = secDefs[di]
|
||||
local cbName = "MetaHuntOptionsAnim_" .. string.gsub(def.key, "%.", "_")
|
||||
local cb = box:Checkbox(cbName, MTH_ANIM_L(def.labelKey, def.label), {
|
||||
checked = function() return MTH_FX_GetFlag(def.key) end,
|
||||
tooltip = (def.tooltip and def.tooltip ~= "") and MTH_ANIM_L(def.tooltipKey, def.tooltip) or nil,
|
||||
})
|
||||
if cb then
|
||||
cb.mthKey = def.key
|
||||
cb:SetScript("OnClick", function()
|
||||
MTH_FX_SetFlag(cb.mthKey, cb:GetChecked())
|
||||
end)
|
||||
table.insert(MTH_ANIM_CTRL, cb)
|
||||
end
|
||||
di = di + 1
|
||||
end
|
||||
end
|
||||
s = s + 1
|
||||
end
|
||||
|
||||
panel:Finish()
|
||||
MTH_ANIM_UpdateChildStates()
|
||||
end
|
||||
+124
-136
@@ -44,6 +44,8 @@ local MTH_AB_STATE = {
|
||||
},
|
||||
}
|
||||
|
||||
local MTH_AB_TRANSIENT_STORE = {}
|
||||
|
||||
local function MTH_AB_SetCheckedNoClick(check, checked)
|
||||
if not check then return end
|
||||
MTH_AB_STATE.syncing = true
|
||||
@@ -51,6 +53,14 @@ local function MTH_AB_SetCheckedNoClick(check, checked)
|
||||
MTH_AB_STATE.syncing = false
|
||||
end
|
||||
|
||||
local function MTH_AB_IsChecked(control)
|
||||
if not control or type(control.GetChecked) ~= "function" then
|
||||
return false
|
||||
end
|
||||
local checked = control:GetChecked()
|
||||
return checked == 1 or checked == true
|
||||
end
|
||||
|
||||
local function MTH_AB_GetEngine()
|
||||
return MTH_AutoBuyEngine
|
||||
end
|
||||
@@ -60,8 +70,7 @@ local function MTH_AB_GetStore()
|
||||
if engine and engine.EnsureDefaults then
|
||||
return engine:EnsureDefaults()
|
||||
end
|
||||
MTH_AutoBuy_Saved = MTH_AutoBuy_Saved or {}
|
||||
return MTH_AutoBuy_Saved
|
||||
return MTH_AB_TRANSIENT_STORE
|
||||
end
|
||||
|
||||
local function MTH_AB_NormalizeRule(rule)
|
||||
@@ -91,10 +100,10 @@ end
|
||||
|
||||
local function MTH_AB_EnsureConfig()
|
||||
local store = MTH_AB_GetStore()
|
||||
if store.enabled == nil then store.enabled = true end
|
||||
if store.enabled == nil then store.enabled = false 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.projectiles.enabled == nil then store.projectiles.enabled = false 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
|
||||
@@ -346,6 +355,15 @@ local function MTH_AB_GetBuyableItems(subtype)
|
||||
if type(MTH_AutoBuy_GetProjectileItems) == "function" then
|
||||
local result = MTH_AutoBuy_GetProjectileItems(subtype)
|
||||
if type(result) == "table" then
|
||||
for i = 1, table.getn(result) do
|
||||
local row = result[i]
|
||||
if type(row) == "table" then
|
||||
local itemId = tonumber(row.itemId)
|
||||
if itemId and MTH and MTH.GetLocalizedItemName then
|
||||
row.name = MTH:GetLocalizedItemName(itemId, row.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
end
|
||||
@@ -356,9 +374,13 @@ local function MTH_AB_GetBuyableItems(subtype)
|
||||
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
|
||||
local defaultName = tostring(row.name or ("Item " .. tostring(itemId)))
|
||||
local localizedName = (MTH and MTH.GetLocalizedItemName)
|
||||
and MTH:GetLocalizedItemName(itemId, defaultName)
|
||||
or defaultName
|
||||
table.insert(result, {
|
||||
itemId = tonumber(itemId) or itemId,
|
||||
name = tostring(row.name or ("Item " .. tostring(itemId))),
|
||||
name = tostring(localizedName),
|
||||
level = tonumber(row.level) or 0,
|
||||
reqlevel = tonumber(row.reqlevel) or 0,
|
||||
})
|
||||
@@ -496,7 +518,7 @@ local function MTH_AB_RefreshOptionsUI()
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
local moduleEnabled = true
|
||||
if MTH and MTH.IsModuleEnabled then
|
||||
moduleEnabled = MTH:IsModuleEnabled("autobuy", true) and true or false
|
||||
moduleEnabled = MTH:IsModuleEnabled("autobuy", false) and true or false
|
||||
end
|
||||
|
||||
MTH_AB_SetCheckedNoClick(MTH_AB_STATE.controls.moduleEnabled, moduleEnabled)
|
||||
@@ -567,7 +589,9 @@ local function MTH_AB_RefreshOptionsUI()
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_AB_CreateRow(container, subtype, index, xBase, yOffset)
|
||||
local function MTH_AB_CreateRow(box, subtype, index)
|
||||
local f, y = box:Row(38)
|
||||
local padX = box:PadX()
|
||||
local row = {
|
||||
subtype = subtype,
|
||||
index = index,
|
||||
@@ -575,14 +599,15 @@ local function MTH_AB_CreateRow(container, subtype, index, xBase, yOffset)
|
||||
}
|
||||
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.check = MTH_CreateCheckbox(f, nameBase .. "Check", "", y, padX)
|
||||
row.label = getglobal(nameBase .. "Label") or f:CreateFontString(nameBase .. "Label", "ARTWORK", "GameFontNormalSmall")
|
||||
row.label:ClearAllPoints()
|
||||
row.label:SetPoint("TOPLEFT", f, "TOPLEFT", padX + 27, y - 5)
|
||||
row.label:SetText("On?")
|
||||
|
||||
row.dropdown = CreateFrame("Frame", nameBase .. "Drop", container, "UIDropDownMenuTemplate")
|
||||
row.dropdown = getglobal(nameBase .. "Drop") or CreateFrame("Frame", nameBase .. "Drop", f, "UIDropDownMenuTemplate")
|
||||
row.dropdown:ClearAllPoints()
|
||||
row.dropdown:SetPoint("TOPLEFT", container, "TOPLEFT", xBase + MTH_AB_LAYOUT.ROW_DROPDOWN_X, yOffset + 3)
|
||||
row.dropdown:SetPoint("TOPLEFT", f, "TOPLEFT", padX + 33, y + 3)
|
||||
if UIDropDownMenu_SetWidth then
|
||||
UIDropDownMenu_SetWidth(MTH_AB_LAYOUT.ROW_DROPDOWN_WIDTH, row.dropdown)
|
||||
end
|
||||
@@ -618,7 +643,8 @@ local function MTH_AB_CreateRow(container, subtype, index, xBase, yOffset)
|
||||
end)
|
||||
end
|
||||
|
||||
row.qty = CreateFrame("EditBox", nameBase .. "Qty", container, "InputBoxTemplate")
|
||||
row.qty = getglobal(nameBase .. "Qty") or CreateFrame("EditBox", nameBase .. "Qty", f, "InputBoxTemplate")
|
||||
row.qty:ClearAllPoints()
|
||||
local dropButton = getglobal(nameBase .. "DropButton")
|
||||
if dropButton then
|
||||
row.qty:SetPoint("LEFT", dropButton, "RIGHT", MTH_AB_LAYOUT.ROW_QTY_GAP, -1)
|
||||
@@ -630,33 +656,31 @@ local function MTH_AB_CreateRow(container, subtype, index, xBase, yOffset)
|
||||
row.qty:SetNumeric(true)
|
||||
row.qty:SetAutoFocus(false)
|
||||
|
||||
row.suffix = container:CreateFontString(nameBase .. "Suffix", "ARTWORK", "GameFontNormalSmall")
|
||||
row.suffix = getglobal(nameBase .. "Suffix") or f:CreateFontString(nameBase .. "Suffix", "ARTWORK", "GameFontNormalSmall")
|
||||
row.suffix:ClearAllPoints()
|
||||
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
|
||||
row.check:SetScript("OnClick", function()
|
||||
if not this 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_SetRuleEnabled(store, subtype, index, MTH_AB_IsChecked(this))
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
end
|
||||
|
||||
row.qty:SetScript("OnEnterPressed", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
row.qty:SetScript("OnEnterPressed", function()
|
||||
if not this then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
MTH_AB_SetRuleStacks(store, subtype, index, self:GetText())
|
||||
MTH_AB_SetRuleStacks(store, subtype, index, this:GetText())
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
self:ClearFocus()
|
||||
this:ClearFocus()
|
||||
end)
|
||||
row.qty:SetScript("OnEditFocusLost", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
row.qty:SetScript("OnEditFocusLost", function()
|
||||
if not this then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
MTH_AB_SetRuleStacks(store, subtype, index, self:GetText())
|
||||
MTH_AB_SetRuleStacks(store, subtype, index, this:GetText())
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
|
||||
@@ -668,167 +692,131 @@ local function MTH_AB_BuildUI(container)
|
||||
return
|
||||
end
|
||||
|
||||
MTH_ClearContainer(container)
|
||||
local panel = MTH_Layout.Panel(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
|
||||
panel:Title("Auto Buy")
|
||||
panel:Note("Configure automatic buying rules that trigger when you open a vendor. Quantity is how many STACKS of the item you want in your bags after purchase.",
|
||||
{ lines = 2, color = MTH_Layout.Theme.colText })
|
||||
|
||||
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
|
||||
controls.moduleEnabled = panel:TopCheckbox("MetaHuntOptionsAutoBuyModuleEnabled", "Enable Auto Buy module", {
|
||||
onClick = function()
|
||||
if not this 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)
|
||||
local ok, err = MTH:SetModuleEnabled("autobuy", MTH_AB_IsChecked(this))
|
||||
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
|
||||
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
|
||||
controls.projectilesEnabled = panel:TopCheckbox("MetaHuntOptionsAutoBuyProjectilesEnabled", "Enable for Projectiles (arrows & bullets)", {
|
||||
onClick = function()
|
||||
if not this or MTH_AB_STATE.syncing then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
store.projectiles.enabled = self:GetChecked() and true or false
|
||||
store.projectiles.enabled = MTH_AB_IsChecked(this)
|
||||
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
|
||||
end,
|
||||
})
|
||||
|
||||
-- ===== LEFT: Arrows / RIGHT: Bullets (3 rules each) =====
|
||||
local arrowBox = panel:Box("left", "MetaHuntOptionsAutoBuyArrowsBox", "Arrows")
|
||||
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)
|
||||
controls.rows.arrow[i] = MTH_AB_CreateRow(arrowBox, "arrow", i)
|
||||
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")
|
||||
local bulletBox = panel:Box("right", "MetaHuntOptionsAutoBuyBulletsBox", "Bullets")
|
||||
for i = 1, 3 do
|
||||
controls.rows.bullet[i] = MTH_AB_CreateRow(bulletBox, "bullet", i)
|
||||
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
|
||||
-- ===== Pet Food =====
|
||||
local petBox = panel:Box(panel:ShorterSide(), "MetaHuntOptionsAutoBuyPetFoodBox", "Pet Food")
|
||||
|
||||
controls.petFoodEnabled = petBox:Checkbox("MetaHuntOptionsAutoBuyPetFoodEnabled", "Enable for Pet Food", {
|
||||
onClick = function()
|
||||
if not this or MTH_AB_STATE.syncing then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
store.petFood.enabled = self:GetChecked() and true or false
|
||||
store.petFood.enabled = MTH_AB_IsChecked(this)
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
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")
|
||||
controls.petFoodQty = petBox:EditRow("MetaHuntOptionsAutoBuyPetFoodQtyLabel", "Quantity:",
|
||||
"MetaHuntOptionsAutoBuyPetFoodQty", 40, { numeric = true, height = 20 })
|
||||
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
|
||||
controls.petFoodQty:SetScript("OnEnterPressed", function()
|
||||
if not this then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
MTH_AB_SetPetFoodStacks(store, self:GetText())
|
||||
MTH_AB_SetPetFoodStacks(store, this:GetText())
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
self:ClearFocus()
|
||||
this:ClearFocus()
|
||||
end)
|
||||
controls.petFoodQty:SetScript("OnEditFocusLost", function(self)
|
||||
self = self or this
|
||||
if not self then return end
|
||||
controls.petFoodQty:SetScript("OnEditFocusLost", function()
|
||||
if not this then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
MTH_AB_SetPetFoodStacks(store, self:GetText())
|
||||
MTH_AB_SetPetFoodStacks(store, this:GetText())
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
local suffix = getglobal("MetaHuntOptionsAutoBuyPetFoodQtySuffix")
|
||||
if not suffix then
|
||||
suffix = petBox:Frame():CreateFontString("MetaHuntOptionsAutoBuyPetFoodQtySuffix", "ARTWORK", "GameFontNormalSmall")
|
||||
end
|
||||
suffix:ClearAllPoints()
|
||||
suffix:SetPoint("LEFT", controls.petFoodQty, "RIGHT", 8, 0)
|
||||
suffix:SetText("stacks")
|
||||
controls.petFoodQtySuffix = suffix
|
||||
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
|
||||
controls.petFoodScopeCurrent = petBox:Checkbox("MetaHuntOptionsAutoBuyPetFoodScopeCurrent", "Buy only for my current pet", {
|
||||
onClick = function()
|
||||
if not this or MTH_AB_STATE.syncing then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
store.petFood.scope = "current"
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
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
|
||||
controls.petFoodScopeAll = petBox:Checkbox("MetaHuntOptionsAutoBuyPetFoodScopeAll", "Buy for all my pets", {
|
||||
onClick = function()
|
||||
if not this or MTH_AB_STATE.syncing then return end
|
||||
local store = MTH_AB_EnsureConfig()
|
||||
store.petFood.scope = "all"
|
||||
MTH_AB_RefreshOptionsUI()
|
||||
end)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
controls.petFoodPetLines = {}
|
||||
for i = 1, 5 do
|
||||
local rowY = -434 - ((i - 1) * 22)
|
||||
local icon = container:CreateTexture("MetaHuntOptionsAutoBuyPetFoodPetIcon" .. tostring(i), "ARTWORK")
|
||||
local f, y = petBox:Row(22)
|
||||
local padX = petBox:PadX()
|
||||
local icon = getglobal("MetaHuntOptionsAutoBuyPetFoodPetIcon" .. tostring(i))
|
||||
if not icon then
|
||||
icon = f:CreateTexture("MetaHuntOptionsAutoBuyPetFoodPetIcon" .. tostring(i), "ARTWORK")
|
||||
end
|
||||
icon:SetWidth(16)
|
||||
icon:SetHeight(16)
|
||||
icon:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X + 28, rowY)
|
||||
icon:ClearAllPoints()
|
||||
icon:SetPoint("TOPLEFT", f, "TOPLEFT", padX + 4, y)
|
||||
icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
|
||||
local text = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodPetText" .. tostring(i), "ARTWORK", "GameFontNormalSmall")
|
||||
local text = getglobal("MetaHuntOptionsAutoBuyPetFoodPetText" .. tostring(i))
|
||||
if not text then
|
||||
text = f:CreateFontString("MetaHuntOptionsAutoBuyPetFoodPetText" .. tostring(i), "ARTWORK", "GameFontNormalSmall")
|
||||
end
|
||||
text:ClearAllPoints()
|
||||
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
|
||||
|
||||
panel:Finish()
|
||||
end
|
||||
|
||||
function MTH_SetupAutoBuyOptions()
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
local MTH_AQ_READY = MTH_OptionsRequire and MTH_OptionsRequire("options-autoquest", {
|
||||
"MTH_GetFrame",
|
||||
"MTH_ClearContainer",
|
||||
"MTH_CreateCheckbox",
|
||||
})
|
||||
|
||||
local function MTH_AQ_IsChecked(control)
|
||||
if not control or type(control.GetChecked) ~= "function" then
|
||||
return false
|
||||
end
|
||||
local checked = control:GetChecked()
|
||||
return checked == 1 or checked == true
|
||||
end
|
||||
|
||||
local function MTH_AQ_EnsureStore()
|
||||
if type(MTH_CharSavedVariables) ~= "table" then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
local store = nil
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
store = MTH:GetModuleCharSavedVariables("autoquest")
|
||||
end
|
||||
if type(store) ~= "table" then
|
||||
if type(MTH_CharSavedVariables.modules) ~= "table" then
|
||||
MTH_CharSavedVariables.modules = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.modules.autoquest) ~= "table" then
|
||||
MTH_CharSavedVariables.modules.autoquest = {}
|
||||
end
|
||||
store = MTH_CharSavedVariables.modules.autoquest
|
||||
end
|
||||
if type(store.settings) ~= "table" then
|
||||
store.settings = {}
|
||||
end
|
||||
local settings = store.settings
|
||||
if settings.scorpokDrazial == nil then
|
||||
settings.scorpokDrazial = false
|
||||
end
|
||||
if settings.arrowsForSissies == nil then
|
||||
settings.arrowsForSissies = false
|
||||
end
|
||||
return settings
|
||||
end
|
||||
|
||||
function MTH_SetupAutoQuestOptions()
|
||||
if not MTH_AQ_READY then return end
|
||||
|
||||
local container = MTH_GetFrame("MetaHuntOptionsAutoQuest")
|
||||
if not container then return end
|
||||
|
||||
local store = MTH_AQ_EnsureStore()
|
||||
if store.scorpokTooltip == nil then
|
||||
store.scorpokTooltip = false
|
||||
end
|
||||
|
||||
local moduleEnabled = true
|
||||
if MTH and MTH.GetModule then
|
||||
local module = MTH:GetModule("autoquest")
|
||||
if type(module) == "table" and module.enabled ~= nil then
|
||||
moduleEnabled = module.enabled and true or false
|
||||
elseif MTH and MTH.IsModuleEnabled then
|
||||
moduleEnabled = MTH:IsModuleEnabled("autoquest", false) and true or false
|
||||
end
|
||||
elseif MTH and MTH.IsModuleEnabled then
|
||||
moduleEnabled = MTH:IsModuleEnabled("autoquest", false) and true or false
|
||||
end
|
||||
|
||||
local panel = MTH_Layout.Panel(container)
|
||||
panel:Title("Auto Quest")
|
||||
panel:Note("Automation for specific quest interactions.")
|
||||
|
||||
panel:TopCheckbox("MetaHuntOptionsAutoQuestModuleToggle", "Enable Auto Quest module", {
|
||||
checked = moduleEnabled,
|
||||
onClick = function()
|
||||
if not this then return end
|
||||
local enabled = MTH_AQ_IsChecked(this)
|
||||
if MTH and MTH.SetModuleEnabled then
|
||||
local ok, err = MTH:SetModuleEnabled("autoquest", enabled)
|
||||
if not ok and MTH and MTH.Print then
|
||||
MTH:Print("Failed to change Auto Quest module state: " .. tostring(err), "error")
|
||||
end
|
||||
end
|
||||
local module = MTH and MTH.GetModule and MTH:GetModule("autoquest") or nil
|
||||
local actual = (type(module) == "table" and module.enabled ~= nil) and (module.enabled and true or false) or enabled
|
||||
this:SetChecked(actual and true or false)
|
||||
end,
|
||||
})
|
||||
|
||||
-- Salt of the Scorpok (left column)
|
||||
local scorpok = panel:Box("left", "MetaHuntAutoQuestScorpokBox", "Salt of the Scorpok")
|
||||
scorpok:Checkbox("MetaHuntOptionsAutoQuestScorpokToggle",
|
||||
"Enable SHIFT-rightclick for Bloodmage Drazial", {
|
||||
checked = store.scorpokDrazial and true or false,
|
||||
onClick = function()
|
||||
local enabled = MTH_AQ_IsChecked(this)
|
||||
store.scorpokDrazial = enabled and true or false
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
local moduleStore = MTH:GetModuleCharSavedVariables("autoquest")
|
||||
if type(moduleStore) == "table" then
|
||||
moduleStore.scorpokDrazial = store.scorpokDrazial and true or false
|
||||
end
|
||||
end
|
||||
local module = MTH and MTH.GetModule and MTH:GetModule("autoquest")
|
||||
if module and module.SetScorpokDrazialEnabled then
|
||||
module:SetScorpokDrazialEnabled(enabled)
|
||||
end
|
||||
end,
|
||||
})
|
||||
scorpok:Checkbox("MetaHuntOptionsAutoQuestScorpokTooltipToggle",
|
||||
"Enhance Tooltip on Drazial and related beasts (requires Tooltips module enabled)", {
|
||||
checked = store.scorpokTooltip and true or false,
|
||||
onClick = function()
|
||||
local enabled = MTH_AQ_IsChecked(this)
|
||||
store.scorpokTooltip = enabled and true or false
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
local moduleStore = MTH:GetModuleCharSavedVariables("autoquest")
|
||||
if type(moduleStore) == "table" then
|
||||
moduleStore.scorpokTooltip = store.scorpokTooltip and true or false
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Arrows Are For Sissies (right column)
|
||||
local sissies = panel:Box("right", "MetaHuntAutoQuestSissiesBox", "Arrows Are For Sissies")
|
||||
sissies:Note("MetaHuntOptionsAutoQuestSissiesNote",
|
||||
"Note that you should not activate this if you are using LazyPig", { lines = 2 })
|
||||
sissies:Checkbox("MetaHuntOptionsAutoQuestSissiesToggle",
|
||||
"Enable SHIFT-rightclick for Artilleryman Sheldonore", {
|
||||
checked = store.arrowsForSissies and true or false,
|
||||
onClick = function()
|
||||
local enabled = MTH_AQ_IsChecked(this)
|
||||
store.arrowsForSissies = enabled and true or false
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
local moduleStore = MTH:GetModuleCharSavedVariables("autoquest")
|
||||
if type(moduleStore) == "table" then
|
||||
moduleStore.arrowsForSissies = store.arrowsForSissies and true or false
|
||||
end
|
||||
end
|
||||
local module = MTH and MTH.GetModule and MTH:GetModule("autoquest")
|
||||
if module and module.SetArrowsForSissiesEnabled then
|
||||
module:SetArrowsForSissiesEnabled(enabled)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
panel:Finish()
|
||||
end
|
||||
+220
-183
@@ -98,11 +98,20 @@ local function MTH_CHRON_CopyTable(source)
|
||||
end
|
||||
|
||||
local function MTH_CHRON_GetProfile()
|
||||
if not (MTH and MTH.GetModuleSavedVariables) then
|
||||
if not (MTH and MTH.GetModuleCharSavedVariables) then
|
||||
return MTH_CHRON_CopyTable(MTH_CHRON_DEFAULTS)
|
||||
end
|
||||
|
||||
local store = MTH:GetModuleSavedVariables("chronometer")
|
||||
local store = MTH:GetModuleCharSavedVariables("chronometer")
|
||||
if type(store) ~= "table" then
|
||||
return MTH_CHRON_CopyTable(MTH_CHRON_DEFAULTS)
|
||||
end
|
||||
if type(store.profile) ~= "table" and MTH.GetModuleSavedVariables then
|
||||
local accountStore = MTH:GetModuleSavedVariables("chronometer")
|
||||
if type(accountStore) == "table" and type(accountStore.profile) == "table" then
|
||||
store.profile = MTH_CHRON_CopyTable(accountStore.profile)
|
||||
end
|
||||
end
|
||||
if type(store.profile) ~= "table" then
|
||||
store.profile = {}
|
||||
end
|
||||
@@ -243,8 +252,14 @@ local function MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end
|
||||
|
||||
engine.profile = profile
|
||||
if engine.SetCandyBarGroupGrowth then
|
||||
engine:SetCandyBarGroupGrowth("MTHChronometer", profile.growup and true or false)
|
||||
if engine.SetCandyBarGroupGrowth and engine.SetCandyBarGroupPoint and engine.anchor then
|
||||
local growup = profile.growup and true or false
|
||||
if growup then
|
||||
engine:SetCandyBarGroupPoint("MTHChronometer", "BOTTOM", engine.anchor, "TOP", 0, 0)
|
||||
else
|
||||
engine:SetCandyBarGroupPoint("MTHChronometer", "TOP", engine.anchor, "BOTTOM", 0, 0)
|
||||
end
|
||||
engine:SetCandyBarGroupGrowth("MTHChronometer", growup)
|
||||
end
|
||||
if engine.SetCandyBarGroupVerticalSpacing then
|
||||
engine:SetCandyBarGroupVerticalSpacing("MTHChronometer", profile.spacing or 0)
|
||||
@@ -298,6 +313,14 @@ local function MTH_CHRON_MakeSmall(parent, name, text, x, y)
|
||||
return label
|
||||
end
|
||||
|
||||
local function MTH_CHRON_IsChecked(control)
|
||||
if not control or type(control.GetChecked) ~= "function" then
|
||||
return false
|
||||
end
|
||||
local checked = control:GetChecked()
|
||||
return checked == 1 or checked == true
|
||||
end
|
||||
|
||||
|
||||
local function MTH_CHRON_MakeInput(parent, name, x, y, width, height, value, onAccept)
|
||||
local edit = CreateFrame("EditBox", name, parent, "InputBoxTemplate")
|
||||
@@ -309,13 +332,12 @@ local function MTH_CHRON_MakeInput(parent, name, x, y, width, height, value, onA
|
||||
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
|
||||
edit:SetScript("OnEnterPressed", function()
|
||||
if not this then return end
|
||||
if type(onAccept) == "function" then
|
||||
onAccept(self:GetText() or "")
|
||||
onAccept(this:GetText() or "")
|
||||
end
|
||||
self:ClearFocus()
|
||||
this:ClearFocus()
|
||||
end)
|
||||
return edit
|
||||
end
|
||||
@@ -336,41 +358,79 @@ local function MTH_CHRON_NormalizeSection(tabKey)
|
||||
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
|
||||
|
||||
local function MTH_CHRON_RenderTimerBox(box, profile, items, bucket, prefix)
|
||||
prefix = tostring(prefix or "Main")
|
||||
if table.getn(items) == 0 then
|
||||
MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerSectionEmpty", "(none)", timerX + 8, timerY)
|
||||
box:Text("MetaHuntOptionsChronometerSectionEmpty" .. prefix, "(none)")
|
||||
return
|
||||
end
|
||||
|
||||
for i = 1, table.getn(items) do
|
||||
if timerY < -560 then
|
||||
MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerOverflow", "...more", timerX + 8, timerY)
|
||||
break
|
||||
local row = items[i]
|
||||
local timerName = row
|
||||
local label = row
|
||||
if type(row) == "table" then
|
||||
timerName = row.key or row.timer or row.name
|
||||
label = row.label or row.name or row.key
|
||||
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
|
||||
timerName = tostring(timerName or "")
|
||||
label = tostring(label or timerName)
|
||||
local controlName = "MetaHuntOptionsChronometerTimer" .. prefix .. tostring(bucket) .. tostring(i)
|
||||
box:Checkbox(controlName, label, {
|
||||
checked = profile.disabledSpells[bucket][timerName] == nil,
|
||||
onClick = function()
|
||||
local currentlyDisabled = profile.disabledSpells[bucket][timerName] ~= nil
|
||||
if currentlyDisabled then
|
||||
profile.disabledSpells[bucket][timerName] = nil
|
||||
this:SetChecked(true)
|
||||
else
|
||||
profile.disabledSpells[bucket][timerName] = {}
|
||||
this:SetChecked(nil)
|
||||
end
|
||||
end)
|
||||
end
|
||||
timerY = timerY - 20
|
||||
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local MTH_CHRON_HUNTER_TRINKETS = {
|
||||
{ labelToken = "Devilsaur Eye", timerToken = "Devilsaur Fury" },
|
||||
{ labelToken = "Zandalarian Hero Medallion", timerToken = "Restless Strength" },
|
||||
{ labelToken = "Earthstrike", timerToken = "Earthstrike" },
|
||||
{ labelToken = "Badge of the Swarmguard", timerToken = "Badge of the Swarmguard" },
|
||||
{ labelToken = "Jom Gabbar", timerToken = "Jom Gabbar" },
|
||||
{ labelToken = "Kiss of the Spider", timerToken = "Kiss of the Spider" },
|
||||
{ labelToken = "Slayer's Crest", timerToken = "Slayer's Crest" },
|
||||
}
|
||||
|
||||
local function MTH_CHRON_LocalizeTimerToken(token)
|
||||
if MTH and MTH.LocalizeSpell then
|
||||
return tostring(MTH:LocalizeSpell(token) or token)
|
||||
end
|
||||
return tostring(token)
|
||||
end
|
||||
|
||||
local function MTH_CHRON_BuildHunterTrinketItems(classEvents)
|
||||
local eventSet = {}
|
||||
for i = 1, table.getn(classEvents or {}) do
|
||||
eventSet[tostring(classEvents[i])] = true
|
||||
end
|
||||
|
||||
local result = {}
|
||||
for i = 1, table.getn(MTH_CHRON_HUNTER_TRINKETS) do
|
||||
local def = MTH_CHRON_HUNTER_TRINKETS[i]
|
||||
local key = MTH_CHRON_LocalizeTimerToken(def.timerToken)
|
||||
if eventSet[key] then
|
||||
table.insert(result, {
|
||||
key = key,
|
||||
label = MTH_CHRON_LocalizeTimerToken(def.labelToken),
|
||||
})
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function MTH_RefreshChronometerOptions(tabKey)
|
||||
if not MTH_CHRON_IsReady() then return end
|
||||
MTH_SetupChronometerOptions(tabKey)
|
||||
@@ -387,208 +447,185 @@ function MTH_SetupChronometerOptions(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 panel = MTH_Layout.Panel(container)
|
||||
panel:Title("Chronometer")
|
||||
|
||||
local moduleCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerEnabled", "Enable Chronometer module", -54)
|
||||
if moduleCheck then
|
||||
local moduleEnabled = false
|
||||
do
|
||||
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
|
||||
moduleEnabled = module and module.enabled and true or false
|
||||
end
|
||||
panel:TopCheckbox("MetaHuntOptionsChronometerEnabled", "Enable Chronometer module", {
|
||||
checked = moduleEnabled,
|
||||
onClick = function()
|
||||
local enabled = this:GetChecked() == 1
|
||||
if MTH and MTH.SetModuleEnabled then
|
||||
MTH:SetModuleEnabled("chronometer", enabled)
|
||||
end
|
||||
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)
|
||||
local genBox = panel:Box("left", "MetaHuntOptionsChronometerGeneralBox", "General")
|
||||
genBox:Slider("MetaHuntOptionsChronometerGhost", {
|
||||
label = "Ghost Duration", min = 0, max = 30, step = 1,
|
||||
value = tonumber(profile.ghost) or 0,
|
||||
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
|
||||
end,
|
||||
})
|
||||
genBox:Checkbox("MetaHuntOptionsChronometerFadeOnKill", "Fade on kill", {
|
||||
checked = profile.fadeonkill and true or false,
|
||||
onClick = function() profile.fadeonkill = this:GetChecked() == 1 end,
|
||||
})
|
||||
genBox:Checkbox("MetaHuntOptionsChronometerFadeOnFade", "Fade on aura fade", {
|
||||
checked = profile.fadeonfade and true or false,
|
||||
onClick = function() profile.fadeonfade = this:GetChecked() == 1 end,
|
||||
})
|
||||
genBox:Checkbox("MetaHuntOptionsChronometerSelfBars", "Show self bars", {
|
||||
checked = profile.selfbars and true or false,
|
||||
onClick = function() profile.selfbars = this:GetChecked() == 1 end,
|
||||
})
|
||||
genBox:Checkbox("MetaHuntOptionsChronometerOnlySelf", "Only self target", {
|
||||
checked = profile.onlyself and true or false,
|
||||
onClick = function() profile.onlyself = this:GetChecked() == 1 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 ctrlBox = panel:Box("left", "MetaHuntOptionsChronometerControlsBox", "Controls")
|
||||
ctrlBox:Buttons({
|
||||
{ name = "MetaHuntOptionsChronometerRunTest", label = "Run Test", width = 120,
|
||||
onClick = function()
|
||||
local engine = MTH_ChronometerHunter
|
||||
if engine and engine.RunTest then engine:RunTest() end
|
||||
end },
|
||||
{ name = "MetaHuntOptionsChronometerAnchor", label = "Toggle Anchor", width = 120,
|
||||
onClick = function()
|
||||
local engine = MTH_ChronometerHunter
|
||||
if engine and engine.ToggleAnchor then engine:ToggleAnchor() end
|
||||
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)
|
||||
ctrlBox:Note("MetaHuntOptionsChronometerAnchorPos",
|
||||
"Anchor: X " .. tostring(anchorX) .. " Y " .. tostring(anchorY))
|
||||
|
||||
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)
|
||||
local dimBox = panel:Box("left", "MetaHuntOptionsChronometerDimBox", "Dimensions")
|
||||
dimBox:Slider("MetaHuntOptionsChronometerBarWidth", {
|
||||
label = "Bar Width", min = 80, max = 320, step = 1,
|
||||
value = tonumber(profile.barwidth) or 220,
|
||||
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)
|
||||
end,
|
||||
})
|
||||
dimBox:Slider("MetaHuntOptionsChronometerBarScale", {
|
||||
label = "Bar Scale", min = 0.5, max = 1.5, step = 0.1,
|
||||
value = tonumber(profile.barscale) or 1,
|
||||
format = function(v) return string.format("%.1f", v) end,
|
||||
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)
|
||||
end,
|
||||
})
|
||||
dimBox:Slider("MetaHuntOptionsChronometerBarHeight", {
|
||||
label = "Bar Height", min = 8, max = 30, step = 1,
|
||||
value = tonumber(profile.barheight) or 16,
|
||||
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)
|
||||
end,
|
||||
})
|
||||
dimBox:Slider("MetaHuntOptionsChronometerBarSpacing", {
|
||||
label = "Bar Spacing", min = 0, max = 15, step = 1,
|
||||
value = tonumber(profile.spacing) or 0,
|
||||
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)
|
||||
end,
|
||||
})
|
||||
dimBox:Slider("MetaHuntOptionsChronometerTextSize", {
|
||||
label = "Text Size", min = 8, max = 20, step = 1,
|
||||
value = tonumber(profile.textsize) or 10,
|
||||
onChange = function(value)
|
||||
profile.textsize = tonumber(value) or 10
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end
|
||||
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
|
||||
local textBox = panel:Box("right", "MetaHuntOptionsChronometerTextBox", "Text & Growth")
|
||||
textBox:Note("MetaHuntOptionsChronometerTextPatternHint",
|
||||
"Bar text tokens: $s = spell, $t = target", { lines = 2 })
|
||||
local patternEdit = textBox:EditRow("MetaHuntOptionsChronometerTextPatternLabel", "Bar Text",
|
||||
"MetaHuntOptionsChronometerTextPattern", 150, { height = 20 })
|
||||
if patternEdit then
|
||||
patternEdit:SetText(tostring(profile.text or "$t"))
|
||||
patternEdit:SetScript("OnEnterPressed", function()
|
||||
local newText = this:GetText() or ""
|
||||
if newText == "" then newText = "$t" end
|
||||
profile.text = tostring(newText)
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
this:ClearFocus()
|
||||
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
|
||||
textBox:Checkbox("MetaHuntOptionsChronometerGrowUp", "Grow bars upward", {
|
||||
checked = profile.growup and true or false,
|
||||
onClick = function()
|
||||
profile.growup = this:GetChecked() == 1
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end)
|
||||
end
|
||||
end,
|
||||
})
|
||||
textBox:Checkbox("MetaHuntOptionsChronometerReverse", "Reverse bars", {
|
||||
checked = profile.reverse and true or false,
|
||||
onClick = function()
|
||||
profile.reverse = this:GetChecked() == 1
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
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()
|
||||
local iconBox = panel:Box("right", "MetaHuntOptionsChronometerIconBox", "Icon Position")
|
||||
local iconLeft, iconRight
|
||||
iconLeft = iconBox:Checkbox("MetaHuntOptionsChronometerIconLeft", "Left", {
|
||||
checked = profile.iconposition ~= "RIGHT",
|
||||
onClick = function()
|
||||
profile.iconposition = "LEFT"
|
||||
iconLeft:SetChecked(true)
|
||||
iconRight:SetChecked(nil)
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end)
|
||||
iconRight:SetScript("OnClick", function()
|
||||
end,
|
||||
})
|
||||
iconRight = iconBox:Checkbox("MetaHuntOptionsChronometerIconRight", "Right", {
|
||||
checked = profile.iconposition == "RIGHT",
|
||||
onClick = function()
|
||||
profile.iconposition = "RIGHT"
|
||||
iconRight:SetChecked(true)
|
||||
iconLeft:SetChecked(nil)
|
||||
MTH_CHRON_ApplyLiveProfile(profile)
|
||||
end)
|
||||
end
|
||||
|
||||
end,
|
||||
})
|
||||
else
|
||||
local classSpells, classEvents, racial = MTH_CHRON_BuildTimerLists()
|
||||
if section == "classspells" then
|
||||
MTH_CHRON_RenderTimerSection(container, profile, "Hunter Spells", classSpells, class)
|
||||
local spellsBox = panel:Box("left", "MetaHuntOptionsChronometerSpellsBox", "Hunter Spells")
|
||||
MTH_CHRON_RenderTimerBox(spellsBox, profile, classSpells, class, "ClassSpells")
|
||||
local trinketBox = panel:Box("right", "MetaHuntOptionsChronometerTrinketsBox", "Hunter Trinkets")
|
||||
MTH_CHRON_RenderTimerBox(trinketBox, profile, MTH_CHRON_BuildHunterTrinketItems(classEvents), class, "ClassTrinkets")
|
||||
elseif section == "classevents" then
|
||||
MTH_CHRON_RenderTimerSection(container, profile, "Hunter Events", classEvents, class)
|
||||
local eventsBox = panel:Box("left", "MetaHuntOptionsChronometerEventsBox", "Hunter Events")
|
||||
MTH_CHRON_RenderTimerBox(eventsBox, profile, classEvents, class, "ClassEvents")
|
||||
elseif section == "racial" then
|
||||
MTH_CHRON_RenderTimerSection(container, profile, "Racial", racial, "RACIAL")
|
||||
local racialBox = panel:Box("left", "MetaHuntOptionsChronometerRacialBox", "Racial")
|
||||
MTH_CHRON_RenderTimerBox(racialBox, profile, racial, "RACIAL", "Racial")
|
||||
end
|
||||
end
|
||||
|
||||
panel:Finish()
|
||||
end
|
||||
|
||||
if _G then
|
||||
|
||||
+35
-34
@@ -89,12 +89,8 @@ function MTH_SetupCreditsOptions()
|
||||
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 panel = MTH_Layout.Panel(container)
|
||||
local title = panel:Title("Credits")
|
||||
|
||||
local scrollFrame = MTH_GetFrame("MetaHuntOptionsCreditsScroll")
|
||||
if not scrollFrame then
|
||||
@@ -115,16 +111,25 @@ function MTH_SetupCreditsOptions()
|
||||
|
||||
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:",
|
||||
"This addon is, among others, a compilation of old Vanilla addons that I loved and played with for years. But most of them had become very buggy on Turtle WoW as new content was added to the server.\nI fixed, reworked, enhanced, and melted them within a modern modular framework that I created.\n"
|
||||
.. "I want to make clear what work is mine, 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.",
|
||||
"- MetaHunt animations are powered by the great PizzaSauce library of Pizzahawaii! Thank you man.",
|
||||
"GameFontNormalSmall", -10)
|
||||
cursor = MTH_CreditsCreateLink(content,
|
||||
"https://codeberg.org/Pizzahawaii/",
|
||||
"https://codeberg.org/Pizzahawaii/",
|
||||
cursor,
|
||||
-4)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- zBars, AntiDaze, and AutoStrip were core functionalities of the Vanilla zHunterMod addon. I kept the \"z\" naming of bars/buttons to always remember it. I improved it and added zBar, zAmmo, zCompanions, zMount, zToys, and zCraft.\nAlso, the SmartAmmo feature idea comes from zHunterMod: there was a file with that functionality, but it was a work in progress, totally non-functional 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.",
|
||||
"- Feed-o-Matic was entirely the work of Fizzwidget. I made it work for Turtle WoW by melting it into a module that calls the MetaHunt core to get the 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",
|
||||
@@ -133,7 +138,7 @@ function MTH_SetupCreditsOptions()
|
||||
-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.",
|
||||
"- The Tooltips module is based on Hunter Helper, another Vanilla addon by 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 into MetaHunt-controlled event handlers, can work with Turtle WoW data, and is now usable for things other than Beasts.",
|
||||
"GameFontNormalSmall", -10)
|
||||
cursor = MTH_CreditsCreateLink(content,
|
||||
"https://legacy-wow.com/vanilla-addons/fizzwidget-hunter-helper/",
|
||||
@@ -142,7 +147,7 @@ function MTH_SetupCreditsOptions()
|
||||
-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.",
|
||||
"- Chronometer was an old and very popular Vanilla addon working with ACE2 libraries. It has had countless iterations by various people over the years, and I can't credit them all here. The current version used by MetaHunt is based on this Turtle WoW conversion made by \"wigan91\". I reworked it to be Hunter-focused only: removed the non-Hunter/racial stuff, improved incorrect spell/effect configuration definitions, added missing Hunter spells (not many), and implemented my own bar color scheme that fits Hunter spells better.",
|
||||
"GameFontNormalSmall", -10)
|
||||
cursor = MTH_CreditsCreateLink(content,
|
||||
"https://github.com/wigan91/Chronometer-TWoW",
|
||||
@@ -150,44 +155,40 @@ function MTH_SetupCreditsOptions()
|
||||
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.",
|
||||
"- ICU was an old Vanilla addon. I used the latest revision below and added many more configuration options to it.",
|
||||
"GameFontNormalSmall", -10)
|
||||
cursor = MTH_CreditsCreateLink(content,
|
||||
"https://github.com/wigan91/ICU-TWoW",
|
||||
"https://github.com/wigan91/ICU-TWoW",
|
||||
cursor,
|
||||
-4)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- The Map/Marker system is from pfQuest by Master Shagu. I mostly left it untouched and only made slight modifications so it can integrate better within MetaHunt and does not 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.",
|
||||
"- The system allowing item links to be fetched in-game, notably in the Hunter Book Weapon tab, is from Atlas Turtle WoW.",
|
||||
"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:",
|
||||
"MetaHunt ships with its own Turtle WoW datastores, limited to Hunter-related information, and most of this data is up to date for Turtle WoW 1.18.1. The beast information comes directly from an extract of the live Turtle WoW database, made for MetaHunt by the Turtle WoW dev lead in March 2026.\n\n"
|
||||
.. "Other 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.",
|
||||
"- Atlas Turtle WoW for ranged weapon sources\n\n"
|
||||
.. "- Turtle WoW Wiki for pet diets (which was incomplete, missing Serpents and Foxes)",
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- This invaluable and very accurate spreadsheet for beasts having pet abilities. A big thanks to all twow players that crafted this! Now you dont need that sheet anymore, you have Metahunt! And the Great Book of Huntards is awesome and accurate also because of YOU.",
|
||||
"GameFontNormalSmall", -10)
|
||||
cursor = MTH_CreditsCreateLink(content,
|
||||
"https://docs.google.com/spreadsheets/d/1u33knqlYXvY-jR6pvTd58gyDg8OYEuwYsSoEnIjeA8k/edit?gid=4136205#gid=4136205",
|
||||
"https://docs.google.com/spreadsheets/d/1u33knqlYXvY-jR6pvTd58gyDg8OYEuwYsSoEnIjeA8k/edit?gid=4136205#gid=4136205",
|
||||
cursor,
|
||||
-4)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"- Atlas twow for ranged weapon sources\n\n"
|
||||
.. "- Twow wiki for pet diet (which was incomplete, missing Serpents and Foxes)",
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"With that being said, go back hunting fellas!",
|
||||
"",
|
||||
"GameFontNormalSmall", -10, 1.00, 0.82, 0.00)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
"\n"
|
||||
.. "Metasploit <Atlantis>, of Nordaanar.",
|
||||
"\n" .. "Metasploit <Atlantis>, of Nordaanar, now N'Zoth",
|
||||
"GameFontNormalSmall", -10)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt: Experimental Ammunition Options Panel
|
||||
-- TWoW 1.18.1 — "Nightmares of Ursol"
|
||||
------------------------------------------------------
|
||||
|
||||
local function MTH_EA_OPT_Ready()
|
||||
return type(MTH_GetFrame) == "function"
|
||||
and type(MTH_CreateCheckbox) == "function"
|
||||
and type(MTH_CreateSlider) == "function"
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_GetCfg()
|
||||
if type(MTH_SavedVariables) ~= "table" then return {} end
|
||||
if type(MTH_SavedVariables.modules) ~= "table" then return {} end
|
||||
return MTH_SavedVariables.modules["expammo"] or {}
|
||||
end
|
||||
|
||||
local MTH_EA_OPT_STATE = { bindStatus = nil, bindButton = nil, captureFrame = nil }
|
||||
local function MTH_EA_OPT_GetBindText()
|
||||
if not GetBindingKey then return "Unbound" end
|
||||
local k1, k2 = GetBindingKey("EXPAMMO ACTION")
|
||||
if not k1 and not k2 then return "Unbound" end
|
||||
if k1 and k2 then return k1 .. " / " .. k2 end
|
||||
return k1 or k2 or "Unbound"
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_UpdateBindStatus()
|
||||
local bs = MTH_EA_OPT_STATE.bindStatus
|
||||
if not bs then return end
|
||||
bs:SetText("Key: " .. MTH_EA_OPT_GetBindText())
|
||||
end
|
||||
|
||||
-- key must already be the full chord string (e.g. "ALT-F", "BUTTON3", "MOUSEWHEELUP")
|
||||
local function MTH_EA_OPT_SaveBinding(key)
|
||||
if not SetBinding or not SaveBindings or not GetBindingKey then return end
|
||||
local old1, old2 = GetBindingKey("EXPAMMO ACTION")
|
||||
if old1 then SetBinding(old1) end
|
||||
if old2 then SetBinding(old2) end
|
||||
if key and key ~= "" then
|
||||
SetBinding(key, "EXPAMMO ACTION")
|
||||
end
|
||||
local bs = 1
|
||||
if GetCurrentBindingSet then bs = GetCurrentBindingSet() or 1 end
|
||||
SaveBindings(bs)
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_GetPrefix()
|
||||
return string.format("%s%s%s",
|
||||
(IsAltKeyDown and IsAltKeyDown() and "ALT-" or ""),
|
||||
(IsControlKeyDown and IsControlKeyDown() and "CTRL-" or ""),
|
||||
(IsShiftKeyDown and IsShiftKeyDown() and "SHIFT-" or ""))
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_StopCapture()
|
||||
if MTH_EA_OPT_STATE.captureFrame then
|
||||
MTH_EA_OPT_STATE.captureFrame:Hide()
|
||||
end
|
||||
local bb = MTH_EA_OPT_STATE.bindButton
|
||||
if bb then bb:SetText("Set Key") end
|
||||
MTH_EA_OPT_UpdateBindStatus()
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_MakeCaptureFrame()
|
||||
local f = CreateFrame("Frame", "MTH_ExpAmmoBindCapture", UIParent)
|
||||
f:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
f:SetAllPoints(UIParent)
|
||||
f:EnableKeyboard(true)
|
||||
f:EnableMouse(true)
|
||||
f:EnableMouseWheel(true)
|
||||
f:Hide()
|
||||
local bg = f:CreateTexture(nil, "BACKGROUND")
|
||||
bg:SetAllPoints(f)
|
||||
bg:SetTexture(0, 0, 0, 0.5)
|
||||
local lbl = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
lbl:SetPoint("CENTER", f, "CENTER", 0, 0)
|
||||
lbl:SetText("|cffffff00Press a key, mouse button, or scroll wheel|r\n|cffaaaaaa(ESC to cancel)|r")
|
||||
f:SetScript("OnKeyUp", function()
|
||||
local key = arg1
|
||||
if not key or key == "" then return end
|
||||
if key == "ESCAPE" then MTH_EA_OPT_StopCapture() return end
|
||||
if key == "UNKNOWN" or key == "PRINTSCREEN" then return end
|
||||
if key == "ALT" or key == "CTRL" or key == "SHIFT" then return end
|
||||
MTH_EA_OPT_SaveBinding(MTH_EA_OPT_GetPrefix() .. key)
|
||||
MTH_EA_OPT_StopCapture()
|
||||
end)
|
||||
f:SetScript("OnMouseUp", function()
|
||||
local btmap = { LeftButton="BUTTON1", RightButton="BUTTON2", MiddleButton="BUTTON3", Button4="BUTTON4", Button5="BUTTON5" }
|
||||
local mapped = btmap[arg1]
|
||||
if not mapped then return end
|
||||
local prefix = MTH_EA_OPT_GetPrefix()
|
||||
if prefix == "" and (mapped == "BUTTON1" or mapped == "BUTTON2") then return end
|
||||
MTH_EA_OPT_SaveBinding(prefix .. mapped)
|
||||
MTH_EA_OPT_StopCapture()
|
||||
end)
|
||||
f:SetScript("OnMouseWheel", function()
|
||||
local wheelkey = (arg1 == 1 and "MOUSEWHEELUP") or (arg1 == -1 and "MOUSEWHEELDOWN") or nil
|
||||
if not wheelkey then return end
|
||||
MTH_EA_OPT_SaveBinding(MTH_EA_OPT_GetPrefix() .. wheelkey)
|
||||
MTH_EA_OPT_StopCapture()
|
||||
end)
|
||||
return f
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_StartCapture()
|
||||
if not MTH_EA_OPT_STATE.captureFrame then
|
||||
MTH_EA_OPT_STATE.captureFrame = MTH_EA_OPT_MakeCaptureFrame()
|
||||
end
|
||||
MTH_EA_OPT_STATE.captureFrame:Show()
|
||||
local bb = MTH_EA_OPT_STATE.bindButton
|
||||
if bb then bb:SetText("Press key...") end
|
||||
end
|
||||
|
||||
local function MTH_EA_OPT_BuildUI(container)
|
||||
local cfg = MTH_EA_OPT_GetCfg()
|
||||
local showHint = cfg.showHint ~= false
|
||||
|
||||
local panel = MTH_Layout.Panel(container)
|
||||
panel:Title("MM Widget — Experimental Ammunition Tracker")
|
||||
|
||||
-- Right-column gating: dim the box frames and disable their interactive
|
||||
-- controls when the "Show action hint cell" checkbox is off.
|
||||
local rightBoxes = {}
|
||||
local rightControls = {}
|
||||
local function setRightEnabled(enabled)
|
||||
local i
|
||||
for i = 1, table.getn(rightBoxes) do
|
||||
local f = rightBoxes[i]
|
||||
if f and f.SetAlpha then f:SetAlpha(enabled and 1.0 or 0.35) end
|
||||
end
|
||||
for i = 1, table.getn(rightControls) do
|
||||
local c = rightControls[i]
|
||||
if c and c.Enable and c.Disable then
|
||||
if enabled then c:Enable() else c:Disable() end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ===== LEFT: Module =====
|
||||
local moduleBox = panel:Box("left", "MTH_ExpAmmoModuleBox", "Module")
|
||||
local moduleEnabled = true
|
||||
if MTH and MTH.IsModuleEnabled then
|
||||
moduleEnabled = MTH:IsModuleEnabled("expammo", true) and true or false
|
||||
end
|
||||
moduleBox:Checkbox("MTH_ExpAmmoModuleCB", "Enable MM Widget module", {
|
||||
checked = moduleEnabled,
|
||||
onClick = function()
|
||||
local v = this:GetChecked() == 1
|
||||
if MTH and MTH.SetModuleEnabled then MTH:SetModuleEnabled("expammo", v) end
|
||||
if MTH_ExpAmmo then MTH_ExpAmmo.SetEnabled(v) end
|
||||
end,
|
||||
})
|
||||
|
||||
-- ===== LEFT: Tracker Visibility =====
|
||||
local visBox = panel:Box("left", "MTH_ExpAmmoVisBox", "Tracker Visibility")
|
||||
visBox:Checkbox("MTH_ExpAmmoShowLnLCB", "Show Lock and Load cell (top)", {
|
||||
checked = cfg.showLnL ~= false,
|
||||
onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetShowLnL(this:GetChecked() == 1) end end,
|
||||
})
|
||||
visBox:Checkbox("MTH_ExpAmmoShowHintCB", "Show action hint cell (bottom)", {
|
||||
checked = showHint,
|
||||
onClick = function()
|
||||
local v = this:GetChecked() == 1
|
||||
if MTH_ExpAmmo then MTH_ExpAmmo.SetShowHint(v) end
|
||||
setRightEnabled(v)
|
||||
end,
|
||||
})
|
||||
visBox:Checkbox("MTH_ExpAmmoHideOOCCB", "Hide widget when out of combat", {
|
||||
checked = cfg.hideOOC == true,
|
||||
onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetHideOOC(this:GetChecked() == 1) end end,
|
||||
})
|
||||
|
||||
-- ===== LEFT: Appearance =====
|
||||
local appBox = panel:Box("left", "MTH_ExpAmmoAppBox", "Appearance")
|
||||
appBox:Slider("MTH_ExpAmmoCellSizeSlider", {
|
||||
label = "Cell Size (px)", min = 20, max = 80, step = 4,
|
||||
value = math.max(20, math.min(80, cfg.cellSize or 40)),
|
||||
onChange = function(val) if MTH_ExpAmmo then MTH_ExpAmmo.SetCellSize(val) end end,
|
||||
})
|
||||
appBox:Slider("MTH_ExpAmmoCellGapSlider", {
|
||||
label = "Cell Spacing (px)", min = 0, max = 20, step = 1,
|
||||
value = math.max(0, math.min(20, cfg.cellGap or 2)),
|
||||
onChange = function(val) if MTH_ExpAmmo then MTH_ExpAmmo.SetCellGap(val) end end,
|
||||
})
|
||||
appBox:Checkbox("MTH_ExpAmmoBigCDCB", "Large cooldown numbers (centered in cell)", {
|
||||
checked = cfg.bigCD == true,
|
||||
onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetBigCD(this:GetChecked() == 1) end end,
|
||||
})
|
||||
|
||||
-- ===== LEFT: Position =====
|
||||
local posBox = panel:Box("left", "MTH_ExpAmmoPosBox", "Position")
|
||||
posBox:Button("MTH_ExpAmmoToggleAnchor", "Toggle Anchor", {
|
||||
width = 130,
|
||||
onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.ToggleAnchor() end end,
|
||||
})
|
||||
posBox:Note("MTH_ExpAmmoPosNote",
|
||||
"Shows 4 drag handles around the widget (top / bottom / left / right). Drag any one to reposition it.",
|
||||
{ color = MTH_Layout.Theme.colText, lines = 3 })
|
||||
|
||||
-- ===== RIGHT: Smart Action Cell (helper + keybind + casting) =====
|
||||
-- One feature = one box. The intro notes explain the bottom cell, and the
|
||||
-- Keybind / Casting controls that drive it live underneath as sub-sections.
|
||||
local cellBox = panel:Box("right", "MTH_ExpAmmoCellBox", "Smart Action Cell")
|
||||
table.insert(rightBoxes, cellBox:Frame())
|
||||
cellBox:Text("MTH_ExpAmmoHintSub",
|
||||
"Options below apply to the bottom cell only. Disable \"Show action hint cell\" to hide the cell and these controls.",
|
||||
{ color = { 1, 1, 1 }, lines = 3 })
|
||||
cellBox:Note("MTH_ExpAmmoHintNam",
|
||||
"Notes:\n - You need NAMPOWER for this to work reliably.\n - Aimed/Steady/Multi/Arcane Shots SHOULD BE on your action bars.",
|
||||
{ lines = 3 })
|
||||
|
||||
-- Keybind sub-section
|
||||
cellBox:Gap(4)
|
||||
cellBox:Text("MTH_ExpAmmoKeyHead", "Keybind",
|
||||
{ font = MTH_Layout.Theme.fontBoxTitle, color = MTH_Layout.Theme.colPanelTitle })
|
||||
cellBox:Note("MTH_ExpAmmoBindTip",
|
||||
"One key to rule them all.\n Aimed Shot when ready, Steady Shot while Aimed is on cooldown, consume-spell (Multi / Serpent / Arcane) when proc is up.",
|
||||
{ lines = 4 })
|
||||
MTH_EA_OPT_STATE.bindStatus = cellBox:Text("MTH_ExpAmmoBindStatus",
|
||||
"Key: " .. MTH_EA_OPT_GetBindText())
|
||||
local keyButtons = cellBox:Buttons({
|
||||
{ name = "MTH_ExpAmmoBindButton", label = "Set Key", width = 90,
|
||||
onClick = function()
|
||||
if MTH_EA_OPT_STATE.captureFrame and MTH_EA_OPT_STATE.captureFrame:IsShown() then
|
||||
MTH_EA_OPT_StopCapture()
|
||||
else
|
||||
MTH_EA_OPT_StartCapture()
|
||||
end
|
||||
end },
|
||||
{ name = "MTH_ExpAmmoBindClear", label = "Clear", width = 70,
|
||||
onClick = function() MTH_EA_OPT_SaveBinding(nil) end },
|
||||
})
|
||||
MTH_EA_OPT_STATE.bindButton = keyButtons[1]
|
||||
table.insert(rightControls, keyButtons[1])
|
||||
table.insert(rightControls, keyButtons[2])
|
||||
|
||||
-- Casting sub-section
|
||||
cellBox:Gap(4)
|
||||
cellBox:Text("MTH_ExpAmmoCastHead", "Casting",
|
||||
{ font = MTH_Layout.Theme.fontBoxTitle, color = MTH_Layout.Theme.colPanelTitle })
|
||||
local quiverCB = cellBox:Checkbox("MTH_ExpAmmoQuiverNoClipCB", "Use Quiver no-clip casting", {
|
||||
checked = cfg.useQuiverNoClip == true,
|
||||
onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetUseQuiverNoClip(this:GetChecked() == 1) end end,
|
||||
})
|
||||
table.insert(rightControls, quiverCB)
|
||||
cellBox:Note("MTH_ExpAmmoQuiverTip",
|
||||
"Requires the Quiver addon. Routes Aimed/Steady/Multi cast through Quiver.CastNoClip to avoid clipping your auto-shot swing timer.",
|
||||
{ lines = 3 })
|
||||
|
||||
-- ===== RIGHT: Rotation =====
|
||||
local rotBox = panel:Box("right", "MTH_ExpAmmoRotBox", "Rotation")
|
||||
table.insert(rightBoxes, rotBox:Frame())
|
||||
rotBox:Note("MTH_ExpAmmoRotTip",
|
||||
"Uncheck a shot to permanently skip it during its proc window. The keybind will cast Steady/Aimed instead.",
|
||||
{ lines = 2 })
|
||||
local multiCB = rotBox:Checkbox("MTH_ExpAmmoBlockMultiCB", "Multi-Shot (Explosive)", {
|
||||
checked = cfg.blockExplosive ~= true,
|
||||
onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockExplosive(not (this:GetChecked() == 1)) end end,
|
||||
})
|
||||
table.insert(rightControls, multiCB)
|
||||
local serpentCB = rotBox:Checkbox("MTH_ExpAmmoBlockSerpentCB", "Serpent Sting (Poisonous)", {
|
||||
checked = cfg.blockPoisonous ~= true,
|
||||
onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockPoisonous(not (this:GetChecked() == 1)) end end,
|
||||
})
|
||||
table.insert(rightControls, serpentCB)
|
||||
local arcaneCB = rotBox:Checkbox("MTH_ExpAmmoBlockArcaneCB", "Arcane Shot (Enchanted)", {
|
||||
checked = cfg.blockEnchanted ~= true,
|
||||
onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockEnchanted(not (this:GetChecked() == 1)) end end,
|
||||
})
|
||||
table.insert(rightControls, arcaneCB)
|
||||
|
||||
panel:Finish()
|
||||
|
||||
-- Apply initial gating state.
|
||||
setRightEnabled(showHint)
|
||||
end
|
||||
|
||||
function MTH_SetupExpAmmoOptions()
|
||||
if not MTH_EA_OPT_Ready() then return end
|
||||
local container = MTH_GetFrame("MetaHuntOptionsExpAmmo")
|
||||
if not container then return end
|
||||
local ok, err = pcall(MTH_EA_OPT_BuildUI, container)
|
||||
if not ok and MTH and MTH.Print then
|
||||
MTH:Print("[MTH ExpAmmo OPTIONS] " .. tostring(err), "error")
|
||||
end
|
||||
end
|
||||
+191
-182
@@ -10,10 +10,17 @@ local MTH_FOM_STATE = {
|
||||
bindStatus = nil,
|
||||
bindButton = nil,
|
||||
bindClearButton = nil,
|
||||
bindingCapture = false,
|
||||
captureFrame = nil,
|
||||
ctrl = {},
|
||||
}
|
||||
|
||||
local function MTH_FOM_L(key, default)
|
||||
if MTH and MTH.GetLocalization then
|
||||
return MTH:GetLocalization(key, default)
|
||||
end
|
||||
return default or key
|
||||
end
|
||||
|
||||
local function MTH_EnsureFOMConfig()
|
||||
if FOM_Config then
|
||||
return
|
||||
@@ -69,16 +76,16 @@ end
|
||||
|
||||
local function MTH_FOM_GetBindingDisplayText()
|
||||
if not GetBindingKey then
|
||||
return "Unavailable"
|
||||
return MTH_FOM_L("FOM_BIND_UNAVAILABLE", "Unavailable")
|
||||
end
|
||||
local key1, key2 = GetBindingKey("FEEDOMATIC")
|
||||
if not key1 and not key2 then
|
||||
return "Unbound"
|
||||
return MTH_FOM_L("FOM_BIND_UNBOUND", "Unbound")
|
||||
end
|
||||
if key1 and key2 then
|
||||
return key1 .. " / " .. key2
|
||||
end
|
||||
return key1 or key2 or "Unbound"
|
||||
return key1 or key2 or MTH_FOM_L("FOM_BIND_UNBOUND", "Unbound")
|
||||
end
|
||||
|
||||
local function MTH_FOM_UpdateBindingStatus()
|
||||
@@ -86,55 +93,95 @@ local function MTH_FOM_UpdateBindingStatus()
|
||||
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
|
||||
bindStatus:SetText(string.format(MTH_FOM_L("FOM_BIND_STATUS_VALUE", "Feed key: %s"), MTH_FOM_GetBindingDisplayText()))
|
||||
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")
|
||||
if MTH_FOM_STATE.captureFrame then
|
||||
MTH_FOM_STATE.captureFrame:Hide()
|
||||
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
|
||||
MTH_FOM_STATE.bindButton:SetText(MTH_FOM_L("FOM_BIND_SET_KEY", "Set Key"))
|
||||
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
|
||||
return string.format("%s%s%s%s",
|
||||
(IsAltKeyDown and IsAltKeyDown() and "ALT-" or ""),
|
||||
(IsControlKeyDown and IsControlKeyDown() and "CTRL-" or ""),
|
||||
(IsShiftKeyDown and IsShiftKeyDown() and "SHIFT-" or ""),
|
||||
key)
|
||||
end
|
||||
|
||||
local function MTH_FOM_SaveBinding(key)
|
||||
-- Forward declaration: defined below but referenced by the capture-frame
|
||||
-- closures above it, so it must be an in-scope local upvalue (not a global).
|
||||
local MTH_FOM_SaveBinding
|
||||
|
||||
local function MTH_FOM_MakeCaptureFrame()
|
||||
local f = CreateFrame("Frame", "MTH_FOMBindCapture", UIParent)
|
||||
f:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
f:SetAllPoints(UIParent)
|
||||
f:EnableKeyboard(true)
|
||||
f:EnableMouse(true)
|
||||
f:EnableMouseWheel(true)
|
||||
f:Hide()
|
||||
local bg = f:CreateTexture(nil, "BACKGROUND")
|
||||
bg:SetAllPoints(f)
|
||||
bg:SetTexture(0, 0, 0, 0.5)
|
||||
local lbl = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
lbl:SetPoint("CENTER", f, "CENTER", 0, 0)
|
||||
lbl:SetText("|cffffff00Press a key, mouse button, or scroll wheel|r\n|cffaaaaaa(ESC to cancel)|r")
|
||||
f:SetScript("OnKeyUp", function()
|
||||
local key = arg1
|
||||
if not key or key == "" then return end
|
||||
if key == "ESCAPE" then MTH_FOM_StopBindingCapture() return end
|
||||
if key == "UNKNOWN" or key == "PRINTSCREEN" then return end
|
||||
if key == "ALT" or key == "CTRL" or key == "SHIFT" then return end
|
||||
MTH_FOM_SaveBinding(MTH_FOM_BuildBindingChord(key))
|
||||
MTH_FOM_StopBindingCapture()
|
||||
end)
|
||||
f:SetScript("OnMouseUp", function()
|
||||
local btmap = { LeftButton="BUTTON1", RightButton="BUTTON2", MiddleButton="BUTTON3", Button4="BUTTON4", Button5="BUTTON5" }
|
||||
local mapped = btmap[arg1]
|
||||
if not mapped then return end
|
||||
local prefix = string.format("%s%s%s",
|
||||
(IsAltKeyDown and IsAltKeyDown() and "ALT-" or ""),
|
||||
(IsControlKeyDown and IsControlKeyDown() and "CTRL-" or ""),
|
||||
(IsShiftKeyDown and IsShiftKeyDown() and "SHIFT-" or ""))
|
||||
if prefix == "" and (mapped == "BUTTON1" or mapped == "BUTTON2") then return end
|
||||
MTH_FOM_SaveBinding(prefix .. mapped)
|
||||
MTH_FOM_StopBindingCapture()
|
||||
end)
|
||||
f:SetScript("OnMouseWheel", function()
|
||||
local wheelkey = (arg1 == 1 and "MOUSEWHEELUP") or (arg1 == -1 and "MOUSEWHEELDOWN") or nil
|
||||
if not wheelkey then return end
|
||||
MTH_FOM_SaveBinding(MTH_FOM_BuildBindingChord(wheelkey))
|
||||
MTH_FOM_StopBindingCapture()
|
||||
end)
|
||||
return f
|
||||
end
|
||||
|
||||
local function MTH_FOM_StartBindingCapture()
|
||||
if not MTH_FOM_STATE.captureFrame then
|
||||
MTH_FOM_STATE.captureFrame = MTH_FOM_MakeCaptureFrame()
|
||||
end
|
||||
MTH_FOM_STATE.captureFrame:Show()
|
||||
if MTH_FOM_STATE.bindButton then
|
||||
MTH_FOM_STATE.bindButton:SetText(MTH_FOM_L("FOM_BIND_PRESS_KEY", "Press key..."))
|
||||
end
|
||||
end
|
||||
|
||||
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.")
|
||||
MTH:Print(MTH_FOM_L("FOM_ERROR_KEYBIND_API_UNAVAILABLE", "Keybinding APIs unavailable."))
|
||||
end
|
||||
return
|
||||
end
|
||||
if InCombatLockdown and InCombatLockdown() then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("Cannot change keybindings during combat.")
|
||||
MTH:Print(MTH_FOM_L("FOM_ERROR_KEYBIND_COMBAT_LOCKED", "Cannot change keybindings during combat."))
|
||||
end
|
||||
MTH_FOM_StopBindingCapture()
|
||||
return
|
||||
@@ -195,179 +242,141 @@ function MTH_RefreshFeedOMaticOptions()
|
||||
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
|
||||
|
||||
-- Small label helper: prefer Feed-O-Matic's own button text, else localized.
|
||||
local function MTH_FOM_Label(key, lkey, fallback)
|
||||
return (FOM_OptionsButtonText and FOM_OptionsButtonText[key]) or MTH_FOM_L(lkey, fallback)
|
||||
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 panel = MTH_Layout.Panel(container)
|
||||
panel:Title(MTH_FOM_L("FOM_TITLE", "Feed-O-Matic"))
|
||||
panel:Note(MTH_FOM_L("FOM_STATUS_NOTICE", "Feed-O-Matic, created by the great Fizzwidget, is not yet entirely ready for Twow 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."), { lines = 4 })
|
||||
|
||||
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
|
||||
MTH_FOM_STATE.enableCheck = panel:TopCheckbox("MetaHuntOptionsFeedOMaticEnabled",
|
||||
MTH_FOM_L("FOM_ENABLE_MODULE", "Enable FeedOMatic module"), {
|
||||
onClick = function()
|
||||
if not this then return end
|
||||
local enabled = this: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))
|
||||
MTH:Print(string.format(MTH_FOM_L("FOM_ERROR_TOGGLE_FAILED", "Failed to change FeedOMatic state: %s"), tostring(err)))
|
||||
end
|
||||
end
|
||||
MTH_RefreshFeedOMaticOptions()
|
||||
end)
|
||||
MTH_FOM_STATE.enableCheck = moduleCheck
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local leftHeader = container:CreateFontString("MetaHuntOptionsFeedOMaticGeneralHeader", "ARTWORK", "GameFontHighlight")
|
||||
leftHeader:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -62 + yAdjust)
|
||||
leftHeader:SetText("General")
|
||||
-- ===== General (left column) =====
|
||||
local general = panel:Box("left", "MetaHuntOptionsFOMGeneralBox", MTH_FOM_L("FOM_HEADER_GENERAL", "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
|
||||
MTH_FOM_STATE.bindStatus = general:Text("MetaHuntOptionsFOMBindStatus",
|
||||
string.format(MTH_FOM_L("FOM_BIND_STATUS_VALUE", "Feed key: %s"), MTH_FOM_GetBindingDisplayText()))
|
||||
|
||||
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 bindButtons = general:Buttons({
|
||||
{ name = "MetaHuntOptionsFOMBindButton", label = MTH_FOM_L("FOM_BIND_SET_KEY", "Set Key"), width = 90,
|
||||
onClick = function()
|
||||
if MTH_FOM_STATE.captureFrame and MTH_FOM_STATE.captureFrame:IsShown() then
|
||||
MTH_FOM_StopBindingCapture()
|
||||
else
|
||||
MTH_FOM_StartBindingCapture()
|
||||
end
|
||||
end },
|
||||
{ name = "MetaHuntOptionsFOMBindClearButton", label = MTH_FOM_L("FOM_BIND_CLEAR", "Clear"), width = 70,
|
||||
onClick = function() MTH_FOM_SaveBinding(nil) end },
|
||||
})
|
||||
MTH_FOM_STATE.bindButton = bindButtons[1]
|
||||
MTH_FOM_STATE.bindClearButton = bindButtons[2]
|
||||
|
||||
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
|
||||
general:Note("MetaHuntOptionsFOMBindHint",
|
||||
MTH_FOM_L("FOM_BIND_HINT", "You should assign a key bind for feed-o-matic,\nin order to feed your pet automatically.\nThe key \"P\" is an excellent candidate !"), { lines = 3 })
|
||||
|
||||
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 = general:Checkbox("MetaHuntOptionsFOMAvoidQuest",
|
||||
MTH_FOM_Label("AvoidQuestFood", "FOM_LABEL_AVOID_QUEST_FOODS", "Avoid quest foods"),
|
||||
{ onClick = function() if not this then return end FOM_Config.AvoidQuestFood = this:GetChecked() == 1 end })
|
||||
MTH_FOM_STATE.ctrl.AvoidBonusFood = general:Checkbox("MetaHuntOptionsFOMAvoidBonus",
|
||||
MTH_FOM_Label("AvoidBonusFood", "FOM_LABEL_AVOID_BONUS_FOODS", "Avoid bonus foods"),
|
||||
{ onClick = function() if not this then return end FOM_Config.AvoidBonusFood = this:GetChecked() == 1 end })
|
||||
MTH_FOM_STATE.ctrl.PreferHigherQuality = general:Checkbox("MetaHuntOptionsFOMPreferQuality",
|
||||
MTH_FOM_Label("PreferHigherQuality", "FOM_LABEL_PREFER_HIGHER_QUALITY_FOOD", "Prefer higher quality food"),
|
||||
{ onClick = function() if not this then return end FOM_Config.PreferHigherQuality = this:GetChecked() == 1 end })
|
||||
MTH_FOM_STATE.ctrl.Fallback = general:Checkbox("MetaHuntOptionsFOMFallback",
|
||||
MTH_FOM_Label("Fallback", "FOM_LABEL_FALLBACK_TO_AVOIDED_FOODS", "Fallback to avoided foods"),
|
||||
{ onClick = function() if not this then return end FOM_Config.Fallback = this:GetChecked() == 1 end })
|
||||
|
||||
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)
|
||||
MTH_FOM_STATE.keepOpenEdit = general:EditRow("MetaHuntOptionsFOMKeepOpenLabel",
|
||||
MTH_FOM_L("FOM_KEEP_OPEN_BAG_SLOTS", "Keep open bag slots:"),
|
||||
"MetaHuntOptionsFOMKeepOpen", 40, {
|
||||
numeric = true,
|
||||
onChange = function()
|
||||
if not this then return end
|
||||
FOM_Config.KeepOpenSlots = tonumber(this:GetText()) or 0
|
||||
end,
|
||||
})
|
||||
|
||||
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
|
||||
-- ===== Notify when feeding (left column) =====
|
||||
local notify = panel:Box("left", "MetaHuntOptionsFOMNotifyBox", MTH_FOM_L("FOM_HEADER_NOTIFY_WHEN_FEEDING", "Notify when feeding"))
|
||||
MTH_FOM_STATE.ctrl.AlertEmote = notify:Checkbox("MetaHuntOptionsFOMAlertEmote",
|
||||
MTH_FOM_Label("AlertEmote", "FOM_LABEL_NOTIFY_VIA_EMOTE", "Via emote"),
|
||||
{ onClick = function() MTH_FOM_SetAlert("emote"); MTH_RefreshFeedOMaticOptions() end })
|
||||
MTH_FOM_STATE.ctrl.AlertChat = notify:Checkbox("MetaHuntOptionsFOMAlertChat",
|
||||
MTH_FOM_Label("AlertChat", "FOM_LABEL_NOTIFY_IN_CHAT", "In chat"),
|
||||
{ onClick = function() MTH_FOM_SetAlert("chat"); MTH_RefreshFeedOMaticOptions() end })
|
||||
MTH_FOM_STATE.ctrl.AlertNone = notify:Checkbox("MetaHuntOptionsFOMAlertNone",
|
||||
MTH_FOM_Label("AlertNone", "FOM_LABEL_NOTIFY_NONE", "Don't notify"),
|
||||
{ onClick = function() MTH_FOM_SetAlert("none"); MTH_RefreshFeedOMaticOptions() end })
|
||||
|
||||
local keepOpenLabel = container:CreateFontString("MetaHuntOptionsFOMKeepOpenLabel", "ARTWORK", "GameFontNormalSmall")
|
||||
keepOpenLabel:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -302 + yAdjust)
|
||||
keepOpenLabel:SetText("Keep open bag slots:")
|
||||
-- ===== Avoid foods used in cooking (right column) =====
|
||||
local avoid = panel:Box("right", "MetaHuntOptionsFOMAvoidBox", MTH_FOM_L("FOM_HEADER_AVOID_FOODS_USED_IN_COOKING", "Avoid foods used in cooking"))
|
||||
MTH_FOM_STATE.ctrl.SaveCookOrange = avoid:Checkbox("MetaHuntOptionsFOMSaveCookOrange",
|
||||
MTH_FOM_Label("SaveForCook_Orange", "FOM_LABEL_ONLY_DIFFICULT_RECIPES", "Only difficult recipes"),
|
||||
{ onClick = function() MTH_FOM_SetCookingLevel(3); MTH_RefreshFeedOMaticOptions() end })
|
||||
MTH_FOM_STATE.ctrl.SaveCookYellow = avoid:Checkbox("MetaHuntOptionsFOMSaveCookYellow",
|
||||
MTH_FOM_Label("SaveForCook_Yellow", "FOM_LABEL_MEDIUM_OR_BETTER", "Medium or better"),
|
||||
{ onClick = function() MTH_FOM_SetCookingLevel(2); MTH_RefreshFeedOMaticOptions() end })
|
||||
MTH_FOM_STATE.ctrl.SaveCookGreen = avoid:Checkbox("MetaHuntOptionsFOMSaveCookGreen",
|
||||
MTH_FOM_Label("SaveForCook_Green", "FOM_LABEL_EASY_OR_BETTER", "Easy or better"),
|
||||
{ onClick = function() MTH_FOM_SetCookingLevel(1); MTH_RefreshFeedOMaticOptions() end })
|
||||
MTH_FOM_STATE.ctrl.SaveCookAll = avoid:Checkbox("MetaHuntOptionsFOMSaveCookAll",
|
||||
MTH_FOM_Label("SaveForCook_All", "FOM_LABEL_ALL_FOODS", "All foods"),
|
||||
{ onClick = function() MTH_FOM_SetCookingLevel(0); MTH_RefreshFeedOMaticOptions() end })
|
||||
MTH_FOM_STATE.ctrl.SaveCookNone = avoid:Checkbox("MetaHuntOptionsFOMSaveCookNone",
|
||||
MTH_FOM_Label("SaveForCook_None", "FOM_LABEL_DO_NOT_SAVE_COOKING_FOODS", "Do not save cooking foods"),
|
||||
{ onClick = function() MTH_FOM_SetCookingLevel(4); MTH_RefreshFeedOMaticOptions() end })
|
||||
|
||||
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
|
||||
-- ===== Warn when pet needs feeding (right column) =====
|
||||
local warn = panel:Box("right", "MetaHuntOptionsFOMWarnBox", MTH_FOM_L("FOM_HEADER_WARN_WHEN_PET_NEEDS_FEEDING", "Warn when pet needs feeding"))
|
||||
MTH_FOM_STATE.ctrl.LevelContent = warn:Checkbox("MetaHuntOptionsFOMLevelContent",
|
||||
MTH_FOM_Label("LevelContent", "FOM_LABEL_WARN_WHEN_CONTENT", "When content"),
|
||||
{ onClick = function() MTH_FOM_SetLevel("content"); MTH_RefreshFeedOMaticOptions() end })
|
||||
MTH_FOM_STATE.ctrl.LevelUnhappy = warn:Checkbox("MetaHuntOptionsFOMLevelUnhappy",
|
||||
MTH_FOM_Label("LevelUnhappy", "FOM_LABEL_WARN_WHEN_UNHAPPY", "When unhappy"),
|
||||
{ onClick = function() MTH_FOM_SetLevel("unhappy"); MTH_RefreshFeedOMaticOptions() end })
|
||||
MTH_FOM_STATE.ctrl.LevelOff = warn:Checkbox("MetaHuntOptionsFOMLevelOff",
|
||||
MTH_FOM_Label("LevelOff", "FOM_LABEL_DONT_WARN", "Don't warn"),
|
||||
{ onClick = function() MTH_FOM_SetLevel("off"); MTH_RefreshFeedOMaticOptions() end })
|
||||
|
||||
local notifyHeader = container:CreateFontString("MetaHuntOptionsFOMNotifyHeader", "ARTWORK", "GameFontHighlight")
|
||||
notifyHeader:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -336 + yAdjust)
|
||||
notifyHeader:SetText("Notify when feeding")
|
||||
warn:Gap(6)
|
||||
|
||||
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_FOM_STATE.ctrl.AudioWarning = warn:Checkbox("MetaHuntOptionsFOMAudioWarning",
|
||||
MTH_FOM_Label("AudioWarning", "FOM_LABEL_PLAY_SOUND", "Play sound"),
|
||||
{ onClick = function() if not this then return end if this:GetChecked() == 1 then if FOM_Config.AudioWarning ~= "bell" then FOM_Config.AudioWarning = true end else FOM_Config.AudioWarning = nil end MTH_RefreshFeedOMaticOptions() end })
|
||||
MTH_FOM_STATE.ctrl.AudioWarningBell = warn:Checkbox("MetaHuntOptionsFOMAudioWarningBell",
|
||||
MTH_FOM_Label("AudioWarningBell", "FOM_LABEL_USE_BELL_SOUND", "Use bell sound"),
|
||||
{ onClick = function() if not this then return end if this:GetChecked() == 1 then FOM_Config.AudioWarning = "bell" else FOM_Config.AudioWarning = true end MTH_RefreshFeedOMaticOptions() end })
|
||||
MTH_FOM_STATE.ctrl.TextWarning = warn:Checkbox("MetaHuntOptionsFOMTextWarning",
|
||||
MTH_FOM_Label("TextWarning", "FOM_LABEL_SHOW_TEXT", "Show text"),
|
||||
{ onClick = function() if not this then return end FOM_Config.TextWarning = this:GetChecked() == 1 end })
|
||||
MTH_FOM_STATE.ctrl.IconWarning = warn:Checkbox("MetaHuntOptionsFOMIconWarning",
|
||||
MTH_FOM_Label("IconWarning", "FOM_LABEL_FLASH_ICON", "Flash icon"),
|
||||
{ onClick = function() if not this then return end FOM_Config.IconWarning = this:GetChecked() == 1 end })
|
||||
|
||||
panel:Finish()
|
||||
MTH_RefreshFeedOMaticOptions()
|
||||
end
|
||||
|
||||
@@ -0,0 +1,498 @@
|
||||
local MTH_ICU_OPT_READY = true
|
||||
local MTH_ICU_OPT_MISSING = {}
|
||||
|
||||
if type(MTH_GetFrame) ~= "function" then table.insert(MTH_ICU_OPT_MISSING, "MTH_GetFrame") end
|
||||
if type(MTH_ClearContainer) ~= "function" then table.insert(MTH_ICU_OPT_MISSING, "MTH_ClearContainer") end
|
||||
if type(MTH_CreateCheckbox) ~= "function" then table.insert(MTH_ICU_OPT_MISSING, "MTH_CreateCheckbox") end
|
||||
|
||||
if table.getn(MTH_ICU_OPT_MISSING) > 0 then
|
||||
MTH_ICU_OPT_READY = false
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("[MTH ICU OPTIONS] missing dependencies: " .. table.concat(MTH_ICU_OPT_MISSING, ", "), "error")
|
||||
end
|
||||
end
|
||||
|
||||
local MTH_ICU_OPT_STATE = {
|
||||
container = nil,
|
||||
built = false,
|
||||
controls = {},
|
||||
}
|
||||
|
||||
local MTH_ICU_LAYOUT = {
|
||||
LEFT_X = 20,
|
||||
LEFT_DROPDOWN_X = 94,
|
||||
RIGHT_X = 280,
|
||||
RIGHT_DROPDOWN_X = 366,
|
||||
DROPDOWN_WIDTH = 108,
|
||||
COLOR_BUTTON_X = 378,
|
||||
COLOR_BUTTON_WIDTH = 78,
|
||||
COLOR_ROW_STEP = 24,
|
||||
}
|
||||
|
||||
local MTH_ICU_OPTIONS_FALLBACK = {
|
||||
ALERT = { "AUTO", "PARTY", "RAID", "SELF", "MTH Message", "OFF" },
|
||||
ANNOUNCE = { "AUTO", "SAY", "YELL", "PARTY", "RAID", "SELF", "MTH Message", "OFF" },
|
||||
ANCHOR = { "TOP", "TOPLEFT", "TOPRIGHT", "BOTTOM", "BOTTOMLEFT", "BOTTOMRIGHT", "LEFT", "RIGHT", "CUSTOM" },
|
||||
POPUP_HIDE_DELAY = { "INSTANT", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" },
|
||||
HEALTH_TEXT_MODE = { "NONE", "PERCENT", "HP", "BOTH" },
|
||||
PLAYER_COLOR_MODE = { "CLASS", "REACTION", "FACTION", "CUSTOM" },
|
||||
}
|
||||
|
||||
local MTH_ICU_DEFAULT_BG_COLORS = {
|
||||
PLAYER_HOSTILE = { 1.00, 0.20, 0.20 },
|
||||
PLAYER_NEUTRAL = { 1.00, 0.85, 0.10 },
|
||||
PLAYER_FRIENDLY = { 0.20, 0.75, 1.00 },
|
||||
NPC_HOSTILE = { 1.00, 0.20, 0.20 },
|
||||
NPC_NEUTRAL = { 1.00, 0.85, 0.10 },
|
||||
NPC_FRIENDLY = { 0.10, 0.90, 0.10 },
|
||||
UNKNOWN = { 0.70, 0.70, 0.70 },
|
||||
}
|
||||
|
||||
local MTH_ICU_COLOR_ROWS = {
|
||||
{ key = "PLAYER_HOSTILE", label = "Player hostile" },
|
||||
{ key = "PLAYER_NEUTRAL", label = "Player neutral" },
|
||||
{ key = "PLAYER_FRIENDLY", label = "Player friendly" },
|
||||
{ key = "NPC_HOSTILE", label = "NPC hostile" },
|
||||
{ key = "NPC_NEUTRAL", label = "NPC neutral" },
|
||||
{ key = "NPC_FRIENDLY", label = "NPC friendly" },
|
||||
{ key = "UNKNOWN", label = "Unknown fallback" },
|
||||
}
|
||||
|
||||
local function MTH_ICU_GetStore()
|
||||
local store = nil
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
store = MTH:GetModuleCharSavedVariables("icu")
|
||||
end
|
||||
if type(store) ~= "table" then
|
||||
ICUvars = ICUvars or {}
|
||||
store = ICUvars
|
||||
end
|
||||
|
||||
if store.ALERT == nil then store.ALERT = "MTH Message" end
|
||||
if store.ALERT == "SAY" or store.ALERT == "YELL" then store.ALERT = "SELF" end
|
||||
if store.ANNOUNCE == nil then store.ANNOUNCE = "MTH Message" end
|
||||
if store.ANCHOR == nil then store.ANCHOR = "BOTTOMRIGHT" end
|
||||
if store.POPUP_HIDE_DELAY == nil then
|
||||
store.POPUP_HIDE_DELAY = "INSTANT"
|
||||
else
|
||||
local rawDelay = tostring(store.POPUP_HIDE_DELAY)
|
||||
if rawDelay ~= "INSTANT" then
|
||||
local n = tonumber(rawDelay)
|
||||
if not n then
|
||||
store.POPUP_HIDE_DELAY = "INSTANT"
|
||||
else
|
||||
n = math.floor(n)
|
||||
if n < 1 then
|
||||
store.POPUP_HIDE_DELAY = "INSTANT"
|
||||
elseif n > 10 then
|
||||
store.POPUP_HIDE_DELAY = "10"
|
||||
else
|
||||
store.POPUP_HIDE_DELAY = tostring(n)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if store.mouseOver == nil then store.mouseOver = false end
|
||||
if store.HEALTH_TEXT_MODE == nil then store.HEALTH_TEXT_MODE = "BOTH" end
|
||||
if store.HEALTH_TEXT_MODE == "AUTO" then store.HEALTH_TEXT_MODE = "BOTH" end
|
||||
if store.PLAYER_COLOR_MODE == nil then store.PLAYER_COLOR_MODE = "CLASS" end
|
||||
if store.REACTION_ONLY_PVP_PLAYERS == nil then store.REACTION_ONLY_PVP_PLAYERS = true end
|
||||
if store.SHOW_GUILD_NAME == nil then store.SHOW_GUILD_NAME = false end
|
||||
if store.SHOW_PLAYER_CLASS == nil then store.SHOW_PLAYER_CLASS = true end
|
||||
if store.SHOW_PLAYER_RACE == nil then store.SHOW_PLAYER_RACE = true end
|
||||
if store.SHOW_CUSTOM_TITLES == nil then store.SHOW_CUSTOM_TITLES = false end
|
||||
if store.EXPAND_UP == nil then store.EXPAND_UP = false end
|
||||
|
||||
if type(store.COLORS) ~= "table" then
|
||||
store.COLORS = {}
|
||||
end
|
||||
for key, def in pairs(MTH_ICU_DEFAULT_BG_COLORS) do
|
||||
if type(store.COLORS[key]) ~= "table" then
|
||||
store.COLORS[key] = { def[1], def[2], def[3] }
|
||||
end
|
||||
end
|
||||
|
||||
ICUvars = store
|
||||
return store
|
||||
end
|
||||
|
||||
local function MTH_ICU_GetOptionValues(key)
|
||||
if type(ICU_OPTIONS) == "table" and type(ICU_OPTIONS[key]) == "table" then
|
||||
return ICU_OPTIONS[key]
|
||||
end
|
||||
return MTH_ICU_OPTIONS_FALLBACK[key] or {}
|
||||
end
|
||||
|
||||
local function MTH_ICU_SetValue(key, value)
|
||||
local store = MTH_ICU_GetStore()
|
||||
store[key] = tostring(value or "")
|
||||
if key == "ANCHOR" and type(ICU_SetPoints) == "function" then
|
||||
ICU_SetPoints()
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_ICU_DropdownSetText(frame, text)
|
||||
if UIDropDownMenu_SetText then
|
||||
UIDropDownMenu_SetText(tostring(text or ""), frame)
|
||||
return
|
||||
end
|
||||
if not frame or not frame.GetName 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_ICU_GetColor(key)
|
||||
local store = MTH_ICU_GetStore()
|
||||
local row = store.COLORS and store.COLORS[key]
|
||||
if type(row) == "table" then
|
||||
return tonumber(row[1]) or 0, tonumber(row[2]) or 0, tonumber(row[3]) or 0
|
||||
end
|
||||
local def = MTH_ICU_DEFAULT_BG_COLORS[key] or MTH_ICU_DEFAULT_BG_COLORS.UNKNOWN
|
||||
return def[1], def[2], def[3]
|
||||
end
|
||||
|
||||
local function MTH_ICU_SetColor(key, r, g, b)
|
||||
local store = MTH_ICU_GetStore()
|
||||
if type(store.COLORS) ~= "table" then
|
||||
store.COLORS = {}
|
||||
end
|
||||
store.COLORS[key] = {
|
||||
math.max(0, math.min(1, tonumber(r) or 0)),
|
||||
math.max(0, math.min(1, tonumber(g) or 0)),
|
||||
math.max(0, math.min(1, tonumber(b) or 0)),
|
||||
}
|
||||
end
|
||||
|
||||
local function MTH_ICU_OpenColorPicker(colorKey)
|
||||
if not ColorPickerFrame then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("ColorPickerFrame is not available in this client", "error")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local currentR, currentG, currentB = MTH_ICU_GetColor(colorKey)
|
||||
local function applyColor()
|
||||
local r, g, b = ColorPickerFrame:GetColorRGB()
|
||||
MTH_ICU_SetColor(colorKey, r, g, b)
|
||||
MTH_RefreshICUOptions()
|
||||
end
|
||||
|
||||
ColorPickerFrame.func = applyColor
|
||||
ColorPickerFrame.cancelFunc = function(prev)
|
||||
if type(prev) == "table" then
|
||||
local r = prev.r or prev[1] or currentR
|
||||
local g = prev.g or prev[2] or currentG
|
||||
local b = prev.b or prev[3] or currentB
|
||||
MTH_ICU_SetColor(colorKey, r, g, b)
|
||||
MTH_RefreshICUOptions()
|
||||
end
|
||||
end
|
||||
ColorPickerFrame:SetColorRGB(currentR, currentG, currentB)
|
||||
ColorPickerFrame.hasOpacity = false
|
||||
ColorPickerFrame.opacity = 1
|
||||
ColorPickerFrame.previousValues = { currentR, currentG, currentB }
|
||||
ColorPickerFrame:Show()
|
||||
end
|
||||
|
||||
local function MTH_ICU_BuildUI(container)
|
||||
MTH_ICU_OPT_STATE.controls = {}
|
||||
MTH_ICU_OPT_STATE.container = container
|
||||
MTH_ICU_OPT_STATE.built = true
|
||||
|
||||
local controls = MTH_ICU_OPT_STATE.controls
|
||||
|
||||
local panel = MTH_Layout.Panel(container)
|
||||
panel:Title("MTH ICU")
|
||||
panel:Note("ICU is a minimap targeting utility module for your Trackings. It provides enhanced target information and click actions for players and NPCs on the minimap.",
|
||||
{ lines = 2 })
|
||||
|
||||
-- Standard UIDropDownMenu initialize factory for a stored option key.
|
||||
local function makeInit(key)
|
||||
return function()
|
||||
local values = MTH_ICU_GetOptionValues(key)
|
||||
local store = MTH_ICU_GetStore()
|
||||
local current = tostring(store[key] or "")
|
||||
local infoFactory = UIDropDownMenu_CreateInfo
|
||||
for i = 1, table.getn(values) do
|
||||
local optionValue = tostring(values[i])
|
||||
local info = type(infoFactory) == "function" and infoFactory() or {}
|
||||
info.text = optionValue
|
||||
info.checked = optionValue == current and 1 or nil
|
||||
info.func = function()
|
||||
MTH_ICU_SetValue(key, optionValue)
|
||||
-- ANCHOR toggles whether the Toggle Anchor / Expand Up
|
||||
-- controls exist, so rebuild instead of a plain refresh.
|
||||
if key == "ANCHOR" then
|
||||
MTH_SetupICUOptions()
|
||||
else
|
||||
MTH_RefreshICUOptions()
|
||||
end
|
||||
end
|
||||
if UIDropDownMenu_AddButton then UIDropDownMenu_AddButton(info) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ===== LEFT: Module =====
|
||||
local moduleBox = panel:Box("left", "MTH_ICUModuleBox", "Module")
|
||||
controls.moduleEnabled = moduleBox:Checkbox("MetaHuntOptionsICUModuleEnabled", "Enable ICU module", {
|
||||
onClick = function()
|
||||
if MTH and MTH.SetModuleEnabled then
|
||||
local ok, err = MTH:SetModuleEnabled("icu", this:GetChecked() == 1)
|
||||
if not ok and MTH and MTH.Print then
|
||||
MTH:Print("Failed to change ICU module state: " .. tostring(err), "error")
|
||||
end
|
||||
end
|
||||
MTH_RefreshICUOptions()
|
||||
end,
|
||||
})
|
||||
controls.mouseOver = moduleBox:Checkbox("MetaHuntOptionsICUMouseOver", "Enable CTRL mouseover scan", {
|
||||
onClick = function()
|
||||
local store = MTH_ICU_GetStore()
|
||||
store.mouseOver = this:GetChecked() == 1 and true or false
|
||||
end,
|
||||
})
|
||||
|
||||
-- ===== LEFT: General Settings =====
|
||||
local genBox = panel:Box("left", "MTH_ICUGeneralBox", "General Settings")
|
||||
controls.ALERT = { dropdown = genBox:Dropdown("MetaHuntOptionsICUAlertDrop",
|
||||
{ label = "ALERT PvP", width = 130, initialize = makeInit("ALERT") }) }
|
||||
controls.ANNOUNCE = { dropdown = genBox:Dropdown("MetaHuntOptionsICUAnnounceDrop",
|
||||
{ label = "ANNOUNCE on click", width = 130, initialize = makeInit("ANNOUNCE") }) }
|
||||
controls.HEALTH_TEXT_MODE = { dropdown = genBox:Dropdown("MetaHuntOptionsICUHealthModeDrop",
|
||||
{ label = "Health text", width = 130, initialize = makeInit("HEALTH_TEXT_MODE") }) }
|
||||
controls.POPUP_HIDE_DELAY = { dropdown = genBox:Dropdown("MetaHuntOptionsICUHideDelayDrop",
|
||||
{ label = "List hide delay", width = 130, initialize = makeInit("POPUP_HIDE_DELAY") }) }
|
||||
controls.ANCHOR = { dropdown = genBox:Dropdown("MetaHuntOptionsICUAnchorDrop",
|
||||
{ label = "ANCHOR", width = 130, initialize = makeInit("ANCHOR") }) }
|
||||
-- The manual-anchor controls only apply to a CUSTOM anchor. Build them
|
||||
-- only in that case so the box never reserves empty space for them.
|
||||
if tostring(MTH_ICU_GetStore().ANCHOR or "") == "CUSTOM" then
|
||||
controls.customAnchorButton = genBox:Button("MetaHuntOptionsICUToggleAnchorButton", "Toggle Anchor", {
|
||||
width = 116,
|
||||
onClick = function() if type(ICU_ToggleAnchor) == "function" then ICU_ToggleAnchor() end end,
|
||||
})
|
||||
controls.expandUp = genBox:Checkbox("MetaHuntOptionsICUExpandUp", "Expand Up", {
|
||||
onClick = function()
|
||||
local store = MTH_ICU_GetStore()
|
||||
store.EXPAND_UP = this:GetChecked() == 1 and true or false
|
||||
if type(ICU_SetPoints) == "function" then ICU_SetPoints() end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
-- ===== RIGHT: Background Colors =====
|
||||
local bgBox = panel:Box("right", "MTH_ICUBgBox", "Background Colors")
|
||||
controls.PLAYER_COLOR_MODE = { dropdown = bgBox:Dropdown("MetaHuntOptionsICUPlayerColorModeDrop",
|
||||
{ label = "Player colors", width = 130, initialize = makeInit("PLAYER_COLOR_MODE") }) }
|
||||
controls.reactionOnlyPvp = bgBox:Checkbox("MetaHuntOptionsICUReactionOnlyPvp", "Reaction only when PvP-flagged", {
|
||||
onClick = function()
|
||||
local store = MTH_ICU_GetStore()
|
||||
store.REACTION_ONLY_PVP_PLAYERS = this:GetChecked() == 1 and true or false
|
||||
end,
|
||||
})
|
||||
|
||||
-- ===== RIGHT: Color Pickers =====
|
||||
local colorBox = panel:Box("right", "MTH_ICUColorBox", "Color Pickers")
|
||||
colorBox:Note("MetaHuntOptionsICUColorsScope",
|
||||
"Player pickers: REACTION / FACTION / CUSTOM\nNPC pickers: always", { lines = 2 })
|
||||
for i = 1, table.getn(MTH_ICU_COLOR_ROWS) do
|
||||
local row = MTH_ICU_COLOR_ROWS[i]
|
||||
local btn, swatch = colorBox:ColorRow("MetaHuntOptionsICUColor" .. tostring(i), {
|
||||
label = row.label,
|
||||
buttonText = "Pick",
|
||||
buttonWidth = 78,
|
||||
onClick = function() MTH_ICU_OpenColorPicker(row.key) end,
|
||||
})
|
||||
controls[row.key] = { button = btn, swatch = swatch, isColor = true }
|
||||
end
|
||||
|
||||
-- ===== Player Info (placed on whichever column has the most room) =====
|
||||
local infoBox = panel:Box(panel:ShorterSide(), "MTH_ICUInfoBox", "Player Info")
|
||||
controls.showGuildName = infoBox:Checkbox("MetaHuntOptionsICUShowGuild", "Show player guild names", {
|
||||
onClick = function()
|
||||
local store = MTH_ICU_GetStore()
|
||||
store.SHOW_GUILD_NAME = this:GetChecked() == 1 and true or false
|
||||
end,
|
||||
})
|
||||
controls.showPlayerClass = infoBox:Checkbox("MetaHuntOptionsICUShowClass", "Show player class", {
|
||||
onClick = function()
|
||||
local store = MTH_ICU_GetStore()
|
||||
store.SHOW_PLAYER_CLASS = this:GetChecked() == 1 and true or false
|
||||
end,
|
||||
})
|
||||
controls.showPlayerRace = infoBox:Checkbox("MetaHuntOptionsICUShowRace", "Show player race", {
|
||||
onClick = function()
|
||||
local store = MTH_ICU_GetStore()
|
||||
store.SHOW_PLAYER_RACE = this:GetChecked() == 1 and true or false
|
||||
end,
|
||||
})
|
||||
controls.showCustomTitles = infoBox:Checkbox("MetaHuntOptionsICUShowTitles", "Show custom titles in alerts", {
|
||||
onClick = function()
|
||||
local store = MTH_ICU_GetStore()
|
||||
store.SHOW_CUSTOM_TITLES = this:GetChecked() == 1 and true or false
|
||||
end,
|
||||
})
|
||||
|
||||
panel:Finish()
|
||||
end
|
||||
|
||||
function MTH_RefreshICUOptions()
|
||||
if not MTH_ICU_OPT_READY then return end
|
||||
|
||||
local controls = MTH_ICU_OPT_STATE.controls
|
||||
if type(controls) ~= "table" then
|
||||
return
|
||||
end
|
||||
|
||||
local store = MTH_ICU_GetStore()
|
||||
local moduleEnabled = MTH and MTH.IsModuleEnabled and MTH:IsModuleEnabled("icu", false) or false
|
||||
|
||||
if controls.moduleEnabled then
|
||||
controls.moduleEnabled:SetChecked(moduleEnabled and 1 or nil)
|
||||
end
|
||||
|
||||
if controls.mouseOver then
|
||||
controls.mouseOver:SetChecked(store.mouseOver and 1 or nil)
|
||||
if controls.mouseOver.Enable then
|
||||
if moduleEnabled then controls.mouseOver:Enable() else controls.mouseOver:Disable() end
|
||||
end
|
||||
end
|
||||
|
||||
if controls.showGuildName then
|
||||
controls.showGuildName:SetChecked(store.SHOW_GUILD_NAME and 1 or nil)
|
||||
if controls.showGuildName.Enable then
|
||||
if moduleEnabled then controls.showGuildName:Enable() else controls.showGuildName:Disable() end
|
||||
end
|
||||
end
|
||||
|
||||
if controls.showPlayerClass then
|
||||
controls.showPlayerClass:SetChecked(store.SHOW_PLAYER_CLASS and 1 or nil)
|
||||
if controls.showPlayerClass.Enable then
|
||||
if moduleEnabled then controls.showPlayerClass:Enable() else controls.showPlayerClass:Disable() end
|
||||
end
|
||||
end
|
||||
|
||||
if controls.showPlayerRace then
|
||||
controls.showPlayerRace:SetChecked(store.SHOW_PLAYER_RACE and 1 or nil)
|
||||
if controls.showPlayerRace.Enable then
|
||||
if moduleEnabled then controls.showPlayerRace:Enable() else controls.showPlayerRace:Disable() end
|
||||
end
|
||||
end
|
||||
|
||||
if controls.showCustomTitles then
|
||||
controls.showCustomTitles:SetChecked(store.SHOW_CUSTOM_TITLES and 1 or nil)
|
||||
if controls.showCustomTitles.Enable then
|
||||
if moduleEnabled then controls.showCustomTitles:Enable() else controls.showCustomTitles:Disable() end
|
||||
end
|
||||
end
|
||||
|
||||
local isCustomAnchor = tostring(store.ANCHOR or "") == "CUSTOM"
|
||||
|
||||
if controls.customAnchorButton then
|
||||
if moduleEnabled and isCustomAnchor then
|
||||
controls.customAnchorButton:Show()
|
||||
if controls.customAnchorButton.Enable then controls.customAnchorButton:Enable() end
|
||||
else
|
||||
controls.customAnchorButton:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
if controls.expandUp then
|
||||
controls.expandUp:SetChecked(store.EXPAND_UP and 1 or nil)
|
||||
if moduleEnabled and isCustomAnchor then
|
||||
controls.expandUp:Show()
|
||||
if controls.expandUp.Enable then controls.expandUp:Enable() end
|
||||
else
|
||||
controls.expandUp:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local optionKeys = { "ALERT", "ANNOUNCE", "ANCHOR", "HEALTH_TEXT_MODE", "POPUP_HIDE_DELAY", "PLAYER_COLOR_MODE" }
|
||||
for i = 1, table.getn(optionKeys) do
|
||||
local key = optionKeys[i]
|
||||
local row = controls[key]
|
||||
if row and row.dropdown then
|
||||
MTH_ICU_DropdownSetText(row.dropdown, tostring(store[key] or ""))
|
||||
if UIDropDownMenu_EnableDropDown and UIDropDownMenu_DisableDropDown then
|
||||
if moduleEnabled then
|
||||
UIDropDownMenu_EnableDropDown(row.dropdown)
|
||||
else
|
||||
UIDropDownMenu_DisableDropDown(row.dropdown)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if controls.reactionOnlyPvp then
|
||||
controls.reactionOnlyPvp:SetChecked(store.REACTION_ONLY_PVP_PLAYERS and 1 or nil)
|
||||
if controls.reactionOnlyPvp.Enable then
|
||||
if moduleEnabled and tostring(store.PLAYER_COLOR_MODE or "") == "REACTION" then
|
||||
controls.reactionOnlyPvp:Enable()
|
||||
else
|
||||
controls.reactionOnlyPvp:Disable()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, table.getn(MTH_ICU_COLOR_ROWS) do
|
||||
local row = MTH_ICU_COLOR_ROWS[i]
|
||||
local ctrl = controls[row.key]
|
||||
if ctrl and ctrl.swatch then
|
||||
local r, g, b = MTH_ICU_GetColor(row.key)
|
||||
ctrl.swatch:SetVertexColor(r, g, b)
|
||||
if ctrl.button and ctrl.button.Enable then
|
||||
if moduleEnabled then ctrl.button:Enable() else ctrl.button:Disable() end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_SetupICUOptions()
|
||||
local container = MTH_GetFrame("MetaHuntOptionsICU")
|
||||
if not container then return end
|
||||
|
||||
if not MTH_ICU_OPT_READY then
|
||||
if type(MTH_ClearContainer) == "function" then
|
||||
MTH_ClearContainer(container)
|
||||
end
|
||||
local title = container:CreateFontString("MetaHuntOptionsICUErrorTitle", "ARTWORK", "GameFontHighlight")
|
||||
title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -10)
|
||||
title:SetText("MTH ICU")
|
||||
|
||||
local body = container:CreateFontString("MetaHuntOptionsICUErrorBody", "ARTWORK", "GameFontNormalSmall")
|
||||
body:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -34)
|
||||
body:SetWidth(620)
|
||||
body:SetJustifyH("LEFT")
|
||||
body:SetJustifyV("TOP")
|
||||
body:SetTextColor(1, 0.3, 0.3)
|
||||
body:SetText("ICU options cannot initialize. Missing dependencies: " .. table.concat(MTH_ICU_OPT_MISSING, ", "))
|
||||
return
|
||||
end
|
||||
|
||||
local ok, err = pcall(function()
|
||||
MTH_ICU_BuildUI(container)
|
||||
MTH_RefreshICUOptions()
|
||||
end)
|
||||
if not ok then
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print("[MTH ICU OPTIONS] setup failed: " .. tostring(err), "error")
|
||||
end
|
||||
if type(MTH_ClearContainer) == "function" then
|
||||
MTH_ClearContainer(container)
|
||||
end
|
||||
local title = container:CreateFontString("MetaHuntOptionsICUCrashTitle", "ARTWORK", "GameFontHighlight")
|
||||
title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -10)
|
||||
title:SetText("MTH ICU")
|
||||
local body = container:CreateFontString("MetaHuntOptionsICUCrashBody", "ARTWORK", "GameFontNormalSmall")
|
||||
body:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -34)
|
||||
body:SetWidth(620)
|
||||
body:SetJustifyH("LEFT")
|
||||
body:SetJustifyV("TOP")
|
||||
body:SetTextColor(1, 0.3, 0.3)
|
||||
body:SetText("ICU options failed to render. Check chat/debug for details.")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,608 @@
|
||||
-- =====================================================================
|
||||
-- MetaHunt Option Panel Layout Engine
|
||||
-- ---------------------------------------------------------------------
|
||||
-- One reusable builder that every option panel uses. It removes ALL
|
||||
-- hand-typed pixel offsets and box heights from the panels: you declare
|
||||
-- rows, the engine stacks them and auto-sizes each box.
|
||||
--
|
||||
-- Think of it as CSS + flexbox for WoW 1.12:
|
||||
-- * MTH_Layout.Theme -> the "CSS variables" (spacing / fonts / colors).
|
||||
-- Change a value here to restyle every panel.
|
||||
-- * panel / box methods -> the "flexbox": append rows, boxes grow to fit.
|
||||
--
|
||||
-- Design rules baked in (so the classic bugs are impossible):
|
||||
-- * Each box TITLE lives INSIDE its box -> titles can never collide.
|
||||
-- * Box HEIGHT is computed from its content -> never cramped/too short.
|
||||
-- * Box CONTENT is parented to the box frame -> correct z-order.
|
||||
-- * Everything is reuse-by-name (getglobal) -> no duplicates on /rl.
|
||||
--
|
||||
-- Typical usage:
|
||||
-- local panel = MTH_Layout.Panel(container)
|
||||
-- panel:Title("Feed-O-Matic")
|
||||
-- panel:Note("blue helper paragraph", { lines = 3 })
|
||||
-- panel:TopCheckbox("MyEnable", "Enable module", { onClick = fn, checked = fn })
|
||||
-- local box = panel:Box("left", "MyBoxName", "General")
|
||||
-- box:Text("MyStatus", "Feed key: P")
|
||||
-- box:Buttons({ { name="A", label="Set Key", width=90, onClick=fn },
|
||||
-- { name="B", label="Clear", width=70, onClick=fn } })
|
||||
-- box:Note("MyHint", "blue helper text", { lines = 3 })
|
||||
-- local cb = box:Checkbox("MyOpt", "Some option", { onClick = fn, checked = true })
|
||||
-- box:EditRow("MyLbl", "Keep open bag slots:", "MyEdit", 40, { numeric = true, onChange = fn })
|
||||
-- panel:Finish()
|
||||
-- =====================================================================
|
||||
|
||||
MTH_Layout = MTH_Layout or {}
|
||||
local L = MTH_Layout
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- THEME ("CSS variables": tweak these to restyle the whole UI)
|
||||
-- ---------------------------------------------------------------------
|
||||
L.Theme = {
|
||||
-- Spacing (pixels)
|
||||
panelPad = 12, -- outer gutter from the panel edges
|
||||
colGap = 12, -- horizontal gap between the two columns
|
||||
boxGap = 16, -- vertical gap between stacked boxes
|
||||
colTopGap = 8, -- gap between the header block and the first boxes
|
||||
|
||||
boxPadX = 10, -- content inset from a box's left edge
|
||||
boxPadTop = 28, -- first row offset from a box's top (clears inside title)
|
||||
boxPadBottom = 12, -- padding beneath the last row inside a box
|
||||
cbInset = 8, -- checkbox inset from a box's left edge
|
||||
|
||||
rowCheckbox = 24, -- vertical step consumed by a checkbox / edit row
|
||||
rowButton = 30, -- vertical step consumed by a button row
|
||||
rowSlider = 46, -- vertical step consumed by a slider (label+track+value)
|
||||
rowDropdown = 50, -- vertical step consumed by a dropdown (label+menu)
|
||||
textLine = 14, -- height of one wrapped text line
|
||||
textGap = 6, -- extra gap after a text/note block
|
||||
|
||||
titleInsetX = 10, -- box title inset X (inside the box)
|
||||
titleInsetY = -12, -- box title inset Y (inside the box)
|
||||
|
||||
headTop = -12, -- Y where the panel header starts
|
||||
headTitleStep = 24, -- vertical step after the panel title
|
||||
headNoteGap = 10, -- extra gap after a header note
|
||||
headRowGap = 8, -- extra gap after a header checkbox
|
||||
headLinkStep = 18, -- vertical step after a header link
|
||||
|
||||
-- Fonts
|
||||
fontPanelTitle = "GameFontNormal", -- panel heading
|
||||
fontBoxTitle = "GameFontNormal", -- box title (recolored)
|
||||
fontLabel = "GameFontNormalSmall", -- checkbox / edit labels
|
||||
fontText = "GameFontNormalSmall", -- plain text
|
||||
fontNote = "GameFontNormalSmall", -- helper paragraphs
|
||||
fontLink = "GameFontHighlightSmall",
|
||||
|
||||
-- Colors { r, g, b }
|
||||
colPanelTitle = { 1.00, 0.82, 0.00 }, -- gold
|
||||
colBoxTitle = { 1.00, 1.00, 1.00 }, -- white
|
||||
colNote = { 0.35, 0.65, 1.00 }, -- blue helper text
|
||||
colText = { 0.93, 0.93, 0.93 }, -- normal text
|
||||
colLink = { 0.40, 0.75, 1.00 }, -- link idle
|
||||
colLinkHover = { 0.60, 0.90, 1.00 }, -- link hover
|
||||
|
||||
-- Templates
|
||||
boxTemplate = "OptionFrameBoxTemplate",
|
||||
checkTemplate = "UICheckButtonTemplate",
|
||||
buttonTemplate = "UIPanelButtonTemplate",
|
||||
editTemplate = "InputBoxTemplate",
|
||||
}
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Small shared helpers
|
||||
-- ---------------------------------------------------------------------
|
||||
local function applyColor(region, c)
|
||||
if region and c then region:SetTextColor(c[1], c[2], c[3]) end
|
||||
end
|
||||
|
||||
local function ensureFontString(parent, name, font)
|
||||
local fs = getglobal(name)
|
||||
if not fs then
|
||||
fs = parent:CreateFontString(name, "ARTWORK", font)
|
||||
end
|
||||
fs:Show()
|
||||
return fs
|
||||
end
|
||||
|
||||
local function ensureFrame(kind, name, parent, template)
|
||||
local f = getglobal(name)
|
||||
if not f then
|
||||
f = CreateFrame(kind, name, parent, template)
|
||||
end
|
||||
f:SetParent(parent)
|
||||
return f
|
||||
end
|
||||
|
||||
-- Attach (or refresh) a checkbox's label, tooltip, checked state and click.
|
||||
-- labelWidth (optional) enables word-wrap; the wrapped text height is returned
|
||||
-- so the caller can grow the row to fit multi-line labels.
|
||||
local function configureCheckbox(cb, label, opts, T, labelWidth)
|
||||
local txt = getglobal(cb:GetName() .. "Text")
|
||||
if not txt then
|
||||
txt = cb:CreateFontString(cb:GetName() .. "Text", "ARTWORK", T.fontLabel)
|
||||
end
|
||||
-- Anchor the label to the checkbox's top-right and let it grow downward so
|
||||
-- wrapped lines never collide with the row above. Centre a single line
|
||||
-- vertically against the checkbox.
|
||||
local cbH = (cb.GetHeight and cb:GetHeight()) or 24
|
||||
local topOff = -((cbH - T.textLine) / 2)
|
||||
if topOff > 0 then topOff = 0 end
|
||||
txt:ClearAllPoints()
|
||||
txt:SetPoint("TOPLEFT", cb, "TOPRIGHT", 4, topOff)
|
||||
if labelWidth and labelWidth > 0 then
|
||||
txt:SetWidth(labelWidth)
|
||||
else
|
||||
txt:SetWidth(0)
|
||||
end
|
||||
txt:SetJustifyH("LEFT")
|
||||
txt:SetJustifyV("TOP")
|
||||
txt:SetText(label or "")
|
||||
txt:Show()
|
||||
|
||||
if opts.checked ~= nil then
|
||||
if type(opts.checked) == "function" then
|
||||
cb:SetChecked(opts.checked())
|
||||
else
|
||||
cb:SetChecked(opts.checked)
|
||||
end
|
||||
end
|
||||
|
||||
if opts.onClick then
|
||||
cb:SetScript("OnClick", opts.onClick)
|
||||
end
|
||||
|
||||
if opts.tooltip and opts.tooltip ~= "" then
|
||||
cb.mthTooltip = opts.tooltip
|
||||
cb:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(cb, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText(cb.mthTooltip, 1, 1, 1, 1, true)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
cb:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
end
|
||||
|
||||
local h = 0
|
||||
if txt.GetStringHeight then h = txt:GetStringHeight() end
|
||||
if (not h or h <= 0) and txt.GetHeight then h = txt:GetHeight() end
|
||||
return h or 0
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Box object (returned by panel:Box). Appends rows top-to-bottom and
|
||||
-- keeps the box height in sync with its content.
|
||||
-- ---------------------------------------------------------------------
|
||||
local function makeBox(container, box, boxWidth, state, T)
|
||||
local self = {}
|
||||
|
||||
local function apply()
|
||||
state.height = -state.cursor + T.boxPadBottom
|
||||
box:SetHeight(state.height)
|
||||
end
|
||||
|
||||
function self:Checkbox(name, label, opts)
|
||||
opts = opts or {}
|
||||
local cb = ensureFrame("CheckButton", name, box, T.checkTemplate)
|
||||
cb:ClearAllPoints()
|
||||
cb:SetPoint("TOPLEFT", box, "TOPLEFT", T.cbInset, state.cursor)
|
||||
cb:Show()
|
||||
-- Wrap width = box content width minus the checkbox frame, the 4px label
|
||||
-- gap, and a 2px right cushion so wrapped text never touches the border.
|
||||
local cbW = (cb.GetWidth and cb:GetWidth()) or 26
|
||||
local labelWidth = boxWidth - T.boxPadX - (T.cbInset + cbW + 4) - 2
|
||||
local txtH = configureCheckbox(cb, label, opts, T, labelWidth)
|
||||
-- A single line keeps the compact row height; a wrapped label grows the
|
||||
-- row to clear its top-centre offset plus a gap before the next row.
|
||||
local rowH = T.rowCheckbox
|
||||
local needed = txtH + 8
|
||||
if txtH > (T.textLine + 4) then needed = txtH + 14 end
|
||||
if needed > rowH then rowH = needed end
|
||||
state.cursor = state.cursor - rowH
|
||||
apply()
|
||||
return cb
|
||||
end
|
||||
|
||||
function self:Text(name, text, opts)
|
||||
opts = opts or {}
|
||||
local fs = ensureFontString(box, name, opts.font or T.fontText)
|
||||
fs:ClearAllPoints()
|
||||
fs:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX, state.cursor)
|
||||
fs:SetWidth(opts.width or (boxWidth - T.boxPadX * 2))
|
||||
fs:SetJustifyH("LEFT")
|
||||
fs:SetJustifyV("TOP")
|
||||
fs:SetText(text or "")
|
||||
applyColor(fs, opts.color or T.colText)
|
||||
-- Reserve vertical space: honour an explicit line count, otherwise
|
||||
-- measure the wrapped height so callers don't have to guess.
|
||||
local reserve
|
||||
if opts.lines then
|
||||
reserve = opts.lines * T.textLine
|
||||
else
|
||||
local h = (fs.GetStringHeight and fs:GetStringHeight()) or 0
|
||||
if (not h or h <= 0) and fs.GetHeight then h = fs:GetHeight() end
|
||||
if not h or h <= 0 then h = T.textLine end
|
||||
reserve = h
|
||||
end
|
||||
state.cursor = state.cursor - reserve - T.textGap
|
||||
apply()
|
||||
return fs
|
||||
end
|
||||
|
||||
-- Blue helper paragraph (same as MM Widget style), usable inside a box.
|
||||
function self:Note(name, text, opts)
|
||||
opts = opts or {}
|
||||
opts.color = opts.color or T.colNote
|
||||
opts.font = opts.font or T.fontNote
|
||||
return self:Text(name, text, opts)
|
||||
end
|
||||
|
||||
-- One row of buttons placed left-to-right.
|
||||
-- list = { { name, label, width, height, onClick, gap, template }, ... }
|
||||
function self:Buttons(list)
|
||||
local out = {}
|
||||
local x = T.boxPadX
|
||||
local i = 1
|
||||
while list and i <= table.getn(list) do
|
||||
local spec = list[i]
|
||||
local b = ensureFrame("Button", spec.name, box, spec.template or T.buttonTemplate)
|
||||
b:ClearAllPoints()
|
||||
b:SetPoint("TOPLEFT", box, "TOPLEFT", x, state.cursor)
|
||||
b:SetWidth(spec.width or 90)
|
||||
b:SetHeight(spec.height or 22)
|
||||
b:SetText(spec.label or "")
|
||||
b:Show()
|
||||
if spec.onClick then b:SetScript("OnClick", spec.onClick) end
|
||||
table.insert(out, b)
|
||||
x = x + (spec.width or 90) + (spec.gap or 8)
|
||||
i = i + 1
|
||||
end
|
||||
state.cursor = state.cursor - T.rowButton
|
||||
apply()
|
||||
return out
|
||||
end
|
||||
|
||||
-- Label + edit box on one row (e.g. "Keep open bag slots: [__]").
|
||||
function self:EditRow(labelName, labelText, editName, editWidth, opts)
|
||||
opts = opts or {}
|
||||
local lbl = ensureFontString(box, labelName, T.fontLabel)
|
||||
lbl:ClearAllPoints()
|
||||
lbl:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX, state.cursor)
|
||||
lbl:SetText(labelText or "")
|
||||
applyColor(lbl, T.colText)
|
||||
|
||||
local edit = ensureFrame("EditBox", editName, box, T.editTemplate)
|
||||
edit:ClearAllPoints()
|
||||
edit:SetPoint("LEFT", lbl, "RIGHT", 8, 0)
|
||||
edit:SetWidth(editWidth or 40)
|
||||
edit:SetHeight(opts.height or 20)
|
||||
if opts.numeric then edit:SetNumeric(true) end
|
||||
edit:SetAutoFocus(false)
|
||||
edit:Show()
|
||||
if opts.onChange then edit:SetScript("OnTextChanged", opts.onChange) end
|
||||
edit:SetScript("OnEnterPressed", function() if this then this:ClearFocus() end end)
|
||||
edit:SetScript("OnEscapePressed", function() if this then this:ClearFocus() end end)
|
||||
|
||||
state.cursor = state.cursor - T.rowCheckbox
|
||||
apply()
|
||||
return edit, lbl
|
||||
end
|
||||
|
||||
-- Slider with a left label and a right-aligned live value readout.
|
||||
-- opts = { label, min, max, step, value, width, onChange, format }
|
||||
-- format(value) -> string (defaults to a rounded integer).
|
||||
function self:Slider(name, opts)
|
||||
opts = opts or {}
|
||||
local lbl = ensureFontString(box, name .. "Caption", T.fontLabel)
|
||||
lbl:ClearAllPoints()
|
||||
lbl:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX, state.cursor)
|
||||
lbl:SetText(opts.label or "")
|
||||
applyColor(lbl, T.colText)
|
||||
|
||||
local valueFs = ensureFontString(box, name .. "Value", T.fontLabel)
|
||||
valueFs:ClearAllPoints()
|
||||
valueFs:SetPoint("TOPRIGHT", box, "TOPRIGHT", -T.boxPadX, state.cursor)
|
||||
valueFs:SetJustifyH("RIGHT")
|
||||
applyColor(valueFs, T.colPanelTitle)
|
||||
|
||||
local slider = ensureFrame("Slider", name, box, "OptionsSliderTemplate")
|
||||
slider:ClearAllPoints()
|
||||
slider:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX + 4, state.cursor - 18)
|
||||
slider:SetWidth(opts.width or (boxWidth - T.boxPadX * 2 - 8))
|
||||
slider:SetMinMaxValues(opts.min or 0, opts.max or 1)
|
||||
slider:SetValueStep(opts.step or 1)
|
||||
slider:Show()
|
||||
|
||||
-- OptionsSliderTemplate ships Low/High/Text children; silence them.
|
||||
local lowFs = getglobal(name .. "Low"); if lowFs then lowFs:SetText(""); lowFs:Hide() end
|
||||
local highFs = getglobal(name .. "High"); if highFs then highFs:SetText(""); highFs:Hide() end
|
||||
local midFs = getglobal(name .. "Text"); if midFs then midFs:SetText("") end
|
||||
|
||||
slider.mthValueFs = valueFs
|
||||
slider.mthFormat = opts.format
|
||||
slider.mthOnChange = opts.onChange
|
||||
slider:SetScript("OnValueChanged", function()
|
||||
local v = tonumber(arg1)
|
||||
if not v and this and this.GetValue then v = this:GetValue() end
|
||||
if not v then v = 0 end
|
||||
if this.mthValueFs then
|
||||
if this.mthFormat then
|
||||
this.mthValueFs:SetText(this.mthFormat(v))
|
||||
else
|
||||
this.mthValueFs:SetText(tostring(math.floor(v + 0.5)))
|
||||
end
|
||||
end
|
||||
if this.mthOnChange then this.mthOnChange(v) end
|
||||
end)
|
||||
slider:SetValue(opts.value or opts.min or 0)
|
||||
|
||||
state.cursor = state.cursor - T.rowSlider
|
||||
apply()
|
||||
return slider
|
||||
end
|
||||
|
||||
-- Dropdown menu with a left label. The caller supplies the standard
|
||||
-- UIDropDownMenu initialize function and (optionally) the current text.
|
||||
-- opts = { label, width, initialize, text }
|
||||
function self:Dropdown(name, opts)
|
||||
opts = opts or {}
|
||||
local lbl = ensureFontString(box, name .. "Caption", T.fontLabel)
|
||||
lbl:ClearAllPoints()
|
||||
lbl:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX, state.cursor)
|
||||
lbl:SetText(opts.label or "")
|
||||
applyColor(lbl, T.colText)
|
||||
|
||||
local dd = ensureFrame("Frame", name, box, "UIDropDownMenuTemplate")
|
||||
dd:ClearAllPoints()
|
||||
-- UIDropDownMenu textures carry ~15px of built-in left inset.
|
||||
dd:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX - 15, state.cursor - 16)
|
||||
dd:Show()
|
||||
if UIDropDownMenu_SetWidth then
|
||||
UIDropDownMenu_SetWidth(opts.width or (boxWidth - T.boxPadX * 2 - 20), dd)
|
||||
end
|
||||
if UIDropDownMenu_JustifyText then UIDropDownMenu_JustifyText("LEFT", dd) end
|
||||
if opts.initialize and UIDropDownMenu_Initialize then
|
||||
UIDropDownMenu_Initialize(dd, opts.initialize)
|
||||
end
|
||||
if opts.text and UIDropDownMenu_SetText then
|
||||
UIDropDownMenu_SetText(opts.text, dd)
|
||||
end
|
||||
|
||||
state.cursor = state.cursor - T.rowDropdown
|
||||
apply()
|
||||
return dd
|
||||
end
|
||||
|
||||
-- Single button occupying one button row (thin wrapper over :Buttons).
|
||||
function self:Button(name, label, opts)
|
||||
opts = opts or {}
|
||||
local out = self:Buttons({ {
|
||||
name = name, label = label,
|
||||
width = opts.width, height = opts.height,
|
||||
onClick = opts.onClick, template = opts.template,
|
||||
} })
|
||||
return out[1]
|
||||
end
|
||||
|
||||
-- Label on the left, a colour swatch + "Pick" button on the right.
|
||||
-- opts = { label, color = {r,g,b}, buttonText, buttonWidth, onClick }
|
||||
function self:ColorRow(name, opts)
|
||||
opts = opts or {}
|
||||
local lbl = ensureFontString(box, name .. "Caption", T.fontLabel)
|
||||
lbl:ClearAllPoints()
|
||||
lbl:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX, state.cursor)
|
||||
lbl:SetText(opts.label or "")
|
||||
applyColor(lbl, T.colText)
|
||||
|
||||
local btn = ensureFrame("Button", name .. "Button", box, T.buttonTemplate)
|
||||
btn:ClearAllPoints()
|
||||
btn:SetPoint("TOPRIGHT", box, "TOPRIGHT", -T.boxPadX, state.cursor + 2)
|
||||
btn:SetWidth(opts.buttonWidth or 60)
|
||||
btn:SetHeight(20)
|
||||
btn:SetText(opts.buttonText or "Pick")
|
||||
btn:Show()
|
||||
if opts.onClick then btn:SetScript("OnClick", opts.onClick) end
|
||||
|
||||
local swatch = getglobal(name .. "Swatch")
|
||||
if not swatch then
|
||||
swatch = btn:CreateTexture(name .. "Swatch", "OVERLAY")
|
||||
swatch:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
swatch:SetWidth(12)
|
||||
swatch:SetHeight(12)
|
||||
swatch:SetPoint("LEFT", btn, "LEFT", 5, 0)
|
||||
end
|
||||
if opts.color then
|
||||
swatch:SetVertexColor(opts.color[1], opts.color[2], opts.color[3])
|
||||
end
|
||||
|
||||
state.cursor = state.cursor - T.rowCheckbox
|
||||
apply()
|
||||
return btn, swatch
|
||||
end
|
||||
|
||||
-- Manual vertical spacer.
|
||||
function self:Gap(px)
|
||||
state.cursor = state.cursor - (px or 12)
|
||||
apply()
|
||||
end
|
||||
|
||||
-- Reserve a custom row of the given pixel height. Returns the box frame and
|
||||
-- the TOPLEFT y-offset (within the box) where custom widgets can anchor.
|
||||
-- Use for composite rows the primitives don't cover (e.g. checkbox +
|
||||
-- dropdown + edit box on one line). x anchoring is left to the caller.
|
||||
function self:Row(height)
|
||||
local y = state.cursor
|
||||
state.cursor = state.cursor - (height or T.rowCheckbox)
|
||||
apply()
|
||||
return box, y
|
||||
end
|
||||
|
||||
-- Left content inset (px) for callers positioning custom row widgets.
|
||||
function self:PadX() return T.boxPadX end
|
||||
|
||||
function self:Frame() return box end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------
|
||||
-- Panel object (returned by MTH_Layout.Panel).
|
||||
-- ---------------------------------------------------------------------
|
||||
function L.Panel(container)
|
||||
if not container then return nil end
|
||||
local T = L.Theme
|
||||
MTH_ClearContainer(container)
|
||||
|
||||
local cname = (container.GetName and container:GetName()) or "MTHPanel"
|
||||
local width = (container.GetWidth and container:GetWidth()) or 0
|
||||
if not width or width < 460 then width = 580 end
|
||||
|
||||
local colWidth = math.floor((width - T.panelPad * 2 - T.colGap) / 2)
|
||||
if colWidth < 220 then colWidth = 220 end
|
||||
local leftX = T.panelPad
|
||||
local rightX = T.panelPad + colWidth + T.colGap
|
||||
local fullWidth = width - T.panelPad * 2
|
||||
|
||||
local headY = T.headTop
|
||||
local started = false
|
||||
local col = {
|
||||
left = { x = leftX, y = 0, open = nil },
|
||||
right = { x = rightX, y = 0, open = nil },
|
||||
}
|
||||
local noteId = 0
|
||||
|
||||
local panel = {}
|
||||
|
||||
local function startColumns()
|
||||
if started then return end
|
||||
started = true
|
||||
local top = headY - T.colTopGap
|
||||
col.left.y = top
|
||||
col.right.y = top
|
||||
end
|
||||
|
||||
local function closeSide(side)
|
||||
local c = col[side]
|
||||
if c and c.open then
|
||||
c.y = c.y - c.open.height - T.boxGap
|
||||
c.open = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- ---- Header elements (full width, above the columns) ----
|
||||
function panel:Title(text)
|
||||
local fs = ensureFontString(container, cname .. "_Title", T.fontPanelTitle)
|
||||
fs:ClearAllPoints()
|
||||
fs:SetPoint("TOPLEFT", container, "TOPLEFT", T.panelPad, headY)
|
||||
fs:SetText(text or "")
|
||||
applyColor(fs, T.colPanelTitle)
|
||||
headY = headY - T.headTitleStep
|
||||
return fs
|
||||
end
|
||||
|
||||
function panel:Note(text, opts)
|
||||
opts = opts or {}
|
||||
noteId = noteId + 1
|
||||
local fs = ensureFontString(container, cname .. "_Note" .. noteId, T.fontNote)
|
||||
fs:ClearAllPoints()
|
||||
fs:SetPoint("TOPLEFT", container, "TOPLEFT", T.panelPad, headY)
|
||||
fs:SetWidth(opts.width or fullWidth)
|
||||
fs:SetJustifyH("LEFT")
|
||||
fs:SetJustifyV("TOP")
|
||||
fs:SetText(text or "")
|
||||
applyColor(fs, opts.color or T.colNote)
|
||||
local lines = opts.lines or 1
|
||||
headY = headY - (lines * T.textLine) - T.headNoteGap
|
||||
return fs
|
||||
end
|
||||
|
||||
function panel:Link(name, text, onClick)
|
||||
local btn = ensureFrame("Button", name, container, nil)
|
||||
btn:ClearAllPoints()
|
||||
btn:SetPoint("TOPLEFT", container, "TOPLEFT", T.panelPad, headY)
|
||||
btn:SetHeight(16)
|
||||
btn:SetWidth(fullWidth)
|
||||
local fs = getglobal(name .. "Text")
|
||||
if not fs then
|
||||
fs = btn:CreateFontString(name .. "Text", "ARTWORK", T.fontLink)
|
||||
fs:SetPoint("LEFT", btn, "LEFT", 0, 0)
|
||||
fs:SetJustifyH("LEFT")
|
||||
end
|
||||
fs:SetText(text or "")
|
||||
applyColor(fs, T.colLink)
|
||||
btn:Show()
|
||||
btn:SetScript("OnEnter", function() applyColor(fs, T.colLinkHover) end)
|
||||
btn:SetScript("OnLeave", function() applyColor(fs, T.colLink) end)
|
||||
if onClick then btn:SetScript("OnClick", onClick) end
|
||||
headY = headY - T.headLinkStep
|
||||
return btn
|
||||
end
|
||||
|
||||
function panel:TopCheckbox(name, label, opts)
|
||||
opts = opts or {}
|
||||
local cb = ensureFrame("CheckButton", name, container, T.checkTemplate)
|
||||
cb:ClearAllPoints()
|
||||
cb:SetPoint("TOPLEFT", container, "TOPLEFT", T.panelPad, headY)
|
||||
cb:Show()
|
||||
local txtH = configureCheckbox(cb, label, opts, T, fullWidth - 26)
|
||||
local rowH = T.rowCheckbox
|
||||
if (txtH + 8) > rowH then rowH = txtH + 8 end
|
||||
headY = headY - rowH - T.headRowGap
|
||||
return cb
|
||||
end
|
||||
|
||||
-- ---- Boxes (columns) ----
|
||||
function panel:Box(side, name, title)
|
||||
startColumns()
|
||||
side = (side == "right") and "right" or "left"
|
||||
closeSide(side)
|
||||
local c = col[side]
|
||||
|
||||
local box = ensureFrame("Frame", name, container, T.boxTemplate)
|
||||
box:ClearAllPoints()
|
||||
box:SetPoint("TOPLEFT", container, "TOPLEFT", c.x, c.y)
|
||||
box:SetWidth(colWidth)
|
||||
box:Show()
|
||||
|
||||
local titleFS = getglobal(name .. "Title")
|
||||
if titleFS then
|
||||
titleFS:SetText(title or "")
|
||||
applyColor(titleFS, T.colBoxTitle)
|
||||
titleFS:ClearAllPoints()
|
||||
titleFS:SetPoint("TOPLEFT", box, "TOPLEFT", T.titleInsetX, T.titleInsetY)
|
||||
end
|
||||
|
||||
local state = {
|
||||
cursor = -T.boxPadTop,
|
||||
height = T.boxPadTop + T.boxPadBottom,
|
||||
}
|
||||
box:SetHeight(state.height)
|
||||
c.open = state
|
||||
|
||||
return makeBox(container, box, colWidth, state, T)
|
||||
end
|
||||
|
||||
-- Returns "left" or "right": whichever column currently has more room
|
||||
-- left (i.e. its content bottom is higher). Lets callers auto-balance
|
||||
-- a variable number of boxes without hand-picking columns.
|
||||
function panel:ShorterSide()
|
||||
startColumns()
|
||||
local function bottom(side)
|
||||
local c = col[side]
|
||||
local y = c.y
|
||||
if c.open then y = y - c.open.height - T.boxGap end
|
||||
return y
|
||||
end
|
||||
if bottom("right") > bottom("left") then return "right" end
|
||||
return "left"
|
||||
end
|
||||
|
||||
function panel:Finish()
|
||||
closeSide("left")
|
||||
closeSide("right")
|
||||
end
|
||||
|
||||
-- Expose geometry for callers that need it (rare).
|
||||
panel.colWidth = colWidth
|
||||
panel.fullWidth = fullWidth
|
||||
|
||||
return panel
|
||||
end
|
||||
@@ -0,0 +1,402 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt Profile Management Options Panel
|
||||
------------------------------------------------------
|
||||
|
||||
local MAX_PROFILE_ROWS = 15
|
||||
local MAX_CHAR_ROWS = 10
|
||||
local ROW_H = 26
|
||||
|
||||
-- Row pools — anonymous frames created once against scroll content, reused each refresh
|
||||
local gProfileRows = {} -- [i] = { frame, nameText, infoText, loadBtn, delBtn }
|
||||
local gCharRows = {} -- [i] = { frame, nameText, infoText, copyBtn }
|
||||
|
||||
-- Static element refs created once
|
||||
local gActiveLabel = nil
|
||||
local gSetupDone = false
|
||||
|
||||
-- Pending confirmations — globals so StaticPopup OnAccept can read them
|
||||
MTH_PROFILE_PENDING_LOAD = nil
|
||||
MTH_PROFILE_PENDING_DELETE = nil
|
||||
MTH_PROFILE_PENDING_SAVE = nil
|
||||
|
||||
-- ── Static popup dialogs ───────────────────────────────────────────────────
|
||||
|
||||
StaticPopupDialogs["MTH_PROFILE_LOAD_CONFIRM"] = {
|
||||
text = "Load profile \"%s\"?\n\nCurrent settings will be replaced.\nThe UI will reload to apply all changes.",
|
||||
button1 = "Load & Reload",
|
||||
button2 = "Cancel",
|
||||
OnAccept = function()
|
||||
if MTH_PROFILE_PENDING_LOAD then
|
||||
local ok, err = MTH_Profile_Load(MTH_PROFILE_PENDING_LOAD)
|
||||
MTH_PROFILE_PENDING_LOAD = nil
|
||||
if ok then
|
||||
ReloadUI()
|
||||
elseif MTH and MTH.Print then
|
||||
MTH:Print("Profile load failed: " .. tostring(err), "error")
|
||||
end
|
||||
end
|
||||
end,
|
||||
timeout = 0, whileDead = 0, hideOnEscape = 1,
|
||||
}
|
||||
|
||||
StaticPopupDialogs["MTH_PROFILE_DELETE_CONFIRM"] = {
|
||||
text = "Delete profile \"%s\"?\n\nThis cannot be undone.",
|
||||
button1 = "Delete",
|
||||
button2 = "Cancel",
|
||||
OnAccept = function()
|
||||
if MTH_PROFILE_PENDING_DELETE then
|
||||
MTH_Profile_Delete(MTH_PROFILE_PENDING_DELETE)
|
||||
MTH_PROFILE_PENDING_DELETE = nil
|
||||
MTH_SetupProfilesOptions()
|
||||
end
|
||||
end,
|
||||
timeout = 0, whileDead = 0, hideOnEscape = 1,
|
||||
}
|
||||
|
||||
StaticPopupDialogs["MTH_PROFILE_SAVE_CONFIRM"] = {
|
||||
text = "Profile \"%s\" already exists.\nOverwrite with current settings?",
|
||||
button1 = "Overwrite",
|
||||
button2 = "Cancel",
|
||||
OnAccept = function()
|
||||
if MTH_PROFILE_PENDING_SAVE then
|
||||
MTH_Profile_Save(MTH_PROFILE_PENDING_SAVE)
|
||||
MTH_PROFILE_PENDING_SAVE = nil
|
||||
MTH_SetupProfilesOptions()
|
||||
end
|
||||
end,
|
||||
timeout = 0, whileDead = 0, hideOnEscape = 1,
|
||||
}
|
||||
|
||||
-- ── internal helpers ───────────────────────────────────────────────────────
|
||||
|
||||
local function PROF_TrimString(s)
|
||||
if type(s) ~= "string" then return "" end
|
||||
return string.gsub(s, "^%s*(.-)%s*$", "%1")
|
||||
end
|
||||
|
||||
local function PROF_DoSave(rawName)
|
||||
local name = PROF_TrimString(rawName)
|
||||
if string.len(name) == 0 then
|
||||
if MTH and MTH.Print then MTH:Print("Enter a profile name first.", "error") end
|
||||
return
|
||||
end
|
||||
if string.len(name) > 50 then
|
||||
if MTH and MTH.Print then MTH:Print("Profile name too long (50 chars max).", "error") end
|
||||
return
|
||||
end
|
||||
local existing = type(MTH_SavedVariables) == "table"
|
||||
and type(MTH_SavedVariables.profiles) == "table"
|
||||
and MTH_SavedVariables.profiles[name] ~= nil
|
||||
if existing then
|
||||
MTH_PROFILE_PENDING_SAVE = name
|
||||
StaticPopup_Show("MTH_PROFILE_SAVE_CONFIRM", name)
|
||||
else
|
||||
MTH_Profile_Save(name)
|
||||
if MTH and MTH.Print then MTH:Print("Profile saved: " .. name) end
|
||||
MTH_SetupProfilesOptions()
|
||||
end
|
||||
end
|
||||
|
||||
-- ── scroll frames + row pool creation (idempotent) ────────────────────────
|
||||
|
||||
local function PROF_EnsureScrollFrames(container)
|
||||
if not MTH_GetFrame("MetaHuntOptionsProfilesScroll") then
|
||||
local sf = CreateFrame("ScrollFrame", "MetaHuntOptionsProfilesScroll", container, "UIPanelScrollFrameTemplate")
|
||||
local sc = CreateFrame("Frame", "MetaHuntOptionsProfilesContent", sf)
|
||||
sc:SetWidth(510)
|
||||
sc:SetHeight(MAX_PROFILE_ROWS * ROW_H + 4)
|
||||
sf:SetScrollChild(sc)
|
||||
end
|
||||
if not MTH_GetFrame("MetaHuntOptionsCharScroll") then
|
||||
local csf = CreateFrame("ScrollFrame", "MetaHuntOptionsCharScroll", container, "UIPanelScrollFrameTemplate")
|
||||
local csc = CreateFrame("Frame", "MetaHuntOptionsCharContent", csf)
|
||||
csc:SetWidth(510)
|
||||
csc:SetHeight(MAX_CHAR_ROWS * ROW_H + 4)
|
||||
csf:SetScrollChild(csc)
|
||||
end
|
||||
end
|
||||
|
||||
local function PROF_EnsureProfileRows()
|
||||
local sc = MTH_GetFrame("MetaHuntOptionsProfilesContent")
|
||||
if not sc then return end
|
||||
for i = 1, MAX_PROFILE_ROWS do
|
||||
if not gProfileRows[i] then
|
||||
local row = CreateFrame("Frame", nil, sc)
|
||||
row:SetHeight(ROW_H)
|
||||
row:SetWidth(510)
|
||||
row:SetPoint("TOPLEFT", sc, "TOPLEFT", 0, -(i - 1) * ROW_H)
|
||||
|
||||
local nameText = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
nameText:SetPoint("LEFT", row, "LEFT", 4, 2)
|
||||
nameText:SetWidth(210)
|
||||
nameText:SetJustifyH("LEFT")
|
||||
|
||||
local infoText = row:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
infoText:SetPoint("LEFT", row, "LEFT", 218, 2)
|
||||
infoText:SetWidth(155)
|
||||
infoText:SetJustifyH("LEFT")
|
||||
infoText:SetTextColor(0.55, 0.55, 0.55)
|
||||
|
||||
local loadBtn = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
|
||||
loadBtn:SetWidth(58)
|
||||
loadBtn:SetHeight(20)
|
||||
loadBtn:SetPoint("RIGHT", row, "RIGHT", -62, 0)
|
||||
loadBtn:SetText("Load")
|
||||
|
||||
local delBtn = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
|
||||
delBtn:SetWidth(58)
|
||||
delBtn:SetHeight(20)
|
||||
delBtn:SetPoint("RIGHT", row, "RIGHT", 0, 0)
|
||||
delBtn:SetText("Delete")
|
||||
|
||||
gProfileRows[i] = { frame = row, nameText = nameText, infoText = infoText, loadBtn = loadBtn, delBtn = delBtn }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function PROF_EnsureCharRows()
|
||||
local csc = MTH_GetFrame("MetaHuntOptionsCharContent")
|
||||
if not csc then return end
|
||||
for i = 1, MAX_CHAR_ROWS do
|
||||
if not gCharRows[i] then
|
||||
local row = CreateFrame("Frame", nil, csc)
|
||||
row:SetHeight(ROW_H)
|
||||
row:SetWidth(510)
|
||||
row:SetPoint("TOPLEFT", csc, "TOPLEFT", 0, -(i - 1) * ROW_H)
|
||||
|
||||
local nameText = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
nameText:SetPoint("LEFT", row, "LEFT", 4, 2)
|
||||
nameText:SetWidth(250)
|
||||
nameText:SetJustifyH("LEFT")
|
||||
nameText:SetTextColor(0.70, 0.85, 1.00)
|
||||
|
||||
local infoText = row:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
infoText:SetPoint("LEFT", row, "LEFT", 258, 2)
|
||||
infoText:SetWidth(120)
|
||||
infoText:SetJustifyH("LEFT")
|
||||
infoText:SetTextColor(0.55, 0.55, 0.55)
|
||||
|
||||
local copyBtn = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
|
||||
copyBtn:SetWidth(80)
|
||||
copyBtn:SetHeight(20)
|
||||
copyBtn:SetPoint("RIGHT", row, "RIGHT", 0, 0)
|
||||
copyBtn:SetText("Import")
|
||||
|
||||
gCharRows[i] = { frame = row, nameText = nameText, infoText = infoText, copyBtn = copyBtn }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ── dynamic refresh functions ──────────────────────────────────────────────
|
||||
|
||||
local function PROF_RefreshActive()
|
||||
if not gActiveLabel then return end
|
||||
local active = type(MTH_Profile_GetActive) == "function" and MTH_Profile_GetActive()
|
||||
if active then
|
||||
gActiveLabel:SetText("Active: |cffffd200" .. active .. "|r")
|
||||
gActiveLabel:SetTextColor(0.90, 0.90, 0.90)
|
||||
else
|
||||
gActiveLabel:SetText("Active: |cff888888Custom (unsaved)|r")
|
||||
gActiveLabel:SetTextColor(0.90, 0.90, 0.90)
|
||||
end
|
||||
gActiveLabel:Show()
|
||||
end
|
||||
|
||||
local function PROF_RefreshProfileList()
|
||||
PROF_EnsureProfileRows()
|
||||
local profiles = type(MTH_Profile_GetList) == "function" and MTH_Profile_GetList() or {}
|
||||
local active = type(MTH_Profile_GetActive) == "function" and MTH_Profile_GetActive()
|
||||
local n = table.getn(profiles)
|
||||
|
||||
local sc = MTH_GetFrame("MetaHuntOptionsProfilesContent")
|
||||
if sc then
|
||||
sc:SetHeight(math.max(n * ROW_H + 4, ROW_H))
|
||||
end
|
||||
|
||||
for i = 1, MAX_PROFILE_ROWS do
|
||||
local row = gProfileRows[i]
|
||||
if not row then break end
|
||||
local name = profiles[i]
|
||||
if name then
|
||||
-- Name (with active indicator)
|
||||
if name == active then
|
||||
row.nameText:SetText(name .. " |cff00cc44[active]|r")
|
||||
else
|
||||
row.nameText:SetText(name)
|
||||
end
|
||||
-- Info: date saved by who
|
||||
local p = type(MTH_SavedVariables) == "table"
|
||||
and type(MTH_SavedVariables.profiles) == "table"
|
||||
and MTH_SavedVariables.profiles[name]
|
||||
local info = ""
|
||||
if type(p) == "table" then
|
||||
if p.savedAt and p.savedAt ~= "" then
|
||||
info = p.savedAt
|
||||
end
|
||||
end
|
||||
row.infoText:SetText(info)
|
||||
-- Wire buttons (SetScript replaces previous handler each refresh)
|
||||
local rowName = name
|
||||
row.loadBtn:SetScript("OnClick", function()
|
||||
MTH_PROFILE_PENDING_LOAD = rowName
|
||||
StaticPopup_Show("MTH_PROFILE_LOAD_CONFIRM", rowName)
|
||||
end)
|
||||
row.delBtn:SetScript("OnClick", function()
|
||||
MTH_PROFILE_PENDING_DELETE = rowName
|
||||
StaticPopup_Show("MTH_PROFILE_DELETE_CONFIRM", rowName)
|
||||
end)
|
||||
row.frame:Show()
|
||||
row.loadBtn:Show()
|
||||
row.delBtn:Show()
|
||||
else
|
||||
row.frame:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function PROF_RefreshCharList()
|
||||
PROF_EnsureCharRows()
|
||||
local chars = type(MTH_Profile_GetCharList) == "function" and MTH_Profile_GetCharList() or {}
|
||||
local n = table.getn(chars)
|
||||
|
||||
local csc = MTH_GetFrame("MetaHuntOptionsCharContent")
|
||||
if csc then
|
||||
csc:SetHeight(math.max(n * ROW_H + 4, ROW_H))
|
||||
end
|
||||
|
||||
for i = 1, MAX_CHAR_ROWS do
|
||||
local row = gCharRows[i]
|
||||
if not row then break end
|
||||
local charKey = chars[i]
|
||||
if charKey then
|
||||
local snap = type(MTH_SavedVariables) == "table"
|
||||
and type(MTH_SavedVariables.charSnapshots) == "table"
|
||||
and MTH_SavedVariables.charSnapshots[charKey]
|
||||
local info = (type(snap) == "table" and snap.savedAt) and snap.savedAt or ""
|
||||
row.nameText:SetText(charKey)
|
||||
row.infoText:SetText(info)
|
||||
local ck = charKey
|
||||
row.copyBtn:SetScript("OnClick", function()
|
||||
local ok, pName = MTH_Profile_CopyFromChar(ck)
|
||||
if ok then
|
||||
MTH_SetupProfilesOptions()
|
||||
if MTH and MTH.Print then
|
||||
MTH:Print('Imported "' .. ck .. '" as profile "' .. tostring(pName) .. '".')
|
||||
end
|
||||
end
|
||||
end)
|
||||
row.frame:Show()
|
||||
row.copyBtn:Show()
|
||||
else
|
||||
row.frame:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ── main setup (static elements created once, dynamic lists refreshed every call) ──
|
||||
|
||||
function MTH_SetupProfilesOptions()
|
||||
local container = MTH_GetFrame("MetaHuntOptionsProfiles")
|
||||
if not container then return end
|
||||
|
||||
if not gSetupDone then
|
||||
local panel = MTH_Layout.Panel(container)
|
||||
|
||||
-- Title (engine header, consistent gold styling)
|
||||
local title = panel:Title("Profiles")
|
||||
|
||||
-- Active profile indicator
|
||||
gActiveLabel = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
gActiveLabel:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -4)
|
||||
gActiveLabel:SetWidth(540)
|
||||
gActiveLabel:SetJustifyH("LEFT")
|
||||
|
||||
-- ── Save As section ───────────────────────────────────────────────
|
||||
local saveHdr = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
||||
saveHdr:SetPoint("TOPLEFT", gActiveLabel, "BOTTOMLEFT", 0, -10)
|
||||
saveHdr:SetText("Save current settings as:")
|
||||
saveHdr:SetTextColor(0.90, 0.90, 0.90)
|
||||
|
||||
local eb = CreateFrame("EditBox", "MTH_ProfileNameInput", container, "InputBoxTemplate")
|
||||
eb:SetWidth(240)
|
||||
eb:SetHeight(20)
|
||||
eb:SetMaxLetters(50)
|
||||
eb:SetAutoFocus(false)
|
||||
eb:SetPoint("TOPLEFT", saveHdr, "BOTTOMLEFT", 2, -6)
|
||||
eb:SetScript("OnEnterPressed", function()
|
||||
PROF_DoSave(this:GetText())
|
||||
this:SetText("")
|
||||
this:ClearFocus()
|
||||
end)
|
||||
eb:SetScript("OnEscapePressed", function()
|
||||
this:SetText("")
|
||||
this:ClearFocus()
|
||||
end)
|
||||
|
||||
local saveBtn = CreateFrame("Button", "MTH_ProfileSaveBtn", container, "UIPanelButtonTemplate")
|
||||
saveBtn:SetWidth(70)
|
||||
saveBtn:SetHeight(20)
|
||||
saveBtn:SetPoint("LEFT", eb, "RIGHT", 6, 0)
|
||||
saveBtn:SetText("Save")
|
||||
saveBtn:SetScript("OnClick", function()
|
||||
local box = MTH_GetFrame("MTH_ProfileNameInput")
|
||||
if box then
|
||||
PROF_DoSave(box:GetText())
|
||||
box:SetText("")
|
||||
box:ClearFocus()
|
||||
end
|
||||
end)
|
||||
|
||||
-- ── Saved Profiles section ────────────────────────────────────────
|
||||
local profHdr = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
||||
profHdr:SetPoint("TOPLEFT", eb, "BOTTOMLEFT", -2, -12)
|
||||
profHdr:SetText("Saved Profiles")
|
||||
profHdr:SetTextColor(1.00, 0.82, 0.00)
|
||||
|
||||
local rule1 = container:CreateTexture(nil, "ARTWORK")
|
||||
rule1:SetPoint("TOPLEFT", profHdr, "BOTTOMLEFT", 0, -3)
|
||||
rule1:SetWidth(540)
|
||||
rule1:SetHeight(1)
|
||||
rule1:SetTexture(0.30, 0.30, 0.30, 0.80)
|
||||
|
||||
PROF_EnsureScrollFrames(container)
|
||||
|
||||
local sf = MTH_GetFrame("MetaHuntOptionsProfilesScroll")
|
||||
sf:SetPoint("TOPLEFT", rule1, "BOTTOMLEFT", 0, -4)
|
||||
sf:SetWidth(530)
|
||||
sf:SetHeight(170)
|
||||
sf:Show()
|
||||
|
||||
-- ── Copy from Character section ───────────────────────────────────
|
||||
local charHdr = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
||||
charHdr:SetPoint("TOPLEFT", sf, "BOTTOMLEFT", 0, -10)
|
||||
charHdr:SetText("Copy from Character")
|
||||
charHdr:SetTextColor(1.00, 0.82, 0.00)
|
||||
|
||||
local charNote = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
||||
charNote:SetPoint("TOPLEFT", charHdr, "BOTTOMLEFT", 0, -2)
|
||||
charNote:SetWidth(540)
|
||||
charNote:SetJustifyH("LEFT")
|
||||
charNote:SetText("Snapshots are saved automatically on logout. Click Import to create a profile from one.")
|
||||
charNote:SetTextColor(0.60, 0.60, 0.60)
|
||||
|
||||
local rule2 = container:CreateTexture(nil, "ARTWORK")
|
||||
rule2:SetPoint("TOPLEFT", charNote, "BOTTOMLEFT", 0, -3)
|
||||
rule2:SetWidth(540)
|
||||
rule2:SetHeight(1)
|
||||
rule2:SetTexture(0.30, 0.30, 0.30, 0.80)
|
||||
|
||||
local csf = MTH_GetFrame("MetaHuntOptionsCharScroll")
|
||||
csf:SetPoint("TOPLEFT", rule2, "BOTTOMLEFT", 0, -4)
|
||||
csf:SetWidth(530)
|
||||
csf:SetHeight(140)
|
||||
csf:Show()
|
||||
|
||||
gSetupDone = true
|
||||
end
|
||||
|
||||
-- Refresh dynamic content every time the panel is opened
|
||||
PROF_RefreshActive()
|
||||
PROF_RefreshProfileList()
|
||||
PROF_RefreshCharList()
|
||||
end
|
||||
+180
-6
@@ -3,8 +3,8 @@ 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.CONTENT_TOP_OFFSET = MTH_OPTIONS_CONST.CONTENT_TOP_OFFSET or -30
|
||||
MTH_OPTIONS_CONST.CONTENT_HEIGHT = MTH_OPTIONS_CONST.CONTENT_HEIGHT or 600
|
||||
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
|
||||
@@ -13,6 +13,69 @@ local MTH_OPTIONS_SHELL_READY = MTH_OptionsRequire and MTH_OptionsRequire("optio
|
||||
"MTH_BuildOptionsTree",
|
||||
})
|
||||
|
||||
local function MTH_OPT_L(key, default)
|
||||
if MTH and MTH.GetLocalization then
|
||||
return MTH:GetLocalization(key, default)
|
||||
end
|
||||
return default or key
|
||||
end
|
||||
|
||||
-- Capture our PizzaSauce instance for the options-window open animation (guarded).
|
||||
local MTH_OPT_Sauce = PizzaSauce
|
||||
local function MTH_OPT_AnimateOpen(frame)
|
||||
if not MTH_OPT_Sauce or not frame then return end
|
||||
if type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("ui.options") then return end
|
||||
-- Hide alpha this frame so there is no one-frame opaque flash before onStart.
|
||||
if type(frame.SetAlpha) == "function" then frame:SetAlpha(0) end
|
||||
-- Bigger travel + a springy "outBack" overshoot so the window clearly
|
||||
-- pops up into place instead of a subtle nudge.
|
||||
MTH_OPT_Sauce:SlideIn(frame, "UP", 110, 0.5, "outBack")
|
||||
end
|
||||
|
||||
-- Fade the active settings panel when switching options tabs, unless disabled.
|
||||
local function MTH_OPT_AnimateTabContent(tabKey)
|
||||
if not MTH_OPT_Sauce then return end
|
||||
if type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("ui.optiontabs") then return end
|
||||
if type(MTH_OPTIONS_TABS) ~= "table" then return 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
|
||||
if type(frame.SetAlpha) == "function" then frame:SetAlpha(0) end
|
||||
-- Slide the panel in from the right while fading so the content
|
||||
-- visibly moves, not just brightens.
|
||||
MTH_OPT_Sauce:SlideIn(frame, "RIGHT", 55, 0.3, "outCubic")
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Slide the options window down and out before hiding it, unless disabled.
|
||||
local function MTH_OPT_AnimateClose()
|
||||
local frame = MetaHuntOptions
|
||||
if not frame then return end
|
||||
if not MTH_OPT_Sauce
|
||||
or (type(MTH_FX_IsEnabled) == "function" and not MTH_FX_IsEnabled("ui.optionsclose")) then
|
||||
frame:SetAlpha(1)
|
||||
frame:Hide()
|
||||
return
|
||||
end
|
||||
-- Capture the resting anchor so SlideOut's downward displacement can be
|
||||
-- restored once the window is hidden (otherwise the next open is offset).
|
||||
local p, rel, rp, x, y = frame:GetPoint(1)
|
||||
MTH_OPT_Sauce:SlideOut(frame, "DOWN", 90, 0.26, "inCubic", {
|
||||
onFinish = function()
|
||||
frame:Hide()
|
||||
frame:SetAlpha(1)
|
||||
if p then
|
||||
frame:ClearAllPoints()
|
||||
frame:SetPoint(p, rel, rp, x, y)
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
local function MTH_RegisterEscClose(frameName)
|
||||
if not (frameName and UISpecialFrames) then
|
||||
return
|
||||
@@ -33,7 +96,7 @@ local function MTH_EnsureOptionsFrame()
|
||||
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")
|
||||
MTH:Print(MTH_OPT_L("OPT_ERR_CREATE_OPTIONS_FRAME", "[MTH OPTIONS] Failed to create MetaHuntOptions"), "error")
|
||||
end
|
||||
return nil
|
||||
end
|
||||
@@ -68,8 +131,8 @@ local function MTH_EnsureOptionsFrame()
|
||||
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)
|
||||
closeBtn:SetText(MTH_OPT_L("COMMON_CLOSE_SHORT", "X"))
|
||||
closeBtn:SetScript("OnClick", function() MTH_OPT_AnimateClose() end)
|
||||
end
|
||||
|
||||
MTH_BuildOptionsTree()
|
||||
@@ -110,6 +173,27 @@ function MTH_SelectOptionsTab(tabKey)
|
||||
MTH_SetupGeneralOptions()
|
||||
MTH_OPTIONS_SETUP["General"] = true
|
||||
end
|
||||
elseif tabKey == "Profiles" then
|
||||
if type(MTH_SetupProfilesOptions) == "function" then
|
||||
MTH_SetupProfilesOptions()
|
||||
MTH_OPTIONS_SETUP["Profiles"] = true
|
||||
end
|
||||
elseif tabKey == "Messages" then
|
||||
if not MTH_OPTIONS_SETUP["Messages"] then
|
||||
if type(MTH_SetupMessagesOptions) == "function" then
|
||||
MTH_SetupMessagesOptions()
|
||||
MTH_OPTIONS_SETUP["Messages"] = true
|
||||
end
|
||||
else
|
||||
if type(MTH_SetupMessagesOptions) == "function" then
|
||||
MTH_SetupMessagesOptions()
|
||||
end
|
||||
end
|
||||
elseif tabKey == "ZBar" then
|
||||
if type(MTH_SetupZBarOptions) == "function" then
|
||||
MTH_SetupZBarOptions()
|
||||
MTH_OPTIONS_SETUP["ZBar"] = true
|
||||
end
|
||||
elseif tabKey == "Pet" then
|
||||
if not MTH_OPTIONS_SETUP["Pet"] then
|
||||
MTH_SetupPetOptions()
|
||||
@@ -130,6 +214,13 @@ function MTH_SelectOptionsTab(tabKey)
|
||||
MTH_SetupTrapOptions()
|
||||
MTH_OPTIONS_SETUP["Trap"] = true
|
||||
end
|
||||
elseif tabKey == "Ranged" then
|
||||
if not MTH_OPTIONS_SETUP["Ranged"] then
|
||||
MTH_SetupRangedOptions()
|
||||
MTH_OPTIONS_SETUP["Ranged"] = true
|
||||
else
|
||||
MTH_SetupRangedOptions()
|
||||
end
|
||||
elseif tabKey == "Ammo" then
|
||||
if not MTH_OPTIONS_SETUP["Ammo"] then
|
||||
MTH_SetupAmmoOptions()
|
||||
@@ -152,6 +243,11 @@ function MTH_SelectOptionsTab(tabKey)
|
||||
MTH_SetupToysOptions()
|
||||
MTH_OPTIONS_SETUP["Toys"] = true
|
||||
end
|
||||
elseif tabKey == "Craft" then
|
||||
if not MTH_OPTIONS_SETUP["Craft"] then
|
||||
MTH_SetupCraftOptions()
|
||||
MTH_OPTIONS_SETUP["Craft"] = true
|
||||
end
|
||||
elseif tabKey == "SmartAmmo" then
|
||||
if not MTH_OPTIONS_SETUP["SmartAmmo"] then
|
||||
MTH_SetupSmartAmmoOptions()
|
||||
@@ -170,6 +266,35 @@ function MTH_SelectOptionsTab(tabKey)
|
||||
else
|
||||
MTH_SetupAutoBuyOptions()
|
||||
end
|
||||
elseif tabKey == "AutoQuest" then
|
||||
if type(MTH_SetupAutoQuestOptions) == "function" then
|
||||
MTH_SetupAutoQuestOptions()
|
||||
MTH_OPTIONS_SETUP["AutoQuest"] = true
|
||||
end
|
||||
elseif tabKey == "ExpAmmo" then
|
||||
if not MTH_OPTIONS_SETUP["ExpAmmo"] then
|
||||
if type(MTH_SetupExpAmmoOptions) == "function" then
|
||||
MTH_SetupExpAmmoOptions()
|
||||
MTH_OPTIONS_SETUP["ExpAmmo"] = true
|
||||
end
|
||||
end
|
||||
elseif tabKey == "ICU" then
|
||||
if type(MTH_SetupICUOptions) == "function" then
|
||||
MTH_SetupICUOptions()
|
||||
MTH_OPTIONS_SETUP["ICU"] = true
|
||||
if type(MTH_RefreshICUOptions) == "function" then
|
||||
MTH_RefreshICUOptions()
|
||||
end
|
||||
elseif MTH and MTH.Print then
|
||||
MTH:Print("ICU options setup function is not loaded", "error")
|
||||
end
|
||||
elseif tabKey == "Animations" then
|
||||
if not MTH_OPTIONS_SETUP["Animations"] then
|
||||
if type(MTH_SetupAnimationsOptions) == "function" then
|
||||
MTH_SetupAnimationsOptions()
|
||||
MTH_OPTIONS_SETUP["Animations"] = true
|
||||
end
|
||||
end
|
||||
elseif tabKey == "Credits" then
|
||||
local setupCredits = _G and _G["MTH_SetupCreditsOptions"]
|
||||
if type(setupCredits) == "function" then
|
||||
@@ -185,7 +310,7 @@ function MTH_SelectOptionsTab(tabKey)
|
||||
MTH_SetupChronometerOptions()
|
||||
MTH_OPTIONS_SETUP[tabKey] = true
|
||||
elseif MTH and MTH.Print then
|
||||
MTH:Print("Chronometer options module not loaded", "error")
|
||||
MTH:Print(MTH_OPT_L("OPT_ERR_CHRON_NOT_LOADED", "Chronometer options module not loaded"), "error")
|
||||
end
|
||||
else
|
||||
if _G then
|
||||
@@ -198,6 +323,7 @@ function MTH_SelectOptionsTab(tabKey)
|
||||
end
|
||||
end
|
||||
end
|
||||
MTH_OPT_AnimateTabContent(tabKey)
|
||||
end
|
||||
|
||||
function MTH_ResetAndSelectOptionsTab(tabKey)
|
||||
@@ -209,6 +335,52 @@ function MTH_ResetAndSelectOptionsTab(tabKey)
|
||||
MTH_SelectOptionsTab(tabKey)
|
||||
end
|
||||
|
||||
-- On a blocked realm the options window must be inert too. Instead of drawing any tabs we
|
||||
-- drop an opaque "DISABLED." panel over the whole window (with its own close button so it can
|
||||
-- still be dismissed). Returns true when the realm gate is active.
|
||||
local function MTH_OPTIONS_UpdateRealmGateOverlay(frame)
|
||||
if not frame then return false end
|
||||
local blocked = (MTH and MTH.IsRealmGateBlocked and MTH:IsRealmGateBlocked()) and true or false
|
||||
|
||||
local overlay = frame.realmGateOverlay
|
||||
if not overlay then
|
||||
if not blocked then return false end
|
||||
|
||||
overlay = CreateFrame("Frame", "MetaHuntOptionsRealmGateOverlay", frame)
|
||||
overlay:SetAllPoints(frame)
|
||||
overlay:EnableMouse(true)
|
||||
overlay:EnableMouseWheel(true)
|
||||
overlay:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
if overlay.SetFrameLevel and frame.GetFrameLevel then
|
||||
overlay:SetFrameLevel(frame:GetFrameLevel() + 50)
|
||||
end
|
||||
|
||||
local bg = overlay:CreateTexture(nil, "BACKGROUND")
|
||||
bg:SetAllPoints(overlay)
|
||||
bg:SetTexture("Interface\\Buttons\\WHITE8X8")
|
||||
if bg.SetVertexColor then bg:SetVertexColor(0, 0, 0, 1) end
|
||||
|
||||
local label = overlay:CreateFontString(nil, "OVERLAY")
|
||||
label:SetPoint("CENTER", overlay, "CENTER", 0, 0)
|
||||
if label.SetFont then label:SetFont("Fonts\\FRIZQT__.TTF", 48, "OUTLINE") end
|
||||
label:SetText("DISABLED.")
|
||||
label:SetTextColor(1.0, 0.1, 0.1)
|
||||
|
||||
local close = CreateFrame("Button", nil, overlay, "UIPanelCloseButton")
|
||||
close:SetPoint("TOPRIGHT", overlay, "TOPRIGHT", -4, -4)
|
||||
close:SetScript("OnClick", function() frame:Hide() end)
|
||||
|
||||
frame.realmGateOverlay = overlay
|
||||
end
|
||||
|
||||
if blocked then
|
||||
overlay:Show()
|
||||
else
|
||||
overlay:Hide()
|
||||
end
|
||||
return blocked
|
||||
end
|
||||
|
||||
function MTH_OpenOptions(tabKey)
|
||||
if not MTH_OPTIONS_SHELL_READY then return end
|
||||
local optionsFrame = MTH_EnsureOptionsFrame()
|
||||
@@ -216,5 +388,7 @@ function MTH_OpenOptions(tabKey)
|
||||
return
|
||||
end
|
||||
optionsFrame:Show()
|
||||
MTH_OPT_AnimateOpen(optionsFrame)
|
||||
if MTH_OPTIONS_UpdateRealmGateOverlay(optionsFrame) then return end
|
||||
MTH_SelectOptionsTab(tabKey or "General")
|
||||
end
|
||||
|
||||
+23
-5
@@ -1,4 +1,4 @@
|
||||
MTH_OPTIONS_TREE_STATE = MTH_OPTIONS_TREE_STATE or { zButtons = true, chronometer = true }
|
||||
MTH_OPTIONS_TREE_STATE = MTH_OPTIONS_TREE_STATE or { zButtons = false, chronometer = false }
|
||||
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
|
||||
@@ -27,42 +27,59 @@ MTH_OPTIONS_CONST = MTH_OPTIONS_CONST or {
|
||||
}
|
||||
|
||||
MTH_OPTIONS_TABS = MTH_OPTIONS_TABS or {
|
||||
{ key = "General", label = "General", frame = "MetaHuntOptionsGeneral" },
|
||||
{ key = "Pet", label = "ZPet", frame = "MetaHuntOptionsPet" },
|
||||
{ key = "General", label = "General", frame = "MetaHuntOptionsGeneral" },
|
||||
{ key = "Profiles", label = "Profiles", frame = "MetaHuntOptionsProfiles" },
|
||||
{ key = "Messages", label = "Messages", frame = "MetaHuntOptionsMessages" },
|
||||
{ key = "ZBar", label = "zBar", frame = "MetaHuntOptionsZBar" },
|
||||
{ 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 = "Ranged", label = "ZRanged", frame = "MetaHuntOptionsRanged" },
|
||||
{ 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 = "Craft", label = "ZCraft", frame = "MetaHuntOptionsCraft" },
|
||||
{ key = "SmartAmmo", label = "Smart Ammo", frame = "MetaHuntOptionsSmartAmmo" },
|
||||
{ key = "FeedOMatic", label = "FeedOMatic", frame = "MetaHuntOptionsFeedOMatic" },
|
||||
{ key = "AutoBuy", label = "Auto Buy", frame = "MetaHuntOptionsAutoBuy" },
|
||||
{ key = "AutoQuest", label = "Auto Quest", frame = "MetaHuntOptionsAutoQuest" },
|
||||
{ key = "ICU", label = "ICU", frame = "MetaHuntOptionsICU" },
|
||||
{ key = "ExpAmmo", label = "MM Widget", frame = "MetaHuntOptionsExpAmmo" },
|
||||
{ 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 = "Animations", label = "Animations", frame = "MetaHuntOptionsAnimations" },
|
||||
{ key = "Credits", label = "Credits", frame = "MetaHuntOptionsCredits" },
|
||||
}
|
||||
|
||||
MTH_OPTIONS_TREE = MTH_OPTIONS_TREE or {
|
||||
{ label = "General", key = "General" },
|
||||
{ label = "General", key = "General" },
|
||||
{ label = "Profiles", key = "Profiles" },
|
||||
{ label = "Messages", key = "Messages" },
|
||||
{ label = "Auto Buy", key = "AutoBuy" },
|
||||
{ label = "Auto Quest", key = "AutoQuest" },
|
||||
{ label = "Feed-O-Matic", key = "FeedOMatic" },
|
||||
{ label = "ICU", key = "ICU" },
|
||||
{ label = "MM Widget", key = "ExpAmmo" },
|
||||
{
|
||||
label = "zButtons",
|
||||
node = "zButtons",
|
||||
children = {
|
||||
{ label = "zAmmo", key = "Ammo" },
|
||||
{ label = "zBar", key = "ZBar" },
|
||||
{ label = "zAmmo", key = "Ammo" },
|
||||
{ label = "zPet", key = "Pet" },
|
||||
{ label = "zAspect", key = "Aspect" },
|
||||
{ label = "zTrack", key = "Track" },
|
||||
{ label = "zTrap", key = "Trap" },
|
||||
{ label = "zRanged", key = "Ranged" },
|
||||
{ label = "zMounts", key = "Mounts" },
|
||||
{ label = "zCompanions", key = "Companions" },
|
||||
{ label = "zToys", key = "Toys" },
|
||||
{ label = "zCraft", key = "Craft" },
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -76,6 +93,7 @@ MTH_OPTIONS_TREE = MTH_OPTIONS_TREE or {
|
||||
{ label = "Racial", key = "ChronometerRacial" },
|
||||
},
|
||||
},
|
||||
{ label = "Animations", key = "Animations" },
|
||||
{ label = "Credits", key = "Credits" },
|
||||
}
|
||||
|
||||
|
||||
+1084
-542
File diff suppressed because it is too large
Load Diff
+3
-3
@@ -68,9 +68,9 @@ function MTH_CreateSlider(parent, name, label, minVal, maxVal, step, yOffset)
|
||||
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)
|
||||
slider:SetScript("OnValueChanged", function()
|
||||
local activeSlider = this or this or slider
|
||||
local current = tonumber(arg1)
|
||||
if not current and activeSlider and activeSlider.GetValue then
|
||||
current = tonumber(activeSlider:GetValue())
|
||||
end
|
||||
|
||||
+11
-3
@@ -46,12 +46,12 @@
|
||||
<Frames>
|
||||
<Frame name="$parentNav" enableMouse="true">
|
||||
<Size>
|
||||
<AbsDimension x="120" y="540"/>
|
||||
<AbsDimension x="120" y="600"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="12" y="-72"/>
|
||||
<AbsDimension x="12" y="-30"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
@@ -64,20 +64,28 @@
|
||||
</Backdrop>
|
||||
</Frame>
|
||||
|
||||
<!-- Content Areas -->
|
||||
<Frame name="$parentZBar" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentGeneral" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentProfiles" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentMessages" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentPet" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentTrack" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentAspect" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentTrap" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentRanged" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentAmmo" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentMounts" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentCompanions" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentToys" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentCraft" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentSmartAmmo" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentFeedOMatic" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentAutoBuy" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentAutoQuest" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentICU" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentExpAmmo" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentChronometer" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentAnimations" hidden="true" enableMouse="true"/>
|
||||
<Frame name="$parentCredits" hidden="true" enableMouse="true"/>
|
||||
|
||||
<!-- Top-right close button -->
|
||||
|
||||
@@ -0,0 +1,330 @@
|
||||
-- MetaHunt: auto-train Growl to a freshly tamed pet.
|
||||
--
|
||||
|
||||
local MTH_GROWL_MAX_RANK = 7
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Saved-variable backed toggle (per character), default ON.
|
||||
-- ---------------------------------------------------------------------------
|
||||
local function MTH_Growl_GetStore()
|
||||
if type(MTH_CharSavedVariables) ~= "table" then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.growlTeach) ~= "table" then
|
||||
MTH_CharSavedVariables.growlTeach = {}
|
||||
end
|
||||
local store = MTH_CharSavedVariables.growlTeach
|
||||
if store.enabled == nil then
|
||||
store.enabled = true
|
||||
end
|
||||
return store
|
||||
end
|
||||
|
||||
function MTH_Growl_IsEnabled()
|
||||
return MTH_Growl_GetStore().enabled and true or false
|
||||
end
|
||||
|
||||
function MTH_Growl_SetEnabled(enabled)
|
||||
MTH_Growl_GetStore().enabled = enabled and true or false
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Helpers
|
||||
-- ---------------------------------------------------------------------------
|
||||
local function MTH_Growl_DebugLog(line)
|
||||
if type(MTH_DebugFrame) == "table" and type(MTH_DebugFrame.AddInfo) == "function" then
|
||||
MTH_DebugFrame:AddInfo("[Growl] " .. tostring(line))
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_Growl_GetGlobal(name)
|
||||
if type(getglobal) == "function" then
|
||||
return getglobal(name)
|
||||
end
|
||||
if _G then
|
||||
return _G[name]
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Rank appropriate for a pet of the given level.
|
||||
local function MTH_Growl_RankForLevel(level)
|
||||
local lvl = tonumber(level) or 0
|
||||
if lvl < 1 then lvl = 1 end
|
||||
local rank = math.floor(lvl / 10) + 1
|
||||
if rank < 1 then rank = 1 end
|
||||
if rank > MTH_GROWL_MAX_RANK then rank = MTH_GROWL_MAX_RANK end
|
||||
return rank
|
||||
end
|
||||
|
||||
-- Parse a rank number out of a craft row's name/subtext (e.g. "Growl", "Rank 5").
|
||||
local function MTH_Growl_ParseRank(name, subText)
|
||||
local sub = tostring(subText or "")
|
||||
local _, _, subRank = string.find(sub, "(%d+)")
|
||||
if subRank then return tonumber(subRank) end
|
||||
local nm = tostring(name or "")
|
||||
local _, _, nameRank = string.find(nm, "(%d+)")
|
||||
if nameRank then return tonumber(nameRank) end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Find the hunter spellbook slot for a spell by name (e.g. "Beast Training").
|
||||
local function MTH_Growl_FindSpellSlot(spellName)
|
||||
if not spellName or spellName == "" then return nil end
|
||||
local getSpellName = MTH_Growl_GetGlobal("GetSpellName")
|
||||
if type(getSpellName) ~= "function" then return nil end
|
||||
|
||||
local getNumSpellTabs = MTH_Growl_GetGlobal("GetNumSpellTabs")
|
||||
local getSpellTabInfo = MTH_Growl_GetGlobal("GetSpellTabInfo")
|
||||
if type(getNumSpellTabs) == "function" and type(getSpellTabInfo) == "function" then
|
||||
local numTabs = getNumSpellTabs() or 0
|
||||
for tabIndex = 1, numTabs do
|
||||
local _, _, offset, numSpells = getSpellTabInfo(tabIndex)
|
||||
offset = tonumber(offset) or 0
|
||||
numSpells = tonumber(numSpells) or 0
|
||||
for spellIndex = (offset + 1), (offset + numSpells) do
|
||||
if getSpellName(spellIndex, "spell") == spellName then
|
||||
return spellIndex
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Fallback: linear scan of the spellbook.
|
||||
local nilStreak = 0
|
||||
for spellIndex = 1, 1024 do
|
||||
local nameAtIndex = getSpellName(spellIndex, "spell")
|
||||
if nameAtIndex then
|
||||
nilStreak = 0
|
||||
if nameAtIndex == spellName then
|
||||
return spellIndex
|
||||
end
|
||||
else
|
||||
nilStreak = nilStreak + 1
|
||||
if nilStreak >= 30 then break end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Open the Beast Training window by casting the hunter "Beast Training" spell.
|
||||
local function MTH_Growl_OpenTrainingWindow()
|
||||
local spellName = MTH_Growl_GetGlobal("ZHUNTER_PET_TRAINING") or "Beast Training"
|
||||
local slot = MTH_Growl_FindSpellSlot(spellName)
|
||||
local castSpell = MTH_Growl_GetGlobal("CastSpell")
|
||||
if slot and type(castSpell) == "function" then
|
||||
local getSpellName = MTH_Growl_GetGlobal("GetSpellName")
|
||||
local atSlot = (type(getSpellName) == "function") and getSpellName(slot, "spell") or "?"
|
||||
castSpell(slot, "spell")
|
||||
MTH_Growl_DebugLog("Opened Beast Training via CastSpell(slot=" .. tostring(slot)
|
||||
.. " name='" .. tostring(atSlot) .. "')")
|
||||
return true
|
||||
end
|
||||
local castSpellByName = MTH_Growl_GetGlobal("CastSpellByName")
|
||||
if type(castSpellByName) == "function" then
|
||||
castSpellByName(spellName)
|
||||
MTH_Growl_DebugLog("Opened Beast Training via CastSpellByName('" .. tostring(spellName) .. "') (slot not found)")
|
||||
return true
|
||||
end
|
||||
MTH_Growl_DebugLog("Could not open Beast Training (no spell slot, no CastSpellByName)")
|
||||
return false
|
||||
end
|
||||
|
||||
-- Is the Beast Training / craft window currently visible?
|
||||
local function MTH_Growl_IsTrainingWindowVisible()
|
||||
local frameNames = { "BeastTrainingFrame", "PetTrainingFrame", "CraftFrame" }
|
||||
for i = 1, table.getn(frameNames) do
|
||||
local frame = MTH_Growl_GetGlobal(frameNames[i])
|
||||
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
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Pending auto-train request. Set when a fresh tame is confirmed; cleared once
|
||||
-- we train (or time out). { rank, name, elapsed, closeAfter, done }
|
||||
-- ---------------------------------------------------------------------------
|
||||
local MTH_Growl_Pending = nil
|
||||
|
||||
-- The offer currently awaiting the player's Yes/No answer. { rank, name, level }
|
||||
local MTH_Growl_Offer = nil
|
||||
|
||||
-- While we auto-open the trainer to teach Growl, silence the Beast Training
|
||||
-- scanner's chat feedback (set on confirm; expires after the teach window).
|
||||
local MTH_Growl_SuppressScanUntil = 0
|
||||
function MTH_Growl_IsTeaching()
|
||||
local getTime = MTH_Growl_GetGlobal("GetTime")
|
||||
local now = (type(getTime) == "function" and getTime()) or 0
|
||||
return now < (MTH_Growl_SuppressScanUntil or 0)
|
||||
end
|
||||
|
||||
local function MTH_Growl_TryTrainNow()
|
||||
if not MTH_Growl_Pending or MTH_Growl_Pending.done then
|
||||
return false
|
||||
end
|
||||
|
||||
local getNumCrafts = MTH_Growl_GetGlobal("GetNumCrafts")
|
||||
local getCraftInfo = MTH_Growl_GetGlobal("GetCraftInfo")
|
||||
local doCraft = MTH_Growl_GetGlobal("DoCraft")
|
||||
if type(getNumCrafts) ~= "function" or type(getCraftInfo) ~= "function" or type(doCraft) ~= "function" then
|
||||
return false
|
||||
end
|
||||
|
||||
local total = tonumber(getNumCrafts()) or 0
|
||||
if total <= 0 then
|
||||
return false -- window not populated yet
|
||||
end
|
||||
|
||||
local target = MTH_Growl_Pending.rank
|
||||
local bestIndex, bestRank = nil, 0
|
||||
for i = 1, total do
|
||||
local craftName, craftSub, craftType = getCraftInfo(i)
|
||||
local lname = string.lower(tostring(craftName or ""))
|
||||
if string.find(lname, "growl", 1, true) and tostring(craftType) ~= "header" then
|
||||
local rank = MTH_Growl_ParseRank(craftName, craftSub)
|
||||
if rank then
|
||||
if rank == target then
|
||||
bestIndex, bestRank = i, rank
|
||||
break
|
||||
elseif rank < target and rank > bestRank then
|
||||
-- Highest rank we could give if the exact target isn't offered.
|
||||
bestIndex, bestRank = i, rank
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not bestIndex then
|
||||
return false -- no Growl row found yet
|
||||
end
|
||||
|
||||
doCraft(bestIndex)
|
||||
MTH_Growl_DebugLog("Trained Growl Rank " .. tostring(bestRank)
|
||||
.. " (target " .. tostring(target) .. ") for '" .. tostring(MTH_Growl_Pending.name)
|
||||
.. "' via DoCraft(" .. tostring(bestIndex) .. ")")
|
||||
if MTH and type(MTH.Print) == "function" then
|
||||
MTH:Print("Auto-taught Growl (Rank " .. tostring(bestRank) .. ") to " .. tostring(MTH_Growl_Pending.name) .. ".")
|
||||
end
|
||||
|
||||
MTH_Growl_Pending.done = true
|
||||
if MTH_Growl_Pending.closeAfter then
|
||||
local closeCraft = MTH_Growl_GetGlobal("CloseCraft")
|
||||
if type(closeCraft) == "function" then
|
||||
closeCraft()
|
||||
end
|
||||
end
|
||||
MTH_Growl_Pending = nil
|
||||
return true
|
||||
end
|
||||
|
||||
-- Driver frame: react to the craft window opening/updating, with an OnUpdate
|
||||
-- poll as backup and a timeout so we never leave a stale pending request.
|
||||
local MTH_Growl_Frame = CreateFrame("Frame", "MTH_GrowlTeachFrame")
|
||||
MTH_Growl_Frame:RegisterEvent("CRAFT_SHOW")
|
||||
MTH_Growl_Frame:RegisterEvent("CRAFT_UPDATE")
|
||||
MTH_Growl_Frame:RegisterEvent("PET_TRAINING_SHOW")
|
||||
MTH_Growl_Frame:SetScript("OnEvent", function()
|
||||
MTH_Growl_TryTrainNow()
|
||||
end)
|
||||
MTH_Growl_Frame:SetScript("OnUpdate", function()
|
||||
if not MTH_Growl_Pending then return end
|
||||
local p = MTH_Growl_Pending
|
||||
p.elapsed = (p.elapsed or 0) + (arg1 or 0)
|
||||
|
||||
-- Train as soon as the window is populated.
|
||||
if MTH_Growl_TryTrainNow() then return end
|
||||
|
||||
if p.elapsed > 8 then
|
||||
local getNumCrafts = MTH_Growl_GetGlobal("GetNumCrafts")
|
||||
local crafts = (type(getNumCrafts) == "function" and tonumber(getNumCrafts())) or -1
|
||||
MTH_Growl_DebugLog("Gave up training Growl Rank " .. tostring(p.rank)
|
||||
.. " for '" .. tostring(p.name) .. "' (timeout: windowVisible="
|
||||
.. tostring(MTH_Growl_IsTrainingWindowVisible()) .. " numCrafts=" .. tostring(crafts)
|
||||
.. " openAttempts=" .. tostring(p.openAttempts) .. ")")
|
||||
MTH_Growl_Pending = nil
|
||||
return
|
||||
end
|
||||
|
||||
-- Window not up yet: re-attempt opening every second (the pet may still be
|
||||
-- settling right after the tame, so the first cast can be ignored).
|
||||
if not MTH_Growl_IsTrainingWindowVisible() then
|
||||
p.sinceOpen = (p.sinceOpen or 0) + (arg1 or 0)
|
||||
if p.sinceOpen >= 1.0 then
|
||||
p.sinceOpen = 0
|
||||
p.openAttempts = (p.openAttempts or 0) + 1
|
||||
if p.openAttempts <= 6 then
|
||||
MTH_Growl_OpenTrainingWindow()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Start training the confirmed offer. Called from the popup's Yes button (a
|
||||
-- hardware event), which makes opening the Beast Training window reliable.
|
||||
-- ---------------------------------------------------------------------------
|
||||
local function MTH_Growl_StartTraining(rank, petName)
|
||||
local alreadyOpen = MTH_Growl_IsTrainingWindowVisible()
|
||||
local getTime = MTH_Growl_GetGlobal("GetTime")
|
||||
MTH_Growl_SuppressScanUntil = ((type(getTime) == "function" and getTime()) or 0) + 10
|
||||
MTH_Growl_Pending = {
|
||||
rank = rank,
|
||||
name = petName,
|
||||
elapsed = 0,
|
||||
sinceOpen = 0,
|
||||
openAttempts = 0,
|
||||
closeAfter = not alreadyOpen,
|
||||
done = false,
|
||||
}
|
||||
MTH_Growl_DebugLog("Confirmed: train Growl Rank " .. tostring(rank) .. " for '"
|
||||
.. tostring(petName) .. "' (windowOpen=" .. tostring(alreadyOpen) .. ")")
|
||||
if alreadyOpen then
|
||||
MTH_Growl_TryTrainNow()
|
||||
else
|
||||
MTH_Growl_Pending.openAttempts = 1
|
||||
MTH_Growl_OpenTrainingWindow()
|
||||
end
|
||||
end
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Confirmation popup. Yes = hardware event -> open Beast Training + DoCraft.
|
||||
-- ---------------------------------------------------------------------------
|
||||
StaticPopupDialogs["MTH_GROWL_TEACH"] = {
|
||||
text = "Teach %s to your newly tamed %s?",
|
||||
button1 = YES or "Yes",
|
||||
button2 = NO or "No",
|
||||
OnAccept = function()
|
||||
local offer = MTH_Growl_Offer
|
||||
MTH_Growl_Offer = nil
|
||||
if not offer then return end
|
||||
MTH_Growl_StartTraining(offer.rank, offer.name)
|
||||
end,
|
||||
OnCancel = function()
|
||||
MTH_Growl_Offer = nil
|
||||
end,
|
||||
timeout = 0,
|
||||
whileDead = 1,
|
||||
hideOnEscape = 1,
|
||||
}
|
||||
|
||||
-- ---------------------------------------------------------------------------
|
||||
-- Public entry point: called when a fresh tame is confirmed.
|
||||
-- ---------------------------------------------------------------------------
|
||||
function MTH_Growl_OfferForTamedPet(petName, petLevel)
|
||||
if not MTH_Growl_IsEnabled() then
|
||||
return
|
||||
end
|
||||
if type(StaticPopup_Show) ~= "function" then
|
||||
return
|
||||
end
|
||||
|
||||
local rank = MTH_Growl_RankForLevel(petLevel)
|
||||
MTH_Growl_Offer = { rank = rank, name = petName, level = petLevel }
|
||||
local spellLabel = "Growl (Rank " .. tostring(rank) .. ")"
|
||||
local name = tostring(petName or "your pet")
|
||||
MTH_Growl_DebugLog("Offer pet='" .. name .. "' level=" .. tostring(petLevel) .. " -> " .. spellLabel)
|
||||
StaticPopup_Show("MTH_GROWL_TEACH", spellLabel, name)
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
if not MTH_ST_LifecycleService then
|
||||
MTH_ST_LifecycleService = {}
|
||||
end
|
||||
|
||||
function MTH_ST_LifecycleService:HandleEvent(evt, eventArg1, eventArg2)
|
||||
if type(MTH_ST_HandlePlayerEnteringWorld) == "function" and MTH_ST_HandlePlayerEnteringWorld(evt) then
|
||||
return true, "lifecycle-enter-world"
|
||||
end
|
||||
if type(MTH_ST_HandleSpellcastEvent) == "function" and MTH_ST_HandleSpellcastEvent(evt, eventArg1, eventArg2) then
|
||||
return true, "lifecycle-spellcast"
|
||||
end
|
||||
if type(MTH_ST_HandleUnitPetEvent) == "function" and MTH_ST_HandleUnitPetEvent(evt, eventArg1) then
|
||||
if evt == "UNIT_PET" then
|
||||
if eventArg1 == "player" then
|
||||
return true, "lifecycle-unit-pet-player"
|
||||
end
|
||||
return true, "lifecycle-unit-pet-other"
|
||||
end
|
||||
return true, "lifecycle-unit-pet"
|
||||
end
|
||||
return false
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
if not MTH_ST_RunawayService then
|
||||
MTH_ST_RunawayService = {}
|
||||
end
|
||||
|
||||
function MTH_ST_RunawayService:HandleEvent(evt, eventArg1, _eventArg2)
|
||||
if type(MTH_ST_HandleRunawayEvent) ~= "function" then
|
||||
return false
|
||||
end
|
||||
return MTH_ST_HandleRunawayEvent(evt, eventArg1) and true or false
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
if not MTH_ST_StableScanService then
|
||||
MTH_ST_StableScanService = {}
|
||||
end
|
||||
|
||||
function MTH_ST_StableScanService:HandleEvent(evt, _eventArg1, _eventArg2)
|
||||
if type(MTH_ST_HandleStableUiEvent) ~= "function" then
|
||||
return false
|
||||
end
|
||||
return MTH_ST_HandleStableUiEvent(evt) and true or false
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
if not MTH_ST_VitalsService then
|
||||
MTH_ST_VitalsService = {}
|
||||
end
|
||||
|
||||
function MTH_ST_VitalsService:HandleEvent(evt, eventArg1, _eventArg2)
|
||||
if type(MTH_ST_HandleVitalsEvent) ~= "function" then
|
||||
return false
|
||||
end
|
||||
return MTH_ST_HandleVitalsEvent(evt, eventArg1) and true or false
|
||||
end
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt Profile System
|
||||
-- Captures:
|
||||
-- MTH_SavedVariables.modules (account-wide option values, e.g. feedomatic)
|
||||
-- MTH_CharSavedVariables.modules (per-char option values: ICU, Chronometer,
|
||||
-- AutoBuy, AutoQuest, SmartAmmo, tooltips...)
|
||||
-- MTH_CharSavedVariables.moduleStates (enabled/disabled per char)
|
||||
-- ZHunterMod_Saved / MTH_CharSavedVariables.modules.zhunter (zButtons layout)
|
||||
-- Auto-snapshots current character on PLAYER_LOGOUT into
|
||||
-- MTH_SavedVariables.charSnapshots["CharName-RealmName"]
|
||||
------------------------------------------------------
|
||||
|
||||
local function MTH_Profile_DeepCopy(orig)
|
||||
if type(orig) ~= "table" then return orig end
|
||||
local copy = {}
|
||||
for k, v in orig do
|
||||
copy[MTH_Profile_DeepCopy(k)] = MTH_Profile_DeepCopy(v)
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
-- Shallow-copy a module's saved data, skipping keys that hold large operational
|
||||
-- blobs (e.g. feedomatic's "legacy" table which mirrors FOM_Cooking, FOM_QuestFood
|
||||
-- etc.) — those are runtime migration artifacts, not user configuration settings.
|
||||
local MTH_PROFILE_SKIP_KEYS = { legacy = true, history = true }
|
||||
|
||||
local function MTH_Profile_CopyModules(src)
|
||||
if type(src) ~= "table" then return {} end
|
||||
local out = {}
|
||||
for modName, modData in src do
|
||||
if type(modData) == "table" then
|
||||
local modCopy = {}
|
||||
for k, v in modData do
|
||||
if not MTH_PROFILE_SKIP_KEYS[k] then
|
||||
modCopy[MTH_Profile_DeepCopy(k)] = MTH_Profile_DeepCopy(v)
|
||||
end
|
||||
end
|
||||
out[modName] = modCopy
|
||||
else
|
||||
out[modName] = modData
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
local function MTH_Profile_EnsureStore()
|
||||
if type(MTH_SavedVariables) ~= "table" then
|
||||
MTH_SavedVariables = {}
|
||||
end
|
||||
if type(MTH_SavedVariables.profiles) ~= "table" then
|
||||
MTH_SavedVariables.profiles = {}
|
||||
end
|
||||
if type(MTH_SavedVariables.charSnapshots) ~= "table" then
|
||||
MTH_SavedVariables.charSnapshots = {}
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_Profile_GetCharKey()
|
||||
local name = (type(UnitName) == "function") and UnitName("player") or "Unknown"
|
||||
local realm = (type(GetRealmName) == "function") and GetRealmName() or "Unknown"
|
||||
name = (name and name ~= "") and name or "Unknown"
|
||||
realm = (realm and realm ~= "") and realm or "Unknown"
|
||||
return name .. "-" .. realm
|
||||
end
|
||||
|
||||
local function MTH_Profile_BuildSnapshot()
|
||||
local snap = {}
|
||||
snap.savedBy = MTH_Profile_GetCharKey()
|
||||
snap.savedAt = (type(date) == "function") and date("%Y-%m-%d %H:%M") or ""
|
||||
|
||||
-- Account-wide module option values (e.g. feedomatic)
|
||||
snap.config = (type(MTH_SavedVariables) == "table"
|
||||
and type(MTH_SavedVariables.modules) == "table")
|
||||
and MTH_Profile_CopyModules(MTH_SavedVariables.modules)
|
||||
or {}
|
||||
|
||||
-- Per-character module option values (ICU, Chronometer, AutoBuy, SmartAmmo, etc.)
|
||||
snap.charConfig = (type(MTH_CharSavedVariables) == "table"
|
||||
and type(MTH_CharSavedVariables.modules) == "table")
|
||||
and MTH_Profile_CopyModules(MTH_CharSavedVariables.modules)
|
||||
or {}
|
||||
|
||||
-- Per-character module enabled/disabled states
|
||||
snap.moduleStates = (type(MTH_CharSavedVariables) == "table"
|
||||
and type(MTH_CharSavedVariables.moduleStates) == "table")
|
||||
and MTH_Profile_DeepCopy(MTH_CharSavedVariables.moduleStates)
|
||||
or {}
|
||||
|
||||
-- zButtons config — kept as dedicated key for clarity
|
||||
-- (also lives in charConfig.zhunter but we keep it explicit)
|
||||
local zh = nil
|
||||
if type(MTH_CharSavedVariables) == "table"
|
||||
and type(MTH_CharSavedVariables.modules) == "table"
|
||||
and type(MTH_CharSavedVariables.modules.zhunter) == "table"
|
||||
then
|
||||
zh = MTH_CharSavedVariables.modules.zhunter
|
||||
elseif type(ZHunterMod_Saved) == "table" then
|
||||
zh = ZHunterMod_Saved
|
||||
end
|
||||
snap.zhunter = zh and MTH_Profile_DeepCopy(zh) or {}
|
||||
|
||||
return snap
|
||||
end
|
||||
|
||||
-- Save current settings as a named profile
|
||||
function MTH_Profile_Save(name)
|
||||
if type(name) ~= "string" or string.len(name) == 0 then
|
||||
return false, "invalid name"
|
||||
end
|
||||
MTH_Profile_EnsureStore()
|
||||
MTH_SavedVariables.profiles[name] = MTH_Profile_BuildSnapshot()
|
||||
return true
|
||||
end
|
||||
|
||||
-- Delete a named profile
|
||||
function MTH_Profile_Delete(name)
|
||||
MTH_Profile_EnsureStore()
|
||||
if MTH_SavedVariables.profiles[name] == nil then return false end
|
||||
MTH_SavedVariables.profiles[name] = nil
|
||||
if type(MTH_CharSavedVariables) == "table"
|
||||
and MTH_CharSavedVariables.activeProfile == name
|
||||
then
|
||||
MTH_CharSavedVariables.activeProfile = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Apply a profile to SavedVariables; a /reload is required for changes to take full effect
|
||||
function MTH_Profile_Load(name)
|
||||
MTH_Profile_EnsureStore()
|
||||
local p = MTH_SavedVariables.profiles[name]
|
||||
if type(p) ~= "table" then return false, "profile not found" end
|
||||
|
||||
if type(MTH_CharSavedVariables) ~= "table" then MTH_CharSavedVariables = {} end
|
||||
if type(MTH_CharSavedVariables.modules) ~= "table" then MTH_CharSavedVariables.modules = {} end
|
||||
|
||||
-- Apply account-wide module option values (e.g. feedomatic)
|
||||
if type(p.config) == "table" then
|
||||
if type(MTH_SavedVariables.modules) ~= "table" then
|
||||
MTH_SavedVariables.modules = {}
|
||||
end
|
||||
for modName, modData in p.config do
|
||||
MTH_SavedVariables.modules[modName] = MTH_Profile_DeepCopy(modData)
|
||||
end
|
||||
end
|
||||
|
||||
-- Apply per-character module option values (ICU, Chronometer, AutoBuy, SmartAmmo, etc.)
|
||||
if type(p.charConfig) == "table" then
|
||||
for modName, modData in p.charConfig do
|
||||
MTH_CharSavedVariables.modules[modName] = MTH_Profile_DeepCopy(modData)
|
||||
end
|
||||
end
|
||||
|
||||
-- Apply module enabled/disabled
|
||||
if type(p.moduleStates) == "table" then
|
||||
if type(MTH_CharSavedVariables.moduleStates) ~= "table" then
|
||||
MTH_CharSavedVariables.moduleStates = {}
|
||||
end
|
||||
for k, v in p.moduleStates do
|
||||
MTH_CharSavedVariables.moduleStates[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
-- Apply zButtons (also written into charConfig.zhunter above, but keep explicit)
|
||||
if type(p.zhunter) == "table" then
|
||||
ZHunterMod_Saved = MTH_Profile_DeepCopy(p.zhunter)
|
||||
MTH_CharSavedVariables.modules.zhunter = ZHunterMod_Saved
|
||||
end
|
||||
|
||||
MTH_CharSavedVariables.activeProfile = name
|
||||
return true
|
||||
end
|
||||
|
||||
-- Create a named profile from another character's auto-snapshot
|
||||
-- profileName defaults to charKey if not provided
|
||||
function MTH_Profile_CopyFromChar(charKey, profileName)
|
||||
MTH_Profile_EnsureStore()
|
||||
local snap = MTH_SavedVariables.charSnapshots[charKey]
|
||||
if type(snap) ~= "table" then
|
||||
return false, "no snapshot for " .. tostring(charKey)
|
||||
end
|
||||
local name = (type(profileName) == "string" and string.len(profileName) > 0)
|
||||
and profileName or charKey
|
||||
MTH_SavedVariables.profiles[name] = MTH_Profile_DeepCopy(snap)
|
||||
return true, name
|
||||
end
|
||||
|
||||
-- Return sorted list of profile names
|
||||
function MTH_Profile_GetList()
|
||||
MTH_Profile_EnsureStore()
|
||||
local list = {}
|
||||
for name, _ in MTH_SavedVariables.profiles do
|
||||
table.insert(list, name)
|
||||
end
|
||||
table.sort(list)
|
||||
return list
|
||||
end
|
||||
|
||||
-- Return sorted list of character snapshot keys
|
||||
function MTH_Profile_GetCharList()
|
||||
MTH_Profile_EnsureStore()
|
||||
local list = {}
|
||||
for charKey, _ in MTH_SavedVariables.charSnapshots do
|
||||
table.insert(list, charKey)
|
||||
end
|
||||
table.sort(list)
|
||||
return list
|
||||
end
|
||||
|
||||
-- Return the active profile name for the current character, or nil
|
||||
function MTH_Profile_GetActive()
|
||||
if type(MTH_CharSavedVariables) == "table" then
|
||||
return MTH_CharSavedVariables.activeProfile
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Auto-snapshot on logout so other characters can import this config
|
||||
local MTH_ProfileEventFrame = CreateFrame("Frame", "MTH_ProfileEventFrame")
|
||||
MTH_ProfileEventFrame:RegisterEvent("PLAYER_LOGOUT")
|
||||
MTH_ProfileEventFrame:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_LOGOUT" then
|
||||
MTH_Profile_EnsureStore()
|
||||
MTH_SavedVariables.charSnapshots[MTH_Profile_GetCharKey()] = MTH_Profile_BuildSnapshot()
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,434 @@
|
||||
------------------------------------------------------
|
||||
-- MetaHunt — Unified SavedVariables schema + migration engine
|
||||
------------------------------------------------------
|
||||
-- PURPOSE
|
||||
-- Collapse every legacy / scattered SavedVariable into just TWO persisted
|
||||
-- globals and one consistent shape, so old-addon names (FOM_*, ZHunterMod_Saved,
|
||||
-- MTHSmartAmmo) and ad-hoc key styles disappear.
|
||||
--
|
||||
-- TARGET SHAPE (canonical)
|
||||
-- MTH_SavedVariables (account-wide) = {
|
||||
-- schemaVersion = <MTH_SV_SCHEMA_VERSION>,
|
||||
-- modules = {
|
||||
-- <id> = { schemaVersion = N, settings = { ...camelCase... }, <dataTables> },
|
||||
-- feedomatic = { settings{...}, foodQuality, cooking, questFood,
|
||||
-- addedFoods, removedFoods, localeInfo },
|
||||
-- },
|
||||
-- messages = {...}, moduleStates = {...}, profiles = {...},
|
||||
-- charSnapshots = {...}, versionCheck = {...},
|
||||
-- }
|
||||
-- MTH_CharSavedVariables (per-character) = {
|
||||
-- schemaVersion = <MTH_SV_SCHEMA_VERSION>,
|
||||
-- modules = {
|
||||
-- <id> = { schemaVersion = N, settings{...}, <dataTables> },
|
||||
-- zhunter = { buttons{...}, bar{...}, widgetSpawnLayout },
|
||||
-- antidaze = { settings{ enabled } },
|
||||
-- autostrip = { settings{ enabled, display }, frame{ point, relativePoint, x, y } },
|
||||
-- minimapbutton = { settings{ angle } },
|
||||
-- },
|
||||
-- moduleStates = {...},
|
||||
-- -- pet-system core stays at root (out of scope, heavily referenced):
|
||||
-- MTH_Pets, feedTracking, petTraining, trainScan, stableScan, petSpellScan,
|
||||
-- }
|
||||
--
|
||||
-- RULES
|
||||
-- * One-shot, gated by schemaVersion on each root. Idempotent. Non-destructive
|
||||
-- (data is COPIED into the new location before the old location is cleared).
|
||||
-- * Lua 5.0-safe (no '#', no string.match/gmatch; pairs()/ipairs() only).
|
||||
-- * Testable offline: MTH_SV_EnsureSchema(account, char) accepts explicit roots
|
||||
-- (defaults to the live globals) and returns a report table.
|
||||
------------------------------------------------------
|
||||
|
||||
MTH_SV_SCHEMA_VERSION = 1
|
||||
|
||||
------------------------------------------------------
|
||||
-- Helpers
|
||||
------------------------------------------------------
|
||||
|
||||
local function MTH_SV_IsTable(v)
|
||||
return type(v) == "table"
|
||||
end
|
||||
|
||||
local function MTH_SV_DeepCopy(orig)
|
||||
if type(orig) ~= "table" then
|
||||
return orig
|
||||
end
|
||||
local copy = {}
|
||||
for k, v in pairs(orig) do
|
||||
copy[MTH_SV_DeepCopy(k)] = MTH_SV_DeepCopy(v)
|
||||
end
|
||||
return copy
|
||||
end
|
||||
|
||||
-- Ensure parent[key] is a table and return it.
|
||||
local function MTH_SV_Ensure(parent, key)
|
||||
if type(parent[key]) ~= "table" then
|
||||
parent[key] = {}
|
||||
end
|
||||
return parent[key]
|
||||
end
|
||||
|
||||
-- Pick the first non-nil value from a list of candidates; else default.
|
||||
local function MTH_SV_Coalesce(candidates, default)
|
||||
for _, v in ipairs(candidates) do
|
||||
if v ~= nil then
|
||||
return v
|
||||
end
|
||||
end
|
||||
return default
|
||||
end
|
||||
|
||||
-- Copy every key from src into dst, applying a rename map (oldKey -> newKey).
|
||||
-- Keys not present in the map keep their original name. Existing dst values win
|
||||
-- (so re-running never clobbers already-migrated data). Returns count copied.
|
||||
local function MTH_SV_CopyRenamed(src, dst, renameMap)
|
||||
local moved = 0
|
||||
if not MTH_SV_IsTable(src) then
|
||||
return moved
|
||||
end
|
||||
for oldKey, value in pairs(src) do
|
||||
local newKey = (renameMap and renameMap[oldKey]) or oldKey
|
||||
if dst[newKey] == nil then
|
||||
dst[newKey] = value
|
||||
moved = moved + 1
|
||||
end
|
||||
end
|
||||
return moved
|
||||
end
|
||||
|
||||
-- Count entries in a table (Lua 5.0-safe).
|
||||
local function MTH_SV_Count(t)
|
||||
local n = 0
|
||||
if MTH_SV_IsTable(t) then
|
||||
for _ in pairs(t) do
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
return n
|
||||
end
|
||||
|
||||
------------------------------------------------------
|
||||
-- Per-module migrators
|
||||
-- Each takes the relevant module store table (already ensured) plus context,
|
||||
-- transforms in place, and records notes into the shared report.
|
||||
------------------------------------------------------
|
||||
|
||||
-- SMARTAMMO (per-character)
|
||||
-- OLD: modules.smartammo = { enabled, smartEnabled, reloadEnabled,
|
||||
-- weaponSwapEnabled, legacy = { MTHSmartAmmo = {
|
||||
-- enabled, reload, weaponSwap } } }
|
||||
-- + optional global MTHSmartAmmo = { enabled, reload, weaponSwap }
|
||||
-- NEW: modules.smartammo = { schemaVersion, settings = {
|
||||
-- smartEnabled, reloadEnabled, weaponSwapEnabled } }
|
||||
local function MTH_SV_Migrate_SmartAmmo(store, globalMTHSmartAmmo, report)
|
||||
if not MTH_SV_IsTable(store) then
|
||||
return
|
||||
end
|
||||
local legacy = MTH_SV_IsTable(store.legacy) and store.legacy.MTHSmartAmmo or nil
|
||||
local g = MTH_SV_IsTable(globalMTHSmartAmmo) and globalMTHSmartAmmo or nil
|
||||
|
||||
local settings = MTH_SV_Ensure(store, "settings")
|
||||
|
||||
-- smartEnabled: modern flat -> settings -> legacy.enabled -> global.enabled -> old `enabled` -> true
|
||||
if settings.smartEnabled == nil then
|
||||
settings.smartEnabled = MTH_SV_Coalesce({
|
||||
store.smartEnabled,
|
||||
legacy and legacy.enabled,
|
||||
g and g.enabled,
|
||||
store.enabled,
|
||||
}, true) and true or false
|
||||
end
|
||||
if settings.reloadEnabled == nil then
|
||||
settings.reloadEnabled = MTH_SV_Coalesce({
|
||||
store.reloadEnabled,
|
||||
legacy and legacy.reload,
|
||||
g and g.reload,
|
||||
}, true) and true or false
|
||||
end
|
||||
if settings.weaponSwapEnabled == nil then
|
||||
settings.weaponSwapEnabled = MTH_SV_Coalesce({
|
||||
store.weaponSwapEnabled,
|
||||
legacy and legacy.weaponSwap,
|
||||
g and g.weaponSwap,
|
||||
}, true) and true or false
|
||||
end
|
||||
|
||||
-- Clear the old flat/legacy shape now that settings holds the truth.
|
||||
store.legacy = nil
|
||||
store.enabled = nil
|
||||
store.smartEnabled = nil
|
||||
store.reloadEnabled = nil
|
||||
store.weaponSwapEnabled = nil
|
||||
store.schemaVersion = 1
|
||||
|
||||
report.smartammo = "settings{smartEnabled=" .. tostring(settings.smartEnabled)
|
||||
.. ",reloadEnabled=" .. tostring(settings.reloadEnabled)
|
||||
.. ",weaponSwapEnabled=" .. tostring(settings.weaponSwapEnabled) .. "}"
|
||||
end
|
||||
|
||||
-- AUTOQUEST (per-character)
|
||||
-- OLD (canonical): MTH_CharSavedVariables.autoquest = { scorpokDrazial,
|
||||
-- arrowsForSissies, scorpokTooltip, _migrated } mirrored into
|
||||
-- modules.autoquest = { scorpokDrazial, arrowsForSissies, scorpokTooltip, enabled }
|
||||
-- plus oldest legacy MTH_CharSavedVariables.questautomation.
|
||||
-- NEW: modules.autoquest = { schemaVersion, settings = {
|
||||
-- scorpokDrazial, arrowsForSissies, scorpokTooltip } }
|
||||
local function MTH_SV_Migrate_AutoQuest(char, report)
|
||||
local modules = MTH_SV_Ensure(char, "modules")
|
||||
local store = MTH_SV_Ensure(modules, "autoquest")
|
||||
local settings = MTH_SV_Ensure(store, "settings")
|
||||
|
||||
local root = MTH_SV_IsTable(char.autoquest) and char.autoquest or {}
|
||||
local qa = MTH_SV_IsTable(char.questautomation) and char.questautomation or {}
|
||||
|
||||
local keys = { "scorpokDrazial", "arrowsForSissies", "scorpokTooltip" }
|
||||
for _, k in ipairs(keys) do
|
||||
if settings[k] == nil then
|
||||
settings[k] = MTH_SV_Coalesce({
|
||||
store[k], -- flat mirror on modules.autoquest
|
||||
root[k], -- former canonical root .autoquest
|
||||
qa[k], -- oldest legacy .questautomation
|
||||
}, false) and true or false
|
||||
end
|
||||
end
|
||||
|
||||
-- Clear old / duplicate locations.
|
||||
store.scorpokDrazial = nil
|
||||
store.arrowsForSissies = nil
|
||||
store.scorpokTooltip = nil
|
||||
store._migrated = nil
|
||||
char.autoquest = nil
|
||||
char.questautomation = nil
|
||||
store.schemaVersion = 1
|
||||
|
||||
report.autoquest = "settings{scorpokDrazial=" .. tostring(settings.scorpokDrazial)
|
||||
.. ",arrowsForSissies=" .. tostring(settings.arrowsForSissies)
|
||||
.. ",scorpokTooltip=" .. tostring(settings.scorpokTooltip) .. "}"
|
||||
end
|
||||
|
||||
-- ANTIDAZE (per-character): char-root .antiDaze{enabled} -> modules.antidaze.settings{enabled}
|
||||
local function MTH_SV_Migrate_AntiDaze(char, report)
|
||||
local modules = MTH_SV_Ensure(char, "modules")
|
||||
local store = MTH_SV_Ensure(modules, "antidaze")
|
||||
local settings = MTH_SV_Ensure(store, "settings")
|
||||
local old = MTH_SV_IsTable(char.antiDaze) and char.antiDaze or {}
|
||||
if settings.enabled == nil and old.enabled ~= nil then
|
||||
settings.enabled = old.enabled and true or false
|
||||
end
|
||||
char.antiDaze = nil
|
||||
store.schemaVersion = 1
|
||||
report.antidaze = "settings{enabled=" .. tostring(settings.enabled) .. "}"
|
||||
end
|
||||
|
||||
-- AUTOSTRIP (per-character): char-root .autoStrip{autostrip,display,point,relativePoint,x,y}
|
||||
-- -> modules.autostrip{autostrip,display,point,relativePoint,x,y}
|
||||
-- (keys preserved verbatim; only the storage location moves under .modules)
|
||||
local function MTH_SV_Migrate_AutoStrip(char, report)
|
||||
local modules = MTH_SV_Ensure(char, "modules")
|
||||
local store = MTH_SV_Ensure(modules, "autostrip")
|
||||
local old = MTH_SV_IsTable(char.autoStrip) and char.autoStrip or {}
|
||||
local carry = { "autostrip", "display", "point", "relativePoint", "x", "y" }
|
||||
for _, k in ipairs(carry) do
|
||||
if store[k] == nil and old[k] ~= nil then
|
||||
store[k] = old[k]
|
||||
end
|
||||
end
|
||||
char.autoStrip = nil
|
||||
store.schemaVersion = 1
|
||||
report.autostrip = "store{autostrip=" .. tostring(store.autostrip)
|
||||
.. ",display=" .. tostring(store.display) .. "}"
|
||||
end
|
||||
|
||||
-- MINIMAPBUTTON (per-character): char-root .minimapButton{angle} -> modules.minimapbutton{angle}
|
||||
local function MTH_SV_Migrate_MinimapButton(char, report)
|
||||
local modules = MTH_SV_Ensure(char, "modules")
|
||||
local store = MTH_SV_Ensure(modules, "minimapbutton")
|
||||
local old = MTH_SV_IsTable(char.minimapButton) and char.minimapButton or {}
|
||||
if store.angle == nil and old.angle ~= nil then
|
||||
store.angle = old.angle
|
||||
end
|
||||
char.minimapButton = nil
|
||||
store.schemaVersion = 1
|
||||
report.minimapbutton = "store{angle=" .. tostring(store.angle) .. "}"
|
||||
end
|
||||
|
||||
-- FEEDOMATIC (account-wide): the 7 old FeedOMatic globals were stored under
|
||||
-- modules.feedomatic.legacy.FOM_* (with _G.FOM_* bound as runtime aliases) and
|
||||
-- duplicated at the account-root alias MTH_SavedVariables.feedomatic.
|
||||
-- NEW: modules.feedomatic = { schemaVersion, settings, foodQuality, addedFoods,
|
||||
-- removedFoods, cooking, questFood, localeInfo } — no .legacy wrapper, no root
|
||||
-- alias. The _G.FOM_* globals stay as RUNTIME aliases (re-bound to these nested
|
||||
-- tables by MTH_FeedOMatic_SyncSavedVariables) so FeedOMatic.lua is untouched.
|
||||
local MTH_SV_FOM_MAP = {
|
||||
{ global = "FOM_Config", key = "settings" },
|
||||
{ global = "FOM_FoodQuality", key = "foodQuality" },
|
||||
{ global = "FOM_AddedFoods", key = "addedFoods" },
|
||||
{ global = "FOM_RemovedFoods", key = "removedFoods" },
|
||||
{ global = "FOM_Cooking", key = "cooking" },
|
||||
{ global = "FOM_QuestFood", key = "questFood" },
|
||||
{ global = "FOM_LocaleInfo", key = "localeInfo" },
|
||||
}
|
||||
|
||||
local function MTH_SV_Migrate_FeedOMatic(account, report)
|
||||
local modules = MTH_SV_Ensure(account, "modules")
|
||||
local store = MTH_SV_Ensure(modules, "feedomatic")
|
||||
local legacy = MTH_SV_IsTable(store.legacy) and store.legacy or {}
|
||||
|
||||
for _, m in ipairs(MTH_SV_FOM_MAP) do
|
||||
if store[m.key] == nil then
|
||||
local src = legacy[m.global]
|
||||
if type(src) ~= "table" and _G then
|
||||
src = _G[m.global]
|
||||
end
|
||||
if type(src) == "table" then
|
||||
store[m.key] = src
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
store.legacy = nil
|
||||
account.feedomatic = nil -- drop the account-root alias duplicate
|
||||
store.schemaVersion = 1
|
||||
report.feedomatic = "keys{settings,foodQuality,addedFoods,removedFoods,cooking,questFood,localeInfo}"
|
||||
end
|
||||
|
||||
-- ZHUNTER (per-character): the zButton layout used to live in the TOC-declared
|
||||
-- global ZHunterMod_Saved AND was mirrored into modules.zhunter (two on-disk
|
||||
-- copies). NEW: modules.zhunter is the single canonical store (button tables,
|
||||
-- _zbar, enabled, _mth_widget_spawn_layout_v1). ZHunterMod_Saved becomes a pure
|
||||
-- RUNTIME alias re-bound by MTH_ZH_GetSavedRoot, and is dropped from the TOC.
|
||||
-- The vestigial account-root alias MTH_SavedVariables.zhunter is removed too.
|
||||
local function MTH_SV_Migrate_ZHunter(account, char, report)
|
||||
local modules = MTH_SV_Ensure(char, "modules")
|
||||
local store = MTH_SV_Ensure(modules, "zhunter")
|
||||
|
||||
-- If the nested store is still empty but a per-char global carries data
|
||||
-- (legacy TOC copy present during the transition), adopt it once.
|
||||
if _G and MTH_SV_IsTable(_G.ZHunterMod_Saved) and _G.ZHunterMod_Saved ~= store then
|
||||
if MTH_SV_Count(store) == 0 then
|
||||
for k, v in pairs(_G.ZHunterMod_Saved) do
|
||||
store[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
account.zhunter = nil -- drop the vestigial account-root alias duplicate
|
||||
report.zhunter = "modules.zhunter canonical; ZHunterMod_Saved -> runtime alias"
|
||||
end
|
||||
|
||||
------------------------------------------------------
|
||||
-- Orchestrator
|
||||
------------------------------------------------------
|
||||
|
||||
-- MTH_SV_EnsureSchema(account, char)
|
||||
-- account / char default to the live globals. Returns a report table.
|
||||
-- Gated by schemaVersion on each root so it only runs once; idempotent anyway.
|
||||
function MTH_SV_EnsureSchema(account, char)
|
||||
if account == nil then account = MTH_SavedVariables end
|
||||
if char == nil then char = MTH_CharSavedVariables end
|
||||
|
||||
local report = { ran = false }
|
||||
|
||||
if not MTH_SV_IsTable(account) or not MTH_SV_IsTable(char) then
|
||||
report.error = "roots unavailable"
|
||||
return report
|
||||
end
|
||||
|
||||
local accountDone = (tonumber(account.schemaVersion) or 0) >= MTH_SV_SCHEMA_VERSION
|
||||
local charDone = (tonumber(char.schemaVersion) or 0) >= MTH_SV_SCHEMA_VERSION
|
||||
if accountDone and charDone then
|
||||
report.skipped = true
|
||||
return report
|
||||
end
|
||||
report.ran = true
|
||||
|
||||
local accountModules = MTH_SV_Ensure(account, "modules")
|
||||
local charModules = MTH_SV_Ensure(char, "modules")
|
||||
|
||||
-- Detect whether there is genuine legacy data to relocate BEFORE the migrators
|
||||
-- consume/delete it. On a brand-new install there is nothing to move, so we still
|
||||
-- stamp the schema (to avoid re-checking) but skip the player-facing announcement.
|
||||
local smartLegacy = MTH_SV_IsTable(charModules.smartammo) and charModules.smartammo.legacy or nil
|
||||
local feedLegacy = MTH_SV_IsTable(accountModules.feedomatic) and accountModules.feedomatic.legacy or nil
|
||||
report.changed = (account.feedomatic ~= nil)
|
||||
or (account.zhunter ~= nil)
|
||||
or (char.antiDaze ~= nil)
|
||||
or (char.autoStrip ~= nil)
|
||||
or (char.minimapButton ~= nil)
|
||||
or (char.autoquest ~= nil)
|
||||
or (char.questautomation ~= nil)
|
||||
or (feedLegacy ~= nil)
|
||||
or (smartLegacy ~= nil)
|
||||
or (_G ~= nil and (_G.FOM_Config ~= nil or _G.ZHunterMod_Saved ~= nil or _G.MTHSmartAmmo ~= nil))
|
||||
or false
|
||||
|
||||
-- ---- account-wide modules ----
|
||||
MTH_SV_Migrate_FeedOMatic(account, report)
|
||||
|
||||
-- ---- per-character modules ----
|
||||
MTH_SV_Migrate_SmartAmmo(
|
||||
MTH_SV_Ensure(charModules, "smartammo"),
|
||||
_G and _G.MTHSmartAmmo or nil,
|
||||
report
|
||||
)
|
||||
if _G then _G.MTHSmartAmmo = nil end
|
||||
|
||||
MTH_SV_Migrate_AutoQuest(char, report)
|
||||
MTH_SV_Migrate_AntiDaze(char, report)
|
||||
MTH_SV_Migrate_AutoStrip(char, report)
|
||||
MTH_SV_Migrate_MinimapButton(char, report)
|
||||
MTH_SV_Migrate_ZHunter(account, char, report)
|
||||
|
||||
-- ---- stamp ----
|
||||
account.schemaVersion = MTH_SV_SCHEMA_VERSION
|
||||
char.schemaVersion = MTH_SV_SCHEMA_VERSION
|
||||
|
||||
-- ---- player-facing announcement (only when real legacy data was relocated) ----
|
||||
-- Guarded on MTH_Log so the offline test harness (no MetaHunt runtime) stays silent.
|
||||
if report.changed and type(MTH_Log) == "function" then
|
||||
local order = {
|
||||
"feedomatic", "smartammo", "autoquest",
|
||||
"antidaze", "autostrip", "minimapbutton", "zhunter",
|
||||
}
|
||||
local done = {}
|
||||
for _, id in ipairs(order) do
|
||||
if report[id] ~= nil then
|
||||
table.insert(done, id)
|
||||
end
|
||||
end
|
||||
MTH_Log("Tidying your saved settings into the new 2.0 layout (one-time)...")
|
||||
if table.getn(done) > 0 then
|
||||
MTH_Log(" consolidated: " .. table.concat(done, ", "))
|
||||
end
|
||||
MTH_Log("Saved-settings cleanup complete. Your options were carried over unchanged.")
|
||||
end
|
||||
|
||||
return report
|
||||
end
|
||||
|
||||
------------------------------------------------------
|
||||
-- Load trigger
|
||||
------------------------------------------------------
|
||||
-- Run the migration exactly once, AFTER WoW has loaded MetaHunt's SavedVariables
|
||||
-- from disk. ADDON_LOADED with arg1 == "MetaHunt" is the earliest point at which the
|
||||
-- real account + per-character saved data is actually available. Running it any earlier
|
||||
-- (e.g. from a top-level MTH:InitSavedVariables() at file-load) operates on the empty
|
||||
-- default tables WoW then overwrites when it loads the SV file — which is exactly why
|
||||
-- the migration used to re-announce every login and /reload. CreateFrame is guarded so
|
||||
-- the offline test harness (no WoW API) simply skips this.
|
||||
local MTH_SV_LoadFrame = CreateFrame and CreateFrame("Frame")
|
||||
if MTH_SV_LoadFrame then
|
||||
MTH_SV_LoadFrame:RegisterEvent("ADDON_LOADED")
|
||||
MTH_SV_LoadFrame:SetScript("OnEvent", function()
|
||||
if event ~= "ADDON_LOADED" or arg1 ~= "MetaHunt" then
|
||||
return
|
||||
end
|
||||
MTH_SV_LoadFrame:UnregisterEvent("ADDON_LOADED")
|
||||
if MTH and MTH.InitSavedVariables then
|
||||
MTH:InitSavedVariables()
|
||||
end
|
||||
if type(MTH_SV_EnsureSchema) == "function" then
|
||||
MTH_SV_EnsureSchema(MTH_SavedVariables, MTH_CharSavedVariables)
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -0,0 +1,159 @@
|
||||
-- MetaHunt shared trainer spell status and cost planner.
|
||||
-- UI, chat notifications, and future level-up messages consume these functions.
|
||||
|
||||
local function MTH_TP_Lower(value)
|
||||
return string.lower(tostring(value or ""))
|
||||
end
|
||||
|
||||
local function MTH_TP_RankNumber(rank)
|
||||
local _, _, value = string.find(tostring(rank or ""), "(%d+)")
|
||||
return tonumber(value) or 0
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_GetPrice(row, honored)
|
||||
local base = tonumber(row and row.trainerCost) or 0
|
||||
if honored then return math.floor(base * 0.90) end
|
||||
return base
|
||||
end
|
||||
|
||||
local function MTH_TP_IsKnownHunter(row)
|
||||
if type(MTH_HT_GetSpellInfo) ~= "function" then return false end
|
||||
local known = MTH_HT_GetSpellInfo(row and row.name)
|
||||
if type(known) ~= "table" then return false end
|
||||
local requiredRank = MTH_TP_RankNumber(row and row.rank)
|
||||
return requiredRank == 0 or (tonumber(known.rankNum) or 0) >= requiredRank
|
||||
end
|
||||
|
||||
local function MTH_TP_IsKnownPet(row)
|
||||
if type(MTH_PETS_GetRootStore) ~= "function" then return false end
|
||||
local pets = MTH_PETS_GetRootStore()
|
||||
local store = pets and pets.petStore
|
||||
local petId = store and store.activeCurrentId
|
||||
local pet = store and store.activeById and petId and store.activeById[tostring(petId)]
|
||||
local spells = pet and pet.petSpellbook and pet.petSpellbook.spells
|
||||
if type(spells) ~= "table" then return false end
|
||||
local requiredName = MTH_TP_Lower(row and row.name)
|
||||
local requiredRank = MTH_TP_RankNumber(row and row.rank)
|
||||
for _, spell in ipairs(spells) do
|
||||
if MTH_TP_Lower(spell and spell.name) == requiredName then
|
||||
local knownRank = tonumber(spell.rank) or 0
|
||||
if requiredRank == 0 or knownRank >= requiredRank then return true end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_IsKnown(kind, row)
|
||||
if kind == "hunter" then return MTH_TP_IsKnownHunter(row) end
|
||||
return MTH_TP_IsKnownPet(row)
|
||||
end
|
||||
|
||||
local function MTH_TP_HasMissingPrerequisite(kind, row)
|
||||
if type(row and row.prerequisites) ~= "table" then return false end
|
||||
for _, prerequisite in ipairs(row.prerequisites) do
|
||||
local _, _, name, rank = string.find(tostring(prerequisite), "^(.+)%s%([Rr]ank%s*(%d+)%)$")
|
||||
if name and rank and not MTH_TrainerPlan_IsKnown(kind, { name = name, rank = "Rank " .. rank }) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_GetStatus(kind, row, playerLevel)
|
||||
if MTH_TrainerPlan_IsKnown(kind, row) then return "known" end
|
||||
local observed = type(MTH_GetTrainerLiveStatus) == "function"
|
||||
and MTH_GetTrainerLiveStatus(kind, row and row.name, row and row.rank) or nil
|
||||
if observed and observed.status == "used" then return "known" end
|
||||
if observed and observed.status == "available" then return "notlearned" end
|
||||
if observed and observed.status == "unavailable" then return "notyet" end
|
||||
local requiredLevel = tonumber(row and row.requiredLevel) or 0
|
||||
if not playerLevel or playerLevel < requiredLevel or MTH_TP_HasMissingPrerequisite(kind, row) then
|
||||
return "notyet"
|
||||
end
|
||||
return "notlearned"
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_Build(kind, options)
|
||||
options = options or {}
|
||||
local catalog = MTH_DS_TrainerCatalog
|
||||
local rows = catalog and catalog[kind] or nil
|
||||
local playerLevel = tonumber(options.playerLevel)
|
||||
if not playerLevel and type(UnitLevel) == "function" then playerLevel = tonumber(UnitLevel("player")) end
|
||||
local honored = options.honored and true or false
|
||||
local plan = {
|
||||
kind = kind,
|
||||
playerLevel = playerLevel,
|
||||
honored = honored,
|
||||
rows = {},
|
||||
now = { count = 0, cost = 0 },
|
||||
nextLevel = { level = nil, count = 0, cost = 0 },
|
||||
remaining = { count = 0, cost = 0 },
|
||||
}
|
||||
if type(rows) ~= "table" then return plan end
|
||||
for _, row in ipairs(rows) do
|
||||
local status = MTH_TrainerPlan_GetStatus(kind, row, playerLevel)
|
||||
local price = MTH_TrainerPlan_GetPrice(row, honored)
|
||||
local entry = { row = row, status = status, price = price }
|
||||
table.insert(plan.rows, entry)
|
||||
if status ~= "known" then
|
||||
plan.remaining.count = plan.remaining.count + 1
|
||||
plan.remaining.cost = plan.remaining.cost + price
|
||||
if status == "notlearned" then
|
||||
plan.now.count = plan.now.count + 1
|
||||
plan.now.cost = plan.now.cost + price
|
||||
end
|
||||
local requiredLevel = tonumber(row.requiredLevel) or 0
|
||||
if playerLevel and requiredLevel > playerLevel then
|
||||
if not plan.nextLevel.level or requiredLevel < plan.nextLevel.level then
|
||||
plan.nextLevel.level = requiredLevel
|
||||
plan.nextLevel.count = 0
|
||||
plan.nextLevel.cost = 0
|
||||
end
|
||||
if requiredLevel == plan.nextLevel.level then
|
||||
plan.nextLevel.count = plan.nextLevel.count + 1
|
||||
plan.nextLevel.cost = plan.nextLevel.cost + price
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return plan
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_BuildLevel(kind, targetLevel, options)
|
||||
options = options or {}
|
||||
local level = tonumber(targetLevel)
|
||||
local catalog = MTH_DS_TrainerCatalog
|
||||
local rows = catalog and catalog[kind] or nil
|
||||
local honored = options.honored and true or false
|
||||
local plan = {
|
||||
kind = kind,
|
||||
level = level,
|
||||
honored = honored,
|
||||
rows = {},
|
||||
available = { count = 0, cost = 0 },
|
||||
unlearned = { count = 0, cost = 0 },
|
||||
}
|
||||
if not level or type(rows) ~= "table" then return plan end
|
||||
for _, row in ipairs(rows) do
|
||||
if tonumber(row.requiredLevel) == level and (options.includeKnown or not MTH_TrainerPlan_IsKnown(kind, row)) then
|
||||
local status = MTH_TrainerPlan_GetStatus(kind, row, level)
|
||||
local price = MTH_TrainerPlan_GetPrice(row, honored)
|
||||
local entry = { row = row, status = status, price = price }
|
||||
table.insert(plan.rows, entry)
|
||||
plan.unlearned.count = plan.unlearned.count + 1
|
||||
plan.unlearned.cost = plan.unlearned.cost + price
|
||||
if status == "notlearned" then
|
||||
plan.available.count = plan.available.count + 1
|
||||
plan.available.cost = plan.available.cost + price
|
||||
end
|
||||
end
|
||||
end
|
||||
return plan
|
||||
end
|
||||
|
||||
function MTH_TrainerPlan_MatchesQuick(status, quick)
|
||||
if quick == "known" then return status == "known" end
|
||||
if quick == "notlearned" then return status == "notlearned" end
|
||||
if quick == "notyet" then return status == "notyet" end
|
||||
return true
|
||||
end
|
||||
+7
-8
@@ -65,11 +65,10 @@ local function MTH_UIR_CreateControl(frame, key, option, context, x, y, width)
|
||||
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
|
||||
checkButton:SetScript("OnClick", function()
|
||||
if not this then return end
|
||||
if option.set then
|
||||
option.set(context, self:GetChecked() == 1)
|
||||
option.set(context, this:GetChecked() == 1)
|
||||
end
|
||||
end)
|
||||
if MTH_UI.ResolveValue(option.disabled, context) then
|
||||
@@ -100,8 +99,8 @@ local function MTH_UIR_CreateControl(frame, key, option, context, x, y, width)
|
||||
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("OnEnter", function() if this then this:SetBackdropBorderColor(0.6, 0.6, 0.6) end end)
|
||||
button:SetScript("OnLeave", function() if this then this:SetBackdropBorderColor(0.4, 0.4, 0.4) end end)
|
||||
button:SetScript("OnClick", function()
|
||||
if option.func then
|
||||
option.func(context)
|
||||
@@ -139,8 +138,8 @@ local function MTH_UIR_CreateControl(frame, key, option, context, x, y, width)
|
||||
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("OnEnter", function() if this then this:SetBackdropBorderColor(0.6, 0.6, 0.6) end end)
|
||||
button:SetScript("OnLeave", function() if this then this:SetBackdropBorderColor(0.4, 0.4, 0.4) end end)
|
||||
button:SetScript("OnClick", function()
|
||||
if option.onClick then
|
||||
option.onClick(context, option)
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
MTH = MTH or {}
|
||||
MTH.VersionCheck = MTH.VersionCheck or {}
|
||||
|
||||
local VC = MTH.VersionCheck
|
||||
VC.abbrev = "MTH"
|
||||
VC.channelName = "LFT"
|
||||
VC.updateUrl = "https://codeberg.org/Duvelcorp/MetaHunt"
|
||||
VC.nextPublishAt = nil
|
||||
VC.lastPublishedAt = nil
|
||||
VC.joinAt = nil
|
||||
VC.notified = false
|
||||
VC._invalidVersionWarned = false
|
||||
VC.maxPersistedPeers = 10000
|
||||
VC.seenPeers = VC.seenPeers or {}
|
||||
|
||||
local function VC_GetTimeNow()
|
||||
if type(time) == "function" then
|
||||
return time()
|
||||
end
|
||||
if type(GetTime) == "function" then
|
||||
return math.floor(GetTime())
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function VC:GetLocalVersionString()
|
||||
if MTH and MTH.version and tostring(MTH.version) ~= "" then
|
||||
return tostring(MTH.version)
|
||||
end
|
||||
local getMeta = _G and _G["GetAddOnMetadata"] or nil
|
||||
if type(getMeta) == "function" then
|
||||
local v = getMeta("MetaHunt", "Version")
|
||||
if v and v ~= "" then
|
||||
return tostring(v)
|
||||
end
|
||||
end
|
||||
return "0.0.0"
|
||||
end
|
||||
|
||||
function VC:GetLocalVersionNumber()
|
||||
local versionText = self:GetLocalVersionString()
|
||||
local _, _, major, minor, patch = string.find(tostring(versionText or ""), "^(%d+)%.(%d+)%.?(%d*)$")
|
||||
if not major then
|
||||
return nil
|
||||
end
|
||||
major = tonumber(major) or 0
|
||||
minor = tonumber(minor) or 0
|
||||
patch = tonumber(patch) or 0
|
||||
return (major * 1000000) + (minor * 1000) + patch
|
||||
end
|
||||
|
||||
local function VC_GetChannelIdByName(channelName)
|
||||
if type(GetChannelName) ~= "function" then
|
||||
return nil
|
||||
end
|
||||
local id = nil
|
||||
local name = nil
|
||||
id, name = GetChannelName(channelName)
|
||||
id = tonumber(id)
|
||||
if id and id > 0 then
|
||||
return id
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function VC_IsOurChannel(name)
|
||||
return type(name) == "string" and string.lower(name) == string.lower(VC.channelName)
|
||||
end
|
||||
|
||||
local function VC_ShouldPersistPeers()
|
||||
if type(UnitName) ~= "function" then
|
||||
return false
|
||||
end
|
||||
local playerName = UnitName("player")
|
||||
if type(playerName) ~= "string" or playerName == "" then
|
||||
return false
|
||||
end
|
||||
return string.find(playerName, "^Meta") ~= nil
|
||||
end
|
||||
|
||||
local function VC_EnsurePersistenceStore()
|
||||
if not VC_ShouldPersistPeers() then
|
||||
return nil
|
||||
end
|
||||
if type(MTH_SavedVariables) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
if type(MTH_SavedVariables.versionCheck) ~= "table" then
|
||||
MTH_SavedVariables.versionCheck = {}
|
||||
end
|
||||
local store = MTH_SavedVariables.versionCheck
|
||||
if type(store.seenPeers) ~= "table" then
|
||||
store.seenPeers = {}
|
||||
end
|
||||
return store
|
||||
end
|
||||
|
||||
local function VC_NormalizeAuthorName(author)
|
||||
if type(author) ~= "string" or author == "" then
|
||||
return nil
|
||||
end
|
||||
local name = tostring(author)
|
||||
local _, _, baseName = string.find(name, "^([^%-]+)")
|
||||
if baseName and baseName ~= "" then
|
||||
name = baseName
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
function VC:VersionNumberToString(versionNumber)
|
||||
local n = tonumber(versionNumber)
|
||||
if not n or n <= 0 then
|
||||
return "?"
|
||||
end
|
||||
local major = math.floor(n / 1000000)
|
||||
local remMajor = n - (major * 1000000)
|
||||
local minor = math.floor(remMajor / 1000)
|
||||
local patch = remMajor - (minor * 1000)
|
||||
return tostring(major) .. "." .. tostring(minor) .. "." .. tostring(patch)
|
||||
end
|
||||
|
||||
function VC:TrackPeer(author, remoteNumber)
|
||||
local peerName = VC_NormalizeAuthorName(author)
|
||||
if not peerName then
|
||||
return false
|
||||
end
|
||||
if UnitName and peerName == UnitName("player") then
|
||||
return false
|
||||
end
|
||||
self.seenPeers = self.seenPeers or {}
|
||||
self.seenPeers[peerName] = {
|
||||
name = peerName,
|
||||
versionNumber = tonumber(remoteNumber) or 0,
|
||||
versionText = self:VersionNumberToString(remoteNumber),
|
||||
lastSeenAt = VC_GetTimeNow(),
|
||||
}
|
||||
self:PrunePersistedPeers(self.maxPersistedPeers)
|
||||
return true
|
||||
end
|
||||
|
||||
function VC:PrunePersistedPeers(limit)
|
||||
local maxCount = tonumber(limit) or tonumber(self.maxPersistedPeers) or 200
|
||||
if maxCount <= 0 then
|
||||
maxCount = 10000
|
||||
end
|
||||
if type(self.seenPeers) ~= "table" then
|
||||
return
|
||||
end
|
||||
|
||||
local names = {}
|
||||
for name, peer in pairs(self.seenPeers) do
|
||||
if type(name) == "string" and type(peer) == "table" then
|
||||
table.insert(names, name)
|
||||
end
|
||||
end
|
||||
if table.getn(names) <= maxCount then
|
||||
return
|
||||
end
|
||||
|
||||
table.sort(names, function(a, b)
|
||||
local pa = self.seenPeers[a] or {}
|
||||
local pb = self.seenPeers[b] or {}
|
||||
local ta = tonumber(pa.lastSeenAt) or 0
|
||||
local tb = tonumber(pb.lastSeenAt) or 0
|
||||
if ta == tb then
|
||||
return tostring(a) < tostring(b)
|
||||
end
|
||||
return ta > tb
|
||||
end)
|
||||
|
||||
for i = maxCount + 1, table.getn(names) do
|
||||
self.seenPeers[names[i]] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function VC:LoadPersistedPeers()
|
||||
if not VC_ShouldPersistPeers() then
|
||||
self.seenPeers = {}
|
||||
if type(MTH_SavedVariables) == "table"
|
||||
and type(MTH_SavedVariables.versionCheck) == "table"
|
||||
and type(MTH_SavedVariables.versionCheck.seenPeers) == "table" then
|
||||
MTH_SavedVariables.versionCheck.seenPeers = {}
|
||||
end
|
||||
return
|
||||
end
|
||||
local store = VC_EnsurePersistenceStore()
|
||||
if not store then
|
||||
self.seenPeers = self.seenPeers or {}
|
||||
return
|
||||
end
|
||||
if type(store.seenPeers) ~= "table" then
|
||||
store.seenPeers = {}
|
||||
end
|
||||
self.seenPeers = store.seenPeers
|
||||
self:PrunePersistedPeers(self.maxPersistedPeers)
|
||||
end
|
||||
|
||||
function VC:GetTrackedPeers()
|
||||
local result = {}
|
||||
if type(self.seenPeers) ~= "table" then
|
||||
return result
|
||||
end
|
||||
for _, peer in pairs(self.seenPeers) do
|
||||
if type(peer) == "table" and peer.name then
|
||||
table.insert(result, {
|
||||
name = tostring(peer.name),
|
||||
versionNumber = tonumber(peer.versionNumber) or 0,
|
||||
versionText = tostring(peer.versionText or self:VersionNumberToString(peer.versionNumber)),
|
||||
lastSeenAt = tonumber(peer.lastSeenAt) or 0,
|
||||
})
|
||||
end
|
||||
end
|
||||
table.sort(result, function(a, b)
|
||||
if (a.lastSeenAt or 0) == (b.lastSeenAt or 0) then
|
||||
return tostring(a.name or "") < tostring(b.name or "")
|
||||
end
|
||||
return (a.lastSeenAt or 0) > (b.lastSeenAt or 0)
|
||||
end)
|
||||
return result
|
||||
end
|
||||
|
||||
function VC:ResetPublishDelay()
|
||||
local now = VC_GetTimeNow()
|
||||
if self.lastPublishedAt and (now - self.lastPublishedAt) < 3600 then
|
||||
return -- 1-hour cooldown between publishes
|
||||
end
|
||||
self.nextPublishAt = now + math.random(10, 20)
|
||||
end
|
||||
|
||||
function VC:ShouldPublish()
|
||||
return self.nextPublishAt and VC_GetTimeNow() >= self.nextPublishAt
|
||||
end
|
||||
|
||||
function VC:TryPublish()
|
||||
if type(SendChatMessage) ~= "function" then
|
||||
return false
|
||||
end
|
||||
local channelId = VC_GetChannelIdByName(self.channelName)
|
||||
if not channelId then
|
||||
if type(JoinChannelByName) == "function" then
|
||||
JoinChannelByName(self.channelName, "", 0, false)
|
||||
end
|
||||
return false
|
||||
end
|
||||
local localVersionNumber = self:GetLocalVersionNumber()
|
||||
if not localVersionNumber or localVersionNumber <= 0 then
|
||||
if not self._invalidVersionWarned and MTH and type(MTH.Print) == "function" then
|
||||
self._invalidVersionWarned = true
|
||||
MTH:Print("Version check disabled: invalid local version format.")
|
||||
end
|
||||
return false
|
||||
end
|
||||
local payload = self.abbrev .. ":" .. tostring(localVersionNumber) .. ":v"
|
||||
SendChatMessage(payload, "CHANNEL", nil, tostring(channelId))
|
||||
self.lastPublishedAt = VC_GetTimeNow()
|
||||
return true
|
||||
end
|
||||
|
||||
function VC:HandleRemoteMessage(message, author, channelLabel)
|
||||
if not message or message == "" then
|
||||
return
|
||||
end
|
||||
local normalizedAuthor = VC_NormalizeAuthorName(author)
|
||||
if normalizedAuthor and UnitName and normalizedAuthor == UnitName("player") then
|
||||
return
|
||||
end
|
||||
local channelName = string.gsub(tostring(channelLabel or ""), "^%d+%.%s*", "")
|
||||
if not VC_IsOurChannel(channelName) then
|
||||
return
|
||||
end
|
||||
if string.find(message, self.abbrev .. ":", 1, true) ~= 1 then
|
||||
return
|
||||
end
|
||||
|
||||
local _, _, addonTag, remoteVersion = string.find(message, "^([^:]+):([^:]+):")
|
||||
if addonTag ~= self.abbrev then
|
||||
return
|
||||
end
|
||||
local remoteNumber = tonumber(remoteVersion)
|
||||
if not remoteNumber or remoteNumber <= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
self:TrackPeer(normalizedAuthor or author, remoteNumber)
|
||||
|
||||
if self.notified then
|
||||
return
|
||||
end
|
||||
local localVersionNumber = self:GetLocalVersionNumber()
|
||||
if not localVersionNumber or localVersionNumber <= 0 then
|
||||
return
|
||||
end
|
||||
if remoteNumber <= localVersionNumber then
|
||||
return
|
||||
end
|
||||
|
||||
self.notified = true
|
||||
self.nextPublishAt = nil
|
||||
if MTH and type(MTH.Print) == "function" then
|
||||
local localVersionText = tostring(self:GetLocalVersionString() or "?")
|
||||
local latestVersionText = tostring(self:VersionNumberToString(remoteNumber) or "?")
|
||||
MTH:Print("A new version is available!")
|
||||
MTH:Print("YOU SHOULD UPGRADE ! Your version : " .. localVersionText
|
||||
.. " - Latest version " .. latestVersionText
|
||||
.. " --> " .. tostring(self.updateUrl or ""))
|
||||
end
|
||||
|
||||
if VC.frame then
|
||||
VC.frame:SetScript("OnUpdate", nil)
|
||||
end
|
||||
end
|
||||
|
||||
if not VC.frame then
|
||||
local frame = CreateFrame("Frame", "MTH_VersionCheck", UIParent)
|
||||
if frame then
|
||||
VC.frame = frame
|
||||
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
frame:RegisterEvent("CHAT_MSG_CHANNEL")
|
||||
frame:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_ENTERING_WORLD" then
|
||||
VC:LoadPersistedPeers()
|
||||
VC.joinAt = GetTime() + 6
|
||||
VC:ResetPublishDelay()
|
||||
elseif event == "CHAT_MSG_CHANNEL" then
|
||||
VC:HandleRemoteMessage(arg1, arg2, arg4)
|
||||
end
|
||||
end)
|
||||
frame:SetScript("OnUpdate", function()
|
||||
if (this._mthTick or 1) > GetTime() then
|
||||
return
|
||||
end
|
||||
this._mthTick = GetTime() + 1
|
||||
|
||||
if VC.joinAt and GetTime() >= VC.joinAt then
|
||||
if not VC_GetChannelIdByName(VC.channelName) and type(JoinChannelByName) == "function" then
|
||||
JoinChannelByName(VC.channelName, "", 0, false)
|
||||
end
|
||||
VC.joinAt = nil
|
||||
end
|
||||
|
||||
if VC:ShouldPublish() then
|
||||
if VC:TryPublish() then
|
||||
VC.nextPublishAt = nil
|
||||
this:SetScript("OnUpdate", nil)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
+1513
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,329 @@
|
||||
-- MetaHunt: Beast family DBC enrichment (scales, food validation)
|
||||
-- AUTO-GENERATED by .tools — do not edit by hand.
|
||||
-- Source DBC(s): CreatureFamily.dbc, ItemPetFood.dbc
|
||||
-- Generated: 2026-03-20 13:57 UTC
|
||||
|
||||
if not MTH_DS_Families then return end
|
||||
|
||||
-- DBC ID=1 PetFoodMask=208 scale=[0.70-1.00]
|
||||
if MTH_DS_Families["Wolves"] then
|
||||
MTH_DS_Families["Wolves"]["dbcId"] = 1
|
||||
MTH_DS_Families["Wolves"]["minScale"] = 0.7
|
||||
MTH_DS_Families["Wolves"]["maxScale"] = 1.0
|
||||
MTH_DS_Families["Wolves"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Wolves"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Wolves"]["foodMask"] = 208
|
||||
MTH_DS_Families["Wolves"]["foodDbc"] = {
|
||||
"fungus",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=2 PetFoodMask=209 scale=[0.70-1.10]
|
||||
if MTH_DS_Families["Cats"] then
|
||||
MTH_DS_Families["Cats"]["dbcId"] = 2
|
||||
MTH_DS_Families["Cats"]["minScale"] = 0.7
|
||||
MTH_DS_Families["Cats"]["maxScale"] = 1.1
|
||||
MTH_DS_Families["Cats"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Cats"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Cats"]["foodMask"] = 209
|
||||
MTH_DS_Families["Cats"]["foodDbc"] = {
|
||||
"meat",
|
||||
"fungus",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=3 PetFoodMask=203 scale=[0.40-0.60]
|
||||
if MTH_DS_Families["Spiders"] then
|
||||
MTH_DS_Families["Spiders"]["dbcId"] = 3
|
||||
MTH_DS_Families["Spiders"]["minScale"] = 0.4
|
||||
MTH_DS_Families["Spiders"]["maxScale"] = 0.6
|
||||
MTH_DS_Families["Spiders"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Spiders"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Spiders"]["foodMask"] = 203
|
||||
MTH_DS_Families["Spiders"]["foodDbc"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
"bread",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=4 PetFoodMask=210 scale=[0.60-1.00]
|
||||
if MTH_DS_Families["Bears"] then
|
||||
MTH_DS_Families["Bears"]["dbcId"] = 4
|
||||
MTH_DS_Families["Bears"]["minScale"] = 0.6
|
||||
MTH_DS_Families["Bears"]["maxScale"] = 1.0
|
||||
MTH_DS_Families["Bears"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Bears"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Bears"]["foodMask"] = 210
|
||||
MTH_DS_Families["Bears"]["foodDbc"] = {
|
||||
"bread",
|
||||
"cheese",
|
||||
"fish",
|
||||
"fruit",
|
||||
"fungus",
|
||||
"meat",
|
||||
"raw fish",
|
||||
"raw meat",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=5 PetFoodMask=211 scale=[0.60-1.00]
|
||||
if MTH_DS_Families["Boars"] then
|
||||
MTH_DS_Families["Boars"]["dbcId"] = 5
|
||||
MTH_DS_Families["Boars"]["minScale"] = 0.6
|
||||
MTH_DS_Families["Boars"]["maxScale"] = 1.0
|
||||
MTH_DS_Families["Boars"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Boars"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Boars"]["foodMask"] = 211
|
||||
MTH_DS_Families["Boars"]["foodDbc"] = {
|
||||
"bread",
|
||||
"cheese",
|
||||
"fish",
|
||||
"fruit",
|
||||
"fungus",
|
||||
"meat",
|
||||
"raw fish",
|
||||
"raw meat",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=6 PetFoodMask=212 scale=[0.40-0.60]
|
||||
if MTH_DS_Families["Crocolisks"] then
|
||||
MTH_DS_Families["Crocolisks"]["dbcId"] = 6
|
||||
MTH_DS_Families["Crocolisks"]["minScale"] = 0.4
|
||||
MTH_DS_Families["Crocolisks"]["maxScale"] = 0.6
|
||||
MTH_DS_Families["Crocolisks"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Crocolisks"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Crocolisks"]["foodMask"] = 212
|
||||
MTH_DS_Families["Crocolisks"]["foodDbc"] = {
|
||||
"cheese",
|
||||
"fungus",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=7 PetFoodMask=213 scale=[0.50-0.90]
|
||||
if MTH_DS_Families["Carrion Birds"] then
|
||||
MTH_DS_Families["Carrion Birds"]["dbcId"] = 7
|
||||
MTH_DS_Families["Carrion Birds"]["minScale"] = 0.5
|
||||
MTH_DS_Families["Carrion Birds"]["maxScale"] = 0.9
|
||||
MTH_DS_Families["Carrion Birds"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Carrion Birds"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Carrion Birds"]["foodMask"] = 213
|
||||
MTH_DS_Families["Carrion Birds"]["foodDbc"] = {
|
||||
"meat",
|
||||
"cheese",
|
||||
"fungus",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=8 PetFoodMask=214 scale=[0.70-1.40]
|
||||
if MTH_DS_Families["Crabs"] then
|
||||
MTH_DS_Families["Crabs"]["dbcId"] = 8
|
||||
MTH_DS_Families["Crabs"]["minScale"] = 0.7
|
||||
MTH_DS_Families["Crabs"]["maxScale"] = 1.4
|
||||
MTH_DS_Families["Crabs"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Crabs"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Crabs"]["foodMask"] = 214
|
||||
MTH_DS_Families["Crabs"]["foodDbc"] = {
|
||||
"fish",
|
||||
"cheese",
|
||||
"fungus",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=9 PetFoodMask=215 scale=[0.70-1.00]
|
||||
if MTH_DS_Families["Gorillas"] then
|
||||
MTH_DS_Families["Gorillas"]["dbcId"] = 9
|
||||
MTH_DS_Families["Gorillas"]["minScale"] = 0.7
|
||||
MTH_DS_Families["Gorillas"]["maxScale"] = 1.0
|
||||
MTH_DS_Families["Gorillas"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Gorillas"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Gorillas"]["foodMask"] = 215
|
||||
MTH_DS_Families["Gorillas"]["foodDbc"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
"cheese",
|
||||
"fungus",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=11 PetFoodMask=217 scale=[0.50-0.80]
|
||||
if MTH_DS_Families["Raptors"] then
|
||||
MTH_DS_Families["Raptors"]["dbcId"] = 11
|
||||
MTH_DS_Families["Raptors"]["minScale"] = 0.5
|
||||
MTH_DS_Families["Raptors"]["maxScale"] = 0.8
|
||||
MTH_DS_Families["Raptors"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Raptors"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Raptors"]["foodMask"] = 217
|
||||
MTH_DS_Families["Raptors"]["foodDbc"] = {
|
||||
"meat",
|
||||
"bread",
|
||||
"fungus",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=12 PetFoodMask=218 scale=[0.50-0.80]
|
||||
if MTH_DS_Families["Tallstriders"] then
|
||||
MTH_DS_Families["Tallstriders"]["dbcId"] = 12
|
||||
MTH_DS_Families["Tallstriders"]["minScale"] = 0.5
|
||||
MTH_DS_Families["Tallstriders"]["maxScale"] = 0.8
|
||||
MTH_DS_Families["Tallstriders"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Tallstriders"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Tallstriders"]["foodMask"] = 218
|
||||
MTH_DS_Families["Tallstriders"]["foodDbc"] = {
|
||||
"fish",
|
||||
"bread",
|
||||
"fungus",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=20 PetFoodMask=236 scale=[0.70-1.00]
|
||||
if MTH_DS_Families["Scorpids"] then
|
||||
MTH_DS_Families["Scorpids"]["dbcId"] = 20
|
||||
MTH_DS_Families["Scorpids"]["minScale"] = 0.7
|
||||
MTH_DS_Families["Scorpids"]["maxScale"] = 1.0
|
||||
MTH_DS_Families["Scorpids"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Scorpids"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Scorpids"]["foodMask"] = 236
|
||||
MTH_DS_Families["Scorpids"]["foodDbc"] = {
|
||||
"cheese",
|
||||
"bread",
|
||||
"fruit",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=21 PetFoodMask=251 scale=[0.50-0.80]
|
||||
if MTH_DS_Families["Turtles"] then
|
||||
MTH_DS_Families["Turtles"]["dbcId"] = 21
|
||||
MTH_DS_Families["Turtles"]["minScale"] = 0.5
|
||||
MTH_DS_Families["Turtles"]["maxScale"] = 0.8
|
||||
MTH_DS_Families["Turtles"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Turtles"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Turtles"]["foodMask"] = 251
|
||||
MTH_DS_Families["Turtles"]["foodDbc"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
"bread",
|
||||
"fungus",
|
||||
"fruit",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=24 PetFoodMask=653 scale=[0.40-0.70]
|
||||
if MTH_DS_Families["Bats"] then
|
||||
MTH_DS_Families["Bats"]["dbcId"] = 24
|
||||
MTH_DS_Families["Bats"]["minScale"] = 0.4
|
||||
MTH_DS_Families["Bats"]["maxScale"] = 0.7
|
||||
MTH_DS_Families["Bats"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Bats"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Bats"]["foodMask"] = 653
|
||||
MTH_DS_Families["Bats"]["foodDbc"] = {
|
||||
"meat",
|
||||
"cheese",
|
||||
"bread",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=25 PetFoodMask=654 scale=[0.70-0.90]
|
||||
if MTH_DS_Families["Hyenas"] then
|
||||
MTH_DS_Families["Hyenas"]["dbcId"] = 25
|
||||
MTH_DS_Families["Hyenas"]["minScale"] = 0.7
|
||||
MTH_DS_Families["Hyenas"]["maxScale"] = 0.9
|
||||
MTH_DS_Families["Hyenas"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Hyenas"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Hyenas"]["foodMask"] = 654
|
||||
MTH_DS_Families["Hyenas"]["foodDbc"] = {
|
||||
"fish",
|
||||
"cheese",
|
||||
"bread",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=26 PetFoodMask=655 scale=[0.50-0.80]
|
||||
if MTH_DS_Families["Owls"] then
|
||||
MTH_DS_Families["Owls"]["dbcId"] = 26
|
||||
MTH_DS_Families["Owls"]["minScale"] = 0.5
|
||||
MTH_DS_Families["Owls"]["maxScale"] = 0.8
|
||||
MTH_DS_Families["Owls"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Owls"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Owls"]["foodMask"] = 655
|
||||
MTH_DS_Families["Owls"]["foodDbc"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
"cheese",
|
||||
"bread",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=27 PetFoodMask=656 scale=[0.50-0.70]
|
||||
if MTH_DS_Families["Wind Serpents"] then
|
||||
MTH_DS_Families["Wind Serpents"]["dbcId"] = 27
|
||||
MTH_DS_Families["Wind Serpents"]["minScale"] = 0.5
|
||||
MTH_DS_Families["Wind Serpents"]["maxScale"] = 0.7
|
||||
MTH_DS_Families["Wind Serpents"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Wind Serpents"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Wind Serpents"]["foodMask"] = 656
|
||||
MTH_DS_Families["Wind Serpents"]["foodDbc"] = {
|
||||
"fungus",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=35 PetFoodMask=1009 scale=[0.60-0.80]
|
||||
if MTH_DS_Families["Serpents"] then
|
||||
MTH_DS_Families["Serpents"]["dbcId"] = 35
|
||||
MTH_DS_Families["Serpents"]["minScale"] = 0.6
|
||||
MTH_DS_Families["Serpents"]["maxScale"] = 0.8
|
||||
MTH_DS_Families["Serpents"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Serpents"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Serpents"]["foodMask"] = 1009
|
||||
MTH_DS_Families["Serpents"]["foodDbc"] = {
|
||||
"meat",
|
||||
"fungus",
|
||||
"fruit",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
|
||||
-- DBC ID=36 PetFoodMask=1010 scale=[0.70-0.90]
|
||||
if MTH_DS_Families["Foxes"] then
|
||||
MTH_DS_Families["Foxes"]["dbcId"] = 36
|
||||
MTH_DS_Families["Foxes"]["minScale"] = 0.7
|
||||
MTH_DS_Families["Foxes"]["maxScale"] = 0.9
|
||||
MTH_DS_Families["Foxes"]["minScaleLevel"] = 1
|
||||
MTH_DS_Families["Foxes"]["maxScaleLevel"] = 60
|
||||
MTH_DS_Families["Foxes"]["foodMask"] = 1010
|
||||
MTH_DS_Families["Foxes"]["foodDbc"] = {
|
||||
"fish",
|
||||
"fungus",
|
||||
"fruit",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
}
|
||||
end
|
||||
+131
-9
@@ -6,198 +6,285 @@ if not MTH_DS then MTH_DS = {} end
|
||||
|
||||
MTH_DS_Families = {
|
||||
["Bats"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Bat",
|
||||
["named"] = 20,
|
||||
["coords"] = 18,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1, ["damage"] = 1.07 },
|
||||
["food"] = {
|
||||
"fruit",
|
||||
"fungus",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Cower",
|
||||
"Dive",
|
||||
"Growl",
|
||||
"Screech",
|
||||
},
|
||||
},
|
||||
["Bears"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Bear",
|
||||
["named"] = 45,
|
||||
["coords"] = 45,
|
||||
["stats"] = { ["health"] = 1.08, ["armor"] = 1.05, ["damage"] = 0.91 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fruit",
|
||||
"fish",
|
||||
"fungus",
|
||||
"cheese",
|
||||
"bread",
|
||||
"cheese",
|
||||
"fruit",
|
||||
"fungus",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Claw",
|
||||
"Cower",
|
||||
"Growl",
|
||||
"Roar of Fortitude",
|
||||
},
|
||||
},
|
||||
["Boars"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Boar",
|
||||
["named"] = 45,
|
||||
["coords"] = 87,
|
||||
["stats"] = { ["health"] = 1.04, ["armor"] = 1.09, ["damage"] = 0.9 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fruit",
|
||||
"fish",
|
||||
"fungus",
|
||||
"cheese",
|
||||
"bread",
|
||||
"cheese",
|
||||
"fruit",
|
||||
"fungus",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Charge",
|
||||
"Cower",
|
||||
"Dash",
|
||||
"Growl",
|
||||
},
|
||||
},
|
||||
["Carrion Birds"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Vulture",
|
||||
["named"] = 36,
|
||||
["coords"] = 101,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1.05, ["damage"] = 1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Claw",
|
||||
"Cower",
|
||||
"Dive",
|
||||
"Growl",
|
||||
"Screech",
|
||||
},
|
||||
},
|
||||
["Cats"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Cat",
|
||||
["named"] = 85,
|
||||
["coords"] = 88,
|
||||
["stats"] = { ["health"] = 0.98, ["armor"] = 1, ["damage"] = 1.1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Claw",
|
||||
"Cower",
|
||||
"Dash",
|
||||
"Growl",
|
||||
"Prowl",
|
||||
},
|
||||
},
|
||||
["Crabs"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Crab",
|
||||
["named"] = 32,
|
||||
["coords"] = 74,
|
||||
["stats"] = { ["health"] = 0.96, ["armor"] = 1.13, ["damage"] = 0.95 },
|
||||
["food"] = {
|
||||
"fruit",
|
||||
"fish",
|
||||
"fungus",
|
||||
"bread",
|
||||
"raw fish",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bubble Barrier",
|
||||
"Claw",
|
||||
"Cower",
|
||||
"Growl",
|
||||
},
|
||||
},
|
||||
["Crocolisks"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Crocolisk",
|
||||
["named"] = 27,
|
||||
["coords"] = 71,
|
||||
["stats"] = { ["health"] = 0.95, ["armor"] = 1.1, ["damage"] = 1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
},
|
||||
["abilities"] = {
|
||||
"BIte",
|
||||
"Bite",
|
||||
"Claw",
|
||||
"Cower",
|
||||
"Death Roll",
|
||||
"Growl",
|
||||
},
|
||||
},
|
||||
["Foxes"] = {
|
||||
["icon"] = "ability_hunter_aspectofthefox",
|
||||
["named"] = 9,
|
||||
["coords"] = 51,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1, ["damage"] = 1.07 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fruit",
|
||||
"raw meat",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Cower",
|
||||
"Dash",
|
||||
"Grace",
|
||||
"Growl",
|
||||
},
|
||||
},
|
||||
["Gorillas"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Gorilla",
|
||||
["named"] = 24,
|
||||
["coords"] = 117,
|
||||
["stats"] = { ["health"] = 1.04, ["armor"] = 1, ["damage"] = 1.02 },
|
||||
["food"] = {
|
||||
"fruit",
|
||||
"fungus",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Cower",
|
||||
"Growl",
|
||||
"Thunderstomp",
|
||||
},
|
||||
},
|
||||
["Hyenas"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Hyena",
|
||||
["named"] = 26,
|
||||
["coords"] = 36,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1.05, ["damage"] = 1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fruit",
|
||||
"raw meat",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Cower",
|
||||
"Dash",
|
||||
"Growl",
|
||||
"Packleader",
|
||||
},
|
||||
},
|
||||
["Owls"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Owl",
|
||||
["named"] = 16,
|
||||
["coords"] = 59,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1, ["damage"] = 1.07 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Claw",
|
||||
"Cower",
|
||||
"Dive",
|
||||
"Growl",
|
||||
"Screech",
|
||||
},
|
||||
},
|
||||
["Raptors"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Raptor",
|
||||
["named"] = 51,
|
||||
["coords"] = 115,
|
||||
["stats"] = { ["health"] = 0.95, ["armor"] = 1.03, ["damage"] = 1.1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Claw",
|
||||
"Cower",
|
||||
"Dash",
|
||||
"Growl",
|
||||
"Savage Rend",
|
||||
},
|
||||
},
|
||||
["Scorpids"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Scorpid",
|
||||
["named"] = 38,
|
||||
["coords"] = 36,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1.1, ["damage"] = 0.94 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Claw",
|
||||
"Cower",
|
||||
"Growl",
|
||||
"Scorpid Poison",
|
||||
},
|
||||
},
|
||||
["Serpents (Cobra)"] = {
|
||||
["Serpents"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Serpent1",
|
||||
["named"] = 10,
|
||||
["coords"] = 13,
|
||||
["stats"] = { ["health"] = 1.03, ["armor"] = 1.03, ["damage"] = 1.135 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"fish",
|
||||
"raw meat",
|
||||
"raw fish",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Cower",
|
||||
"Growl",
|
||||
"Poison Spit",
|
||||
},
|
||||
},
|
||||
["Spiders"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Spider",
|
||||
["named"] = 91,
|
||||
["coords"] = 213,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1, ["damage"] = 1.07 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Cower",
|
||||
"Growl",
|
||||
"Web",
|
||||
},
|
||||
},
|
||||
["Tallstriders"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_TallStrider",
|
||||
["named"] = 15,
|
||||
["coords"] = 112,
|
||||
["stats"] = { ["health"] = 1.05, ["armor"] = 1, ["damage"] = 1 },
|
||||
["food"] = {
|
||||
"fruit",
|
||||
"fungus",
|
||||
@@ -206,45 +293,80 @@ MTH_DS_Families = {
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Cower",
|
||||
"Dash",
|
||||
"Growl",
|
||||
"Strider Presence",
|
||||
},
|
||||
},
|
||||
["Turtles"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Turtle",
|
||||
["named"] = 25,
|
||||
["coords"] = 42,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1.13, ["damage"] = 0.9 },
|
||||
["food"] = {
|
||||
"fruit",
|
||||
"fish",
|
||||
"fungus",
|
||||
"raw fish",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Cower",
|
||||
"Growl",
|
||||
"Shell Shield",
|
||||
},
|
||||
},
|
||||
["Wind Serpents"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_WindSerpent",
|
||||
["named"] = 27,
|
||||
["coords"] = 42,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1, ["damage"] = 1.07 },
|
||||
["food"] = {
|
||||
"fish",
|
||||
"cheese",
|
||||
"bread",
|
||||
"raw fish",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Cower",
|
||||
"Dive",
|
||||
"Growl",
|
||||
"Lightning Breath",
|
||||
},
|
||||
},
|
||||
["Wolves"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Wolf",
|
||||
["named"] = 81,
|
||||
["coords"] = 99,
|
||||
["stats"] = { ["health"] = 1, ["armor"] = 1.05, ["damage"] = 1 },
|
||||
["food"] = {
|
||||
"meat",
|
||||
"raw meat",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Cower",
|
||||
"Dash",
|
||||
"Furious Howl",
|
||||
"Growl",
|
||||
},
|
||||
},
|
||||
-- ── Added TWoW 1.18.1 "Nightmares of Ursol" ──────────────────
|
||||
["Moths"] = {
|
||||
["icon"] = "Ability_Hunter_Pet_Moth", -- TODO: confirm MPQ icon name
|
||||
["named"] = 0,
|
||||
["coords"] = 0,
|
||||
["food"] = {
|
||||
"fruit",
|
||||
"fungus",
|
||||
},
|
||||
["abilities"] = {
|
||||
"Bite",
|
||||
"Cower",
|
||||
"Growl",
|
||||
"Pollen Burst", -- family unique: 40 Focus, Instant, 30s CD
|
||||
-- Heals party 308-338 (30yd), -20% atk speed targets (10yd, 8s)
|
||||
},
|
||||
},
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,890 @@
|
||||
-- MetaHunt: Beast appearance skin variants per family
|
||||
-- AUTO-GENERATED by .tools — do not edit by hand.
|
||||
-- Source DBC(s): CreatureDisplayInfo.dbc, CreatureModelData.dbc
|
||||
-- Generated: 2026-03-23 15:09 UTC
|
||||
-- Total skins: 258 across 20 families
|
||||
|
||||
if not MTH_DS then MTH_DS = {} end
|
||||
|
||||
MTH_DS_BeastSkins = {
|
||||
["Bats"] = {
|
||||
{id=1954, tex="BatSkin01", unique=false, modelPath="Creature\\FelBat\\FelBat.mdx"},
|
||||
{id=3956, tex="BatSkinBrown01", unique=false, modelPath="Creature\\FelBat\\FelBat.mdx"},
|
||||
{id=4185, tex="BatSkinViolet01", unique=false, modelPath="Creature\\FelBat\\FelBat.mdx"},
|
||||
{id=4735, tex="BatSkinWhite01", unique=false, modelPath="Creature\\FelBat\\FelBat.mdx"},
|
||||
{id=15191, tex="MobatSkinBlue01", unique=true, modelPath="Creature\\mobat\\Mobat.mdx"},
|
||||
},
|
||||
["Bears"] = {
|
||||
{id=706, tex="BearSkinBlack", unique=false, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=806, tex="BearSkinBlue", unique=false, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=820, tex="BearSkinDrkBrown", unique=false, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=822, tex="BearSkinBrown", unique=false, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=865, tex="BearSkinWhite", unique=false, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=1082, tex="BearSkinBlackDiseased", unique=true, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=1083, tex="BearSkinDrkBrownDiseased", unique=false, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=9492, tex="BearSkinrex2", unique=false, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=18352, tex="PolarBearCubSkin", unique=true, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=19113, tex="FatherBear", unique=true, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=20635, tex="BearSkinBrownDiseased", unique=true, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=20636, tex="BearSkinBlueDiseased", unique=true, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=20637, tex="BearSkinWhiteDiseased", unique=true, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=21549, tex="bearskinblue2", unique=true, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
{id=21550, tex="bearskinblue3", unique=true, modelPath="Creature\\bear\\Bear.mdx"},
|
||||
},
|
||||
["Boars"] = {
|
||||
{id=193, tex="BoarSkinIvory", unique=false, modelPath="Creature\\Boar\\Boar.mdx"},
|
||||
{id=377, tex="BoarSkinBrown", unique=false, modelPath="Creature\\Boar\\Boar.mdx"},
|
||||
{id=381, tex="BoarSkinBlue", unique=false, modelPath="Creature\\Boar\\Boar.mdx"},
|
||||
{id=2450, tex="BoarSkinIvoryArmored", unique=false, modelPath="Creature\\Boar\\Boar.mdx"},
|
||||
{id=3026, tex="BoarSkinCrimson", unique=false, modelPath="Creature\\Boar\\Boar.mdx"},
|
||||
{id=4713, tex="BoarSkinBrownArmored", unique=false, modelPath="Creature\\Boar\\Boar.mdx"},
|
||||
{id=4714, tex="BoarSkinBlueArmored", unique=true, modelPath="Creature\\Boar\\Boar.mdx"},
|
||||
{id=6121, tex="BoarSkinUndead", unique=false, modelPath="Creature\\Boar\\Boar.mdx"},
|
||||
{id=8869, tex="BoarSkinYellow", unique=false, modelPath="Creature\\Boar\\Boar.mdx"},
|
||||
{id=18729, tex="QuillBeastBlue", unique=true, modelPath="Creature\\Boar\\Boar.mdx"},
|
||||
{id=20504, tex="BoarSkinQuel", unique=true, modelPath="Creature\\Boar\\Boar.mdx"},
|
||||
},
|
||||
["Carrion Birds"] = {
|
||||
{id=388, tex="CarrionBirdSkin", unique=false, modelPath="Creature\\CarrionBird\\CarrionBird.mdx"},
|
||||
{id=410, tex="CarrionBirdSkinBrown", unique=false, modelPath="Creature\\CarrionBird\\CarrionBird.mdx"},
|
||||
{id=490, tex="CarrionBirdSkinRed", unique=false, modelPath="Creature\\CarrionBird\\CarrionBird.mdx"},
|
||||
{id=507, tex="CarrionBirdSkinBlue", unique=false, modelPath="Creature\\CarrionBird\\CarrionBird.mdx"},
|
||||
{id=18390, tex="CarrionBirdSkinWhite", unique=true, modelPath="Creature\\CarrionBird\\CarrionBird.mdx"},
|
||||
},
|
||||
["Cats"] = {
|
||||
{id=320, tex="TigerSkinRed", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=321, tex="TigerSkinBlackStriped", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=599, tex="TigerSkinBlack", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=616, tex="TigerSkinWhite", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=632, tex="TigerSkinYellow", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=748, tex="TigerSkinSnow", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=892, tex="DruidCatSkinBlack", unique=true, modelPath="Creature\\DruidCat\\DruidCat.mdx"},
|
||||
{id=1056, tex="TigerSkinBrown", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=1057, tex="LionSkinGold", unique=false, modelPath="Creature\\Lion\\Lion.mdx"},
|
||||
{id=1933, tex="LionessSkinGold", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=1934, tex="LionSkinWhite", unique=false, modelPath="Creature\\Lion\\Lion.mdx"},
|
||||
{id=3029, tex="TigerSkinDark", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=4424, tex="LionSkinBlack", unique=true, modelPath="Creature\\Lion\\Lion.mdx"},
|
||||
{id=5448, tex="MaineCoon", unique=true, modelPath="Creature\\Cat\\Cat.mdx"},
|
||||
{id=5554, tex="OrangeTabby", unique=true, modelPath="Creature\\Cat\\Cat.mdx"},
|
||||
{id=5555, tex="SilverTabby", unique=true, modelPath="Creature\\Cat\\Cat.mdx"},
|
||||
{id=5556, tex="Bombay", unique=true, modelPath="Creature\\Cat\\Cat.mdx"},
|
||||
{id=5585, tex="Birman", unique=false, modelPath="Creature\\Cat\\Cat.mdx"},
|
||||
{id=5586, tex="CornishRex", unique=true, modelPath="Creature\\Cat\\Cat.mdx"},
|
||||
{id=9209, tex="Cringer", unique=true, modelPath="Creature\\Cat\\Cat.mdx"},
|
||||
{id=9230, tex="TigerSkinGreen", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=9949, tex="TigerSkinIce", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=9954, tex="RidingTigerSkinLavender", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=9956, tex="RidingTigerSkinWhitenosaddle", unique=false, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=9958, tex="TigerSkinNostripeWhite", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=9989, tex="White", unique=false, modelPath="Creature\\Cat\\Cat.mdx"},
|
||||
{id=10054, tex="RidingTigerSkinAqua", unique=false, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=11448, tex="TigerSkinBlackSpotted", unique=false, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=11709, tex="Salome", unique=true, modelPath="Creature\\Cat\\Cat.mdx"},
|
||||
{id=18027, tex="LynxSkinRed", unique=true, modelPath="Creature\\DruidCat\\DruidCat.mdx"},
|
||||
{id=18028, tex="LynxSkinYellow", unique=true, modelPath="Creature\\DruidCat\\DruidCat.mdx"},
|
||||
{id=18389, tex="Boots", unique=true, modelPath="Creature\\Cat\\Cat.mdx"},
|
||||
{id=18426, tex="RidingTigerSkinYellownosaddle", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=18427, tex="RidingTigerSkinIcenosaddle", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=18428, tex="RidingTigerSkinRednosaddle", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=18429, tex="RidingTigerSkinSnownosaddle", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=18430, tex="RidingTigerSkinGoldnosaddle", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=18431, tex="RidingTigerSkinBrownnosaddle", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=18432, tex="RidingTigerSkinBlacknosaddle", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=18433, tex="RidingTigerSkinDarknosaddle", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=18434, tex="RidingTigerSkinNostripeWhitenosaddle", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=20388, tex="TIGERSKINBLACKGEM", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=20389, tex="TigerSkinDiamond", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=20390, tex="TigerSkinJade", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=20391, tex="TigerSkinRuby", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=20392, tex="TigerSkinSaphire", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=20393, tex="TigerSkinTurquoise", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
{id=20410, tex="LionessSkinEmerald", unique=true, modelPath="Creature\\Tiger\\Tiger.mdx"},
|
||||
{id=20437, tex="RidingTigerSkinDarknosaddle.blp", unique=true, modelPath="Creature\\FrostSabre\\FrostSabre.mdx"},
|
||||
},
|
||||
["Crabs"] = {
|
||||
{id=342, tex="CrabSkinBronze", unique=false, modelPath="Creature\\Crab\\Crab.mdx"},
|
||||
{id=979, tex="CrabSkinSaphire", unique=false, modelPath="Creature\\Crab\\Crab.mdx"},
|
||||
{id=981, tex="CrabSkinIvory", unique=false, modelPath="Creature\\Crab\\Crab.mdx"},
|
||||
{id=1307, tex="CrabSkinVermillian", unique=false, modelPath="Creature\\Crab\\Crab.mdx"},
|
||||
{id=18757, tex="CrabSkinDarkDiamond", unique=true, modelPath="Creature\\Crab\\Crab.mdx"},
|
||||
{id=18758, tex="CrabSkinTrueSilver", unique=true, modelPath="Creature\\Crab\\Crab.mdx"},
|
||||
},
|
||||
["Crocolisks"] = {
|
||||
{id=807, tex="CrocodileSkinSwamp", unique=false, modelPath="Creature\\Crocodile\\Crocodile.mdx"},
|
||||
{id=833, tex="CrocodileSkinRiver", unique=false, modelPath="Creature\\Crocodile\\Crocodile.mdx"},
|
||||
{id=925, tex="CrocodileSkinMarsh", unique=false, modelPath="Creature\\Crocodile\\Crocodile.mdx"},
|
||||
{id=1609, tex="CrocodileSkinAlbino", unique=false, modelPath="Creature\\Crocodile\\Crocodile.mdx"},
|
||||
},
|
||||
["Foxes"] = {
|
||||
{id=18004, tex="MistFoxSkin", unique=true, modelPath="Creature\\Mistfox\\Mistfox.mdx"},
|
||||
{id=18005, tex="MistFoxSkinArctic", unique=true, modelPath="Creature\\Mistfox\\Mistfox.mdx"},
|
||||
{id=18006, tex="MistFoxSkinBlack", unique=false, modelPath="Creature\\Mistfox\\Mistfox.mdx"},
|
||||
{id=18007, tex="MistFoxSkinBrown", unique=true, modelPath="Creature\\Mistfox\\Mistfox.mdx"},
|
||||
{id=18008, tex="MistFoxSkinRed", unique=false, modelPath="Creature\\Mistfox\\Mistfox.mdx"},
|
||||
{id=18244, tex="MistFoxSkinSpirit", unique=true, modelPath="Creature\\Mistfox\\Mistfox.mdx"},
|
||||
{id=18773, tex="MistFoxSkinPink", unique=true, modelPath="Creature\\Mistfox\\Mistfox.mdx"},
|
||||
{id=19105, tex="MistFoxSkinEvil", unique=true, modelPath="Creature\\Mistfox\\Mistfox.mdx"},
|
||||
{id=19106, tex="MistFoxSkinGolden", unique=true, modelPath="Creature\\Mistfox\\Mistfox.mdx"},
|
||||
{id=21282, tex="fox2_black", unique=true, modelPath="Creature\\fox2\\fox2.mdx"},
|
||||
{id=21283, tex="fox2_blue", unique=true, modelPath="Creature\\fox2\\fox2.mdx"},
|
||||
{id=21284, tex="fox2_darkred", unique=true, modelPath="Creature\\fox2\\fox2.mdx"},
|
||||
{id=21285, tex="fox2_green", unique=true, modelPath="Creature\\fox2\\fox2.mdx"},
|
||||
{id=21286, tex="fox2_orange", unique=true, modelPath="Creature\\fox2\\fox2.mdx"},
|
||||
{id=21287, tex="fox2_purple", unique=true, modelPath="Creature\\fox2\\fox2.mdx"},
|
||||
{id=21288, tex="fox2_red", unique=true, modelPath="Creature\\fox2\\fox2.mdx"},
|
||||
{id=21289, tex="fox2_teal", unique=true, modelPath="Creature\\fox2\\fox2.mdx"},
|
||||
{id=21290, tex="fox2_white", unique=true, modelPath="Creature\\fox2\\fox2.mdx"},
|
||||
},
|
||||
["Gorillas"] = {
|
||||
{id=792, tex="GorillaSkinSilver", unique=false, modelPath="Creature\\Gorilla\\Gorilla.mdx"},
|
||||
{id=809, tex="GorillaSkinBlack", unique=false, modelPath="Creature\\Gorilla\\Gorilla.mdx"},
|
||||
{id=838, tex="GorillaSkinGrey", unique=false, modelPath="Creature\\Gorilla\\Gorilla.mdx"},
|
||||
{id=840, tex="GorillaSkinWhite", unique=false, modelPath="Creature\\Gorilla\\Gorilla.mdx"},
|
||||
{id=3186, tex="GorillaSkinRed", unique=false, modelPath="Creature\\Gorilla\\Gorilla.mdx"},
|
||||
{id=8841, tex="GorillaSkinAmy", unique=true, modelPath="Creature\\Gorilla\\Gorilla.mdx"},
|
||||
},
|
||||
["Hyenas"] = {
|
||||
{id=1534, tex="HyenaSkinOarnge", unique=false, modelPath="Creature\\Hyena\\Hyena.mdx"},
|
||||
{id=1536, tex="HyenaSkin", unique=false, modelPath="Creature\\Hyena\\Hyena.mdx"},
|
||||
{id=2709, tex="HyenaSkinRed", unique=false, modelPath="Creature\\Hyena\\Hyena.mdx"},
|
||||
{id=2710, tex="HyenaSkinYellow", unique=false, modelPath="Creature\\Hyena\\Hyena.mdx"},
|
||||
{id=2713, tex="HyenaSkinBlue", unique=false, modelPath="Creature\\Hyena\\Hyena.mdx"},
|
||||
{id=2714, tex="HyenaSkinWhite", unique=false, modelPath="Creature\\Hyena\\Hyena.mdx"},
|
||||
{id=2726, tex="HyenaSkinBlack", unique=false, modelPath="Creature\\Hyena\\Hyena.mdx"},
|
||||
},
|
||||
["Moths"] = {
|
||||
{id=21298, tex="mothardenwealdmount_blue", unique=true, modelPath="Creature\\mothardenwealdmount\\mothardenwealdmount.mdx"},
|
||||
{id=21299, tex="mothardenwealdmount_dark", unique=true, modelPath="Creature\\mothardenwealdmount\\mothardenwealdmount.mdx"},
|
||||
{id=21300, tex="mothardenwealdmount_mint", unique=true, modelPath="Creature\\mothardenwealdmount\\mothardenwealdmount.mdx"},
|
||||
{id=21301, tex="mothardenwealdmount_red", unique=true, modelPath="Creature\\mothardenwealdmount\\mothardenwealdmount.mdx"},
|
||||
{id=21431, tex="MothSkinBeige", unique=true, modelPath="Creature\\Moth\\Moth.mdx"},
|
||||
{id=21432, tex="MothSkinBlue", unique=true, modelPath="Creature\\Moth\\Moth.mdx"},
|
||||
{id=21433, tex="mothskingrey", unique=true, modelPath="Creature\\Moth\\Moth.mdx"},
|
||||
{id=21434, tex="MothSkinRed", unique=true, modelPath="Creature\\Moth\\Moth.mdx"},
|
||||
{id=21435, tex="MothSkinWhite", unique=true, modelPath="Creature\\Moth\\Moth.mdx"},
|
||||
{id=21436, tex="mothskinyellow", unique=true, modelPath="Creature\\Moth\\Moth.mdx"},
|
||||
},
|
||||
["Owls"] = {
|
||||
{id=4566, tex="OwlBrown", unique=false, modelPath="Creature\\Owl\\Owl.mdx"},
|
||||
{id=4877, tex="OwlGrey", unique=false, modelPath="Creature\\Owl\\Owl.mdx"},
|
||||
{id=6212, tex="OwlWhite", unique=false, modelPath="Creature\\Owl\\Owl.mdx"},
|
||||
{id=6298, tex="OwlBlue", unique=true, modelPath="Creature\\Owl\\Owl.mdx"},
|
||||
{id=6299, tex="OwlBlack", unique=true, modelPath="Creature\\Owl\\Owl.mdx"},
|
||||
{id=18338, tex="Eagle", unique=false, modelPath="Creature\\Eagle\\Eagle.mdx"},
|
||||
{id=18339, tex="BrownEagle", unique=true, modelPath="Creature\\Eagle\\Eagle.mdx"},
|
||||
{id=18368, tex="OwlSpirit", unique=true, modelPath="Creature\\Owl\\Owl.mdx"},
|
||||
{id=18746, tex="EagleRaven", unique=true, modelPath="Creature\\Eagle\\Eagle.mdx"},
|
||||
{id=21109, tex="OwlRuby", unique=true, modelPath="Creature\\Owl\\Owl.mdx"},
|
||||
{id=21110, tex="OwlSapphire", unique=true, modelPath="Creature\\Owl\\Owl.mdx"},
|
||||
},
|
||||
["Raptors"] = {
|
||||
{id=180, tex="RaptorSkinOrange", unique=false, modelPath="Creature\\Raptor\\Raptor.mdx"},
|
||||
{id=322, tex="RaptorSkinMottledDarkGreen", unique=false, modelPath="Creature\\Raptor\\Raptor.mdx"},
|
||||
{id=675, tex="RaptorSkinMottledBlueGreen", unique=false, modelPath="Creature\\Raptor\\Raptor.mdx"},
|
||||
{id=787, tex="RaptorSkinYellow", unique=false, modelPath="Creature\\Raptor\\Raptor.mdx"},
|
||||
{id=960, tex="RaptorSkinViolet", unique=false, modelPath="Creature\\Raptor\\Raptor.mdx"},
|
||||
{id=1337, tex="RaptorSkinGrey", unique=false, modelPath="Creature\\Raptor\\Raptor.mdx"},
|
||||
{id=1959, tex="RaptorSkinRed", unique=false, modelPath="Creature\\Raptor\\Raptor.mdx"},
|
||||
{id=5291, tex="RidingRaptorSkinObsidian", unique=false, modelPath="Creature\\Raptor\\Raptor.mdx"},
|
||||
},
|
||||
["Scorpids"] = {
|
||||
{id=2414, tex="ScorpionskinPink", unique=false, modelPath="Creature\\Scorpion\\Scorpion.mdx"},
|
||||
{id=2485, tex="ScorpionskinBeige", unique=false, modelPath="Creature\\Scorpion\\Scorpion.mdx"},
|
||||
{id=2487, tex="ScorpionskinYellow", unique=false, modelPath="Creature\\Scorpion\\Scorpion.mdx"},
|
||||
{id=2488, tex="ScorpionskinBlack", unique=false, modelPath="Creature\\Scorpion\\Scorpion.mdx"},
|
||||
{id=2489, tex="ScorpionskinRed", unique=false, modelPath="Creature\\Scorpion\\Scorpion.mdx"},
|
||||
{id=2729, tex="ScorpionskinGolden", unique=false, modelPath="Creature\\Scorpion\\Scorpion.mdx"},
|
||||
{id=2730, tex="ScorpionskinBlue", unique=false, modelPath="Creature\\Scorpion\\Scorpion.mdx"},
|
||||
{id=7469, tex="SilithidScarabSkinBlack", unique=false, modelPath="Creature\\SILITHIDSCARAB\\SilithidScarab.mdx"},
|
||||
{id=8971, tex="SilithidScarabSkinRed", unique=false, modelPath="Creature\\SILITHIDSCARAB\\SilithidScarab.mdx"},
|
||||
{id=9028, tex="SilithidScarabSkinWhite", unique=false, modelPath="Creature\\SILITHIDSCARAB\\SilithidScarab.mdx"},
|
||||
{id=9469, tex="SilithidScarabSkinYellow", unique=false, modelPath="Creature\\SILITHIDSCARAB\\SilithidScarab.mdx"},
|
||||
{id=10983, tex="ScorpionskinSilver", unique=false, modelPath="Creature\\Scorpion\\Scorpion.mdx"},
|
||||
{id=11093, tex="SilithidScarabSkinTeal", unique=false, modelPath="Creature\\SILITHIDSCARAB\\SilithidScarab.mdx"},
|
||||
{id=11104, tex="SilithidScarabSkinGold", unique=false, modelPath="Creature\\SILITHIDSCARAB\\SilithidScarab.mdx"},
|
||||
{id=11107, tex="SilithidScarabSkinPurple", unique=false, modelPath="Creature\\SILITHIDSCARAB\\SilithidScarab.mdx"},
|
||||
{id=11143, tex="SilithidScarabSkinTan", unique=false, modelPath="Creature\\SILITHIDSCARAB\\SilithidScarab.mdx"},
|
||||
{id=18373, tex="SilithidScarabSkinGreen", unique=true, modelPath="Creature\\SILITHIDSCARAB\\SilithidScarab.mdx"},
|
||||
{id=21268, tex="ScorpionskinDarkIron", unique=true, modelPath="Creature\\Scorpion\\Scorpion.mdx"},
|
||||
},
|
||||
["Serpents"] = {
|
||||
{id=1206, tex="SnakeSkinBlack", unique=false, modelPath="Creature\\snake\\snake.mdx"},
|
||||
{id=1986, tex="SnakeSkinDarkGreenYellow", unique=false, modelPath="Creature\\snake\\snake.mdx"},
|
||||
{id=1987, tex="SnakeSkinYellowBlack", unique=false, modelPath="Creature\\snake\\snake.mdx"},
|
||||
{id=2954, tex="SnakeSkinRedWhiteBlack", unique=true, modelPath="Creature\\snake\\snake.mdx"},
|
||||
{id=2955, tex="SnakeSkinWhite", unique=false, modelPath="Creature\\snake\\snake.mdx"},
|
||||
{id=2956, tex="SnakeSkinBlue", unique=false, modelPath="Creature\\snake\\snake.mdx"},
|
||||
{id=2957, tex="SnakeSkinBrown", unique=true, modelPath="Creature\\snake\\snake.mdx"},
|
||||
{id=2958, tex="SnakeSkinGreen", unique=false, modelPath="Creature\\snake\\snake.mdx"},
|
||||
{id=4305, tex="SerpantSkinWhite", unique=false, modelPath="Creature\\Serpent\\Serpent.mdx"},
|
||||
{id=4312, tex="SerpantSkinPurple", unique=false, modelPath="Creature\\Serpent\\Serpent.mdx"},
|
||||
{id=4317, tex="SerpantSkinBlue", unique=false, modelPath="Creature\\Serpent\\Serpent.mdx"},
|
||||
{id=4435, tex="SerpantSkinGreen", unique=false, modelPath="Creature\\Serpent\\Serpent.mdx"},
|
||||
{id=6303, tex="SnakeSkinRed", unique=true, modelPath="Creature\\snake\\snake.mdx"},
|
||||
{id=11034, tex="SerpantSkinOlive", unique=false, modelPath="Creature\\Serpent\\Serpent.mdx"},
|
||||
{id=15182, tex="SerpantSkinBrown", unique=true, modelPath="Creature\\Serpent\\Serpent.mdx"},
|
||||
{id=20633, tex="SerpantSkinViolet", unique=true, modelPath="Creature\\Serpent\\Serpent.mdx"},
|
||||
{id=20634, tex="SerpantSkinRuby", unique=true, modelPath="Creature\\Serpent\\Serpent.mdx"},
|
||||
{id=21269, tex="SerpentSkinRuby", unique=true, modelPath="Creature\\Serpent\\Serpent.mdx"},
|
||||
},
|
||||
["Spiders"] = {
|
||||
{id=30, tex="MineSpiderSkinSteel", unique=false, modelPath="Creature\\MineSpider\\MineSpider.mdx"},
|
||||
{id=283, tex="MineSpiderSkinGreen", unique=false, modelPath="Creature\\MineSpider\\MineSpider.mdx"},
|
||||
{id=336, tex="TarantulaSkinGreen", unique=false, modelPath="Creature\\Tarantula\\Tarantula.mdx"},
|
||||
{id=366, tex="TarantulaSkinOrange", unique=false, modelPath="Creature\\Tarantula\\Tarantula.mdx"},
|
||||
{id=513, tex="MineSpiderSkinOlive", unique=false, modelPath="Creature\\MineSpider\\MineSpider.mdx"},
|
||||
{id=520, tex="TarantulaSkinBrown", unique=false, modelPath="Creature\\Tarantula\\Tarantula.mdx"},
|
||||
{id=711, tex="MineSpiderSkinWetLands", unique=false, modelPath="Creature\\MineSpider\\MineSpider.mdx"},
|
||||
{id=955, tex="MineSpiderSkinCave", unique=false, modelPath="Creature\\MineSpider\\MineSpider.mdx"},
|
||||
{id=963, tex="MineSpiderSkinBlood", unique=false, modelPath="Creature\\MineSpider\\MineSpider.mdx"},
|
||||
{id=1091, tex="TarantulaSkinGrey", unique=false, modelPath="Creature\\Tarantula\\Tarantula.mdx"},
|
||||
{id=2536, tex="MineSpiderSkinJungle", unique=false, modelPath="Creature\\MineSpider\\MineSpider.mdx"},
|
||||
{id=4456, tex="MineSpiderSkinCrystal", unique=false, modelPath="Creature\\MineSpider\\MineSpider.mdx"},
|
||||
{id=4457, tex="TarantulaSkinMagma", unique=false, modelPath="Creature\\Tarantula\\Tarantula.mdx"},
|
||||
{id=15937, tex="MineSpiderSkinViolet", unique=true, modelPath="Creature\\MineSpider\\MineSpider.mdx"},
|
||||
},
|
||||
["Tallstriders"] = {
|
||||
{id=38, tex="TallStriderSkinTurquoise", unique=false, modelPath="Creature\\Tallstrider\\TallStrider.mdx"},
|
||||
{id=178, tex="TallStriderSkinBrown", unique=false, modelPath="Creature\\Tallstrider\\TallStrider.mdx"},
|
||||
{id=1220, tex="TallStriderSkinGray", unique=false, modelPath="Creature\\Tallstrider\\TallStrider.mdx"},
|
||||
{id=1221, tex="TallStriderSkinIvory", unique=false, modelPath="Creature\\Tallstrider\\TallStrider.mdx"},
|
||||
{id=1281, tex="TallStriderSkinPurple", unique=true, modelPath="Creature\\Tallstrider\\TallStrider.mdx"},
|
||||
{id=1961, tex="TallStriderSkinPink", unique=true, modelPath="Creature\\Tallstrider\\TallStrider.mdx"},
|
||||
{id=20509, tex="HawkstriderBlue", unique=true, modelPath="Creature\\Hawkstrider\\Hawkstrider.mdx"},
|
||||
{id=20510, tex="HawkstriderGold", unique=true, modelPath="Creature\\Hawkstrider\\Hawkstrider.mdx"},
|
||||
{id=20511, tex="HawkstriderGreen", unique=true, modelPath="Creature\\Hawkstrider\\Hawkstrider.mdx"},
|
||||
{id=20512, tex="HawkstriderRed", unique=true, modelPath="Creature\\Hawkstrider\\Hawkstrider.mdx"},
|
||||
{id=20513, tex="HawkstriderWhite", unique=true, modelPath="Creature\\Hawkstrider\\Hawkstrider.mdx"},
|
||||
{id=21554, tex="TallStriderSkinBlue", unique=true, modelPath="Creature\\Tallstrider\\TallStrider.mdx"},
|
||||
{id=21555, tex="TallStriderSkinViolet", unique=true, modelPath="Creature\\Tallstrider\\TallStrider.mdx"},
|
||||
},
|
||||
["Turtles"] = {
|
||||
{id=1244, tex="SeaTurtleSkin", unique=false, modelPath="Creature\\SeaTurtle\\SeaTurtle.mdx"},
|
||||
{id=2307, tex="SeaTurtleSkinWhite", unique=false, modelPath="Creature\\SeaTurtle\\SeaTurtle.mdx"},
|
||||
{id=4829, tex="SeaTurtleSkinGrey", unique=false, modelPath="Creature\\SeaTurtle\\SeaTurtle.mdx"},
|
||||
{id=5026, tex="SeaTurtleSkinRed", unique=false, modelPath="Creature\\SeaTurtle\\SeaTurtle.mdx"},
|
||||
{id=5126, tex="SeaTurtleSkinBlue", unique=false, modelPath="Creature\\SeaTurtle\\SeaTurtle.mdx"},
|
||||
{id=16259, tex="TurtleSkinGreen", unique=false, modelPath="Creature\\Turtle\\Turtle.mdx"},
|
||||
{id=16360, tex="TurtleSkinBrown", unique=true, modelPath="Creature\\Turtle\\Turtle.mdx"},
|
||||
{id=16361, tex="TurtleSkinBlack", unique=true, modelPath="Creature\\Turtle\\Turtle.mdx"},
|
||||
},
|
||||
["Wind Serpents"] = {
|
||||
{id=1336, tex="WindSerpentSkin", unique=false, modelPath="Creature\\WindSerpent\\WindSerpent.mdx"},
|
||||
{id=2699, tex="WindSerpentSkinRed", unique=false, modelPath="Creature\\WindSerpent\\WindSerpent.mdx"},
|
||||
{id=2700, tex="WindSerpentSkinGreen", unique=false, modelPath="Creature\\WindSerpent\\WindSerpent.mdx"},
|
||||
{id=2703, tex="WindSerpentSkinWhite", unique=false, modelPath="Creature\\WindSerpent\\WindSerpent.mdx"},
|
||||
{id=3006, tex="WindSerpentSkinBlack", unique=false, modelPath="Creature\\WindSerpent\\WindSerpent.mdx"},
|
||||
{id=18386, tex="WindSerpentSkinOarnge", unique=true, modelPath="Creature\\WindSerpent\\WindSerpent.mdx"},
|
||||
{id=20514, tex="ManaWurmSkinBlack", unique=true, modelPath="Creature\\Manawurm\\manawurm.mdx"},
|
||||
{id=20515, tex="MANAWURMSKINGREEN", unique=true, modelPath="Creature\\Manawurm\\manawurm.mdx"},
|
||||
{id=20516, tex="ManaWurmSkinPurple", unique=true, modelPath="Creature\\Manawurm\\manawurm.mdx"},
|
||||
{id=20517, tex="ManaWurmSkinQuel", unique=true, modelPath="Creature\\Manawurm\\manawurm.mdx"},
|
||||
{id=20518, tex="ManaWurmSkinSilver1", unique=true, modelPath="Creature\\Manawurm\\manawurm.mdx"},
|
||||
{id=20519, tex="ManaWurmSkinWhite", unique=true, modelPath="Creature\\Manawurm\\manawurm.mdx"},
|
||||
},
|
||||
["Wolves"] = {
|
||||
{id=73, tex="Direwolfskinbrown", unique=true, modelPath="Creature\\DireWolf\\DireWolf.mdx"},
|
||||
{id=161, tex="WolfSkinCoyote", unique=false, modelPath="Creature\\Wolf\\Wolf.mdx"},
|
||||
{id=165, tex="WolfSkinTimber", unique=false, modelPath="Creature\\Wolf\\Wolf.mdx"},
|
||||
{id=246, tex="DireWolfSkinDarkBlack", unique=false, modelPath="Creature\\DireWolf\\DireWolf.mdx"},
|
||||
{id=644, tex="WolfSkinBlack", unique=false, modelPath="Creature\\Wolf\\Wolf.mdx"},
|
||||
{id=720, tex="DireWolfSkinLightBlue", unique=false, modelPath="Creature\\DireWolf\\DireWolf.mdx"},
|
||||
{id=776, tex="WolfSkinArctic", unique=false, modelPath="Creature\\Wolf\\Wolf.mdx"},
|
||||
{id=868, tex="WolfSkinArcticAlpha", unique=true, modelPath="Creature\\Wolf\\Wolf.mdx"},
|
||||
{id=1950, tex="WolfSkin_Ghost", unique=true, modelPath="Creature\\Wolf\\Wolf.mdx"},
|
||||
{id=3916, tex="HoundSkinBlue", unique=false, modelPath="Creature\\DarkHound\\DarkHound.mdx"},
|
||||
{id=4124, tex="WolfSkinDiseased", unique=false, modelPath="Creature\\Wolf\\Wolf.mdx"},
|
||||
{id=6195, tex="HoundSkinGrey", unique=false, modelPath="Creature\\DarkHound\\DarkHound.mdx"},
|
||||
{id=7890, tex="HoundSkinPurple", unique=false, modelPath="Creature\\DarkHound\\DarkHound.mdx"},
|
||||
{id=8180, tex="HoundSkinRed", unique=false, modelPath="Creature\\DarkHound\\DarkHound.mdx"},
|
||||
{id=9369, tex="DireWolfSkinReddishBrown", unique=false, modelPath="Creature\\DireWolf\\DireWolf.mdx"},
|
||||
{id=9564, tex="DireWolfSkinLightGrey", unique=false, modelPath="Creature\\DireWolf\\DireWolf.mdx"},
|
||||
{id=9567, tex="DireWolfSkinBlueBrown", unique=true, modelPath="Creature\\DireWolf\\DireWolf.mdx"},
|
||||
{id=9568, tex="DireWolfSkinDarkBrown", unique=false, modelPath="Creature\\DireWolf\\DireWolf.mdx"},
|
||||
{id=11414, tex="DireWolfSkinDarkGrey", unique=true, modelPath="Creature\\DireWolf\\DireWolf.mdx"},
|
||||
{id=14312, tex="HoundSkinWhite", unique=false, modelPath="Creature\\DarkHound\\DarkHound.mdx"},
|
||||
},
|
||||
}
|
||||
|
||||
-- Maps any beast displayId to the canonical skin id used in MTH_DS_BeastSkins
|
||||
-- Total: 577 entries
|
||||
MTH_DS_DisplayToSkin = {
|
||||
[368] = 30,
|
||||
[372] = 165,
|
||||
[380] = 165,
|
||||
[382] = 366,
|
||||
[389] = 193,
|
||||
[418] = 30,
|
||||
[447] = 165,
|
||||
[471] = 320,
|
||||
[498] = 366,
|
||||
[499] = 193,
|
||||
[503] = 193,
|
||||
[538] = 513,
|
||||
[539] = 30,
|
||||
[545] = 283,
|
||||
[557] = 161,
|
||||
[566] = 520,
|
||||
[598] = 320,
|
||||
[604] = 165,
|
||||
[607] = 381,
|
||||
[613] = 599,
|
||||
[614] = 320,
|
||||
[615] = 322,
|
||||
[633] = 599,
|
||||
[641] = 342,
|
||||
[643] = 161,
|
||||
[647] = 165,
|
||||
[648] = 322,
|
||||
[649] = 180,
|
||||
[651] = 366,
|
||||
[659] = 513,
|
||||
[666] = 165,
|
||||
[670] = 180,
|
||||
[671] = 180,
|
||||
[672] = 180,
|
||||
[673] = 180,
|
||||
[674] = 322,
|
||||
[676] = 322,
|
||||
[677] = 675,
|
||||
[695] = 322,
|
||||
[698] = 320,
|
||||
[699] = 342,
|
||||
[703] = 377,
|
||||
[704] = 377,
|
||||
[707] = 706,
|
||||
[709] = 336,
|
||||
[719] = 706,
|
||||
[741] = 246,
|
||||
[744] = 381,
|
||||
[749] = 748,
|
||||
[755] = 675,
|
||||
[759] = 336,
|
||||
[760] = 336,
|
||||
[762] = 706,
|
||||
[768] = 283,
|
||||
[780] = 644,
|
||||
[781] = 644,
|
||||
[782] = 644,
|
||||
[783] = 165,
|
||||
[784] = 644,
|
||||
[785] = 776,
|
||||
[788] = 180,
|
||||
[801] = 776,
|
||||
[802] = 776,
|
||||
[814] = 807,
|
||||
[815] = 807,
|
||||
[821] = 520,
|
||||
[827] = 520,
|
||||
[831] = 807,
|
||||
[834] = 833,
|
||||
[835] = 807,
|
||||
[837] = 792,
|
||||
[839] = 809,
|
||||
[841] = 792,
|
||||
[842] = 838,
|
||||
[843] = 838,
|
||||
[844] = 792,
|
||||
[845] = 809,
|
||||
[855] = 776,
|
||||
[869] = 776,
|
||||
[902] = 822,
|
||||
[903] = 165,
|
||||
[909] = 520,
|
||||
[910] = 520,
|
||||
[913] = 865,
|
||||
[917] = 632,
|
||||
[918] = 632,
|
||||
[945] = 180,
|
||||
[948] = 180,
|
||||
[949] = 675,
|
||||
[954] = 748,
|
||||
[957] = 283,
|
||||
[958] = 955,
|
||||
[959] = 955,
|
||||
[961] = 787,
|
||||
[982] = 822,
|
||||
[996] = 979,
|
||||
[997] = 981,
|
||||
[998] = 979,
|
||||
[999] = 981,
|
||||
[1000] = 979,
|
||||
[1001] = 981,
|
||||
[1006] = 822,
|
||||
[1007] = 806,
|
||||
[1008] = 632,
|
||||
[1030] = 720,
|
||||
[1034] = 807,
|
||||
[1035] = 925,
|
||||
[1036] = 925,
|
||||
[1037] = 833,
|
||||
[1038] = 925,
|
||||
[1039] = 833,
|
||||
[1040] = 38,
|
||||
[1041] = 178,
|
||||
[1042] = 178,
|
||||
[1043] = 632,
|
||||
[1055] = 632,
|
||||
[1058] = 1056,
|
||||
[1059] = 1056,
|
||||
[1080] = 807,
|
||||
[1087] = 955,
|
||||
[1088] = 513,
|
||||
[1089] = 513,
|
||||
[1090] = 955,
|
||||
[1094] = 809,
|
||||
[1095] = 599,
|
||||
[1100] = 161,
|
||||
[1103] = 1091,
|
||||
[1104] = 1091,
|
||||
[1105] = 388,
|
||||
[1106] = 388,
|
||||
[1157] = 955,
|
||||
[1160] = 955,
|
||||
[1164] = 161,
|
||||
[1167] = 193,
|
||||
[1192] = 507,
|
||||
[1207] = 776,
|
||||
[1208] = 377,
|
||||
[1210] = 490,
|
||||
[1219] = 178,
|
||||
[1222] = 161,
|
||||
[1228] = 388,
|
||||
[1229] = 388,
|
||||
[1250] = 833,
|
||||
[1251] = 833,
|
||||
[1259] = 180,
|
||||
[1283] = 178,
|
||||
[1284] = 1221,
|
||||
[1338] = 960,
|
||||
[1339] = 1337,
|
||||
[1340] = 1337,
|
||||
[1535] = 1534,
|
||||
[1742] = 1336,
|
||||
[1744] = 960,
|
||||
[1746] = 1337,
|
||||
[1747] = 960,
|
||||
[1873] = 513,
|
||||
[1938] = 1307,
|
||||
[1939] = 1307,
|
||||
[1955] = 1954,
|
||||
[1960] = 1959,
|
||||
[1962] = 1959,
|
||||
[1971] = 1057,
|
||||
[1972] = 1057,
|
||||
[1973] = 1057,
|
||||
[1974] = 1336,
|
||||
[1975] = 1336,
|
||||
[1977] = 1057,
|
||||
[1978] = 1933,
|
||||
[1989] = 336,
|
||||
[1990] = 822,
|
||||
[2031] = 1056,
|
||||
[2239] = 979,
|
||||
[2278] = 1056,
|
||||
[2279] = 616,
|
||||
[2300] = 1056,
|
||||
[2301] = 1536,
|
||||
[2305] = 410,
|
||||
[2308] = 2307,
|
||||
[2352] = 246,
|
||||
[2378] = 822,
|
||||
[2424] = 366,
|
||||
[2433] = 30,
|
||||
[2437] = 599,
|
||||
[2453] = 2450,
|
||||
[2486] = 2485,
|
||||
[2491] = 2488,
|
||||
[2537] = 2536,
|
||||
[2538] = 2536,
|
||||
[2539] = 2536,
|
||||
[2540] = 283,
|
||||
[2541] = 283,
|
||||
[2542] = 366,
|
||||
[2543] = 366,
|
||||
[2544] = 366,
|
||||
[2545] = 366,
|
||||
[2546] = 366,
|
||||
[2548] = 925,
|
||||
[2549] = 925,
|
||||
[2550] = 807,
|
||||
[2570] = 1959,
|
||||
[2571] = 1959,
|
||||
[2573] = 1959,
|
||||
[2574] = 1959,
|
||||
[2609] = 1534,
|
||||
[2702] = 2699,
|
||||
[2705] = 2703,
|
||||
[2706] = 2700,
|
||||
[2707] = 2700,
|
||||
[2708] = 2700,
|
||||
[2711] = 2710,
|
||||
[2712] = 2710,
|
||||
[2715] = 2714,
|
||||
[2716] = 2714,
|
||||
[2731] = 2730,
|
||||
[2732] = 2487,
|
||||
[2741] = 1220,
|
||||
[2765] = 2729,
|
||||
[2766] = 2729,
|
||||
[2850] = 1609,
|
||||
[2864] = 2488,
|
||||
[2902] = 1244,
|
||||
[2995] = 2958,
|
||||
[2996] = 1609,
|
||||
[3027] = 3026,
|
||||
[3030] = 3029,
|
||||
[3031] = 3029,
|
||||
[3035] = 3026,
|
||||
[3126] = 1206,
|
||||
[3187] = 3186,
|
||||
[3188] = 3186,
|
||||
[3200] = 806,
|
||||
[3201] = 806,
|
||||
[3202] = 165,
|
||||
[3203] = 165,
|
||||
[3204] = 2699,
|
||||
[3205] = 2699,
|
||||
[3247] = 2489,
|
||||
[3248] = 490,
|
||||
[4065] = 2699,
|
||||
[4090] = 1609,
|
||||
[4091] = 2700,
|
||||
[4167] = 2956,
|
||||
[4268] = 1206,
|
||||
[4343] = 1986,
|
||||
[4440] = 1987,
|
||||
[4442] = 960,
|
||||
[4458] = 4457,
|
||||
[4472] = 748,
|
||||
[4480] = 4312,
|
||||
[4613] = 161,
|
||||
[4615] = 4566,
|
||||
[4636] = 2710,
|
||||
[4689] = 2709,
|
||||
[4716] = 4713,
|
||||
[4732] = 3956,
|
||||
[4733] = 4185,
|
||||
[4734] = 3956,
|
||||
[4767] = 4317,
|
||||
[4768] = 4435,
|
||||
[5027] = 5026,
|
||||
[5030] = 5026,
|
||||
[5052] = 2307,
|
||||
[5066] = 1933,
|
||||
[5067] = 599,
|
||||
[5068] = 3029,
|
||||
[5069] = 632,
|
||||
[5127] = 5126,
|
||||
[5242] = 960,
|
||||
[5244] = 792,
|
||||
[5245] = 838,
|
||||
[5290] = 675,
|
||||
[5292] = 960,
|
||||
[5294] = 792,
|
||||
[5295] = 838,
|
||||
[5296] = 838,
|
||||
[5510] = 822,
|
||||
[5557] = 342,
|
||||
[5558] = 981,
|
||||
[5559] = 979,
|
||||
[5560] = 1307,
|
||||
[5905] = 2488,
|
||||
[5966] = 4305,
|
||||
[5985] = 2489,
|
||||
[6068] = 2488,
|
||||
[6069] = 2488,
|
||||
[6070] = 2488,
|
||||
[6073] = 1091,
|
||||
[6076] = 1220,
|
||||
[6082] = 1056,
|
||||
[6122] = 6121,
|
||||
[6126] = 1244,
|
||||
[6196] = 6195,
|
||||
[6210] = 616,
|
||||
[6213] = 1091,
|
||||
[6214] = 1091,
|
||||
[6248] = 616,
|
||||
[6300] = 4877,
|
||||
[6368] = 5126,
|
||||
[6468] = 5291,
|
||||
[6805] = 3029,
|
||||
[6807] = 3026,
|
||||
[6808] = 336,
|
||||
[6942] = 1056,
|
||||
[6943] = 599,
|
||||
[7046] = 5126,
|
||||
[7114] = 5126,
|
||||
[7339] = 644,
|
||||
[7347] = 2489,
|
||||
[7348] = 490,
|
||||
[7349] = 490,
|
||||
[7409] = 1206,
|
||||
[7470] = 7469,
|
||||
[7510] = 4457,
|
||||
[7511] = 7469,
|
||||
[7569] = 2703,
|
||||
[7619] = 7469,
|
||||
[7691] = 2703,
|
||||
[7829] = 3006,
|
||||
[7836] = 4829,
|
||||
[7837] = 4829,
|
||||
[7839] = 4829,
|
||||
[7840] = 4829,
|
||||
[7891] = 7890,
|
||||
[7892] = 7890,
|
||||
[7893] = 7890,
|
||||
[7894] = 4185,
|
||||
[7896] = 1954,
|
||||
[7897] = 4185,
|
||||
[7906] = 2699,
|
||||
[8014] = 520,
|
||||
[8050] = 2714,
|
||||
[8129] = 840,
|
||||
[8173] = 2955,
|
||||
[8174] = 1987,
|
||||
[8181] = 8180,
|
||||
[8271] = 4456,
|
||||
[8472] = 675,
|
||||
[8473] = 960,
|
||||
[8808] = 4185,
|
||||
[8837] = 865,
|
||||
[8838] = 806,
|
||||
[8839] = 792,
|
||||
[8840] = 806,
|
||||
[8842] = 865,
|
||||
[8843] = 706,
|
||||
[8844] = 3186,
|
||||
[8870] = 381,
|
||||
[8871] = 8869,
|
||||
[8969] = 2487,
|
||||
[8970] = 2487,
|
||||
[9019] = 8180,
|
||||
[9020] = 3916,
|
||||
[9021] = 3916,
|
||||
[9022] = 7890,
|
||||
[9029] = 9028,
|
||||
[9050] = 675,
|
||||
[9074] = 1954,
|
||||
[9276] = 1083,
|
||||
[9277] = 1083,
|
||||
[9278] = 4124,
|
||||
[9280] = 4124,
|
||||
[9287] = 644,
|
||||
[9354] = 7469,
|
||||
[9370] = 9369,
|
||||
[9371] = 9369,
|
||||
[9372] = 9369,
|
||||
[9509] = 644,
|
||||
[9511] = 644,
|
||||
[9535] = 3956,
|
||||
[9558] = 644,
|
||||
[9562] = 246,
|
||||
[9563] = 246,
|
||||
[9565] = 981,
|
||||
[9566] = 979,
|
||||
[9569] = 9568,
|
||||
[9570] = 342,
|
||||
[9571] = 342,
|
||||
[9572] = 9568,
|
||||
[9573] = 979,
|
||||
[9749] = 2488,
|
||||
[9750] = 4735,
|
||||
[9755] = 4456,
|
||||
[9756] = 4456,
|
||||
[9829] = 513,
|
||||
[9910] = 30,
|
||||
[9913] = 2699,
|
||||
[9929] = 4456,
|
||||
[9934] = 336,
|
||||
[9950] = 9949,
|
||||
[9951] = 9949,
|
||||
[9953] = 616,
|
||||
[9955] = 9949,
|
||||
[9957] = 9949,
|
||||
[9990] = 9989,
|
||||
[9992] = 9230,
|
||||
[9993] = 9230,
|
||||
[9997] = 4456,
|
||||
[10015] = 193,
|
||||
[10031] = 8971,
|
||||
[10033] = 1954,
|
||||
[10055] = 1934,
|
||||
[10113] = 10054,
|
||||
[10114] = 1934,
|
||||
[10133] = 840,
|
||||
[10183] = 2699,
|
||||
[10209] = 246,
|
||||
[10270] = 2726,
|
||||
[10271] = 2726,
|
||||
[10273] = 507,
|
||||
[10274] = 776,
|
||||
[10278] = 720,
|
||||
[10618] = 865,
|
||||
[10824] = 388,
|
||||
[10825] = 410,
|
||||
[10826] = 507,
|
||||
[10827] = 490,
|
||||
[10828] = 6212,
|
||||
[10829] = 4877,
|
||||
[10830] = 4877,
|
||||
[10831] = 4877,
|
||||
[10832] = 4877,
|
||||
[10833] = 6212,
|
||||
[10902] = 2726,
|
||||
[10903] = 2713,
|
||||
[10904] = 2713,
|
||||
[10947] = 2307,
|
||||
[10984] = 2487,
|
||||
[10985] = 2487,
|
||||
[10986] = 2414,
|
||||
[10987] = 2414,
|
||||
[10988] = 10983,
|
||||
[10991] = 2700,
|
||||
[11029] = 320,
|
||||
[11031] = 320,
|
||||
[11032] = 3956,
|
||||
[11089] = 9469,
|
||||
[11094] = 11093,
|
||||
[11098] = 2485,
|
||||
[11105] = 11104,
|
||||
[11108] = 11107,
|
||||
[11144] = 11143,
|
||||
[11315] = 1959,
|
||||
[11316] = 787,
|
||||
[11317] = 960,
|
||||
[11318] = 675,
|
||||
[11319] = 322,
|
||||
[11348] = 366,
|
||||
[11411] = 246,
|
||||
[11412] = 720,
|
||||
[11413] = 9564,
|
||||
[11415] = 9564,
|
||||
[11416] = 776,
|
||||
[11417] = 9564,
|
||||
[11418] = 9564,
|
||||
[11419] = 9564,
|
||||
[11420] = 9369,
|
||||
[11421] = 246,
|
||||
[11422] = 720,
|
||||
[11444] = 9958,
|
||||
[11445] = 9958,
|
||||
[11446] = 616,
|
||||
[11447] = 320,
|
||||
[11449] = 321,
|
||||
[11450] = 321,
|
||||
[11451] = 1056,
|
||||
[11452] = 599,
|
||||
[11453] = 11448,
|
||||
[11454] = 3029,
|
||||
[11455] = 320,
|
||||
[11829] = 4735,
|
||||
[11908] = 820,
|
||||
[11909] = 820,
|
||||
[11910] = 599,
|
||||
[12151] = 11093,
|
||||
[12156] = 11107,
|
||||
[12157] = 11104,
|
||||
[12159] = 9469,
|
||||
[12433] = 410,
|
||||
[12966] = 2710,
|
||||
[13096] = 7469,
|
||||
[13097] = 10983,
|
||||
[13111] = 963,
|
||||
[13589] = 1609,
|
||||
[14256] = 925,
|
||||
[14313] = 490,
|
||||
[14315] = 822,
|
||||
[14316] = 822,
|
||||
[14318] = 11448,
|
||||
[14319] = 507,
|
||||
[14364] = 9956,
|
||||
[14557] = 11034,
|
||||
[14558] = 4435,
|
||||
[14559] = 4305,
|
||||
[14562] = 3956,
|
||||
[14572] = 599,
|
||||
[14657] = 5126,
|
||||
[14658] = 4829,
|
||||
[14659] = 1244,
|
||||
[14660] = 5026,
|
||||
[14661] = 2307,
|
||||
[14780] = 1954,
|
||||
[14853] = 711,
|
||||
[14935] = 599,
|
||||
[14950] = 955,
|
||||
[14952] = 955,
|
||||
[15072] = 711,
|
||||
[15132] = 4317,
|
||||
[15133] = 4435,
|
||||
[15135] = 4312,
|
||||
[15136] = 711,
|
||||
[15137] = 3006,
|
||||
[15138] = 320,
|
||||
[15145] = 833,
|
||||
[15150] = 4317,
|
||||
[15151] = 320,
|
||||
[15180] = 9369,
|
||||
[15181] = 14312,
|
||||
[15204] = 9369,
|
||||
[15227] = 4735,
|
||||
[15275] = 2699,
|
||||
[15302] = 3186,
|
||||
[15327] = 2489,
|
||||
[15336] = 2485,
|
||||
[15339] = 11104,
|
||||
[15383] = 10983,
|
||||
[15384] = 2729,
|
||||
[15385] = 10983,
|
||||
[15422] = 2730,
|
||||
[15423] = 2730,
|
||||
[15433] = 2488,
|
||||
[15464] = 9028,
|
||||
[15467] = 11104,
|
||||
[15468] = 11143,
|
||||
[15469] = 2485,
|
||||
[15470] = 2729,
|
||||
[15471] = 2489,
|
||||
[15533] = 2414,
|
||||
[15534] = 6212,
|
||||
[15682] = 11107,
|
||||
[15878] = 776,
|
||||
[15929] = 7469,
|
||||
[15938] = 513,
|
||||
[15939] = 2536,
|
||||
[15964] = 14312,
|
||||
[15969] = 3916,
|
||||
[16145] = 5126,
|
||||
[16359] = 16259,
|
||||
[16622] = 5585,
|
||||
[18115] = 9492,
|
||||
[18177] = 18008,
|
||||
[18353] = 18338,
|
||||
[18493] = 18006,
|
||||
[20369] = 18027,
|
||||
[20370] = 1933,
|
||||
[20371] = 11448,
|
||||
[20372] = 321,
|
||||
[20373] = 9230,
|
||||
[20374] = 18432,
|
||||
[20375] = 18431,
|
||||
[20376] = 18433,
|
||||
[20377] = 18430,
|
||||
[20378] = 18427,
|
||||
[20379] = 18434,
|
||||
[20380] = 18428,
|
||||
[20381] = 18429,
|
||||
[20382] = 9956,
|
||||
[20383] = 18426,
|
||||
[20384] = 11448,
|
||||
[20385] = 321,
|
||||
[20386] = 9230,
|
||||
[20387] = 1933,
|
||||
[20394] = 20388,
|
||||
[20395] = 20389,
|
||||
[20396] = 20390,
|
||||
[20397] = 20391,
|
||||
[20398] = 20392,
|
||||
[20399] = 20393,
|
||||
}
|
||||
+32590
-6387
File diff suppressed because it is too large
Load Diff
+11602
-11602
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,844 @@
|
||||
-- MetaHunt: Hunter spell data extracted from Spell.dbc (SpellFamilyName=9). Field layout: 173 fields. Indexed by spell ID: MTH_DS_HunterSpellsDBC[spellId]. Named lookup (rank 1): MTH_DS_HunterSpellsByName['SpellName'].
|
||||
-- AUTO-GENERATED by .tools — do not edit by hand.
|
||||
-- Source DBC(s): Spell.dbc, SpellCastTimes.dbc, SpellDuration.dbc, SpellIcon.dbc
|
||||
-- Generated: 2026-03-20 14:10 UTC
|
||||
if not MTH_DS then MTH_DS = {} end
|
||||
|
||||
MTH_DS_HunterSpellsDBC = {}
|
||||
do
|
||||
local t = MTH_DS_HunterSpellsDBC
|
||||
t[19596] = {name="[Deprecated] Bestial Swiftness",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19574] = {name="[Deprecated] Bestial Wrath",cast=0,cd=120000,categoryCD=120000,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=0}
|
||||
t[19500] = {name="[Deprecated] Hawk Eye",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19457] = {name="[Deprecated] Improved Arcane Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19458] = {name="[Deprecated] Improved Arcane Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24386] = {name="[Deprecated] Improved Aspect of the Monkey",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24387] = {name="[Deprecated] Improved Aspect of the Monkey",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19424] = {name="[Deprecated] Improved Hunter's Mark",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19425] = {name="[Deprecated] Improved Hunter's Mark",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19494] = {name="[Deprecated] Improved Scorpid Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19234] = {name="[Deprecated] Improved Wing Clip",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19235] = {name="[Deprecated] Improved Wing Clip",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19374] = {name="[Deprecated] Killer Instinct",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19375] = {name="[Deprecated] Killer Instinct",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19562] = {name="[Deprecated] Pathfinding",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19563] = {name="[Deprecated] Pathfinding",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[14275] = {name="[Deprecated] Scorpid Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=6000,manaCost=90,icon="",familyFlags=32768}
|
||||
t[14276] = {name="[Deprecated] Scorpid Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=3600000,manaCost=125,icon="",familyFlags=32768}
|
||||
t[14277] = {name="[Deprecated] Scorpid Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=165,icon="",familyFlags=32768}
|
||||
t[19611] = {name="[Deprecated] Thick Hide",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19613] = {name="[Deprecated] Thick Hide",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51520] = {name="[Deprecated] Trueshot Aura Percent Bonus",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=0}
|
||||
t[51521] = {name="[Deprecated] Trueshot Aura Percent Bonus",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=0}
|
||||
t[51522] = {name="[Deprecated] Trueshot Aura Percent Bonus",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=0}
|
||||
t[28752] = {name="Adrenaline Rush",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19434] = {name="Aimed Shot",cast=3000,cd=6000,categoryCD=0,recoveryCD=6000,duration=60000,manaCost=75,icon="",familyFlags=131072}
|
||||
t[20900] = {name="Aimed Shot",cast=3000,cd=6000,categoryCD=0,recoveryCD=6000,duration=5000,manaCost=115,icon="",familyFlags=131072}
|
||||
t[20901] = {name="Aimed Shot",cast=3000,cd=6000,categoryCD=0,recoveryCD=6000,duration=1000,manaCost=160,icon="",familyFlags=131072}
|
||||
t[20902] = {name="Aimed Shot",cast=3000,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=210,icon="",familyFlags=131072}
|
||||
t[20903] = {name="Aimed Shot",cast=3000,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=260,icon="",familyFlags=131072}
|
||||
t[20904] = {name="Aimed Shot",cast=3000,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=310,icon="",familyFlags=131072}
|
||||
t[27632] = {name="Aimed Shot",cast=3000,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=310,icon="",familyFlags=131072}
|
||||
t[23721] = {name="Arcane Infused",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[3044] = {name="Arcane Shot",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=600000,manaCost=25,icon="",familyFlags=2048}
|
||||
t[14281] = {name="Arcane Shot",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=30000,manaCost=35,icon="",familyFlags=2048}
|
||||
t[14282] = {name="Arcane Shot",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=60000,manaCost=50,icon="",familyFlags=2048}
|
||||
t[14283] = {name="Arcane Shot",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=5000,manaCost=80,icon="",familyFlags=2048}
|
||||
t[14284] = {name="Arcane Shot",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=1000,manaCost=105,icon="",familyFlags=2048}
|
||||
t[14285] = {name="Arcane Shot",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=135,icon="",familyFlags=2048}
|
||||
t[14286] = {name="Arcane Shot",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=160,icon="",familyFlags=2048}
|
||||
t[14287] = {name="Arcane Shot",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=190,icon="",familyFlags=2048}
|
||||
t[23157] = {name="Arcane Shot Mana Reduction",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[13161] = {name="Aspect of the Beast",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1800000,manaCost=50,icon="",familyFlags=0}
|
||||
t[5118] = {name="Aspect of the Cheetah",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=40,icon="",familyFlags=2097152}
|
||||
t[45651] = {name="Aspect of the Fox",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=120000,manaCost=140,icon="",familyFlags=0}
|
||||
t[13165] = {name="Aspect of the Hawk",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=20,icon="",familyFlags=1048576}
|
||||
t[14318] = {name="Aspect of the Hawk",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=20000,manaCost=35,icon="",familyFlags=1048576}
|
||||
t[14319] = {name="Aspect of the Hawk",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=5000,manaCost=50,icon="",familyFlags=1048576}
|
||||
t[14320] = {name="Aspect of the Hawk",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=11000,manaCost=70,icon="",familyFlags=1048576}
|
||||
t[14321] = {name="Aspect of the Hawk",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=90,icon="",familyFlags=1048576}
|
||||
t[14322] = {name="Aspect of the Hawk",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=110,icon="",familyFlags=1048576}
|
||||
t[25296] = {name="Aspect of the Hawk",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=120,icon="",familyFlags=1048576}
|
||||
t[13163] = {name="Aspect of the Monkey",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=120000,manaCost=20,icon="",familyFlags=524288}
|
||||
t[13159] = {name="Aspect of the Pack",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=100,icon="",familyFlags=2097152}
|
||||
t[45652] = {name="Aspect of the Snake",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=120000,manaCost=20,icon="",familyFlags=0}
|
||||
t[45664] = {name="Aspect of the Snake",cast=0,cd=1500,categoryCD=1500,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[45649] = {name="Aspect of the Turtle",cast=0,cd=180000,categoryCD=180000,recoveryCD=0,duration=60000,manaCost=10,icon="",familyFlags=0}
|
||||
t[20043] = {name="Aspect of the Wild",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=90,icon="",familyFlags=0}
|
||||
t[20190] = {name="Aspect of the Wild",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=115,icon="",familyFlags=0}
|
||||
t[45650] = {name="Aspect of the Wolf",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=20,icon="",familyFlags=1048576}
|
||||
t[51496] = {name="Aspect of the Wolf",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=20000,manaCost=35,icon="",familyFlags=1048576}
|
||||
t[51497] = {name="Aspect of the Wolf",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=5000,manaCost=50,icon="",familyFlags=1048576}
|
||||
t[51498] = {name="Aspect of the Wolf",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=11000,manaCost=70,icon="",familyFlags=1048576}
|
||||
t[51499] = {name="Aspect of the Wolf",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=90,icon="",familyFlags=1048576}
|
||||
t[51500] = {name="Aspect of the Wolf",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=110,icon="",familyFlags=1048576}
|
||||
t[51501] = {name="Aspect of the Wolf",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=120,icon="",familyFlags=1048576}
|
||||
t[75] = {name="Auto Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=0,icon="",familyFlags=1}
|
||||
t[46023] = {name="Avoidance",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19767] = {name="Aynasha's Bow",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[46321] = {name="Baited Shot",cast=0,cd=8000,categoryCD=8000,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19461] = {name="Barrage",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19462] = {name="Barrage",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24691] = {name="Barrage",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[22996] = {name="Battle Standard",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[23005] = {name="Battle Standard",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[23034] = {name="Battle Standard",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[23035] = {name="Battle Standard",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[23538] = {name="Battle Standard",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[23539] = {name="Battle Standard",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[47355] = {name="Beast Mastery",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[29233] = {name="Beast of the Unseen Path",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19590] = {name="Bestial Discipline",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19592] = {name="Bestial Discipline",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19603] = {name="Bestial Fury",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19605] = {name="Bestial Fury",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19606] = {name="Bestial Fury",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19607] = {name="Bestial Fury",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19608] = {name="Bestial Fury",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51554] = {name="Bestial Precision",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51555] = {name="Bestial Precision",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24395] = {name="Bestial Wrath",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=0}
|
||||
t[24396] = {name="Bestial Wrath",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=0}
|
||||
t[24397] = {name="Bestial Wrath",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=0}
|
||||
t[26592] = {name="Bestial Wrath",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=0}
|
||||
t[17253] = {name="Bite",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=10000,manaCost=35,icon="",familyFlags=0}
|
||||
t[17255] = {name="Bite",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=15000,manaCost=35,icon="",familyFlags=0}
|
||||
t[17256] = {name="Bite",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=230000,manaCost=35,icon="",familyFlags=0}
|
||||
t[17257] = {name="Bite",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=160000,manaCost=35,icon="",familyFlags=0}
|
||||
t[17258] = {name="Bite",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=6000,manaCost=35,icon="",familyFlags=0}
|
||||
t[17259] = {name="Bite",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=1200000,manaCost=35,icon="",familyFlags=0}
|
||||
t[17260] = {name="Bite",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=35,icon="",familyFlags=0}
|
||||
t[17261] = {name="Bite",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=35,icon="",familyFlags=0}
|
||||
t[36523] = {name="Bubble Barrier",cast=0,cd=30000,categoryCD=0,recoveryCD=30000,duration=60000,manaCost=20,icon="",familyFlags=0}
|
||||
t[36524] = {name="Bubble Barrier",cast=0,cd=30000,categoryCD=0,recoveryCD=30000,duration=160000,manaCost=20,icon="",familyFlags=0}
|
||||
t[36525] = {name="Bubble Barrier",cast=0,cd=30000,categoryCD=0,recoveryCD=30000,duration=1200000,manaCost=20,icon="",familyFlags=0}
|
||||
t[36526] = {name="Bubble Barrier",cast=0,cd=30000,categoryCD=0,recoveryCD=30000,duration=0,manaCost=20,icon="",familyFlags=0}
|
||||
t[51575] = {name="Carve",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=60000,manaCost=0,icon="",familyFlags=0}
|
||||
t[52415] = {name="Carve",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=11000,manaCost=0,icon="",familyFlags=0}
|
||||
t[52416] = {name="Carve",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24348] = {name="Cheetah Sprint",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[3009] = {name="Claw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=25,icon="",familyFlags=0}
|
||||
t[3010] = {name="Claw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=25,icon="",familyFlags=0}
|
||||
t[16827] = {name="Claw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=25,icon="",familyFlags=0}
|
||||
t[16828] = {name="Claw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=15000,manaCost=25,icon="",familyFlags=0}
|
||||
t[16829] = {name="Claw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=230000,manaCost=25,icon="",familyFlags=0}
|
||||
t[16830] = {name="Claw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=160000,manaCost=25,icon="",familyFlags=0}
|
||||
t[16831] = {name="Claw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=6000,manaCost=25,icon="",familyFlags=0}
|
||||
t[16832] = {name="Claw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=25,icon="",familyFlags=0}
|
||||
t[19239] = {name="Clever Traps",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19245] = {name="Clever Traps",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[20576] = {name="Command",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[21563] = {name="Command",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[5116] = {name="Concussive Shot",cast=0,cd=12000,categoryCD=12000,recoveryCD=0,duration=15000,manaCost=0,icon="",familyFlags=512}
|
||||
t[23158] = {name="Concussive Shot Cooldown Reduction",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[49559] = {name="Coordinated Assault",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[28317] = {name="Copy of Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[19306] = {name="Counterattack",cast=0,cd=5000,categoryCD=0,recoveryCD=5000,duration=1800000,manaCost=45,icon="",familyFlags=0}
|
||||
t[20909] = {name="Counterattack",cast=0,cd=5000,categoryCD=0,recoveryCD=5000,duration=3600000,manaCost=65,icon="",familyFlags=0}
|
||||
t[20910] = {name="Counterattack",cast=0,cd=5000,categoryCD=0,recoveryCD=5000,duration=0,manaCost=85,icon="",familyFlags=0}
|
||||
t[36548] = {name="Death Roll",cast=0,cd=30000,categoryCD=30000,recoveryCD=0,duration=60000,manaCost=50,icon="",familyFlags=0}
|
||||
t[36549] = {name="Death Roll",cast=0,cd=30000,categoryCD=30000,recoveryCD=0,duration=60000,manaCost=50,icon="",familyFlags=0}
|
||||
t[36550] = {name="Death Roll",cast=0,cd=30000,categoryCD=30000,recoveryCD=0,duration=1800000,manaCost=50,icon="",familyFlags=0}
|
||||
t[36551] = {name="Death Roll",cast=0,cd=30000,categoryCD=30000,recoveryCD=0,duration=1200000,manaCost=50,icon="",familyFlags=0}
|
||||
t[36552] = {name="Death Roll",cast=0,cd=30000,categoryCD=30000,recoveryCD=0,duration=0,manaCost=50,icon="",familyFlags=0}
|
||||
t[36553] = {name="Death Roll",cast=0,cd=30000,categoryCD=30000,recoveryCD=0,duration=0,manaCost=50,icon="",familyFlags=0}
|
||||
t[24992] = {name="Demonic Transformation",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=0}
|
||||
t[23578] = {name="Detect Weakness",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19263] = {name="Deterrence",cast=0,cd=300000,categoryCD=300000,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=0}
|
||||
t[781] = {name="Disengage",cast=0,cd=5000,categoryCD=0,recoveryCD=5000,duration=60000,manaCost=50,icon="",familyFlags=0}
|
||||
t[14272] = {name="Disengage",cast=0,cd=5000,categoryCD=0,recoveryCD=5000,duration=0,manaCost=100,icon="",familyFlags=0}
|
||||
t[14273] = {name="Disengage",cast=0,cd=5000,categoryCD=0,recoveryCD=5000,duration=0,manaCost=150,icon="",familyFlags=0}
|
||||
t[14274] = {name="Distracting Shot",cast=0,cd=8000,categoryCD=0,recoveryCD=8000,duration=60000,manaCost=30,icon="",familyFlags=65536}
|
||||
t[15629] = {name="Distracting Shot",cast=0,cd=8000,categoryCD=0,recoveryCD=8000,duration=1800000,manaCost=50,icon="",familyFlags=65536}
|
||||
t[15630] = {name="Distracting Shot",cast=0,cd=8000,categoryCD=0,recoveryCD=8000,duration=1200000,manaCost=70,icon="",familyFlags=65536}
|
||||
t[15631] = {name="Distracting Shot",cast=0,cd=8000,categoryCD=0,recoveryCD=8000,duration=0,manaCost=90,icon="",familyFlags=65536}
|
||||
t[15632] = {name="Distracting Shot",cast=0,cd=8000,categoryCD=0,recoveryCD=8000,duration=0,manaCost=110,icon="",familyFlags=65536}
|
||||
t[20736] = {name="Distracting Shot",cast=0,cd=8000,categoryCD=0,recoveryCD=8000,duration=30000,manaCost=20,icon="",familyFlags=65536}
|
||||
t[19416] = {name="Efficiency",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19417] = {name="Efficiency",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19418] = {name="Efficiency",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19419] = {name="Efficiency",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19420] = {name="Efficiency",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51552] = {name="Elusiveness",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51553] = {name="Elusiveness",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51517] = {name="Endless Quiver",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51518] = {name="Endless Quiver",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19583] = {name="Endurance Training",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19584] = {name="Endurance Training",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19585] = {name="Endurance Training",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19586] = {name="Endurance Training",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19587] = {name="Endurance Training",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19184] = {name="Entrapment",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19387] = {name="Entrapment",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19388] = {name="Entrapment",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19389] = {name="Entrapment",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19390] = {name="Entrapment",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[13813] = {name="Explosive Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=0,manaCost=275,icon="",familyFlags=128}
|
||||
t[14316] = {name="Explosive Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=0,manaCost=395,icon="",familyFlags=128}
|
||||
t[14317] = {name="Explosive Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=0,manaCost=520,icon="",familyFlags=128}
|
||||
t[13812] = {name="Explosive Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=4}
|
||||
t[14314] = {name="Explosive Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=4}
|
||||
t[14315] = {name="Explosive Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=4}
|
||||
t[51519] = {name="Extra Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=0,icon="",familyFlags=1}
|
||||
t[1002] = {name="Eyes of the Beast",cast=2000,cd=0,categoryCD=0,recoveryCD=0,duration=120000,manaCost=20,icon="",familyFlags=4194304}
|
||||
t[17331] = {name="Fang of the Crystal Spider",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[52402] = {name="Faster Bleeds 5 (Hunter)",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[5384] = {name="Feign Death",cast=0,cd=30000,categoryCD=30000,recoveryCD=0,duration=1800000,manaCost=80,icon="Spell_Nature_Regenerate",familyFlags=256}
|
||||
t[19598] = {name="Ferocity",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19599] = {name="Ferocity",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19600] = {name="Ferocity",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19601] = {name="Ferocity",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19602] = {name="Ferocity",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[46269] = {name="Fire Breath",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=10000,manaCost=50,icon="",familyFlags=0}
|
||||
t[46270] = {name="Fire Breath",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=0,manaCost=50,icon="",familyFlags=0}
|
||||
t[19781] = {name="Flame Spear",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=75000,manaCost=0,icon="",familyFlags=0}
|
||||
t[1543] = {name="Flare",cast=0,cd=15000,categoryCD=15000,recoveryCD=0,duration=6000,manaCost=50,icon="Spell_Magic_PolymorphChicken",familyFlags=0}
|
||||
t[1499] = {name="Freezing Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=60000,manaCost=50,icon="",familyFlags=128}
|
||||
t[14310] = {name="Freezing Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=1200000,manaCost=75,icon="",familyFlags=128}
|
||||
t[14311] = {name="Freezing Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=0,manaCost=100,icon="",familyFlags=128}
|
||||
t[27753] = {name="Freezing Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=0,manaCost=100,icon="Spell_Nature_Regenerate",familyFlags=128}
|
||||
t[21750] = {name="Freezing Trap (TEST)",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=60000,manaCost=50,icon="",familyFlags=0}
|
||||
t[3355] = {name="Freezing Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=8}
|
||||
t[14308] = {name="Freezing Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=8}
|
||||
t[14309] = {name="Freezing Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=8}
|
||||
t[19621] = {name="Frenzy",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19622] = {name="Frenzy",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19623] = {name="Frenzy",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19624] = {name="Frenzy",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19625] = {name="Frenzy",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[13809] = {name="Frost Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=5000,manaCost=60,icon="",familyFlags=128}
|
||||
t[13810] = {name="Frost Trap Aura",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=5000,manaCost=0,icon="",familyFlags=16}
|
||||
t[14207] = {name="Frost Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=5000,manaCost=0,icon="",familyFlags=16}
|
||||
t[24597] = {name="Furious Howl",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=0,manaCost=50,icon="",familyFlags=0}
|
||||
t[24603] = {name="Furious Howl",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=1200000,manaCost=50,icon="",familyFlags=0}
|
||||
t[24604] = {name="Furious Howl",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=60000,manaCost=50,icon="",familyFlags=0}
|
||||
t[24605] = {name="Furious Howl",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=160000,manaCost=50,icon="",familyFlags=0}
|
||||
t[46296] = {name="Grace",cast=0,cd=60000,categoryCD=60000,recoveryCD=0,duration=60000,manaCost=10,icon="Spell_Nature_GuardianWard",familyFlags=0}
|
||||
t[14824] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[14825] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[14826] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[14827] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[14828] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[14829] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[29413] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[29414] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[29415] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[29416] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[29417] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[29418] = {name="Haste",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19498] = {name="Hawk Eye",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19499] = {name="Hawk Eye",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[21942] = {name="Hunter Giant Stalker",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[1130] = {name="Hunter's Mark",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=600000,manaCost=15,icon="",familyFlags=1024}
|
||||
t[14323] = {name="Hunter's Mark",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=45000,manaCost=30,icon="",familyFlags=1024}
|
||||
t[14324] = {name="Hunter's Mark",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=45,icon="",familyFlags=1024}
|
||||
t[14325] = {name="Hunter's Mark",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=60,icon="",familyFlags=1024}
|
||||
t[13795] = {name="Immolation Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=230000,manaCost=50,icon="",familyFlags=128}
|
||||
t[14302] = {name="Immolation Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=240000,manaCost=90,icon="",familyFlags=128}
|
||||
t[14303] = {name="Immolation Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=1000,manaCost=135,icon="",familyFlags=128}
|
||||
t[14304] = {name="Immolation Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=0,manaCost=190,icon="",familyFlags=128}
|
||||
t[14305] = {name="Immolation Trap",cast=0,cd=15000,categoryCD=0,recoveryCD=15000,duration=0,manaCost=245,icon="",familyFlags=128}
|
||||
t[13797] = {name="Immolation Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=230000,manaCost=0,icon="",familyFlags=4}
|
||||
t[14298] = {name="Immolation Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=240000,manaCost=0,icon="Spell_Nature_MoonGlow",familyFlags=4}
|
||||
t[14299] = {name="Immolation Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1000,manaCost=0,icon="Spell_Nature_MoonGlow",familyFlags=4}
|
||||
t[14300] = {name="Immolation Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="Spell_Nature_MoonGlow",familyFlags=4}
|
||||
t[14301] = {name="Immolation Trap Effect",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="Spell_Nature_MoonGlow",familyFlags=4}
|
||||
t[15860] = {name="Impale",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="Spell_Holy_Resurrection",familyFlags=1}
|
||||
t[16001] = {name="Impale",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="Spell_Holy_Resurrection",familyFlags=1}
|
||||
t[24049] = {name="Impale",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=0,icon="Spell_Holy_Resurrection",familyFlags=1}
|
||||
t[26548] = {name="Impale",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=0,icon="Spell_Holy_Resurrection",familyFlags=1}
|
||||
t[23559] = {name="Improved Aspect of the Hawk",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19407] = {name="Improved Concussive Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19412] = {name="Improved Concussive Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19413] = {name="Improved Concussive Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19414] = {name="Improved Concussive Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19415] = {name="Improved Concussive Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24465] = {name="Improved Concussive Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19378] = {name="Improved Explosive Trap",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19379] = {name="Improved Explosive Trap",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19557] = {name="Improved Eyes of the Beast",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19558] = {name="Improved Eyes of the Beast",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[45662] = {name="Improved Eyes of the Beast",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[45663] = {name="Improved Eyes of the Beast",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19286] = {name="Improved Feign Death",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19287] = {name="Improved Feign Death",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24432] = {name="Improved Feign Death",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19288] = {name="Improved Freezing Trap",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24298] = {name="Improved Freezing Trap",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24299] = {name="Improved Freezing Trap",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19421] = {name="Improved Hunter's Mark",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19422] = {name="Improved Hunter's Mark",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19423] = {name="Improved Hunter's Mark",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19246] = {name="Improved Immolation Trap",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19247] = {name="Improved Immolation Trap",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19248] = {name="Improved Immolation Trap",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19572] = {name="Improved Mend Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19573] = {name="Improved Mend Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[23560] = {name="Improved Mend Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24406] = {name="Improved Mend Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19549] = {name="Improved Primal Aspects",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19550] = {name="Improved Primal Aspects",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19551] = {name="Improved Primal Aspects",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19575] = {name="Improved Revive Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24443] = {name="Improved Revive Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19491] = {name="Improved Scorpid Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19493] = {name="Improved Scorpid Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24467] = {name="Improved Serpent Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51515] = {name="Improved Steady Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51516] = {name="Improved Steady Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19464] = {name="Improved Stings",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19465] = {name="Improved Stings",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19466] = {name="Improved Stings",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19467] = {name="Improved Stings",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19468] = {name="Improved Stings",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[52603] = {name="Improved Swift Aspects",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[23566] = {name="Improved Volley and Multishot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19228] = {name="Improved Wing Clip",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19232] = {name="Improved Wing Clip",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19233] = {name="Improved Wing Clip",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[22854] = {name="Increased Hunter Pet Damage",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[27205] = {name="Increased Pet Damage +3%",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[52413] = {name="Increased Pet Damage +4%",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[41827] = {name="Kill Command",cast=0,cd=12000,categoryCD=12000,recoveryCD=0,duration=0,manaCost=0,icon="Spell_Holy_PrayerOfHealing",familyFlags=0}
|
||||
t[41828] = {name="Kill Command",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19162] = {name="Killer Instinct",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19163] = {name="Killer Instinct",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19165] = {name="Killer Instinct",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19370] = {name="Killer Instinct",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19371] = {name="Killer Instinct",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19373] = {name="Killer Instinct",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[20907] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=80,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[20908] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=95,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[24118] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=65,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[24119] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=80,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[24120] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=95,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[45653] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=65,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[45654] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=80,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[45655] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=95,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[45656] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=0}
|
||||
t[45657] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=80,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[45658] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=95,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[48049] = {name="Lacerate",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[48050] = {name="Lacerate",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[24844] = {name="Lightning Breath",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=50,icon="Spell_Shadow_GatherShadows",familyFlags=0}
|
||||
t[25008] = {name="Lightning Breath",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=30000,manaCost=50,icon="Spell_Shadow_GatherShadows",familyFlags=0}
|
||||
t[25009] = {name="Lightning Breath",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=160000,manaCost=50,icon="Spell_Shadow_GatherShadows",familyFlags=0}
|
||||
t[25010] = {name="Lightning Breath",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1000,manaCost=50,icon="Spell_Shadow_GatherShadows",familyFlags=0}
|
||||
t[25011] = {name="Lightning Breath",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=50,icon="Spell_Shadow_GatherShadows",familyFlags=0}
|
||||
t[25012] = {name="Lightning Breath",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=50,icon="Spell_Shadow_GatherShadows",familyFlags=0}
|
||||
t[19168] = {name="Lightning Reflexes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19180] = {name="Lightning Reflexes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19181] = {name="Lightning Reflexes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24296] = {name="Lightning Reflexes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24297] = {name="Lightning Reflexes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[46297] = {name="Lizard Bolt",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=30000,manaCost=35,icon="Ability_Gouge",familyFlags=0}
|
||||
t[46298] = {name="Lizard Bolt",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=160000,manaCost=35,icon="Ability_Gouge",familyFlags=0}
|
||||
t[46299] = {name="Lizard Bolt",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1000,manaCost=35,icon="Ability_Gouge",familyFlags=0}
|
||||
t[46300] = {name="Lizard Bolt",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=35,icon="Ability_Gouge",familyFlags=0}
|
||||
t[46301] = {name="Lizard Bolt",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=35,icon="Ability_Gouge",familyFlags=0}
|
||||
t[136] = {name="Mend Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=30000,manaCost=50,icon="",familyFlags=8388608}
|
||||
t[3111] = {name="Mend Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=90,icon="",familyFlags=8388608}
|
||||
t[3661] = {name="Mend Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=5000,manaCost=155,icon="",familyFlags=8388608}
|
||||
t[3662] = {name="Mend Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1000,manaCost=225,icon="",familyFlags=8388608}
|
||||
t[13542] = {name="Mend Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=300,icon="",familyFlags=8388608}
|
||||
t[13543] = {name="Mend Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=385,icon="",familyFlags=8388608}
|
||||
t[13544] = {name="Mend Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=480,icon="",familyFlags=8388608}
|
||||
t[1495] = {name="Mongoose Bite",cast=0,cd=5000,categoryCD=0,recoveryCD=5000,duration=230000,manaCost=30,icon="Trade_BrewPoison",familyFlags=2}
|
||||
t[14269] = {name="Mongoose Bite",cast=0,cd=5000,categoryCD=0,recoveryCD=5000,duration=1800000,manaCost=40,icon="Trade_BrewPoison",familyFlags=2}
|
||||
t[14270] = {name="Mongoose Bite",cast=0,cd=5000,categoryCD=0,recoveryCD=5000,duration=0,manaCost=50,icon="Trade_BrewPoison",familyFlags=2}
|
||||
t[14271] = {name="Mongoose Bite",cast=0,cd=5000,categoryCD=0,recoveryCD=5000,duration=0,manaCost=65,icon="Trade_BrewPoison",familyFlags=2}
|
||||
t[19485] = {name="Mortal Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19487] = {name="Mortal Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19488] = {name="Mortal Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19489] = {name="Mortal Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19490] = {name="Mortal Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[2643] = {name="Multi-Shot",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=20000,manaCost=100,icon="",familyFlags=4096}
|
||||
t[14288] = {name="Multi-Shot",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=1800000,manaCost=140,icon="",familyFlags=4096}
|
||||
t[14289] = {name="Multi-Shot",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=3600000,manaCost=175,icon="",familyFlags=4096}
|
||||
t[14290] = {name="Multi-Shot",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=0,manaCost=210,icon="",familyFlags=4096}
|
||||
t[25294] = {name="Multi-Shot",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=0,manaCost=230,icon="",familyFlags=4096}
|
||||
t[28751] = {name="Multi-Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[28539] = {name="Multi-Shot Damage Increase",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[52602] = {name="Multishot and Carve Cooldown",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[28761] = {name="Not There",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[36532] = {name="Packleader",cast=0,cd=90000,categoryCD=90000,recoveryCD=0,duration=60000,manaCost=50,icon="INV_Potion_40",familyFlags=0}
|
||||
t[36562] = {name="Packleader",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="INV_Potion_40",familyFlags=0}
|
||||
t[19559] = {name="Pathfinding",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19560] = {name="Pathfinding",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[52685] = {name="Pet Focus Regen Bonus",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51512] = {name="Piercing Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51513] = {name="Piercing Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51514] = {name="Piercing Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=120000,manaCost=0,icon="",familyFlags=0}
|
||||
t[46292] = {name="Pin",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=230000,manaCost=10,icon="",familyFlags=0}
|
||||
t[46293] = {name="Pin",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=6000,manaCost=10,icon="",familyFlags=0}
|
||||
t[46294] = {name="Pin",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=10,icon="",familyFlags=0}
|
||||
t[46295] = {name="Pin",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=10,icon="",familyFlags=0}
|
||||
t[51576] = {name="Planning Ahead",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51577] = {name="Planning Ahead",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51578] = {name="Planning Ahead Dummy",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[46271] = {name="Poison Spit",cast=0,cd=8000,categoryCD=8000,recoveryCD=8000,duration=300000,manaCost=35,icon="Spell_Nature_LightningShield",familyFlags=0}
|
||||
t[46272] = {name="Poison Spit",cast=0,cd=8000,categoryCD=8000,recoveryCD=8000,duration=0,manaCost=35,icon="Spell_Nature_LightningShield",familyFlags=0}
|
||||
t[46273] = {name="Poison Spit",cast=0,cd=8000,categoryCD=8000,recoveryCD=8000,duration=0,manaCost=35,icon="Spell_Nature_LightningShield",familyFlags=0}
|
||||
t[24450] = {name="Prowl",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=1800000,manaCost=40,icon="Spell_Fire_Fireball02",familyFlags=0}
|
||||
t[24452] = {name="Prowl",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=1200000,manaCost=40,icon="Spell_Fire_Fireball02",familyFlags=0}
|
||||
t[24453] = {name="Prowl",cast=0,cd=10000,categoryCD=0,recoveryCD=10000,duration=0,manaCost=40,icon="Spell_Fire_Fireball02",familyFlags=0}
|
||||
t[51547] = {name="Quick Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51548] = {name="Quick Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51549] = {name="Quick Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51550] = {name="Quick Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51551] = {name="Quick Shots",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51542] = {name="Quick Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="Spell_Shadow_ImpPhaseShift",familyFlags=0}
|
||||
t[51543] = {name="Quick Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="Spell_Shadow_ImpPhaseShift",familyFlags=0}
|
||||
t[51544] = {name="Quick Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="Spell_Shadow_ImpPhaseShift",familyFlags=0}
|
||||
t[51545] = {name="Quick Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="Spell_Shadow_ImpPhaseShift",familyFlags=0}
|
||||
t[51546] = {name="Quick Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="Spell_Shadow_ImpPhaseShift",familyFlags=0}
|
||||
t[19511] = {name="Ranged Weapon Specialization",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[3045] = {name="Rapid Fire",cast=0,cd=300000,categoryCD=0,recoveryCD=300000,duration=240000,manaCost=100,icon="",familyFlags=32}
|
||||
t[28755] = {name="Rapid Fire",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[2973] = {name="Raptor Strike",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=10000,manaCost=15,icon="Spell_Nature_NatureTouchGrow",familyFlags=2}
|
||||
t[14260] = {name="Raptor Strike",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=15000,manaCost=25,icon="Spell_Nature_NatureTouchGrow",familyFlags=2}
|
||||
t[14261] = {name="Raptor Strike",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=230000,manaCost=35,icon="Spell_Nature_NatureTouchGrow",familyFlags=2}
|
||||
t[14262] = {name="Raptor Strike",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=160000,manaCost=45,icon="Spell_Nature_NatureTouchGrow",familyFlags=2}
|
||||
t[14263] = {name="Raptor Strike",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=6000,manaCost=55,icon="Spell_Nature_NatureTouchGrow",familyFlags=2}
|
||||
t[14264] = {name="Raptor Strike",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=1200000,manaCost=70,icon="Spell_Nature_NatureTouchGrow",familyFlags=2}
|
||||
t[14265] = {name="Raptor Strike",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=85,icon="Spell_Nature_NatureTouchGrow",familyFlags=2}
|
||||
t[14266] = {name="Raptor Strike",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=100,icon="Spell_Nature_NatureTouchGrow",familyFlags=2}
|
||||
t[23989] = {name="Readiness",cast=0,cd=300000,categoryCD=300000,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51570] = {name="Resourcefulness",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51571] = {name="Resourcefulness",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51572] = {name="Resourcefulness",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51573] = {name="Resourcefulness",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51574] = {name="Resourcefulness",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[982] = {name="Revive Pet",cast=10000,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=16777216}
|
||||
t[23499] = {name="Revive Pet",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=16777216}
|
||||
t[36535] = {name="Roar of Fortitude",cast=0,cd=120000,categoryCD=120000,recoveryCD=0,duration=1800000,manaCost=50,icon="",familyFlags=0}
|
||||
t[36536] = {name="Savage Rend",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=10000,manaCost=35,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[36537] = {name="Savage Rend",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=30000,manaCost=35,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[36538] = {name="Savage Rend",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=160000,manaCost=35,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[36539] = {name="Savage Rend",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=1000,manaCost=35,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[36540] = {name="Savage Rend",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=0,manaCost=35,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[36541] = {name="Savage Rend",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=0,manaCost=35,icon="INV_Gauntlets_06",familyFlags=0}
|
||||
t[46282] = {name="Savage Rend",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=25,icon="",familyFlags=0}
|
||||
t[46283] = {name="Savage Rend",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=25,icon="",familyFlags=0}
|
||||
t[46284] = {name="Savage Rend",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=25,icon="",familyFlags=0}
|
||||
t[46285] = {name="Savage Rend",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=25,icon="",familyFlags=0}
|
||||
t[46286] = {name="Savage Rend",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=25,icon="",familyFlags=0}
|
||||
t[19159] = {name="Savage Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19160] = {name="Savage Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19503] = {name="Scatter Shot",cast=0,cd=30000,categoryCD=30000,recoveryCD=0,duration=1800000,manaCost=0,icon="INV_Flask_05",familyFlags=262144}
|
||||
t[23601] = {name="Scatter Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1800000,manaCost=0,icon="INV_Flask_05",familyFlags=262144}
|
||||
t[24583] = {name="Scorpid Poison",cast=0,cd=3000,categoryCD=0,recoveryCD=3000,duration=160000,manaCost=25,icon="Spell_Nature_LightningShield",familyFlags=0}
|
||||
t[24586] = {name="Scorpid Poison",cast=0,cd=3000,categoryCD=0,recoveryCD=3000,duration=1200000,manaCost=25,icon="Spell_Nature_LightningShield",familyFlags=0}
|
||||
t[24587] = {name="Scorpid Poison",cast=0,cd=3000,categoryCD=0,recoveryCD=3000,duration=0,manaCost=25,icon="Spell_Nature_LightningShield",familyFlags=0}
|
||||
t[24640] = {name="Scorpid Poison",cast=0,cd=3000,categoryCD=0,recoveryCD=3000,duration=15000,manaCost=25,icon="Spell_Nature_LightningShield",familyFlags=0}
|
||||
t[45659] = {name="Scorpid Stike",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=165,icon="",familyFlags=32768}
|
||||
t[3043] = {name="Scorpid Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=45000,manaCost=70,icon="",familyFlags=32768}
|
||||
t[51509] = {name="Scorpid Sting Damage Reduction",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=45000,manaCost=0,icon="",familyFlags=0}
|
||||
t[24423] = {name="Screech",cast=0,cd=5000,categoryCD=5000,recoveryCD=0,duration=15000,manaCost=20,icon="",familyFlags=0}
|
||||
t[24577] = {name="Screech",cast=0,cd=5000,categoryCD=5000,recoveryCD=0,duration=160000,manaCost=20,icon="",familyFlags=0}
|
||||
t[24578] = {name="Screech",cast=0,cd=5000,categoryCD=5000,recoveryCD=0,duration=0,manaCost=20,icon="",familyFlags=0}
|
||||
t[24579] = {name="Screech",cast=0,cd=5000,categoryCD=5000,recoveryCD=0,duration=0,manaCost=20,icon="",familyFlags=0}
|
||||
t[1978] = {name="Serpent Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=120000,manaCost=15,icon="",familyFlags=16384}
|
||||
t[13549] = {name="Serpent Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=30,icon="",familyFlags=16384}
|
||||
t[13550] = {name="Serpent Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=20000,manaCost=50,icon="",familyFlags=16384}
|
||||
t[13551] = {name="Serpent Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=240000,manaCost=80,icon="",familyFlags=16384}
|
||||
t[13552] = {name="Serpent Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=115,icon="",familyFlags=16384}
|
||||
t[13553] = {name="Serpent Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=3600000,manaCost=150,icon="",familyFlags=16384}
|
||||
t[13554] = {name="Serpent Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=190,icon="",familyFlags=16384}
|
||||
t[13555] = {name="Serpent Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=230,icon="",familyFlags=16384}
|
||||
t[25295] = {name="Serpent Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=250,icon="",familyFlags=16384}
|
||||
t[45661] = {name="Serpent Strike",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=250,icon="",familyFlags=16384}
|
||||
t[29218] = {name="Server Spell",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[26064] = {name="Shell Shield",cast=0,cd=60000,categoryCD=60000,recoveryCD=0,duration=60000,manaCost=10,icon="",familyFlags=0}
|
||||
t[6660] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[15547] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[15620] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[16100] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[16496] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[17353] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[18561] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[20463] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[22121] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[22411] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[22907] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[23073] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[23337] = {name="Shoot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[2480] = {name="Shoot Bow",cast=1000,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=0,icon="",familyFlags=1}
|
||||
t[7919] = {name="Shoot Crossbow",cast=1000,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=0,icon="",familyFlags=1}
|
||||
t[7918] = {name="Shoot Gun",cast=1000,cd=0,categoryCD=0,recoveryCD=0,duration=10000,manaCost=0,icon="",familyFlags=1}
|
||||
t[25200] = {name="Shoot Silithus Rifleman Long Range",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[26282] = {name="Shoot Tauren Rifleman",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[28318] = {name="Slime Shot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[19501] = {name="Spell - Reuse 19501",cast=0,cd=30000,categoryCD=30000,recoveryCD=0,duration=1800000,manaCost=0,icon="",familyFlags=262144}
|
||||
t[27919] = {name="Spit",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=1}
|
||||
t[28758] = {name="Stalker's Ally",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[3035] = {name="Steady Shot",cast=1000,cd=0,categoryCD=0,recoveryCD=0,duration=120000,manaCost=0,icon="",familyFlags=0}
|
||||
t[3036] = {name="Steady Shot",cast=1000,cd=0,categoryCD=0,recoveryCD=0,duration=120000,manaCost=0,icon="",familyFlags=0}
|
||||
t[3037] = {name="Steady Shot",cast=1000,cd=0,categoryCD=0,recoveryCD=0,duration=45000,manaCost=0,icon="",familyFlags=0}
|
||||
t[3038] = {name="Steady Shot",cast=1000,cd=0,categoryCD=0,recoveryCD=0,duration=1800000,manaCost=0,icon="",familyFlags=0}
|
||||
t[3668] = {name="Steady Shot",cast=1000,cd=0,categoryCD=0,recoveryCD=0,duration=11000,manaCost=0,icon="",familyFlags=0}
|
||||
t[45970] = {name="Steady Shot",cast=1000,cd=0,categoryCD=0,recoveryCD=0,duration=11000,manaCost=0,icon="",familyFlags=0}
|
||||
t[45972] = {name="Steady Shot",cast=1000,cd=0,categoryCD=0,recoveryCD=0,duration=11000,manaCost=0,icon="",familyFlags=0}
|
||||
t[45974] = {name="Steady Shot",cast=1000,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[52684] = {name="Steady Shot and Raptor Strike Crit Bonus",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51579] = {name="Stinging Nettle",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51580] = {name="Stinging Nettle",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[36531] = {name="Strider Presence",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=50,icon="",familyFlags=0}
|
||||
t[49557] = {name="Strike Together",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[49558] = {name="Strike Together",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[26173] = {name="Striker's Arcane Shot Bonus",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[26174] = {name="Striker's Rapid Fire Bonus",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24416] = {name="Superior Aspects",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24269] = {name="Superior Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24270] = {name="Superior Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24271] = {name="Superior Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24272] = {name="Superior Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24273] = {name="Superior Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19290] = {name="Surefooted",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19294] = {name="Surefooted",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24283] = {name="Surefooted",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24286] = {name="Surefooted",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[24287] = {name="Surefooted",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19255] = {name="Survivalist",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19256] = {name="Survivalist",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19257] = {name="Survivalist",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19258] = {name="Survivalist",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19259] = {name="Survivalist",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19552] = {name="Swift Aspects",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19553] = {name="Swift Aspects",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19554] = {name="Swift Aspects",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19555] = {name="Swift Aspects",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19556] = {name="Swift Aspects",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19454] = {name="Swiftshot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19455] = {name="Swiftshot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19456] = {name="Swiftshot",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[46287] = {name="Swipe",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=10000,manaCost=20,icon="Spell_Frost_ChillingBolt",familyFlags=0}
|
||||
t[46288] = {name="Swipe",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=10000,manaCost=20,icon="Spell_Frost_ChillingBolt",familyFlags=0}
|
||||
t[46289] = {name="Swipe",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=10000,manaCost=20,icon="Spell_Frost_ChillingBolt",familyFlags=0}
|
||||
t[46290] = {name="Swipe",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=10000,manaCost=20,icon="Spell_Frost_ChillingBolt",familyFlags=0}
|
||||
t[46291] = {name="Swipe",cast=0,cd=10000,categoryCD=10000,recoveryCD=0,duration=10000,manaCost=20,icon="Spell_Frost_ChillingBolt",familyFlags=0}
|
||||
t[19678] = {name="Tame Adult Plainstrider",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[19683] = {name="Tame Armored Scorpid",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44017] = {name="Tame Armored Scorpid",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[46705] = {name="Tame Ashfeather Swooper",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[46701] = {name="Tame Blackvenom Scorpid",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[50904] = {name="Tame Decrepit Darkhound",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44007] = {name="Tame Decrepit Darkhound.",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[19681] = {name="Tame Dire Mottled Boar",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44013] = {name="Tame Dire Mottled Boar.",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[46091] = {name="Tame Elder Crimson Lynx",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[50905] = {name="Tame Greater Dusk Bat",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44009] = {name="Tame Greater Duskbat",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[19597] = {name="Tame Ice Claw Bear",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44023] = {name="Tame Ice Claw Bear",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[46093] = {name="Tame Ivory Hawkstrider",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[19677] = {name="Tame Large Crag Boar",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44019] = {name="Tame Large Crag Boar.",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[46095] = {name="Tame Lavender Mana Wyrm",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[46703] = {name="Tame Muckreef Crawler",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[19685] = {name="Tame Nightsaber Stalker",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[19679] = {name="Tame Prairie Stalker",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44003] = {name="Tame Prowler",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[19676] = {name="Tame Snow Leopard",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44021] = {name="Tame Snow Leopard",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44001] = {name="Tame Stonetusk Boar",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[19686] = {name="Tame Strigid Screecher",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[19682] = {name="Tame Surf Crawler",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44015] = {name="Tame Surf Crawler",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[19680] = {name="Tame Swoop",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44011] = {name="Tame Tirisfal Plagued Bear",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[50906] = {name="Tame Tirisfal Plagued Bear",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[19684] = {name="Tame Webwood Lurker",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[44005] = {name="Tame Young Forest Bear",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2048}
|
||||
t[8875] = {name="Tamed Pet Passive (DND)",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=33554432}
|
||||
t[19580] = {name="Tamed Pet Passive (DND)",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=67108864}
|
||||
t[19581] = {name="Tamed Pet Passive (DND)",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=134217728}
|
||||
t[19582] = {name="Tamed Pet Passive (DND)",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=268435456}
|
||||
t[19589] = {name="Tamed Pet Passive (DND)",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=536870912}
|
||||
t[19591] = {name="Tamed Pet Passive (DND)",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=1073741824}
|
||||
t[20782] = {name="Tamed Pet Passive (DND)",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[20784] = {name="Tamed Pet Passive (DND)",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=2147483648}
|
||||
t[19609] = {name="Thick Hide",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19610] = {name="Thick Hide",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19612] = {name="Thick Hide",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[10277] = {name="Throw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=0}
|
||||
t[15607] = {name="Throw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=0}
|
||||
t[15795] = {name="Throw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=0}
|
||||
t[16000] = {name="Throw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=0}
|
||||
t[19785] = {name="Throw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=75000,manaCost=0,icon="",familyFlags=0}
|
||||
t[22887] = {name="Throw",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=60000,manaCost=0,icon="",familyFlags=0}
|
||||
t[26090] = {name="Thunderstomp",cast=0,cd=40000,categoryCD=40000,recoveryCD=0,duration=1800000,manaCost=60,icon="",familyFlags=0}
|
||||
t[26187] = {name="Thunderstomp",cast=0,cd=40000,categoryCD=40000,recoveryCD=0,duration=1200000,manaCost=60,icon="",familyFlags=0}
|
||||
t[26188] = {name="Thunderstomp",cast=0,cd=40000,categoryCD=40000,recoveryCD=0,duration=0,manaCost=60,icon="",familyFlags=0}
|
||||
t[51156] = {name="Thunderstomp",cast=0,cd=40000,categoryCD=40000,recoveryCD=0,duration=0,manaCost=60,icon="",familyFlags=0}
|
||||
t[19801] = {name="Tranquilizing Shot",cast=0,cd=20000,categoryCD=20000,recoveryCD=0,duration=0,manaCost=270,icon="Ability_Warrior_Disarm",familyFlags=65536}
|
||||
t[19376] = {name="Trap Mastery",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19377] = {name="Trap Mastery",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51581] = {name="Trap Mastery",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19506] = {name="Trueshot Aura",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=0}
|
||||
t[20905] = {name="Trueshot Aura",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[20906] = {name="Trueshot Aura",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19616] = {name="Unleashed Fury",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19617] = {name="Unleashed Fury",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19618] = {name="Unleashed Fury",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19619] = {name="Unleashed Fury",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[19620] = {name="Unleashed Fury",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[26176] = {name="Unseen Path Pet Bonus",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51586] = {name="Untamed Trapper",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51584] = {name="Vicious Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[51585] = {name="Vicious Strikes",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[3034] = {name="Viper Sting",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=1000,manaCost=135,icon="",familyFlags=65536}
|
||||
t[14279] = {name="Viper Sting",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=175,icon="",familyFlags=65536}
|
||||
t[14280] = {name="Viper Sting",cast=0,cd=6000,categoryCD=0,recoveryCD=6000,duration=0,manaCost=215,icon="",familyFlags=65536}
|
||||
t[45660] = {name="Viper Strike",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=215,icon="",familyFlags=65536}
|
||||
t[1510] = {name="Volley",cast=0,cd=60000,categoryCD=0,recoveryCD=60000,duration=1200000,manaCost=350,icon="",familyFlags=8192}
|
||||
t[14294] = {name="Volley",cast=0,cd=60000,categoryCD=0,recoveryCD=60000,duration=0,manaCost=420,icon="",familyFlags=8192}
|
||||
t[14295] = {name="Volley",cast=0,cd=60000,categoryCD=0,recoveryCD=60000,duration=0,manaCost=490,icon="",familyFlags=8192}
|
||||
t[22908] = {name="Volley",cast=0,cd=60000,categoryCD=0,recoveryCD=60000,duration=0,manaCost=0,icon="",familyFlags=8192}
|
||||
t[36533] = {name="Web",cast=0,cd=60000,categoryCD=0,recoveryCD=60000,duration=0,manaCost=50,icon="Spell_Holy_Heal02",familyFlags=0}
|
||||
t[26226] = {name="Widow Bite",cast=0,cd=4000,categoryCD=0,recoveryCD=4000,duration=60000,manaCost=40,icon="Ability_Hunter_AimedShot",familyFlags=0}
|
||||
t[2974] = {name="Wing Clip",cast=0,cd=3000,categoryCD=0,recoveryCD=3000,duration=30000,manaCost=40,icon="INV_Food_Egg_02",familyFlags=64}
|
||||
t[14267] = {name="Wing Clip",cast=0,cd=3000,categoryCD=0,recoveryCD=3000,duration=11000,manaCost=60,icon="INV_Food_Egg_02",familyFlags=64}
|
||||
t[14268] = {name="Wing Clip",cast=0,cd=3000,categoryCD=0,recoveryCD=3000,duration=0,manaCost=80,icon="INV_Food_Egg_02",familyFlags=64}
|
||||
t[27633] = {name="Wing Clip",cast=0,cd=3000,categoryCD=0,recoveryCD=3000,duration=0,manaCost=80,icon="INV_Food_Egg_02",familyFlags=64}
|
||||
t[19386] = {name="Wyvern Sting",cast=0,cd=120000,categoryCD=0,recoveryCD=120000,duration=1200000,manaCost=115,icon="",familyFlags=65536}
|
||||
t[24131] = {name="Wyvern Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=1200000,manaCost=0,icon="",familyFlags=65536}
|
||||
t[24132] = {name="Wyvern Sting",cast=0,cd=120000,categoryCD=0,recoveryCD=120000,duration=0,manaCost=155,icon="",familyFlags=65536}
|
||||
t[24133] = {name="Wyvern Sting",cast=0,cd=120000,categoryCD=0,recoveryCD=120000,duration=0,manaCost=205,icon="",familyFlags=65536}
|
||||
t[24134] = {name="Wyvern Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=65536}
|
||||
t[24135] = {name="Wyvern Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=65536}
|
||||
t[24335] = {name="Wyvern Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=205,icon="",familyFlags=0}
|
||||
t[24336] = {name="Wyvern Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=65536}
|
||||
t[26180] = {name="Wyvern Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=0}
|
||||
t[26233] = {name="Wyvern Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=65536}
|
||||
t[26748] = {name="Wyvern Sting",cast=0,cd=0,categoryCD=0,recoveryCD=0,duration=0,manaCost=0,icon="",familyFlags=65536}
|
||||
end
|
||||
|
||||
MTH_DS_HunterSpellsByName = {}
|
||||
do
|
||||
local t = MTH_DS_HunterSpellsByName
|
||||
t["[Deprecated] Bestial Swiftness"] = MTH_DS_HunterSpellsDBC[19596]
|
||||
t["[Deprecated] Bestial Wrath"] = MTH_DS_HunterSpellsDBC[19574]
|
||||
t["[Deprecated] Hawk Eye"] = MTH_DS_HunterSpellsDBC[19500]
|
||||
t["[Deprecated] Improved Arcane Shot"] = MTH_DS_HunterSpellsDBC[19457]
|
||||
t["[Deprecated] Improved Aspect of the Monkey"] = MTH_DS_HunterSpellsDBC[24386]
|
||||
t["[Deprecated] Improved Hunter's Mark"] = MTH_DS_HunterSpellsDBC[19424]
|
||||
t["[Deprecated] Improved Scorpid Sting"] = MTH_DS_HunterSpellsDBC[19494]
|
||||
t["[Deprecated] Improved Wing Clip"] = MTH_DS_HunterSpellsDBC[19234]
|
||||
t["[Deprecated] Killer Instinct"] = MTH_DS_HunterSpellsDBC[19374]
|
||||
t["[Deprecated] Pathfinding"] = MTH_DS_HunterSpellsDBC[19562]
|
||||
t["[Deprecated] Scorpid Sting"] = MTH_DS_HunterSpellsDBC[14275]
|
||||
t["[Deprecated] Thick Hide"] = MTH_DS_HunterSpellsDBC[19611]
|
||||
t["[Deprecated] Trueshot Aura Percent Bonus"] = MTH_DS_HunterSpellsDBC[51520]
|
||||
t["Adrenaline Rush"] = MTH_DS_HunterSpellsDBC[28752]
|
||||
t["Aimed Shot"] = MTH_DS_HunterSpellsDBC[19434]
|
||||
t["Arcane Infused"] = MTH_DS_HunterSpellsDBC[23721]
|
||||
t["Arcane Shot"] = MTH_DS_HunterSpellsDBC[3044]
|
||||
t["Arcane Shot Mana Reduction"] = MTH_DS_HunterSpellsDBC[23157]
|
||||
t["Aspect of the Beast"] = MTH_DS_HunterSpellsDBC[13161]
|
||||
t["Aspect of the Cheetah"] = MTH_DS_HunterSpellsDBC[5118]
|
||||
t["Aspect of the Fox"] = MTH_DS_HunterSpellsDBC[45651]
|
||||
t["Aspect of the Hawk"] = MTH_DS_HunterSpellsDBC[13165]
|
||||
t["Aspect of the Monkey"] = MTH_DS_HunterSpellsDBC[13163]
|
||||
t["Aspect of the Pack"] = MTH_DS_HunterSpellsDBC[13159]
|
||||
t["Aspect of the Snake"] = MTH_DS_HunterSpellsDBC[45652]
|
||||
t["Aspect of the Turtle"] = MTH_DS_HunterSpellsDBC[45649]
|
||||
t["Aspect of the Wild"] = MTH_DS_HunterSpellsDBC[20043]
|
||||
t["Aspect of the Wolf"] = MTH_DS_HunterSpellsDBC[45650]
|
||||
t["Auto Shot"] = MTH_DS_HunterSpellsDBC[75]
|
||||
t["Avoidance"] = MTH_DS_HunterSpellsDBC[46023]
|
||||
t["Aynasha's Bow"] = MTH_DS_HunterSpellsDBC[19767]
|
||||
t["Baited Shot"] = MTH_DS_HunterSpellsDBC[46321]
|
||||
t["Barrage"] = MTH_DS_HunterSpellsDBC[19461]
|
||||
t["Battle Standard"] = MTH_DS_HunterSpellsDBC[22996]
|
||||
t["Beast Mastery"] = MTH_DS_HunterSpellsDBC[47355]
|
||||
t["Beast of the Unseen Path"] = MTH_DS_HunterSpellsDBC[29233]
|
||||
t["Bestial Discipline"] = MTH_DS_HunterSpellsDBC[19590]
|
||||
t["Bestial Fury"] = MTH_DS_HunterSpellsDBC[19603]
|
||||
t["Bestial Precision"] = MTH_DS_HunterSpellsDBC[51554]
|
||||
t["Bestial Wrath"] = MTH_DS_HunterSpellsDBC[24395]
|
||||
t["Bite"] = MTH_DS_HunterSpellsDBC[17253]
|
||||
t["Bubble Barrier"] = MTH_DS_HunterSpellsDBC[36523]
|
||||
t["Carve"] = MTH_DS_HunterSpellsDBC[51575]
|
||||
t["Cheetah Sprint"] = MTH_DS_HunterSpellsDBC[24348]
|
||||
t["Claw"] = MTH_DS_HunterSpellsDBC[3009]
|
||||
t["Clever Traps"] = MTH_DS_HunterSpellsDBC[19239]
|
||||
t["Command"] = MTH_DS_HunterSpellsDBC[20576]
|
||||
t["Concussive Shot"] = MTH_DS_HunterSpellsDBC[5116]
|
||||
t["Concussive Shot Cooldown Reduction"] = MTH_DS_HunterSpellsDBC[23158]
|
||||
t["Coordinated Assault"] = MTH_DS_HunterSpellsDBC[49559]
|
||||
t["Copy of Shoot"] = MTH_DS_HunterSpellsDBC[28317]
|
||||
t["Counterattack"] = MTH_DS_HunterSpellsDBC[19306]
|
||||
t["Death Roll"] = MTH_DS_HunterSpellsDBC[36548]
|
||||
t["Demonic Transformation"] = MTH_DS_HunterSpellsDBC[24992]
|
||||
t["Detect Weakness"] = MTH_DS_HunterSpellsDBC[23578]
|
||||
t["Deterrence"] = MTH_DS_HunterSpellsDBC[19263]
|
||||
t["Disengage"] = MTH_DS_HunterSpellsDBC[781]
|
||||
t["Distracting Shot"] = MTH_DS_HunterSpellsDBC[14274]
|
||||
t["Efficiency"] = MTH_DS_HunterSpellsDBC[19416]
|
||||
t["Elusiveness"] = MTH_DS_HunterSpellsDBC[51552]
|
||||
t["Endless Quiver"] = MTH_DS_HunterSpellsDBC[51517]
|
||||
t["Endurance Training"] = MTH_DS_HunterSpellsDBC[19583]
|
||||
t["Entrapment"] = MTH_DS_HunterSpellsDBC[19184]
|
||||
t["Explosive Trap"] = MTH_DS_HunterSpellsDBC[13813]
|
||||
t["Explosive Trap Effect"] = MTH_DS_HunterSpellsDBC[13812]
|
||||
t["Extra Shot"] = MTH_DS_HunterSpellsDBC[51519]
|
||||
t["Eyes of the Beast"] = MTH_DS_HunterSpellsDBC[1002]
|
||||
t["Fang of the Crystal Spider"] = MTH_DS_HunterSpellsDBC[17331]
|
||||
t["Faster Bleeds 5 (Hunter)"] = MTH_DS_HunterSpellsDBC[52402]
|
||||
t["Feign Death"] = MTH_DS_HunterSpellsDBC[5384]
|
||||
t["Ferocity"] = MTH_DS_HunterSpellsDBC[19598]
|
||||
t["Fire Breath"] = MTH_DS_HunterSpellsDBC[46269]
|
||||
t["Flame Spear"] = MTH_DS_HunterSpellsDBC[19781]
|
||||
t["Flare"] = MTH_DS_HunterSpellsDBC[1543]
|
||||
t["Freezing Trap"] = MTH_DS_HunterSpellsDBC[1499]
|
||||
t["Freezing Trap (TEST)"] = MTH_DS_HunterSpellsDBC[21750]
|
||||
t["Freezing Trap Effect"] = MTH_DS_HunterSpellsDBC[3355]
|
||||
t["Frenzy"] = MTH_DS_HunterSpellsDBC[19621]
|
||||
t["Frost Trap"] = MTH_DS_HunterSpellsDBC[13809]
|
||||
t["Frost Trap Aura"] = MTH_DS_HunterSpellsDBC[13810]
|
||||
t["Frost Trap Effect"] = MTH_DS_HunterSpellsDBC[14207]
|
||||
t["Furious Howl"] = MTH_DS_HunterSpellsDBC[24597]
|
||||
t["Grace"] = MTH_DS_HunterSpellsDBC[46296]
|
||||
t["Haste"] = MTH_DS_HunterSpellsDBC[14824]
|
||||
t["Hawk Eye"] = MTH_DS_HunterSpellsDBC[19498]
|
||||
t["Hunter Giant Stalker"] = MTH_DS_HunterSpellsDBC[21942]
|
||||
t["Hunter's Mark"] = MTH_DS_HunterSpellsDBC[1130]
|
||||
t["Immolation Trap"] = MTH_DS_HunterSpellsDBC[13795]
|
||||
t["Immolation Trap Effect"] = MTH_DS_HunterSpellsDBC[13797]
|
||||
t["Impale"] = MTH_DS_HunterSpellsDBC[15860]
|
||||
t["Improved Aspect of the Hawk"] = MTH_DS_HunterSpellsDBC[23559]
|
||||
t["Improved Concussive Shot"] = MTH_DS_HunterSpellsDBC[19407]
|
||||
t["Improved Explosive Trap"] = MTH_DS_HunterSpellsDBC[19378]
|
||||
t["Improved Eyes of the Beast"] = MTH_DS_HunterSpellsDBC[19557]
|
||||
t["Improved Feign Death"] = MTH_DS_HunterSpellsDBC[19286]
|
||||
t["Improved Freezing Trap"] = MTH_DS_HunterSpellsDBC[19288]
|
||||
t["Improved Hunter's Mark"] = MTH_DS_HunterSpellsDBC[19421]
|
||||
t["Improved Immolation Trap"] = MTH_DS_HunterSpellsDBC[19246]
|
||||
t["Improved Mend Pet"] = MTH_DS_HunterSpellsDBC[19572]
|
||||
t["Improved Primal Aspects"] = MTH_DS_HunterSpellsDBC[19549]
|
||||
t["Improved Revive Pet"] = MTH_DS_HunterSpellsDBC[19575]
|
||||
t["Improved Scorpid Sting"] = MTH_DS_HunterSpellsDBC[19491]
|
||||
t["Improved Serpent Sting"] = MTH_DS_HunterSpellsDBC[24467]
|
||||
t["Improved Steady Shot"] = MTH_DS_HunterSpellsDBC[51515]
|
||||
t["Improved Stings"] = MTH_DS_HunterSpellsDBC[19464]
|
||||
t["Improved Swift Aspects"] = MTH_DS_HunterSpellsDBC[52603]
|
||||
t["Improved Volley and Multishot"] = MTH_DS_HunterSpellsDBC[23566]
|
||||
t["Improved Wing Clip"] = MTH_DS_HunterSpellsDBC[19228]
|
||||
t["Increased Hunter Pet Damage"] = MTH_DS_HunterSpellsDBC[22854]
|
||||
t["Increased Pet Damage +3%"] = MTH_DS_HunterSpellsDBC[27205]
|
||||
t["Increased Pet Damage +4%"] = MTH_DS_HunterSpellsDBC[52413]
|
||||
t["Kill Command"] = MTH_DS_HunterSpellsDBC[41827]
|
||||
t["Killer Instinct"] = MTH_DS_HunterSpellsDBC[19162]
|
||||
t["Lacerate"] = MTH_DS_HunterSpellsDBC[20907]
|
||||
t["Lightning Breath"] = MTH_DS_HunterSpellsDBC[24844]
|
||||
t["Lightning Reflexes"] = MTH_DS_HunterSpellsDBC[19168]
|
||||
t["Lizard Bolt"] = MTH_DS_HunterSpellsDBC[46297]
|
||||
t["Mend Pet"] = MTH_DS_HunterSpellsDBC[136]
|
||||
t["Mongoose Bite"] = MTH_DS_HunterSpellsDBC[1495]
|
||||
t["Mortal Shots"] = MTH_DS_HunterSpellsDBC[19485]
|
||||
t["Multi-Shot"] = MTH_DS_HunterSpellsDBC[2643]
|
||||
t["Multi-Shot Damage Increase"] = MTH_DS_HunterSpellsDBC[28539]
|
||||
t["Multishot and Carve Cooldown"] = MTH_DS_HunterSpellsDBC[52602]
|
||||
t["Not There"] = MTH_DS_HunterSpellsDBC[28761]
|
||||
t["Packleader"] = MTH_DS_HunterSpellsDBC[36532]
|
||||
t["Pathfinding"] = MTH_DS_HunterSpellsDBC[19559]
|
||||
t["Pet Focus Regen Bonus"] = MTH_DS_HunterSpellsDBC[52685]
|
||||
t["Piercing Shots"] = MTH_DS_HunterSpellsDBC[51512]
|
||||
t["Pin"] = MTH_DS_HunterSpellsDBC[46292]
|
||||
t["Planning Ahead"] = MTH_DS_HunterSpellsDBC[51576]
|
||||
t["Planning Ahead Dummy"] = MTH_DS_HunterSpellsDBC[51578]
|
||||
t["Poison Spit"] = MTH_DS_HunterSpellsDBC[46271]
|
||||
t["Prowl"] = MTH_DS_HunterSpellsDBC[24450]
|
||||
t["Quick Shots"] = MTH_DS_HunterSpellsDBC[51547]
|
||||
t["Quick Strikes"] = MTH_DS_HunterSpellsDBC[51542]
|
||||
t["Ranged Weapon Specialization"] = MTH_DS_HunterSpellsDBC[19511]
|
||||
t["Rapid Fire"] = MTH_DS_HunterSpellsDBC[3045]
|
||||
t["Raptor Strike"] = MTH_DS_HunterSpellsDBC[2973]
|
||||
t["Readiness"] = MTH_DS_HunterSpellsDBC[23989]
|
||||
t["Resourcefulness"] = MTH_DS_HunterSpellsDBC[51570]
|
||||
t["Revive Pet"] = MTH_DS_HunterSpellsDBC[982]
|
||||
t["Roar of Fortitude"] = MTH_DS_HunterSpellsDBC[36535]
|
||||
t["Savage Rend"] = MTH_DS_HunterSpellsDBC[36536]
|
||||
t["Savage Strikes"] = MTH_DS_HunterSpellsDBC[19159]
|
||||
t["Scatter Shot"] = MTH_DS_HunterSpellsDBC[19503]
|
||||
t["Scorpid Poison"] = MTH_DS_HunterSpellsDBC[24583]
|
||||
t["Scorpid Stike"] = MTH_DS_HunterSpellsDBC[45659]
|
||||
t["Scorpid Sting"] = MTH_DS_HunterSpellsDBC[3043]
|
||||
t["Scorpid Sting Damage Reduction"] = MTH_DS_HunterSpellsDBC[51509]
|
||||
t["Screech"] = MTH_DS_HunterSpellsDBC[24423]
|
||||
t["Serpent Sting"] = MTH_DS_HunterSpellsDBC[1978]
|
||||
t["Serpent Strike"] = MTH_DS_HunterSpellsDBC[45661]
|
||||
t["Server Spell"] = MTH_DS_HunterSpellsDBC[29218]
|
||||
t["Shell Shield"] = MTH_DS_HunterSpellsDBC[26064]
|
||||
t["Shoot"] = MTH_DS_HunterSpellsDBC[6660]
|
||||
t["Shoot Bow"] = MTH_DS_HunterSpellsDBC[2480]
|
||||
t["Shoot Crossbow"] = MTH_DS_HunterSpellsDBC[7919]
|
||||
t["Shoot Gun"] = MTH_DS_HunterSpellsDBC[7918]
|
||||
t["Shoot Silithus Rifleman Long Range"] = MTH_DS_HunterSpellsDBC[25200]
|
||||
t["Shoot Tauren Rifleman"] = MTH_DS_HunterSpellsDBC[26282]
|
||||
t["Slime Shot"] = MTH_DS_HunterSpellsDBC[28318]
|
||||
t["Spell - Reuse 19501"] = MTH_DS_HunterSpellsDBC[19501]
|
||||
t["Spit"] = MTH_DS_HunterSpellsDBC[27919]
|
||||
t["Stalker's Ally"] = MTH_DS_HunterSpellsDBC[28758]
|
||||
t["Steady Shot"] = MTH_DS_HunterSpellsDBC[3035]
|
||||
t["Steady Shot and Raptor Strike Crit Bonus"] = MTH_DS_HunterSpellsDBC[52684]
|
||||
t["Stinging Nettle"] = MTH_DS_HunterSpellsDBC[51579]
|
||||
t["Strider Presence"] = MTH_DS_HunterSpellsDBC[36531]
|
||||
t["Strike Together"] = MTH_DS_HunterSpellsDBC[49557]
|
||||
t["Striker's Arcane Shot Bonus"] = MTH_DS_HunterSpellsDBC[26173]
|
||||
t["Striker's Rapid Fire Bonus"] = MTH_DS_HunterSpellsDBC[26174]
|
||||
t["Superior Aspects"] = MTH_DS_HunterSpellsDBC[24416]
|
||||
t["Superior Strikes"] = MTH_DS_HunterSpellsDBC[24269]
|
||||
t["Surefooted"] = MTH_DS_HunterSpellsDBC[19290]
|
||||
t["Survivalist"] = MTH_DS_HunterSpellsDBC[19255]
|
||||
t["Swift Aspects"] = MTH_DS_HunterSpellsDBC[19552]
|
||||
t["Swiftshot"] = MTH_DS_HunterSpellsDBC[19454]
|
||||
t["Swipe"] = MTH_DS_HunterSpellsDBC[46287]
|
||||
t["Tame Adult Plainstrider"] = MTH_DS_HunterSpellsDBC[19678]
|
||||
t["Tame Armored Scorpid"] = MTH_DS_HunterSpellsDBC[19683]
|
||||
t["Tame Ashfeather Swooper"] = MTH_DS_HunterSpellsDBC[46705]
|
||||
t["Tame Blackvenom Scorpid"] = MTH_DS_HunterSpellsDBC[46701]
|
||||
t["Tame Decrepit Darkhound"] = MTH_DS_HunterSpellsDBC[50904]
|
||||
t["Tame Decrepit Darkhound."] = MTH_DS_HunterSpellsDBC[44007]
|
||||
t["Tame Dire Mottled Boar"] = MTH_DS_HunterSpellsDBC[19681]
|
||||
t["Tame Dire Mottled Boar."] = MTH_DS_HunterSpellsDBC[44013]
|
||||
t["Tame Elder Crimson Lynx"] = MTH_DS_HunterSpellsDBC[46091]
|
||||
t["Tame Greater Dusk Bat"] = MTH_DS_HunterSpellsDBC[50905]
|
||||
t["Tame Greater Duskbat"] = MTH_DS_HunterSpellsDBC[44009]
|
||||
t["Tame Ice Claw Bear"] = MTH_DS_HunterSpellsDBC[19597]
|
||||
t["Tame Ivory Hawkstrider"] = MTH_DS_HunterSpellsDBC[46093]
|
||||
t["Tame Large Crag Boar"] = MTH_DS_HunterSpellsDBC[19677]
|
||||
t["Tame Large Crag Boar."] = MTH_DS_HunterSpellsDBC[44019]
|
||||
t["Tame Lavender Mana Wyrm"] = MTH_DS_HunterSpellsDBC[46095]
|
||||
t["Tame Muckreef Crawler"] = MTH_DS_HunterSpellsDBC[46703]
|
||||
t["Tame Nightsaber Stalker"] = MTH_DS_HunterSpellsDBC[19685]
|
||||
t["Tame Prairie Stalker"] = MTH_DS_HunterSpellsDBC[19679]
|
||||
t["Tame Prowler"] = MTH_DS_HunterSpellsDBC[44003]
|
||||
t["Tame Snow Leopard"] = MTH_DS_HunterSpellsDBC[19676]
|
||||
t["Tame Stonetusk Boar"] = MTH_DS_HunterSpellsDBC[44001]
|
||||
t["Tame Strigid Screecher"] = MTH_DS_HunterSpellsDBC[19686]
|
||||
t["Tame Surf Crawler"] = MTH_DS_HunterSpellsDBC[19682]
|
||||
t["Tame Swoop"] = MTH_DS_HunterSpellsDBC[19680]
|
||||
t["Tame Tirisfal Plagued Bear"] = MTH_DS_HunterSpellsDBC[44011]
|
||||
t["Tame Webwood Lurker"] = MTH_DS_HunterSpellsDBC[19684]
|
||||
t["Tame Young Forest Bear"] = MTH_DS_HunterSpellsDBC[44005]
|
||||
t["Tamed Pet Passive (DND)"] = MTH_DS_HunterSpellsDBC[8875]
|
||||
t["Thick Hide"] = MTH_DS_HunterSpellsDBC[19609]
|
||||
t["Throw"] = MTH_DS_HunterSpellsDBC[10277]
|
||||
t["Thunderstomp"] = MTH_DS_HunterSpellsDBC[26090]
|
||||
t["Tranquilizing Shot"] = MTH_DS_HunterSpellsDBC[19801]
|
||||
t["Trap Mastery"] = MTH_DS_HunterSpellsDBC[19376]
|
||||
t["Trueshot Aura"] = MTH_DS_HunterSpellsDBC[19506]
|
||||
t["Unleashed Fury"] = MTH_DS_HunterSpellsDBC[19616]
|
||||
t["Unseen Path Pet Bonus"] = MTH_DS_HunterSpellsDBC[26176]
|
||||
t["Untamed Trapper"] = MTH_DS_HunterSpellsDBC[51586]
|
||||
t["Vicious Strikes"] = MTH_DS_HunterSpellsDBC[51584]
|
||||
t["Viper Sting"] = MTH_DS_HunterSpellsDBC[3034]
|
||||
t["Viper Strike"] = MTH_DS_HunterSpellsDBC[45660]
|
||||
t["Volley"] = MTH_DS_HunterSpellsDBC[1510]
|
||||
t["Web"] = MTH_DS_HunterSpellsDBC[36533]
|
||||
t["Widow Bite"] = MTH_DS_HunterSpellsDBC[26226]
|
||||
t["Wing Clip"] = MTH_DS_HunterSpellsDBC[2974]
|
||||
t["Wyvern Sting"] = MTH_DS_HunterSpellsDBC[19386]
|
||||
end
|
||||
@@ -0,0 +1,224 @@
|
||||
|
||||
-- ds-hunter-spells.lua
|
||||
-- Hunter spell data for TWoW 1.18.1 "Nightmares of Ursol"
|
||||
-- Used by: ExpAmmo cycle tracker, HunterBook (future spell reference tab)
|
||||
--
|
||||
-- TODO markers:
|
||||
-- SPELL_ID : exact spell ID not yet extracted from Spell.dbc (needs MPQ)
|
||||
-- ICON : icon texture not yet confirmed from game files
|
||||
-- VERIFY : value observed in patch notes but subject to change on PTR/live
|
||||
-- PENDING : mechanic not yet verified against live server
|
||||
|
||||
if not MTH_DS then MTH_DS = {} end
|
||||
|
||||
-- ============================================================
|
||||
-- BASELINE HUNTER SPELLS (available to all specs from 1.18.1)
|
||||
-- ============================================================
|
||||
|
||||
MTH_DS_HunterSpells = {
|
||||
|
||||
["baseline"] = {
|
||||
|
||||
{
|
||||
["name"] = "Steady Shot",
|
||||
["addedIn"] = "1.18.1",
|
||||
["note"] = "Moved from Marksmanship talent tree to baseline at level 20.",
|
||||
["castTime"] = "1.5s", -- TODO VERIFY
|
||||
["cooldown"] = "n/a",
|
||||
["school"] = "Physical",
|
||||
["icon"] = "Ability_Hunter_SteadyShot", -- TODO ICON confirm
|
||||
},
|
||||
|
||||
{
|
||||
["name"] = "Trueshot Aura",
|
||||
["addedIn"] = "1.18.1",
|
||||
["note"] = "Moved from Marksmanship capstone to baseline. All hunters now get this.",
|
||||
["castTime"] = "Instant",
|
||||
["cooldown"] = "n/a",
|
||||
["school"] = "Physical",
|
||||
["icon"] = "Ability_Trueshot", -- TODO ICON confirm
|
||||
},
|
||||
|
||||
{
|
||||
["name"] = "Aspect of the Viper",
|
||||
["addedIn"] = "1.18.1",
|
||||
["note"] = "New baseline aspect learned at level 56. Regenerates mana while dealing reduced damage.",
|
||||
["castTime"] = "Instant",
|
||||
["cooldown"] = "n/a",
|
||||
["school"] = "Nature",
|
||||
["icon"] = "Spell_Nature_Poison", -- TODO ICON confirm
|
||||
},
|
||||
|
||||
{
|
||||
["name"] = "Volley",
|
||||
["addedIn"] = "1.18.1",
|
||||
["note"] = "Changed from channelled to a 3-second cast AoE. Now a proper cast-bar spell.",
|
||||
["castTime"] = "3s", -- TODO VERIFY
|
||||
["cooldown"] = "n/a",
|
||||
["school"] = "Physical",
|
||||
["icon"] = "Ability_Whirlwind", -- TODO ICON confirm
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
-- ============================================================
|
||||
-- MARKSMANSHIP TALENTS (1.18.1 changes)
|
||||
-- ============================================================
|
||||
|
||||
["marksmanship"] = {
|
||||
|
||||
{
|
||||
["name"] = "Aimed Shot",
|
||||
["addedIn"] = "1.18.1",
|
||||
["note"] = "Now has a visible 2s cast bar. Triggers ExpAmmo cycle start (resets to FIRE state).",
|
||||
["castTime"] = "2s",
|
||||
["cooldown"] = "26s", -- TODO VERIFY vs Spell.dbc
|
||||
["school"] = "Physical",
|
||||
["icon"] = "Inv_Spear_07", -- TODO ICON confirm
|
||||
["expAmmoTrigger"] = true, -- resets ExpAmmo cycle to state 1 (FIRE)
|
||||
},
|
||||
|
||||
{
|
||||
["name"] = "Lock and Load",
|
||||
["addedIn"] = "1.18.1",
|
||||
["talentRow"] = 7, -- MM capstone row
|
||||
["note"] = "10s proc buff. Triggered by Aimed Shot crit or trap activation (TODO VERIFY exact condition).",
|
||||
["castTime"] = "Instant",
|
||||
["cooldown"] = "n/a",
|
||||
["duration"] = "10s", -- TODO VERIFY
|
||||
["school"] = "Physical",
|
||||
["type"] = "buff_proc",
|
||||
["icon"] = "Ability_Hunter_LockAndLoad", -- TODO ICON confirm
|
||||
},
|
||||
|
||||
{
|
||||
["name"] = "Rapid Fire",
|
||||
["note"] = "15% haste, 15s duration, 5min CD. Unchanged in 1.18.1 baseline values.",
|
||||
["castTime"] = "Instant",
|
||||
["cooldown"] = "5min",
|
||||
["duration"] = "15s",
|
||||
["school"] = "Physical",
|
||||
["type"] = "buff",
|
||||
["icon"] = "Ability_Hunter_RapidKilling",
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
-- ============================================================
|
||||
-- BEAST MASTERY TALENTS (1.18.1 changes)
|
||||
-- ============================================================
|
||||
|
||||
["beastmastery"] = {
|
||||
|
||||
{
|
||||
["name"] = "Bestial Wrath",
|
||||
["note"] = "Returned to BM tree. 18s buff, 90s CD (1.5min). Pet immune to fear/CC during.",
|
||||
["castTime"] = "Instant",
|
||||
["cooldown"] = "90s",
|
||||
["duration"] = "18s", -- TODO VERIFY
|
||||
["school"] = "Physical",
|
||||
["type"] = "buff",
|
||||
["icon"] = "Ability_Druid_Ferociousbite", -- TODO ICON confirm
|
||||
},
|
||||
|
||||
{
|
||||
["name"] = "Kill Command",
|
||||
["addedIn"] = "1.18.1",
|
||||
["talentRow"] = 7, -- BM capstone row
|
||||
["note"] = "8s CD, triggered on crit. Commands pet to inflict bonus damage on next hit.",
|
||||
["castTime"] = "Instant",
|
||||
["cooldown"] = "8s",
|
||||
["school"] = "Physical",
|
||||
["icon"] = "Ability_Hunter_KillCommand", -- TODO ICON confirm
|
||||
},
|
||||
|
||||
{
|
||||
["name"] = "Scent of Blood",
|
||||
["addedIn"] = "1.18.1",
|
||||
["talentRow"] = 5,
|
||||
["note"] = "8s pet rage proc buff. Triggers when Bestial Wrath is active (TODO VERIFY exact condition).",
|
||||
["castTime"] = "Instant (proc)",
|
||||
["cooldown"] = "n/a",
|
||||
["duration"] = "8s", -- TODO VERIFY
|
||||
["school"] = "Physical",
|
||||
["type"] = "buff_proc",
|
||||
["icon"] = "Ability_Warrior_BloodFrenzy", -- TODO ICON confirm
|
||||
},
|
||||
|
||||
{
|
||||
["name"] = "Baited Shot",
|
||||
["removedIn"] = "1.18.1",
|
||||
["note"] = "REMOVED. Replaced by Kill Command as the BM capstone. Any saved data for this spell can be cleared.",
|
||||
["deprecated"] = true,
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
-- ============================================================
|
||||
-- SURVIVAL TALENTS (1.18.1 changes)
|
||||
-- ============================================================
|
||||
|
||||
["survival"] = {
|
||||
|
||||
{
|
||||
["name"] = "Alone Against the World",
|
||||
["addedIn"] = "1.18.1",
|
||||
["talentRow"] = 2,
|
||||
["note"] = "New 2-point talent. Bonus to pet/self when no group members nearby (TODO VERIFY exact bonus values).",
|
||||
["type"] = "passive",
|
||||
["icon"] = "Ability_Hunter_AloneAgainstTheWorld", -- TODO ICON confirm
|
||||
},
|
||||
|
||||
{
|
||||
["name"] = "Lacerate",
|
||||
["addedIn"] = "1.18.1",
|
||||
["note"] = "40% Attack Power bleed, +15% from all bleed sides. (TODO VERIFY: rank values from Spell.dbc).",
|
||||
["castTime"] = "Instant",
|
||||
["school"] = "Physical",
|
||||
["type"] = "dot",
|
||||
["icon"] = "Ability_Hunter_Lacerate", -- TODO ICON confirm
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
-- ============================================================
|
||||
-- EXPERIMENTAL AMMUNITION (new 1.18.1 mechanic)
|
||||
-- States cycle: FIRE -> ARCANE -> NATURE -> (loop)
|
||||
-- Aimed Shot resets cycle to FIRE each time it is fired.
|
||||
-- ============================================================
|
||||
|
||||
["expAmmoStates"] = {
|
||||
|
||||
{
|
||||
["state"] = 1,
|
||||
["label"] = "Fire Ammo",
|
||||
["school"] = "Fire",
|
||||
["duration"] = "60s", -- TODO VERIFY from server
|
||||
["consumeSpell"] = "Multi-Shot", -- TODO VERIFY spell that consumes this state
|
||||
["icon"] = "Spell_Fire_FlameBolt", -- TODO ICON confirm
|
||||
["buffName"] = nil, -- TODO: exact buff name from Spell.dbc
|
||||
},
|
||||
|
||||
{
|
||||
["state"] = 2,
|
||||
["label"] = "Arcane Ammo",
|
||||
["school"] = "Arcane",
|
||||
["duration"] = "60s", -- TODO VERIFY
|
||||
["consumeSpell"] = "Arcane Shot", -- TODO VERIFY
|
||||
["icon"] = "Spell_Arcane_ArcaneMissiles", -- TODO ICON confirm
|
||||
["buffName"] = nil, -- TODO: exact buff name from Spell.dbc
|
||||
},
|
||||
|
||||
{
|
||||
["state"] = 3,
|
||||
["label"] = "Nature Ammo",
|
||||
["school"] = "Nature",
|
||||
["duration"] = "60s", -- TODO VERIFY
|
||||
["consumeSpell"] = "Serpent Sting", -- TODO VERIFY
|
||||
["icon"] = "Spell_Nature_NatureBolt", -- TODO ICON confirm
|
||||
["buffName"] = nil, -- TODO: exact buff name from Spell.dbc
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
@@ -137,6 +137,11 @@ local MTH_DS_AtlasSources = {
|
||||
[9426] = {["boss"] = true, },
|
||||
[9456] = {["boss"] = true, },
|
||||
[9487] = {["boss"] = true, },
|
||||
[42198] = {["crafted"] = true, },
|
||||
[42199] = {["crafted"] = true, },
|
||||
[42200] = {["crafted"] = true, },
|
||||
[42201] = {["crafted"] = true, },
|
||||
[42202] = {["crafted"] = true, },
|
||||
}
|
||||
|
||||
for itemId, sourceMap in pairs(MTH_DS_AtlasSources) do
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+72
-7
@@ -10,6 +10,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 5,
|
||||
["reqlevel"] = 1,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 1.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[150] = 0,
|
||||
@@ -118,6 +119,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 15,
|
||||
["reqlevel"] = 10,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 3.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[150] = 0,
|
||||
@@ -275,6 +277,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 5,
|
||||
["reqlevel"] = 1,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 2,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[151] = 0,
|
||||
@@ -359,6 +362,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 15,
|
||||
["reqlevel"] = 10,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 5.5,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[227] = 0,
|
||||
@@ -494,6 +498,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 30,
|
||||
["reqlevel"] = 25,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 5.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[227] = 0,
|
||||
@@ -621,6 +626,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 30,
|
||||
["reqlevel"] = 25,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 7.5,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[227] = 0,
|
||||
@@ -732,29 +738,25 @@ MTH_DS_AmmoItems = {
|
||||
[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",
|
||||
["dps"] = 7.5,
|
||||
["subtype"] = "arrow",
|
||||
},
|
||||
[3465] = {
|
||||
["name"] = "Exploding Shot",
|
||||
["level"] = 36,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 7.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[4960] = {
|
||||
["name"] = "Flash Pellet",
|
||||
["level"] = 7,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 3.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[5568] = {
|
||||
@@ -762,6 +764,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 18,
|
||||
["reqlevel"] = 13,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 3.5,
|
||||
["subtype"] = "bullet",
|
||||
["drops"] = {
|
||||
[2156] = 30.97,
|
||||
@@ -788,6 +791,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 10,
|
||||
["reqlevel"] = 5,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 2,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[8068] = {
|
||||
@@ -795,6 +799,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 20,
|
||||
["reqlevel"] = 15,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 5.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[8069] = {
|
||||
@@ -802,6 +807,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 35,
|
||||
["reqlevel"] = 30,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 7.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[9399] = {
|
||||
@@ -809,6 +815,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 40,
|
||||
["reqlevel"] = 35,
|
||||
["icon"] = "INV_Ammo_Arrow_01",
|
||||
["dps"] = 7.5,
|
||||
["subtype"] = "arrow",
|
||||
["drops"] = {
|
||||
[6906] = 100,
|
||||
@@ -819,6 +826,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 42,
|
||||
["reqlevel"] = 37,
|
||||
["icon"] = "INV_Ammo_Bullet_01",
|
||||
["dps"] = 12.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[10513] = {
|
||||
@@ -826,6 +834,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 49,
|
||||
["reqlevel"] = 44,
|
||||
["icon"] = "INV_Ammo_Bullet_01",
|
||||
["dps"] = 12.5,
|
||||
["subtype"] = "bullet",
|
||||
},
|
||||
[10579] = {
|
||||
@@ -833,6 +842,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 42,
|
||||
["reqlevel"] = 37,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 12.5,
|
||||
["subtype"] = "arrow",
|
||||
},
|
||||
[11284] = {
|
||||
@@ -840,6 +850,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 45,
|
||||
["reqlevel"] = 40,
|
||||
["icon"] = "INV_Ammo_Bullet_01",
|
||||
["dps"] = 10.5,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[734] = 0,
|
||||
@@ -941,6 +952,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 45,
|
||||
["reqlevel"] = 40,
|
||||
["icon"] = "INV_Weapon_ShortBlade_25",
|
||||
["dps"] = 10.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[228] = 0,
|
||||
@@ -1050,6 +1062,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 52,
|
||||
["reqlevel"] = 47,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 10.5,
|
||||
["subtype"] = "bullet",
|
||||
["drops"] = {
|
||||
[9025] = 16,
|
||||
@@ -1060,6 +1073,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 59,
|
||||
["reqlevel"] = 54,
|
||||
["icon"] = "Ability_Hunter_CriticalShot",
|
||||
["dps"] = 20,
|
||||
["subtype"] = "arrow",
|
||||
["drops"] = {
|
||||
[9236] = 100,
|
||||
@@ -1070,6 +1084,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 61,
|
||||
["reqlevel"] = 56,
|
||||
["icon"] = "INV_Ammo_Bullet_02",
|
||||
["dps"] = 20.5,
|
||||
["subtype"] = "bullet",
|
||||
["drops"] = {
|
||||
[10997] = 100,
|
||||
@@ -1080,6 +1095,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 57,
|
||||
["reqlevel"] = 52,
|
||||
["icon"] = "INV_Ammo_Bullet_03",
|
||||
["dps"] = 17.5,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[17078] = 0,
|
||||
@@ -1090,6 +1106,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 57,
|
||||
["reqlevel"] = 52,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 17.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[17078] = 0,
|
||||
@@ -1100,6 +1117,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 1,
|
||||
["reqlevel"] = 1,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["dps"] = 1.5,
|
||||
["subtype"] = "arrow",
|
||||
},
|
||||
[19316] = {
|
||||
@@ -1107,6 +1125,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 54,
|
||||
["reqlevel"] = 51,
|
||||
["icon"] = "Spell_Frost_IceShard",
|
||||
["dps"] = 14.5,
|
||||
["subtype"] = "arrow",
|
||||
["vendors"] = {
|
||||
[13216] = 0,
|
||||
@@ -1120,6 +1139,7 @@ MTH_DS_AmmoItems = {
|
||||
["level"] = 54,
|
||||
["reqlevel"] = 51,
|
||||
["icon"] = "Spell_Frost_FrostBlast",
|
||||
["dps"] = 14.5,
|
||||
["subtype"] = "bullet",
|
||||
["vendors"] = {
|
||||
[13216] = 0,
|
||||
@@ -1128,6 +1148,51 @@ MTH_DS_AmmoItems = {
|
||||
[13219] = 0,
|
||||
},
|
||||
},
|
||||
[42198] = {
|
||||
["name"] = "Bright Wood Arrows",
|
||||
["level"] = 35,
|
||||
["reqlevel"] = 30,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["subtype"] = "arrow",
|
||||
["dps"] = 10.5,
|
||||
["crafted"] = true,
|
||||
},
|
||||
[42199] = {
|
||||
["name"] = "Shade Wood Arrows",
|
||||
["level"] = 45,
|
||||
["reqlevel"] = 40,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["subtype"] = "arrow",
|
||||
["dps"] = 14.5,
|
||||
["crafted"] = true,
|
||||
},
|
||||
[42200] = {
|
||||
["name"] = "Smooth Ironfeather Arrows",
|
||||
["level"] = 55,
|
||||
["reqlevel"] = 50,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["subtype"] = "arrow",
|
||||
["dps"] = 17.5,
|
||||
["crafted"] = true,
|
||||
},
|
||||
[42201] = {
|
||||
["name"] = "Starfeather Arrows",
|
||||
["level"] = 60,
|
||||
["reqlevel"] = 55,
|
||||
["icon"] = "INV_Ammo_Arrow_02",
|
||||
["subtype"] = "arrow",
|
||||
["dps"] = 19,
|
||||
["crafted"] = true,
|
||||
},
|
||||
[42202] = {
|
||||
["name"] = "Enchanted Thorium Shells",
|
||||
["level"] = 60,
|
||||
["reqlevel"] = 55,
|
||||
["icon"] = "INV_Ammo_Bullet_03",
|
||||
["subtype"] = "bullet",
|
||||
["dps"] = 19,
|
||||
["crafted"] = true,
|
||||
},
|
||||
}
|
||||
|
||||
MTH_DS_Items = MTH_DS_AmmoItems
|
||||
+125
-117
@@ -2,120 +2,128 @@ 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 },
|
||||
}
|
||||
[9] = { 5137.50, 3425.00 }, -- Mulgore
|
||||
[11] = { 2487.50, 1658.34 }, -- Barrens
|
||||
[15] = { 2170.84, 1447.90 }, -- Alterac
|
||||
[19] = { 5287.50, 3525.00 }, -- BlastedLands
|
||||
[20] = { 4518.75, 3012.50 }, -- Tirisfal
|
||||
[21] = { 4200.00, 2800.00 }, -- Silverpine
|
||||
[22] = { 2231.25, 1487.50 }, -- WesternPlaguelands
|
||||
[23] = { 3870.83, 2581.25 }, -- EasternPlaguelands
|
||||
[24] = { 3200.00, 2133.33 }, -- Hilsbrad
|
||||
[26] = { 3850.00, 2566.67 }, -- Hinterlands
|
||||
[27] = { 4925.00, 3283.34 }, -- DunMorogh
|
||||
[29] = { 2929.16, 1952.08 }, -- BurningSteppes
|
||||
[30] = { 3470.84, 2314.62 }, -- Elwynn
|
||||
[32] = { 5091.66, 3393.70 }, -- DeadwindPass
|
||||
[34] = { 2700.00, 1800.03 }, -- Duskwood
|
||||
[35] = { 2293.75, 1529.17 }, -- LochModan
|
||||
[37] = { 6381.25, 4254.10 }, -- Stranglethorn
|
||||
[39] = { 4135.42, 2756.25 }, -- Westfall
|
||||
[40] = { 10133.34, 6756.25 }, -- Wetlands
|
||||
[41] = { 5250.00, 3500.00 }, -- Teldrassil
|
||||
[42] = { 6550.00, 4366.66 }, -- Darkshore
|
||||
[43] = { 5766.67, 3843.75 }, -- Ashenvale
|
||||
[61] = { 4400.00, 2933.33 }, -- ThousandNeedles
|
||||
[81] = { 6018.00, 4010.00 }, -- StonetalonMountains
|
||||
[101] = { 4495.83, 2997.91 }, -- Desolace
|
||||
[121] = { 6950.00, 4633.33 }, -- Feralas
|
||||
[141] = { 2800.00, 1866.67 }, -- Dustwallow
|
||||
[161] = { 6900.00, 4600.00 }, -- Tanaris
|
||||
[181] = { 3600.00, 2400.00 }, -- Azshara
|
||||
[182] = { 5750.00, 3833.33 }, -- Felwood
|
||||
[201] = { 3700.00, 2466.66 }, -- UngoroCrater
|
||||
[241] = { 2308.33, 1539.59 }, -- Moonglade
|
||||
[261] = { 3483.33, 2322.92 }, -- Silithus
|
||||
[281] = { 7100.00, 4733.33 }, -- Winterspring
|
||||
[301] = { 1737.50, 1158.34 }, -- Stormwind
|
||||
[321] = { 1402.61, 935.42 }, -- Orgrimmar
|
||||
[341] = { 790.63, 527.61 }, -- Ironforge
|
||||
[362] = { 1043.75, 695.83 }, -- ThunderBluff
|
||||
[381] = { 1058.33, 705.71 }, -- Darnassus
|
||||
[382] = { 959.37, 640.11 }, -- Undercity
|
||||
[401] = { 4237.50, 2825.00 }, -- AlteracValley
|
||||
[443] = { 1145.84, 764.58 }, -- WarsongGulch
|
||||
[461] = { 1756.25, 1170.83 }, -- ArathiBasin
|
||||
[500] = { 828.00, 541.00 }, -- pfQ876
|
||||
[501] = { 3206.00, 2142.00 }, -- pfQ616
|
||||
[502] = { 3159.00, 2108.00 }, -- pfQ4012
|
||||
[503] = { 988.63, 1393.17 }, -- pfQ5023
|
||||
[504] = { 2901.45, 1915.90 }, -- pfQ409
|
||||
[505] = { 3092.10, 2047.00 }, -- pfQ408
|
||||
[507] = { 3227.00, 2187.00 }, -- pfQ5121
|
||||
[509] = { 1468.00, 976.00 }, -- pfQ2040
|
||||
[510] = { 3666.00, 2442.00 }, -- pfQ5179
|
||||
[511] = { 1608.00, 1075.00 }, -- pfQ5024
|
||||
[512] = { 2472.00, 1665.00 }, -- pfQ5536
|
||||
[513] = { 3082.00, 2061.00 }, -- pfQ5225
|
||||
[514] = { 449.89, 300.00 }, -- pfQ1581
|
||||
[516] = { 572.78, 381.85 }, -- pfQ718
|
||||
[517] = { 2112.09, 1410.89 }, -- pfQ2100
|
||||
[518] = { 769.67, 513.11 }, -- pfQ721
|
||||
[519] = { 711.56, 468.68 }, -- pfQ25
|
||||
[520] = { 203.66, 135.04 }, -- pfQ796
|
||||
[521] = { 893.67, 595.78 }, -- pfQ1337
|
||||
[522] = { 4139.02, 2946.00 }, -- pfQ3478
|
||||
[523] = { 1348.24, 888.09 }, -- pfQ1941
|
||||
[601] = { 1432.00, 977.00 }, -- pfQ5130
|
||||
[603] = { 738.86, 492.57 }, -- pfQ2437
|
||||
[605] = { 1383.33, 922.91 }, -- pfQ1176
|
||||
[607] = { 695.03, 463.35 }, -- pfQ1477
|
||||
[609] = { 1221.87, 806.42 }, -- pfQ719
|
||||
[611] = { 378.15, 252.10 }, -- pfQ717
|
||||
[617] = { 1495.00, 983.00 }, -- pfQ2717
|
||||
[619] = { 2120.83, 1414.60 }, -- pfQ1977
|
||||
[623] = { 1985.00, 1268.00 }, -- pfQ1584
|
||||
[625] = { 2512.50, 1675.00 }, -- pfQ3429
|
||||
[627] = { 483.11, 322.08 }, -- pfQ2159
|
||||
[629] = { 886.84, 591.23 }, -- pfQ1583
|
||||
[635] = { 499.43, 332.95 }, -- pfQ2677
|
||||
[637] = { 656.59, 434.97 }, -- pfQ5138
|
||||
[639] = { 881.00, 589.00 }, -- pfQ722
|
||||
[641] = { 736.45, 490.96 }, -- pfQ491
|
||||
[643] = { 619.98, 413.32 }, -- pfQ5136
|
||||
[644] = { 320.19, 213.46 }, -- pfQ5135
|
||||
[645] = { 612.69, 408.46 }, -- pfQ5153
|
||||
[646] = { 703.30, 468.87 }, -- pfQ5163
|
||||
[648] = { 320.05, 213.37 }, -- pfQ2057
|
||||
[650] = { 381.00, 254.00 }, -- pfQ209
|
||||
[652] = { 1185.35, 789.86 }, -- pfQ2017
|
||||
[654] = { 977.56, 651.70 }, -- pfQ3428
|
||||
[655] = { 2777.54, 1851.69 }, -- pfQ5147
|
||||
[657] = { 619.00, 410.00 }, -- pfQ5723
|
||||
[659] = { 312.00, 208.00 }, -- pfQ2257
|
||||
[662] = { 1271.99, 845.48 }, -- pfQ5204
|
||||
[663] = { 1085.86, 726.61 }, -- pfQ2366
|
||||
[665] = { 1250.18, 837.44 }, -- pfQ5208
|
||||
[667] = { 1991.69, 1318.42 }, -- pfQ3456
|
||||
[668] = { 652.10, 439.67 }, -- pfQ5148
|
||||
[670] = { 2643.21, 1751.16 }, -- pfQ5077
|
||||
[672] = { 752.12, 510.33 }, -- pfQ5103
|
||||
[674] = { 546.75, 391.90 }, -- pfQ5086
|
||||
[676] = { 354.50, 234.74 }, -- pfQ5087
|
||||
[678] = { 1273.10, 853.72 }, -- pfQ5097
|
||||
[680] = { 1007.70, 671.80 }, -- pfQ5053
|
||||
[682] = { 823.00, 549.00 }, -- pfQ3457
|
||||
[683] = { 2800.00, 1868.00 }, -- pfQ5557
|
||||
[684] = { 5387.00, 3584.00 }, -- pfQ5602
|
||||
[685] = { 3098.00, 2068.00 }, -- pfQ5561
|
||||
[686] = { 3241.00, 2157.00 }, -- pfQ5581
|
||||
[689] = { 1324.00, 869.00 }, -- pfQ2557
|
||||
[691] = { 1634.00, 1073.00 }, -- pfQ5601
|
||||
[693] = { 1980.00, 1350.00 }, -- pfQ5628
|
||||
[698] = { 1234.00, 829.00 }, -- pfQ5641
|
||||
[699] = { 7856.00, 5241.00 }, -- pfQ5642
|
||||
[700] = { 861.00, 607.00 }, -- pfQ1769
|
||||
[701] = { 1347.00, 971.00 }, -- pfQ5640
|
||||
[702] = { 1047.00, 694.00 }, -- pfQ5722
|
||||
[704] = { 1011.00, 653.00 }, -- pfQ5734
|
||||
[705] = { 440.00, 279.00 }, -- pfQ5731
|
||||
[613] = { 1125.00, 740.00 },
|
||||
[615] = { 893.67, 595.78 },
|
||||
[621] = { 1919.00, 1250.00 },
|
||||
[631] = { 1170.00, 785.00 },
|
||||
[633] = { 2112.09, 1410.89 },
|
||||
[696] = { 1425.00, 962.00 },
|
||||
[5144] = { 309.00, 208.00 },
|
||||
}
|
||||
@@ -51,7 +51,7 @@ local growlTrainerRows = {
|
||||
["rank"] = "Rank 1",
|
||||
["rankNumber"] = 1,
|
||||
["school"] = "Physical",
|
||||
["sourceUrl"] = "https://database.turtlecraft.gg/?spell=2649",
|
||||
["sourceUrl"] = "https://octowow.st/db/?spell=2649",
|
||||
["trainLevel"] = 1,
|
||||
},
|
||||
{
|
||||
@@ -72,7 +72,7 @@ local growlTrainerRows = {
|
||||
["rank"] = "Rank 2",
|
||||
["rankNumber"] = 2,
|
||||
["school"] = "Physical",
|
||||
["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14916",
|
||||
["sourceUrl"] = "https://octowow.st/db/?spell=14916",
|
||||
["trainLevel"] = 10,
|
||||
},
|
||||
{
|
||||
@@ -93,7 +93,7 @@ local growlTrainerRows = {
|
||||
["rank"] = "Rank 3",
|
||||
["rankNumber"] = 3,
|
||||
["school"] = "Physical",
|
||||
["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14917",
|
||||
["sourceUrl"] = "https://octowow.st/db/?spell=14917",
|
||||
["trainLevel"] = 20,
|
||||
},
|
||||
{
|
||||
@@ -114,7 +114,7 @@ local growlTrainerRows = {
|
||||
["rank"] = "Rank 4",
|
||||
["rankNumber"] = 4,
|
||||
["school"] = "Physical",
|
||||
["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14918",
|
||||
["sourceUrl"] = "https://octowow.st/db/?spell=14918",
|
||||
["trainLevel"] = 30,
|
||||
},
|
||||
{
|
||||
@@ -135,7 +135,7 @@ local growlTrainerRows = {
|
||||
["rank"] = "Rank 5",
|
||||
["rankNumber"] = 5,
|
||||
["school"] = "Physical",
|
||||
["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14919",
|
||||
["sourceUrl"] = "https://octowow.st/db/?spell=14919",
|
||||
["trainLevel"] = 40,
|
||||
},
|
||||
{
|
||||
@@ -156,7 +156,7 @@ local growlTrainerRows = {
|
||||
["rank"] = "Rank 6",
|
||||
["rankNumber"] = 6,
|
||||
["school"] = "Physical",
|
||||
["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14920",
|
||||
["sourceUrl"] = "https://octowow.st/db/?spell=14920",
|
||||
["trainLevel"] = 50,
|
||||
},
|
||||
{
|
||||
@@ -177,7 +177,7 @@ local growlTrainerRows = {
|
||||
["rank"] = "Rank 7",
|
||||
["rankNumber"] = 7,
|
||||
["school"] = "Physical",
|
||||
["sourceUrl"] = "https://database.turtlecraft.gg/?spell=14921",
|
||||
["sourceUrl"] = "https://octowow.st/db/?spell=14921",
|
||||
["trainLevel"] = 60,
|
||||
},
|
||||
}
|
||||
@@ -228,7 +228,7 @@ end)
|
||||
|
||||
MTH_DS_PetSpells.byAbility["Growl"] = {
|
||||
["ability"] = "Growl",
|
||||
["searchUrl"] = "https://database.turtlecraft.gg/?search=Growl",
|
||||
["searchUrl"] = "https://octowow.st/db/?search=Growl",
|
||||
["spellCount"] = table.getn(growlTrainerRows),
|
||||
["spells"] = growlTrainerRows,
|
||||
}
|
||||
|
||||
+258
-218
File diff suppressed because it is too large
Load Diff
+42
-42
@@ -6,7 +6,7 @@ MTH_DS_Racial = {
|
||||
version = "1.0",
|
||||
generatedAtUTC = "2026-02-20T00:00:00Z",
|
||||
source = {
|
||||
site = "https://database.turtlecraft.gg",
|
||||
site = "https://octowow.st/db",
|
||||
runRoot = ".TurtleDBparsing/racial-audit/runs/20260220-164554Z",
|
||||
selection = "user-curated-approved",
|
||||
},
|
||||
@@ -25,46 +25,46 @@ MTH_DS_Racial = {
|
||||
["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" },
|
||||
[2481] = { id = 2481, name = "Find Treasure", rank = "Racial", level = 1, school = 0, race = "Dwarf", sourceUrl = "https://octowow.st/db/?spell=2481" },
|
||||
[5227] = { id = 5227, name = "Underwater Breathing", rank = "Racial Passive", level = 0, school = 0, race = "Undead", sourceUrl = "https://octowow.st/db/?spell=5227" },
|
||||
[7340] = { id = 7340, name = "Language Gnomish", rank = "", level = 1, school = 0, race = "Gnome", sourceUrl = "https://octowow.st/db/?spell=7340" },
|
||||
[7744] = { id = 7744, name = "Will of the Forsaken", rank = "Racial", level = 0, school = 0, race = "Undead", sourceUrl = "https://octowow.st/db/?spell=7744" },
|
||||
[20549] = { id = 20549, name = "War Stomp", rank = "Racial", level = 0, school = 0, race = "Tauren", sourceUrl = "https://octowow.st/db/?spell=20549" },
|
||||
[20550] = { id = 20550, name = "Endurance", rank = "Racial Passive", level = 0, school = 0, race = "Tauren", sourceUrl = "https://octowow.st/db/?spell=20550" },
|
||||
[20551] = { id = 20551, name = "Nature Resistance", rank = "Racial Passive", level = 0, school = 0, race = "Tauren", raceGuess = true, sourceUrl = "https://octowow.st/db/?spell=20551" },
|
||||
[20552] = { id = 20552, name = "Cultivation", rank = "Racial Passive", level = 0, school = 0, race = "Tauren", sourceUrl = "https://octowow.st/db/?spell=20552" },
|
||||
[20554] = { id = 20554, name = "Berserking", rank = "Racial", level = 0, school = 0, race = "Troll", sourceUrl = "https://octowow.st/db/?spell=20554" },
|
||||
[20555] = { id = 20555, name = "Regeneration", rank = "Racial Passive", level = 0, school = 0, race = "Troll", sourceUrl = "https://octowow.st/db/?spell=20555" },
|
||||
[20557] = { id = 20557, name = "Beast Slaying", rank = "Racial Passive", level = 0, school = 0, race = "Troll", sourceUrl = "https://octowow.st/db/?spell=20557" },
|
||||
[20558] = { id = 20558, name = "Throwing Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Troll", sourceUrl = "https://octowow.st/db/?spell=20558" },
|
||||
[20572] = { id = 20572, name = "Blood Fury", rank = "Racial", level = 0, school = 0, race = "Orc", sourceUrl = "https://octowow.st/db/?spell=20572" },
|
||||
[20573] = { id = 20573, name = "Hardiness", rank = "Racial Passive", level = 0, school = 0, race = "Orc", sourceUrl = "https://octowow.st/db/?spell=20573" },
|
||||
[20574] = { id = 20574, name = "Axe Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Orc", sourceUrl = "https://octowow.st/db/?spell=20574" },
|
||||
[20575] = { id = 20575, name = "Command", rank = "Racial Passive", level = 0, school = 0, race = "Orc", sourceUrl = "https://octowow.st/db/?spell=20575" },
|
||||
[20576] = { id = 20576, name = "Command", rank = "Racial Passive", level = 0, school = 0, race = "Orc", sourceUrl = "https://octowow.st/db/?spell=20576" },
|
||||
[20577] = { id = 20577, name = "Cannibalize", rank = "Racial", level = 0, school = 0, race = "Undead", sourceUrl = "https://octowow.st/db/?spell=20577" },
|
||||
[20579] = { id = 20579, name = "Shadow Resistance", rank = "Racial Passive", level = 0, school = 0, race = "Undead", sourceUrl = "https://octowow.st/db/?spell=20579" },
|
||||
[20580] = { id = 20580, name = "Shadowmeld", rank = "Racial", level = 1, school = 0, race = "Night Elf", sourceUrl = "https://octowow.st/db/?spell=20580" },
|
||||
[20582] = { id = 20582, name = "Quickness", rank = "Racial Passive", level = 0, school = 0, race = "Night Elf", sourceUrl = "https://octowow.st/db/?spell=20582" },
|
||||
[20583] = { id = 20583, name = "Nature Resistance", rank = "Racial Passive", level = 0, school = 0, race = "Night Elf", raceGuess = true, sourceUrl = "https://octowow.st/db/?spell=20583" },
|
||||
[20585] = { id = 20585, name = "Wisp Spirit", rank = "Racial Passive", level = 0, school = 0, race = "Night Elf", sourceUrl = "https://octowow.st/db/?spell=20585" },
|
||||
[20589] = { id = 20589, name = "Escape Artist", rank = "Racial", level = 0, school = 0, race = "Gnome", sourceUrl = "https://octowow.st/db/?spell=20589" },
|
||||
[20591] = { id = 20591, name = "Expansive Mind", rank = "Racial Passive", level = 0, school = 0, race = "Gnome", sourceUrl = "https://octowow.st/db/?spell=20591" },
|
||||
[20592] = { id = 20592, name = "Arcane Resistance", rank = "Racial Passive", level = 0, school = 0, race = "Gnome", sourceUrl = "https://octowow.st/db/?spell=20592" },
|
||||
[20593] = { id = 20593, name = "Engineering Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Gnome", sourceUrl = "https://octowow.st/db/?spell=20593" },
|
||||
[20594] = { id = 20594, name = "Stoneform", rank = "Racial", level = 0, school = 0, race = "Dwarf", sourceUrl = "https://octowow.st/db/?spell=20594" },
|
||||
[20595] = { id = 20595, name = "Gun Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Dwarf", sourceUrl = "https://octowow.st/db/?spell=20595" },
|
||||
[20596] = { id = 20596, name = "Frost Resistance", rank = "Racial Passive", level = 0, school = 0, race = "Dwarf", sourceUrl = "https://octowow.st/db/?spell=20596" },
|
||||
[20597] = { id = 20597, name = "Sword Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Human", sourceUrl = "https://octowow.st/db/?spell=20597" },
|
||||
[20598] = { id = 20598, name = "The Human Spirit", rank = "Racial Passive", level = 0, school = 0, race = "Human", sourceUrl = "https://octowow.st/db/?spell=20598" },
|
||||
[20599] = { id = 20599, name = "Diplomacy", rank = "Racial Passive", level = 0, school = 0, race = "Human", sourceUrl = "https://octowow.st/db/?spell=20599" },
|
||||
[20600] = { id = 20600, name = "Perception", rank = "Racial", level = 0, school = 0, race = "Human", sourceUrl = "https://octowow.st/db/?spell=20600" },
|
||||
[20864] = { id = 20864, name = "Mace Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Human", sourceUrl = "https://octowow.st/db/?spell=20864" },
|
||||
[21009] = { id = 21009, name = "Shadowmeld Passive", rank = "Racial Passive", level = 0, school = 0, race = "Night Elf", sourceUrl = "https://octowow.st/db/?spell=21009" },
|
||||
[21563] = { id = 21563, name = "Command", rank = "Racial Passive", level = 0, school = 0, race = "Orc", sourceUrl = "https://octowow.st/db/?spell=21563" },
|
||||
[26290] = { id = 26290, name = "Bow Specialization", rank = "Racial Passive", level = 0, school = 0, race = "Troll", sourceUrl = "https://octowow.st/db/?spell=26290" },
|
||||
[26296] = { id = 26296, name = "Berserking", rank = "Racial", level = 0, school = 0, race = "Troll", sourceUrl = "https://octowow.st/db/?spell=26296" },
|
||||
[26297] = { id = 26297, name = "Berserking", rank = "Racial", level = 0, school = 0, race = "Troll", sourceUrl = "https://octowow.st/db/?spell=26297" },
|
||||
[52522] = { id = 52522, name = "Vengeance", rank = "Racial Passive", level = 0, school = 0, race = "Undead", sourceUrl = "https://octowow.st/db/?spell=52522" },
|
||||
},
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+23493
-23493
File diff suppressed because it is too large
Load Diff
+223
-167
File diff suppressed because it is too large
Load Diff
+219
-219
File diff suppressed because it is too large
Load Diff
@@ -54,22 +54,31 @@ MTH_DS_ZoneNamesFallback = {
|
||||
[1519] = "Stormwind City",
|
||||
[1537] = "Ironforge",
|
||||
[1583] = "Blackrock Spire",
|
||||
[1584] = "Blackrock Depths",
|
||||
[1941] = "Caverns of Time",
|
||||
[1977] = "Zul'Gurub",
|
||||
[2040] = "Alah'Thalas",
|
||||
[2100] = "Maraudon",
|
||||
[2717] = "Molten Core",
|
||||
[2597] = "Alterac Valley",
|
||||
[3456] = "Naxxramas",
|
||||
[3457] = "Tower of Karazhan",
|
||||
[4012] = "Scarlet Enclave",
|
||||
[5023] = "Sunnyglade",
|
||||
[5024] = "Icepoint Rock",
|
||||
[5077] = "Crescent Grove",
|
||||
[5121] = "Tel'Abim",
|
||||
[5130] = "Winter Veil Vale",
|
||||
[5135] = "Scarlet Monastery Library",
|
||||
[5179] = "Gilneas",
|
||||
[5208] = "Gilneas City",
|
||||
[5225] = "Thalassian Highlands",
|
||||
[5536] = "Blackstone Island",
|
||||
[5723] = "Karazhan",
|
||||
[5561] = "Balor",
|
||||
[5581] = "Northwind",
|
||||
[5601] = "Dragonmaw Retreat",
|
||||
[5602] = "Grim Reaches",
|
||||
[5641] = "Windhorn Canyon",
|
||||
[5642] = "Moonwhisper Coast",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
-- MetaHunt: WMA (WorldMapArea.dbc) zone reference data
|
||||
-- AUTO-GENERATED by .tools/migrate_wma.py — do not edit by hand.
|
||||
-- Source: Haxxor WMA CSV (patch-9.mpq)
|
||||
--
|
||||
-- Provides MTH_MAP_PFQ_TO_WMA: pfQ AreaTable ID → WMA ID lookup
|
||||
-- Used by map-markers.lua to translate GetCurrentMapAreaID() results
|
||||
-- and by GetCurrentMapID() to return WMA zone IDs for coord matching.
|
||||
--
|
||||
-- Provides MTH_DS_WMAZones: WMA ID → worldBounds + pfqAreaId
|
||||
-- Used for map overlay features without modifying the pfQ-keyed MTH_DS_Zones.
|
||||
|
||||
if not MTH_DS then MTH_DS = {} end
|
||||
|
||||
-- pfQ AreaTable ID → WMA ID runtime bridge
|
||||
MTH_MAP_PFQ_TO_WMA = {
|
||||
[1] = 27, -- DunMorogh
|
||||
[3] = 17, -- Badlands
|
||||
[4] = 19, -- BlastedLands
|
||||
[8] = 38, -- SwampOfSorrows
|
||||
[10] = 34, -- Duskwood
|
||||
[11] = 40, -- Wetlands
|
||||
[12] = 30, -- Elwynn
|
||||
[14] = 4, -- Durotar
|
||||
[15] = 141, -- Dustwallow
|
||||
[16] = 181, -- Aszhara
|
||||
[17] = 11, -- Barrens
|
||||
[25] = 519, -- BlackrockMountain
|
||||
[28] = 22, -- WesternPlaguelands
|
||||
[33] = 37, -- Stranglethorn
|
||||
[36] = 15, -- Alterac
|
||||
[38] = 35, -- LochModan
|
||||
[40] = 39, -- Westfall
|
||||
[41] = 32, -- DeadwindPass
|
||||
[44] = 36, -- Redridge
|
||||
[45] = 16, -- Arathi
|
||||
[46] = 29, -- BurningSteppes
|
||||
[47] = 26, -- Hinterlands
|
||||
[51] = 28, -- SearingGorge
|
||||
[85] = 20, -- Tirisfal
|
||||
[130] = 21, -- Silverpine
|
||||
[139] = 23, -- EasternPlaguelands
|
||||
[141] = 41, -- Teldrassil
|
||||
[148] = 42, -- Darkshore
|
||||
[209] = 650, -- ShadowfangKeep
|
||||
[215] = 9, -- Mulgore
|
||||
[267] = 24, -- Hilsbrad
|
||||
[331] = 43, -- Ashenvale
|
||||
[357] = 121, -- Feralas
|
||||
[361] = 182, -- Felwood
|
||||
[400] = 61, -- ThousandNeedles
|
||||
[405] = 101, -- Desolace
|
||||
[406] = 81, -- StonetalonMountains
|
||||
[408] = 505, -- Gillijim
|
||||
[409] = 504, -- Lapidis
|
||||
[440] = 161, -- Tanaris
|
||||
[490] = 201, -- UngoroCrater
|
||||
[491] = 641, -- RazorfenKraul
|
||||
[493] = 241, -- Moonglade
|
||||
[616] = 501, -- Hyjal
|
||||
[618] = 281, -- Winterspring
|
||||
[717] = 611, -- TheStockade
|
||||
[718] = 516, -- WailingCavernsEntrance
|
||||
[719] = 609, -- BlackFathomDeeps
|
||||
[721] = 518, -- GnomereganEntrance
|
||||
[722] = 639, -- RazorfenDowns
|
||||
[796] = 520, -- ScarletMonasteryEntrance
|
||||
[876] = 500, -- GMIsland
|
||||
[1176] = 605, -- ZulFarrak
|
||||
[1337] = 521, -- UldamanEntrance
|
||||
[1377] = 261, -- Silithus
|
||||
[1477] = 607, -- TheTempleOfAtalHakkar
|
||||
[1497] = 382, -- Undercity
|
||||
[1519] = 301, -- Stormwind
|
||||
[1537] = 341, -- Ironforge
|
||||
[1581] = 514, -- DeadminesEntrance
|
||||
[1583] = 629, -- BlackrockSpire
|
||||
[1584] = 623, -- BlackrockDepths
|
||||
[1637] = 321, -- Orgrimmar
|
||||
[1638] = 362, -- ThunderBluff
|
||||
[1657] = 381, -- Darnassus
|
||||
[1769] = 700, -- TimbermawTunnels
|
||||
[1941] = 523, -- CavernsOfTime
|
||||
[1977] = 619, -- ZulGurub
|
||||
[2017] = 652, -- Stratholme
|
||||
[2040] = 509, -- AlahThalas
|
||||
[2057] = 648, -- Scholomance
|
||||
[2100] = 517, -- MaraudonEntrance
|
||||
[2159] = 627, -- OnyxiasLair
|
||||
[2257] = 659, -- DeeprunTram
|
||||
[2366] = 663, -- BlackMorass2f
|
||||
[2437] = 603, -- Ragefire
|
||||
[2557] = 689, -- DireMaulEntrance
|
||||
[2597] = 401, -- AlteracValley
|
||||
[2677] = 635, -- BlackwingLair
|
||||
[2717] = 617, -- MoltenCore
|
||||
[3277] = 443, -- WarsongGulch
|
||||
[3358] = 461, -- ArathiBasin
|
||||
[3428] = 654, -- AhnQiraj
|
||||
[3429] = 625, -- RuinsofAhnQiraj
|
||||
[3456] = 667, -- Naxxramas
|
||||
[3457] = 682, -- UpperKarazhan
|
||||
[3478] = 522, -- AhnQirajEntrance
|
||||
[4012] = 502, -- ScarletEnclave
|
||||
[5023] = 503, -- Sunnyglade
|
||||
[5024] = 511, -- Icepoint
|
||||
[5053] = 680, -- Moomoo
|
||||
[5077] = 670, -- CrescentGrove
|
||||
[5086] = 674, -- KarazhanCrypt
|
||||
[5087] = 676, -- StormwindVault
|
||||
[5097] = 678, -- EmeraldSanctum
|
||||
[5103] = 672, -- HateforgeQuarry
|
||||
[5121] = 507, -- TelAbim
|
||||
[5130] = 601, -- WinterVeilVale
|
||||
[5135] = 644, -- ScarletMonastery2f
|
||||
[5136] = 643, -- ScarletMonastery
|
||||
[5138] = 637, -- TheDeadmines
|
||||
[5147] = 655, -- AhnQiraj2f
|
||||
[5148] = 668, -- Naxxramas2f
|
||||
[5153] = 645, -- ScarletMonastery3f
|
||||
[5163] = 646, -- ScarletMonastery4f
|
||||
[5179] = 510, -- Gilneas
|
||||
[5204] = 662, -- BlackMorass
|
||||
[5208] = 665, -- GilneasCity
|
||||
[5225] = 513, -- ThalassianHighlands
|
||||
[5536] = 512, -- BlackstoneIsland
|
||||
[5557] = 683, -- UpperKarazhan2f
|
||||
[5561] = 685, -- Balor
|
||||
[5581] = 686, -- Northwind
|
||||
[5601] = 691, -- DragonmawRetreat
|
||||
[5602] = 684, -- GrimReaches
|
||||
[5628] = 693, -- StormwroughtRuins
|
||||
[5640] = 701, -- TimbermawEntrance
|
||||
[5641] = 698, -- WindhornCanyon
|
||||
[5642] = 699, -- Moonwhisper
|
||||
[5722] = 702, -- ThornGorge
|
||||
[5723] = 657, -- Karazhan
|
||||
[5731] = 705, -- WindhornCanyonEntrance
|
||||
[5734] = 704, -- FrostmaneHollow
|
||||
}
|
||||
|
||||
-- WMA ID → pfQ AreaTable ID (for fallback lookups)
|
||||
MTH_MAP_WMA_TO_PFQ = {
|
||||
[4] = 14, -- Durotar
|
||||
[9] = 215, -- Mulgore
|
||||
[11] = 17, -- Barrens
|
||||
[15] = 36, -- Alterac
|
||||
[16] = 45, -- Arathi
|
||||
[17] = 3, -- Badlands
|
||||
[19] = 4, -- BlastedLands
|
||||
[20] = 85, -- Tirisfal
|
||||
[21] = 130, -- Silverpine
|
||||
[22] = 28, -- WesternPlaguelands
|
||||
[23] = 139, -- EasternPlaguelands
|
||||
[24] = 267, -- Hilsbrad
|
||||
[26] = 47, -- Hinterlands
|
||||
[27] = 1, -- DunMorogh
|
||||
[28] = 51, -- SearingGorge
|
||||
[29] = 46, -- BurningSteppes
|
||||
[30] = 12, -- Elwynn
|
||||
[32] = 41, -- DeadwindPass
|
||||
[34] = 10, -- Duskwood
|
||||
[35] = 38, -- LochModan
|
||||
[36] = 44, -- Redridge
|
||||
[37] = 33, -- Stranglethorn
|
||||
[38] = 8, -- SwampOfSorrows
|
||||
[39] = 40, -- Westfall
|
||||
[40] = 11, -- Wetlands
|
||||
[41] = 141, -- Teldrassil
|
||||
[42] = 148, -- Darkshore
|
||||
[43] = 331, -- Ashenvale
|
||||
[61] = 400, -- ThousandNeedles
|
||||
[81] = 406, -- StonetalonMountains
|
||||
[101] = 405, -- Desolace
|
||||
[121] = 357, -- Feralas
|
||||
[141] = 15, -- Dustwallow
|
||||
[161] = 440, -- Tanaris
|
||||
[181] = 16, -- Aszhara
|
||||
[182] = 361, -- Felwood
|
||||
[201] = 490, -- UngoroCrater
|
||||
[241] = 493, -- Moonglade
|
||||
[261] = 1377, -- Silithus
|
||||
[281] = 618, -- Winterspring
|
||||
[301] = 1519, -- Stormwind
|
||||
[321] = 1637, -- Orgrimmar
|
||||
[341] = 1537, -- Ironforge
|
||||
[362] = 1638, -- ThunderBluff
|
||||
[381] = 1657, -- Darnassus
|
||||
[382] = 1497, -- Undercity
|
||||
[401] = 2597, -- AlteracValley
|
||||
[443] = 3277, -- WarsongGulch
|
||||
[461] = 3358, -- ArathiBasin
|
||||
[500] = 876, -- GMIsland
|
||||
[501] = 616, -- Hyjal
|
||||
[502] = 4012, -- ScarletEnclave
|
||||
[503] = 5023, -- Sunnyglade
|
||||
[504] = 409, -- Lapidis
|
||||
[505] = 408, -- Gillijim
|
||||
[507] = 5121, -- TelAbim
|
||||
[509] = 2040, -- AlahThalas
|
||||
[510] = 5179, -- Gilneas
|
||||
[511] = 5024, -- Icepoint
|
||||
[512] = 5536, -- BlackstoneIsland
|
||||
[513] = 5225, -- ThalassianHighlands
|
||||
[514] = 1581, -- DeadminesEntrance
|
||||
[516] = 718, -- WailingCavernsEntrance
|
||||
[517] = 2100, -- MaraudonEntrance
|
||||
[518] = 721, -- GnomereganEntrance
|
||||
[519] = 25, -- BlackrockMountain
|
||||
[520] = 796, -- ScarletMonasteryEntrance
|
||||
[521] = 1337, -- UldamanEntrance
|
||||
[522] = 3478, -- AhnQirajEntrance
|
||||
[523] = 1941, -- CavernsOfTime
|
||||
[601] = 5130, -- WinterVeilVale
|
||||
[603] = 2437, -- Ragefire
|
||||
[605] = 1176, -- ZulFarrak
|
||||
[607] = 1477, -- TheTempleOfAtalHakkar
|
||||
[609] = 719, -- BlackFathomDeeps
|
||||
[611] = 717, -- TheStockade
|
||||
[615] = 1584, -- BlackrockDepths (interior)
|
||||
[617] = 2717, -- MoltenCore
|
||||
[619] = 1977, -- ZulGurub
|
||||
[623] = 1584, -- BlackrockDepths
|
||||
[625] = 3429, -- RuinsofAhnQiraj
|
||||
[627] = 2159, -- OnyxiasLair
|
||||
[629] = 1583, -- BlackrockSpire
|
||||
[631] = 718, -- WailingCaverns (interior)
|
||||
[633] = 2100, -- Maraudon (interior)
|
||||
[635] = 2677, -- BlackwingLair
|
||||
[637] = 5138, -- TheDeadmines
|
||||
[639] = 722, -- RazorfenDowns
|
||||
[641] = 491, -- RazorfenKraul
|
||||
[643] = 5136, -- ScarletMonastery
|
||||
[644] = 5135, -- ScarletMonastery2f
|
||||
[645] = 5153, -- ScarletMonastery3f
|
||||
[646] = 5163, -- ScarletMonastery4f
|
||||
[648] = 2057, -- Scholomance
|
||||
[650] = 209, -- ShadowfangKeep
|
||||
[652] = 2017, -- Stratholme
|
||||
[654] = 3428, -- AhnQiraj
|
||||
[655] = 5147, -- AhnQiraj2f
|
||||
[657] = 5723, -- Karazhan
|
||||
[659] = 2257, -- DeeprunTram
|
||||
[662] = 5204, -- BlackMorass
|
||||
[663] = 2366, -- BlackMorass2f
|
||||
[665] = 5208, -- GilneasCity
|
||||
[667] = 3456, -- Naxxramas
|
||||
[668] = 5148, -- Naxxramas2f
|
||||
[670] = 5077, -- CrescentGrove
|
||||
[672] = 5103, -- HateforgeQuarry
|
||||
[674] = 5086, -- KarazhanCrypt
|
||||
[676] = 5087, -- StormwindVault
|
||||
[678] = 5097, -- EmeraldSanctum
|
||||
[680] = 5053, -- Moomoo
|
||||
[682] = 3457, -- UpperKarazhan
|
||||
[683] = 5557, -- UpperKarazhan2f
|
||||
[684] = 5602, -- GrimReaches
|
||||
[685] = 5561, -- Balor
|
||||
[686] = 5581, -- Northwind
|
||||
[689] = 2557, -- DireMaulEntrance
|
||||
[691] = 5601, -- DragonmawRetreat
|
||||
[693] = 5628, -- StormwroughtRuins
|
||||
[698] = 5641, -- WindhornCanyon
|
||||
[699] = 5642, -- Moonwhisper
|
||||
[700] = 1769, -- TimbermawTunnels
|
||||
[701] = 5640, -- TimbermawEntrance
|
||||
[702] = 5722, -- ThornGorge
|
||||
[704] = 5734, -- FrostmaneHollow
|
||||
[705] = 5731, -- WindhornCanyonEntrance
|
||||
}
|
||||
|
||||
-- WMA ID → worldBounds + pfqAreaId (for map overlay and minimap calibration)
|
||||
MTH_DS_WMAZones = {
|
||||
[4] = { pfqAreaId=14, mapId=1, left=-1962.50, right=-7250.00, top=1808.33, bottom=-1716.67, width=5287.50, height=3525.00 }, -- Durotar
|
||||
[9] = { pfqAreaId=215, mapId=1, left=2047.92, right=-3089.58, top=-272.92, bottom=-3697.92, width=5137.50, height=3425.00 }, -- Mulgore
|
||||
[11] = { pfqAreaId=17, mapId=1, left=2622.92, right=-7510.42, top=1612.50, bottom=-5143.75, width=10133.34, height=6756.25 }, -- Barrens
|
||||
[13] = { pfqAreaId=0, mapId=1, left=17066.60, right=-19733.21, top=12799.90, bottom=-11733.30, width=36799.81, height=24533.20 }, -- Kalimdor
|
||||
[14] = { pfqAreaId=0, mapId=0, left=16000.00, right=-19199.90, top=7466.60, bottom=-16000.00, width=35199.90, height=23466.60 }, -- Azeroth
|
||||
[15] = { pfqAreaId=36, mapId=0, left=783.33, right=-2016.67, top=1500.00, bottom=-366.67, width=2800.00, height=1866.67 }, -- Alterac
|
||||
[16] = { pfqAreaId=45, mapId=0, left=-866.67, right=-4466.67, top=-133.33, bottom=-2533.33, width=3600.00, height=2400.00 }, -- Arathi
|
||||
[17] = { pfqAreaId=3, mapId=0, left=-2079.17, right=-4566.67, top=-5889.58, bottom=-7547.92, width=2487.50, height=1658.34 }, -- Badlands
|
||||
[19] = { pfqAreaId=4, mapId=0, left=-1241.67, right=-4591.67, top=-10566.67, bottom=-12800.00, width=3350.00, height=2233.33 }, -- BlastedLands
|
||||
[20] = { pfqAreaId=85, mapId=0, left=3033.33, right=-1485.42, top=3837.50, bottom=825.00, width=4518.75, height=3012.50 }, -- Tirisfal
|
||||
[21] = { pfqAreaId=130, mapId=0, left=3450.00, right=-750.00, top=1666.67, bottom=-1133.33, width=4200.00, height=2800.00 }, -- Silverpine
|
||||
[22] = { pfqAreaId=28, mapId=0, left=416.67, right=-3883.33, top=3366.67, bottom=500.00, width=4300.00, height=2866.67 }, -- WesternPlaguelands
|
||||
[23] = { pfqAreaId=139, mapId=0, left=-2185.42, right=-6056.25, top=3800.00, bottom=1218.75, width=3870.83, height=2581.25 }, -- EasternPlaguelands
|
||||
[24] = { pfqAreaId=267, mapId=0, left=1066.67, right=-2133.33, top=400.00, bottom=-1733.33, width=3200.00, height=2133.33 }, -- Hilsbrad
|
||||
[26] = { pfqAreaId=47, mapId=0, left=-1575.00, right=-5425.00, top=1466.67, bottom=-1100.00, width=3850.00, height=2566.67 }, -- Hinterlands
|
||||
[27] = { pfqAreaId=1, mapId=0, left=1802.08, right=-3122.92, top=-3877.08, bottom=-7160.42, width=4925.00, height=3283.34 }, -- DunMorogh
|
||||
[28] = { pfqAreaId=51, mapId=0, left=-322.92, right=-2554.17, top=-6100.00, bottom=-7587.50, width=2231.25, height=1487.50 }, -- SearingGorge
|
||||
[29] = { pfqAreaId=46, mapId=0, left=-266.67, right=-3195.83, top=-7031.25, bottom=-8983.33, width=2929.16, height=1952.08 }, -- BurningSteppes
|
||||
[30] = { pfqAreaId=12, mapId=0, left=1535.42, right=-1935.42, top=-7939.58, bottom=-10254.17, width=3470.84, height=2314.59 }, -- Elwynn
|
||||
[32] = { pfqAreaId=41, mapId=0, left=-833.33, right=-3333.33, top=-9866.67, bottom=-11533.33, width=2500.00, height=1666.66 }, -- DeadwindPass
|
||||
[34] = { pfqAreaId=10, mapId=0, left=833.33, right=-1866.67, top=-9716.67, bottom=-11516.67, width=2700.00, height=1800.00 }, -- Duskwood
|
||||
[35] = { pfqAreaId=38, mapId=0, left=-1993.75, right=-4752.08, top=-4487.50, bottom=-6327.08, width=2758.33, height=1839.58 }, -- LochModan
|
||||
[36] = { pfqAreaId=44, mapId=0, left=-1570.83, right=-3741.67, top=-8575.00, bottom=-10022.92, width=2170.84, height=1447.92 }, -- Redridge
|
||||
[37] = { pfqAreaId=33, mapId=0, left=2220.83, right=-4160.42, top=-11168.75, bottom=-15422.92, width=6381.25, height=4254.17 }, -- Stranglethorn
|
||||
[38] = { pfqAreaId=8, mapId=0, left=-2222.92, right=-4516.67, top=-9620.83, bottom=-11150.00, width=2293.75, height=1529.17 }, -- SwampOfSorrows
|
||||
[39] = { pfqAreaId=40, mapId=0, left=3016.67, right=-483.33, top=-9400.00, bottom=-11733.33, width=3500.00, height=2333.33 }, -- Westfall
|
||||
[40] = { pfqAreaId=11, mapId=0, left=-389.58, right=-4525.00, top=-2147.92, bottom=-4904.17, width=4135.42, height=2756.25 }, -- Wetlands
|
||||
[41] = { pfqAreaId=141, mapId=1, left=3814.58, right=-1277.08, top=11831.25, bottom=8437.50, width=5091.66, height=3393.75 }, -- Teldrassil
|
||||
[42] = { pfqAreaId=148, mapId=1, left=2941.67, right=-3608.33, top=8333.33, bottom=3966.67, width=6550.00, height=4366.66 }, -- Darkshore
|
||||
[43] = { pfqAreaId=331, mapId=1, left=1700.00, right=-4066.67, top=4672.92, bottom=829.17, width=5766.67, height=3843.75 }, -- Ashenvale
|
||||
[61] = { pfqAreaId=400, mapId=1, left=-433.33, right=-4833.33, top=-3966.67, bottom=-6900.00, width=4400.00, height=2933.33 }, -- ThousandNeedles
|
||||
[81] = { pfqAreaId=406, mapId=1, left=3245.83, right=-1637.50, top=2916.67, bottom=-339.58, width=4883.33, height=3256.25 }, -- StonetalonMountains
|
||||
[101] = { pfqAreaId=405, mapId=1, left=4233.33, right=-262.50, top=452.08, bottom=-2545.83, width=4495.83, height=2997.91 }, -- Desolace
|
||||
[121] = { pfqAreaId=357, mapId=1, left=5441.67, right=-1508.33, top=-2366.67, bottom=-7000.00, width=6950.00, height=4633.33 }, -- Feralas
|
||||
[141] = { pfqAreaId=15, mapId=1, left=-975.00, right=-6225.00, top=-2033.33, bottom=-5533.33, width=5250.00, height=3500.00 }, -- Dustwallow
|
||||
[161] = { pfqAreaId=440, mapId=1, left=-218.75, right=-7118.75, top=-5875.00, bottom=-10475.00, width=6900.00, height=4600.00 }, -- Tanaris
|
||||
[181] = { pfqAreaId=16, mapId=1, left=-3277.08, right=-8347.92, top=5341.67, bottom=1960.42, width=5070.84, height=3381.25 }, -- Aszhara
|
||||
[182] = { pfqAreaId=361, mapId=1, left=1641.67, right=-4108.33, top=7133.33, bottom=3300.00, width=5750.00, height=3833.33 }, -- Felwood
|
||||
[201] = { pfqAreaId=490, mapId=1, left=533.33, right=-3166.67, top=-5966.67, bottom=-8433.33, width=3700.00, height=2466.66 }, -- UngoroCrater
|
||||
[241] = { pfqAreaId=493, mapId=1, left=-1381.25, right=-3689.58, top=8491.67, bottom=6952.08, width=2308.33, height=1539.59 }, -- Moonglade
|
||||
[261] = { pfqAreaId=1377, mapId=1, left=2537.50, right=-945.83, top=-5958.33, bottom=-8281.25, width=3483.33, height=2322.92 }, -- Silithus
|
||||
[281] = { pfqAreaId=618, mapId=1, left=-316.67, right=-7416.67, top=8533.33, bottom=3800.00, width=7100.00, height=4733.33 }, -- Winterspring
|
||||
[301] = { pfqAreaId=1519, mapId=0, left=1722.92, right=-14.58, top=-7995.83, bottom=-9154.17, width=1737.50, height=1158.34 }, -- Stormwind
|
||||
[321] = { pfqAreaId=1637, mapId=1, left=-3680.60, right=-5083.21, top=2273.88, bottom=1338.46, width=1402.61, height=935.42 }, -- Orgrimmar
|
||||
[341] = { pfqAreaId=1537, mapId=0, left=-713.59, right=-1504.22, top=-4569.24, bottom=-5096.85, width=790.63, height=527.61 }, -- Ironforge
|
||||
[362] = { pfqAreaId=1638, mapId=1, left=516.67, right=-527.08, top=-850.00, bottom=-1545.83, width=1043.75, height=695.83 }, -- ThunderBluff
|
||||
[381] = { pfqAreaId=1657, mapId=1, left=2938.36, right=1880.03, top=10238.32, bottom=9532.59, width=1058.33, height=705.73 }, -- Darnassus
|
||||
[382] = { pfqAreaId=1497, mapId=0, left=873.19, right=-86.18, top=1877.95, bottom=1237.84, width=959.37, height=640.11 }, -- Undercity
|
||||
[401] = { pfqAreaId=2597, mapId=30, left=1781.25, right=-2456.25, top=1085.42, bottom=-1739.58, width=4237.50, height=2825.00 }, -- AlteracValley
|
||||
[443] = { pfqAreaId=3277, mapId=489, left=2041.67, right=895.83, top=1627.08, bottom=862.50, width=1145.84, height=764.58 }, -- WarsongGulch
|
||||
[461] = { pfqAreaId=3358, mapId=529, left=1858.33, right=102.08, top=1508.33, bottom=337.50, width=1756.25, height=1170.83 }, -- ArathiBasin
|
||||
[500] = { pfqAreaId=876, mapId=1, left=16748.00, right=15920.00, top=16525.00, bottom=15984.00, width=828.00, height=541.00 }, -- GMIsland
|
||||
[501] = { pfqAreaId=616, mapId=1, left=-1080.00, right=-4286.00, top=6125.00, bottom=3983.00, width=3206.00, height=2142.00 }, -- Hyjal
|
||||
[502] = { pfqAreaId=4012, mapId=0, left=-4050.00, right=-7209.00, top=3087.00, bottom=979.00, width=3159.00, height=2108.00 }, -- ScarletEnclave
|
||||
[503] = { pfqAreaId=5023, mapId=27, left=1013.00, right=-435.00, top=1823.00, bottom=858.00, width=1448.00, height=965.00 }, -- Sunnyglade
|
||||
[504] = { pfqAreaId=409, mapId=0, left=4933.33, right=2031.88, top=-11043.48, bottom=-12959.42, width=2901.45, height=1915.94 }, -- Lapidis
|
||||
[505] = { pfqAreaId=408, mapId=0, left=4436.22, right=1344.12, top=-12528.71, bottom=-14575.72, width=3092.10, height=2047.01 }, -- Gillijim
|
||||
[507] = { pfqAreaId=5121, mapId=1, left=-5179.00, right=-8406.00, top=-7184.00, bottom=-9371.00, width=3227.00, height=2187.00 }, -- TelAbim
|
||||
[509] = { pfqAreaId=2040, mapId=0, left=-2169.00, right=-3637.00, top=4907.00, bottom=3931.00, width=1468.00, height=976.00 }, -- AlahThalas
|
||||
[510] = { pfqAreaId=5179, mapId=0, left=3253.00, right=-413.00, top=-359.00, bottom=-2801.00, width=3666.00, height=2442.00 }, -- Gilneas
|
||||
[511] = { pfqAreaId=5024, mapId=1, left=-5595.00, right=-7203.00, top=14405.00, bottom=13330.00, width=1608.00, height=1075.00 }, -- Icepoint
|
||||
[512] = { pfqAreaId=5536, mapId=1, left=-6274.00, right=-8746.00, top=799.00, bottom=-866.00, width=2472.00, height=1665.00 }, -- BlackstoneIsland
|
||||
[513] = { pfqAreaId=5225, mapId=0, left=-1005.00, right=-4087.00, top=4952.00, bottom=2891.00, width=3082.00, height=2061.00 }, -- ThalassianHighlands
|
||||
[514] = { pfqAreaId=1581, mapId=0, left=1793.18, right=1343.29, top=-11054.94, bottom=-11354.86, width=449.89, height=299.92 }, -- DeadminesEntrance
|
||||
[516] = { pfqAreaId=718, mapId=1, left=-1900.87, right=-2473.65, top=-488.64, bottom=-870.49, width=572.78, height=381.85 }, -- WailingCavernsEntrance
|
||||
[517] = { pfqAreaId=2100, mapId=1, left=3208.00, right=2384.00, top=-1096.00, bottom=-1646.00, width=824.00, height=550.00 }, -- MaraudonEntrance
|
||||
[518] = { pfqAreaId=721, mapId=0, left=1027.64, right=456.45, top=-4806.32, bottom=-5185.46, width=571.19, height=379.14 }, -- GnomereganEntrance
|
||||
[519] = { pfqAreaId=25, mapId=0, left=-760.94, right=-1472.50, top=-7327.82, bottom=-7796.50, width=711.56, height=468.68 }, -- BlackrockMountain
|
||||
[520] = { pfqAreaId=796, mapId=0, left=-659.96, right=-863.62, top=2947.37, bottom=2812.33, width=203.66, height=135.04 }, -- ScarletMonasteryEntrance
|
||||
[521] = { pfqAreaId=1337, mapId=0, left=-2747.15, right=-3310.46, top=-5953.41, bottom=-6329.51, width=563.31, height=376.10 }, -- UldamanEntrance
|
||||
[522] = { pfqAreaId=3478, mapId=1, left=3932.67, right=-206.35, top=-8005.20, bottom=-10951.25, width=4139.02, height=2946.05 }, -- AhnQirajEntrance
|
||||
[523] = { pfqAreaId=1941, mapId=1, left=-3695.85, right=-5044.09, top=-8023.09, bottom=-8911.18, width=1348.24, height=888.09 }, -- CavernsOfTime
|
||||
[601] = { pfqAreaId=5130, mapId=813, left=1916.00, right=484.00, top=-2332.00, bottom=-3309.00, width=1432.00, height=977.00 }, -- WinterVeilVale
|
||||
[603] = { pfqAreaId=2437, mapId=389, left=452.87, right=-285.99, top=39.62, bottom=-452.95, width=738.86, height=492.57 }, -- Ragefire
|
||||
[605] = { pfqAreaId=1176, mapId=209, left=1625.00, right=241.67, top=2052.08, bottom=1129.17, width=1383.33, height=922.91 }, -- ZulFarrak
|
||||
[607] = { pfqAreaId=1477, mapId=109, left=442.76, right=-252.27, top=-255.17, bottom=-718.52, width=695.03, height=463.35 }, -- TheTempleOfAtalHakkar
|
||||
[609] = { pfqAreaId=719, mapId=48, left=485.41, right=-736.46, top=-107.91, bottom=-914.33, width=1221.87, height=806.42 }, -- BlackFathomDeeps
|
||||
[611] = { pfqAreaId=717, mapId=34, left=189.82, right=-188.33, top=220.63, bottom=-31.47, width=378.15, height=252.10 }, -- TheStockade
|
||||
[613] = { pfqAreaId=721, mapId=90, left=863.00, right=-262.00, top=-245.00, bottom=-985.00, width=1125.00, height=740.00 }, -- Gnomeregan
|
||||
[615] = { pfqAreaId=1337, mapId=70, left=645.12, right=-248.55, top=204.31, bottom=-391.47, width=893.67, height=595.78 }, -- Uldaman
|
||||
[617] = { pfqAreaId=2717, mapId=409, left=-11.00, right=-1506.00, top=1439.00, bottom=456.00, width=1495.00, height=983.00 }, -- MoltenCore
|
||||
[619] = { pfqAreaId=1977, mapId=309, left=-612.50, right=-2733.33, top=-11225.00, bottom=-12639.58, width=2120.83, height=1414.58 }, -- ZulGurub
|
||||
[621] = { pfqAreaId=2557, mapId=429, left=1007.00, right=-912.00, top=955.00, bottom=-295.00, width=1919.00, height=1250.00 }, -- DireMaul
|
||||
[623] = { pfqAreaId=1584, mapId=230, left=875.00, right=-1110.00, top=1513.00, bottom=245.00, width=1985.00, height=1268.00 }, -- BlackrockDepths
|
||||
[625] = { pfqAreaId=3429, mapId=509, left=3035.42, right=522.92, top=-8233.33, bottom=-9908.33, width=2512.50, height=1675.00 }, -- RuinsofAhnQiraj
|
||||
[627] = { pfqAreaId=2159, mapId=249, left=155.00, right=-366.00, top=86.00, bottom=-262.00, width=521.00, height=348.00 }, -- OnyxiasLair
|
||||
[629] = { pfqAreaId=1583, mapId=229, left=10.59, right=-876.25, top=304.40, bottom=-286.83, width=886.84, height=591.23 }, -- BlackrockSpire
|
||||
[631] = { pfqAreaId=718, mapId=43, left=790.00, right=-380.00, top=215.00, bottom=-570.00, width=1170.00, height=785.00 }, -- WailingCaverns
|
||||
[633] = { pfqAreaId=2100, mapId=349, left=485.41, right=-1626.68, top=1199.76, bottom=-211.13, width=2112.09, height=1410.89 }, -- Maraudon
|
||||
[635] = { pfqAreaId=2677, mapId=469, left=-645.00, right=-1422.00, top=-7255.00, bottom=-7760.00, width=777.00, height=505.00 }, -- BlackwingLair
|
||||
[637] = { pfqAreaId=5138, mapId=36, left=-322.94, right=-979.53, top=36.93, bottom=-398.04, width=656.59, height=434.97 }, -- TheDeadmines
|
||||
[639] = { pfqAreaId=722, mapId=129, left=1271.00, right=390.00, top=2683.00, bottom=2094.00, width=881.00, height=589.00 }, -- RazorfenDowns
|
||||
[641] = { pfqAreaId=491, mapId=47, left=2058.92, right=1322.47, top=2349.64, bottom=1858.68, width=736.45, height=490.96 }, -- RazorfenKraul
|
||||
[643] = { pfqAreaId=5136, mapId=189, left=1567.97, right=947.99, top=2030.18, bottom=1616.86, width=619.98, height=413.32 }, -- ScarletMonastery
|
||||
[644] = { pfqAreaId=5135, mapId=189, left=-162.27, right=-482.46, top=307.37, bottom=93.91, width=320.19, height=213.46 }, -- ScarletMonastery2f
|
||||
[645] = { pfqAreaId=5153, mapId=189, left=50.27, right=-562.42, top=2009.10, bottom=1600.64, width=612.69, height=408.46 }, -- ScarletMonastery3f
|
||||
[646] = { pfqAreaId=5163, mapId=189, left=1743.99, right=1040.69, top=1281.29, bottom=812.42, width=703.30, height=468.87 }, -- ScarletMonastery4f
|
||||
[648] = { pfqAreaId=2057, mapId=289, left=251.41, right=-68.64, top=317.70, bottom=104.33, width=320.05, height=213.37 }, -- Scholomance
|
||||
[650] = { pfqAreaId=209, mapId=33, left=2436.00, right=2055.00, top=-61.00, bottom=-315.00, width=381.00, height=254.00 }, -- ShadowfangKeep
|
||||
[652] = { pfqAreaId=2017, mapId=329, left=-2639.83, right=-3825.18, top=4163.90, bottom=3374.04, width=1185.35, height=789.86 }, -- Stratholme
|
||||
[654] = { pfqAreaId=3428, mapId=531, left=2515.63, right=1538.07, top=-8051.80, bottom=-8703.50, width=977.56, height=651.70 }, -- AhnQiraj
|
||||
[655] = { pfqAreaId=5147, mapId=531, left=2915.62, right=138.08, top=-7659.29, bottom=-9510.98, width=2777.54, height=1851.69 }, -- AhnQiraj2f
|
||||
[657] = { pfqAreaId=5723, mapId=532, left=-1546.00, right=-2165.00, top=-10789.00, bottom=-11199.00, width=619.00, height=410.00 }, -- Karazhan
|
||||
[659] = { pfqAreaId=2257, mapId=369, left=2623.50, right=2311.50, top=95.50, bottom=-112.50, width=312.00, height=208.00 }, -- DeeprunTram
|
||||
[662] = { pfqAreaId=5204, mapId=269, left=7338.47, right=6066.48, top=-1247.55, bottom=-2093.03, width=1271.99, height=845.48 }, -- BlackMorass
|
||||
[663] = { pfqAreaId=2366, mapId=269, left=7651.85, right=6565.99, top=-1500.47, bottom=-2227.08, width=1085.86, height=726.61 }, -- BlackMorass2f
|
||||
[665] = { pfqAreaId=5208, mapId=815, left=3290.32, right=2040.14, top=-760.72, bottom=-1598.16, width=1250.18, height=837.44 }, -- GilneasCity
|
||||
[667] = { pfqAreaId=3456, mapId=533, left=-2375.27, right=-4366.96, top=3656.70, bottom=2338.28, width=1991.69, height=1318.42 }, -- Naxxramas
|
||||
[668] = { pfqAreaId=5148, mapId=533, left=-4869.36, right=-5521.46, top=3816.56, bottom=3376.89, width=652.10, height=439.67 }, -- Naxxramas2f
|
||||
[670] = { pfqAreaId=5077, mapId=802, left=1207.72, right=-1435.49, top=882.98, bottom=-868.18, width=2643.21, height=1751.16 }, -- CrescentGrove
|
||||
[672] = { pfqAreaId=5103, mapId=808, left=-3024.33, right=-3776.45, top=-7888.28, bottom=-8398.61, width=752.12, height=510.33 }, -- HateforgeQuarry
|
||||
[674] = { pfqAreaId=5086, mapId=800, left=-1302.51, right=-1849.26, top=-10916.17, bottom=-11308.14, width=546.75, height=391.97 }, -- KarazhanCrypt
|
||||
[676] = { pfqAreaId=5087, mapId=35, left=314.34, right=-40.16, top=89.30, bottom=-145.44, width=354.50, height=234.74 }, -- StormwindVault
|
||||
[678] = { pfqAreaId=5097, mapId=807, left=3939.10, right=2666.00, top=3519.72, bottom=2666.00, width=1273.10, height=853.72 }, -- EmeraldSanctum
|
||||
[680] = { pfqAreaId=5053, mapId=801, left=17296.30, right=16288.62, top=17158.52, bottom=16486.73, width=1007.68, height=671.79 }, -- Moomoo
|
||||
[682] = { pfqAreaId=3457, mapId=814, left=-1378.00, right=-2201.00, top=-10887.00, bottom=-11436.00, width=823.00, height=549.00 }, -- UpperKarazhan
|
||||
[683] = { pfqAreaId=5557, mapId=814, left=-1483.00, right=-4283.00, top=-5800.00, bottom=-7668.00, width=2800.00, height=1868.00 }, -- UpperKarazhan2f
|
||||
[684] = { pfqAreaId=5602, mapId=0, left=-2072.00, right=-7459.00, top=-2904.00, bottom=-6488.00, width=5387.00, height=3584.00 }, -- GrimReaches
|
||||
[685] = { pfqAreaId=5561, mapId=0, left=4728.00, right=1630.00, top=-7577.00, bottom=-9645.00, width=3098.00, height=2068.00 }, -- Balor
|
||||
[686] = { pfqAreaId=5581, mapId=0, left=2258.00, right=-983.00, top=-6327.00, bottom=-8484.00, width=3241.00, height=2157.00 }, -- Northwind
|
||||
[689] = { pfqAreaId=2557, mapId=1, left=2014.00, right=690.00, top=-3401.00, bottom=-4270.00, width=1324.00, height=869.00 }, -- DireMaulEntrance
|
||||
[691] = { pfqAreaId=5601, mapId=816, left=-3428.00, right=-5062.00, top=-5599.00, bottom=-6672.00, width=1634.00, height=1073.00 }, -- DragonmawRetreat
|
||||
[693] = { pfqAreaId=5628, mapId=818, left=-3365.00, right=-5345.00, top=-5424.00, bottom=-6774.00, width=1980.00, height=1350.00 }, -- StormwroughtRuins
|
||||
[694] = { pfqAreaId=0, mapId=0, left=0.00, right=0.00, top=0.00, bottom=0.00, width=0.00, height=0.00 }, -- World
|
||||
[696] = { pfqAreaId=5640, mapId=819, left=-2991.00, right=-4416.00, top=-7195.00, bottom=-8157.00, width=1425.00, height=962.00 }, -- Timbermaw
|
||||
[698] = { pfqAreaId=5641, mapId=820, left=-3184.00, right=-4418.00, top=-7163.00, bottom=-7992.00, width=1234.00, height=829.00 }, -- WindhornCanyon
|
||||
[699] = { pfqAreaId=5642, mapId=1, left=-1354.00, right=-9210.00, top=10204.00, bottom=4963.00, width=7856.00, height=5241.00 }, -- Moonwhisper
|
||||
[700] = { pfqAreaId=1769, mapId=1, left=-1862.00, right=-2723.00, top=7395.00, bottom=6788.00, width=861.00, height=607.00 }, -- TimbermawTunnels
|
||||
[701] = { pfqAreaId=5640, mapId=1, left=-4646.00, right=-5993.00, top=5175.00, bottom=4204.00, width=1347.00, height=971.00 }, -- TimbermawEntrance
|
||||
[702] = { pfqAreaId=5722, mapId=821, left=2105.00, right=1058.00, top=2522.00, bottom=1828.00, width=1047.00, height=694.00 }, -- ThornGorge
|
||||
[704] = { pfqAreaId=5734, mapId=822, left=-3157.00, right=-4168.00, top=-7453.00, bottom=-8106.00, width=1011.00, height=653.00 }, -- FrostmaneHollow
|
||||
[705] = { pfqAreaId=5731, mapId=1, left=-3231.00, right=-3671.00, top=-5053.00, bottom=-5332.00, width=440.00, height=279.00 }, -- WindhornCanyonEntrance
|
||||
}
|
||||
-1052
File diff suppressed because it is too large
Load Diff
+161
-16
@@ -26,6 +26,90 @@ end
|
||||
|
||||
MTH_DS_MarkNotInGameRangedWeapons()
|
||||
|
||||
local function MTH_DS_ApplyCapturedProjectileQualities()
|
||||
if type(MTH_DS_AmmoItems) ~= "table" then return end
|
||||
|
||||
local capturedQualityByItemId = {
|
||||
[2512] = 1,
|
||||
[2515] = 1,
|
||||
[2516] = 1,
|
||||
[2519] = 1,
|
||||
[3030] = 1,
|
||||
[3033] = 1,
|
||||
[3464] = 2,
|
||||
[3465] = 2,
|
||||
[4960] = 1,
|
||||
[5568] = 1,
|
||||
[8067] = 1,
|
||||
[8068] = 1,
|
||||
[8069] = 1,
|
||||
[9399] = 2,
|
||||
[10512] = 2,
|
||||
[10513] = 2,
|
||||
[10579] = 3,
|
||||
[11284] = 1,
|
||||
[11285] = 1,
|
||||
[11630] = 3,
|
||||
[12654] = 3,
|
||||
[13377] = 3,
|
||||
[15997] = 2,
|
||||
[18042] = 2,
|
||||
[19316] = 2,
|
||||
[19317] = 2,
|
||||
[42198] = 2,
|
||||
[42199] = 2,
|
||||
[42200] = 2,
|
||||
[42201] = 3,
|
||||
[42202] = 3,
|
||||
}
|
||||
|
||||
for itemId, quality in pairs(capturedQualityByItemId) do
|
||||
local row = MTH_DS_AmmoItems[itemId]
|
||||
if type(row) == "table" then
|
||||
row.quality = quality
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
MTH_DS_ApplyCapturedProjectileQualities()
|
||||
|
||||
local function MTH_DS_ApplyCapturedAmmoBagQualities()
|
||||
local bagItems = MTH_DS_BagItems or MTH_DS_Bags
|
||||
if type(bagItems) ~= "table" then return end
|
||||
|
||||
local capturedQualityByItemId = {
|
||||
[2101] = 1,
|
||||
[2102] = 1,
|
||||
[3573] = 1,
|
||||
[3574] = 1,
|
||||
[3604] = 2,
|
||||
[3605] = 2,
|
||||
[5439] = 1,
|
||||
[5441] = 1,
|
||||
[7278] = 1,
|
||||
[7279] = 1,
|
||||
[7371] = 2,
|
||||
[7372] = 2,
|
||||
[8217] = 2,
|
||||
[8218] = 2,
|
||||
[11362] = 1,
|
||||
[11363] = 1,
|
||||
[18714] = 4,
|
||||
[19319] = 3,
|
||||
[19320] = 3,
|
||||
[61549] = 3,
|
||||
}
|
||||
|
||||
for itemId, quality in pairs(capturedQualityByItemId) do
|
||||
local row = bagItems[itemId]
|
||||
if type(row) == "table" then
|
||||
row.quality = quality
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
MTH_DS_ApplyCapturedAmmoBagQualities()
|
||||
|
||||
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
|
||||
@@ -128,7 +212,7 @@ local function MTH_DS_EnsurePetSpellsTrainerGrowl()
|
||||
rank = src.rank,
|
||||
rankNumber = src.rankNumber,
|
||||
school = "Physical",
|
||||
sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id),
|
||||
sourceUrl = "https://octowow.st/db/?spell=" .. tostring(src.id),
|
||||
trainLevel = src.trainLevel,
|
||||
}
|
||||
table.insert(growlSpells, row)
|
||||
@@ -178,7 +262,7 @@ local function MTH_DS_EnsurePetSpellsTrainerGrowl()
|
||||
rank = src.rank,
|
||||
rankNumber = src.rankNumber,
|
||||
school = "Physical",
|
||||
sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id),
|
||||
sourceUrl = "https://octowow.st/db/?spell=" .. tostring(src.id),
|
||||
trainLevel = src.trainLevel,
|
||||
}
|
||||
table.insert(staminaSpells, row)
|
||||
@@ -228,7 +312,7 @@ local function MTH_DS_EnsurePetSpellsTrainerGrowl()
|
||||
rank = src.rank,
|
||||
rankNumber = src.rankNumber,
|
||||
school = "Physical",
|
||||
sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id),
|
||||
sourceUrl = "https://octowow.st/db/?spell=" .. tostring(src.id),
|
||||
trainLevel = src.trainLevel,
|
||||
}
|
||||
table.insert(armorSpells, row)
|
||||
@@ -272,7 +356,7 @@ local function MTH_DS_EnsurePetSpellsTrainerGrowl()
|
||||
rank = src.rank,
|
||||
rankNumber = src.rankNumber,
|
||||
school = "Physical",
|
||||
sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id),
|
||||
sourceUrl = "https://octowow.st/db/?spell=" .. tostring(src.id),
|
||||
trainLevel = src.trainLevel,
|
||||
}
|
||||
table.insert(fireResSpells, row)
|
||||
@@ -316,7 +400,7 @@ local function MTH_DS_EnsurePetSpellsTrainerGrowl()
|
||||
rank = src.rank,
|
||||
rankNumber = src.rankNumber,
|
||||
school = "Physical",
|
||||
sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id),
|
||||
sourceUrl = "https://octowow.st/db/?spell=" .. tostring(src.id),
|
||||
trainLevel = src.trainLevel,
|
||||
}
|
||||
table.insert(frostResSpells, row)
|
||||
@@ -360,7 +444,7 @@ local function MTH_DS_EnsurePetSpellsTrainerGrowl()
|
||||
rank = src.rank,
|
||||
rankNumber = src.rankNumber,
|
||||
school = "Physical",
|
||||
sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id),
|
||||
sourceUrl = "https://octowow.st/db/?spell=" .. tostring(src.id),
|
||||
trainLevel = src.trainLevel,
|
||||
}
|
||||
table.insert(arcaneResSpells, row)
|
||||
@@ -404,7 +488,7 @@ local function MTH_DS_EnsurePetSpellsTrainerGrowl()
|
||||
rank = src.rank,
|
||||
rankNumber = src.rankNumber,
|
||||
school = "Physical",
|
||||
sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id),
|
||||
sourceUrl = "https://octowow.st/db/?spell=" .. tostring(src.id),
|
||||
trainLevel = src.trainLevel,
|
||||
}
|
||||
table.insert(natureResSpells, row)
|
||||
@@ -448,7 +532,7 @@ local function MTH_DS_EnsurePetSpellsTrainerGrowl()
|
||||
rank = src.rank,
|
||||
rankNumber = src.rankNumber,
|
||||
school = "Physical",
|
||||
sourceUrl = "https://database.turtlecraft.gg/?spell=" .. tostring(src.id),
|
||||
sourceUrl = "https://octowow.st/db/?spell=" .. tostring(src.id),
|
||||
trainLevel = src.trainLevel,
|
||||
}
|
||||
table.insert(shadowResSpells, row)
|
||||
@@ -472,56 +556,56 @@ local function MTH_DS_EnsurePetSpellsTrainerGrowl()
|
||||
|
||||
MTH_DS_PetSpells.byAbility["Growl"] = {
|
||||
ability = "Growl",
|
||||
searchUrl = "https://database.turtlecraft.gg/?search=Growl",
|
||||
searchUrl = "https://octowow.st/db/?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",
|
||||
searchUrl = "https://octowow.st/db/?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",
|
||||
searchUrl = "https://octowow.st/db/?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",
|
||||
searchUrl = "https://octowow.st/db/?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",
|
||||
searchUrl = "https://octowow.st/db/?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",
|
||||
searchUrl = "https://octowow.st/db/?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",
|
||||
searchUrl = "https://octowow.st/db/?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",
|
||||
searchUrl = "https://octowow.st/db/?search=Shadow+Resistance",
|
||||
spellCount = table.getn(shadowResSpells),
|
||||
spells = shadowResSpells,
|
||||
}
|
||||
@@ -529,6 +613,66 @@ local function MTH_DS_EnsurePetSpellsTrainerGrowl()
|
||||
MTH_DS_PetSpells.totalSpellRows = table.getn(MTH_DS_PetSpells.allSpells)
|
||||
end
|
||||
|
||||
-- Restore the "unique appearance" flag on beasts.
|
||||
-- This is a curated allowlist of beasts whose look is genuinely one-of-a-kind
|
||||
-- (named individuals / distinctive rares that a collector prizes). We do NOT derive
|
||||
-- this from the skin datastore's unique flag: that flag only means "this texture
|
||||
-- appears once in the DBC", which over-marks whole color-variant families (e.g. every
|
||||
-- Moth and Fox colour) and would wrongly flag common mobs like Frenzied Dustwing.
|
||||
-- Keyed by NPC id (names such as "Crimson Lynx" repeat across entries). Hand-maintained.
|
||||
local MTH_DS_UniqueBeastIds = {
|
||||
[5828] = true, -- Humar the Pridelord (Cats)
|
||||
[61599] = true, -- Darksaber (Cats)
|
||||
[61699] = true, -- Shar'lan (Cats)
|
||||
[7434] = true, -- Frostsaber Pride Watcher (Cats)
|
||||
[3068] = true, -- Mazzranache (Tallstriders)
|
||||
[61789] = true, -- Moonfeather (Tallstriders)
|
||||
[61488] = true, -- Azurebeak (Owls)
|
||||
[59998] = true, -- Blue Owl (Owls)
|
||||
[59997] = true, -- Red Owl (Owls)
|
||||
[63147] = true, -- Withered Companion (Owls)
|
||||
[62778] = true, -- Ahgk'tos the Pure (Spiders)
|
||||
[8211] = true, -- Old Cliff Jumper (Wolves)
|
||||
[4512] = true, -- Rotting Agam'ar (Boars)
|
||||
[62750] = true, -- Magmalash Scorpid (Scorpids)
|
||||
[11371] = true, -- Razzashi Serpent (Serpents)
|
||||
[80260] = true, -- Spirit Fox (Foxes)
|
||||
[61716] = true, -- Crimson Hawkstrider (Tallstriders)
|
||||
[61691] = true, -- Forest Hawkstrider (Tallstriders)
|
||||
[61695] = true, -- Ivory Hawkstrider (Tallstriders)
|
||||
[62817] = true, -- Moonstrider Matriarch (Tallstriders)
|
||||
[62818] = true, -- Moonstrider Rooster (Tallstriders)
|
||||
[61774] = true, -- Brilliant Mana Wyrm (Wind Serpents)
|
||||
[61775] = true, -- Emerald Mana Wyrm (Wind Serpents)
|
||||
[61776] = true, -- Lavender Mana Wyrm (Wind Serpents)
|
||||
[61480] = true, -- Brilliant Wind Serpent (Wind Serpents)
|
||||
[1815] = true, -- Diseased Black Bear (Bears)
|
||||
[80466] = true, -- Tirisfal Plagued Bear (Bears)
|
||||
[62820] = true, -- Nightpelt Grizzly (Bears)
|
||||
[62821] = true, -- Nightpelt Ursa (Bears)
|
||||
[80816] = true, -- Hinterlands Eagle (Owls)
|
||||
[61697] = true, -- Bright Lynx (Cats)
|
||||
[61698] = true, -- Bright Lynx Matriarch (Cats)
|
||||
[62338] = true, -- Stormwing Buzzard (Carrion Birds)
|
||||
[62339] = true, -- Stormwing Vulture (Carrion Birds)
|
||||
}
|
||||
|
||||
local function MTH_DS_DeriveBeastUnique()
|
||||
if type(MTH_DS_Beasts) ~= "table" then
|
||||
return
|
||||
end
|
||||
|
||||
for npcId, beast in pairs(MTH_DS_Beasts) do
|
||||
if type(beast) == "table" then
|
||||
if MTH_DS_UniqueBeastIds[npcId] then
|
||||
beast.unique = true
|
||||
else
|
||||
beast.unique = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_DS_OnLoad()
|
||||
if MTH_DS.loaded then
|
||||
return
|
||||
@@ -539,6 +683,7 @@ function MTH_DS_OnLoad()
|
||||
|
||||
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_DeriveBeastUnique()
|
||||
MTH_DS.loaded = true
|
||||
|
||||
-- Count entries
|
||||
|
||||
+25
-1
@@ -1,21 +1,32 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Include file="..\api\PizzaSauce.lua"/>
|
||||
<Include file="..\api\constants.lua"/>
|
||||
<Include file="..\api\debug-frame.lua"/>
|
||||
<Include file="..\api\savedvariables.lua"/>
|
||||
<Include file="..\api\fx-celebrate.lua"/>
|
||||
<Include file="..\api\minimap-button.lua"/>
|
||||
<Include file="..\api\AutoStrip.xml"/>
|
||||
<Include file="..\api\AutoStrip.lua"/>
|
||||
<Include file="..\api\AntiDaze.lua"/>
|
||||
<Include file="..\api\uilib.lua"/>
|
||||
<Include file="..\api\uirenderer.lua"/>
|
||||
<Include file="..\init\localization.xml"/>
|
||||
<Include file="..\api\options-tree.lua"/>
|
||||
<Include file="..\api\options.xml"/>
|
||||
<Include file="..\api\options.lua"/>
|
||||
<Include file="..\api\options-layout.lua"/>
|
||||
<Include file="..\api\options-chronometer.lua"/>
|
||||
<Include file="..\api\options-credits.lua"/>
|
||||
<Include file="..\api\options-animations.lua"/>
|
||||
<Include file="..\api\options-autobuy.lua"/>
|
||||
<Include file="..\api\options-autoquest.lua"/>
|
||||
<Include file="..\api\options-icu.lua"/>
|
||||
<Include file="..\api\options-expammo.lua"/>
|
||||
<Include file="..\api\profile.lua"/>
|
||||
<Include file="..\api\options-profiles.lua"/>
|
||||
<Include file="..\api\options-shell.lua"/>
|
||||
<Include file="..\api\options-zbuttons.lua"/>
|
||||
<Include file="..\api\bag-ammo-labels.lua"/>
|
||||
<Include file="..\api\options-feedomatic.lua"/>
|
||||
<Include file="..\api\hunterbook.xml"/>
|
||||
<Include file="..\api\hunterbook.lua"/>
|
||||
@@ -26,12 +37,25 @@
|
||||
<Include file="..\api\hunterbook-tab-npcfinder.lua"/>
|
||||
<Include file="..\api\hunterbook-tab-stable.lua"/>
|
||||
<Include file="..\api\hunterbook-tab-pethistory.lua"/>
|
||||
<Include file="..\api\hunterbook-tab-trainers.lua"/>
|
||||
<Include file="..\api\hunterbook-model-viewer.lua"/>
|
||||
<Include file="..\api\map-markers.lua"/>
|
||||
<Include file="..\api\debug-frame.lua"/>
|
||||
<Include file="..\api\core-framework.lua"/>
|
||||
<Include file="..\api\core-scan-stablemaster.lua"/>
|
||||
<Include file="..\api\pets-service-lifecycle.lua"/>
|
||||
<Include file="..\api\pets-service-vitals.lua"/>
|
||||
<Include file="..\api\pets-service-runaway.lua"/>
|
||||
<Include file="..\api\pets-service-stablescan.lua"/>
|
||||
<Include file="..\api\pet-growl-teach.lua"/>
|
||||
<Include file="..\api\core-scan-beasttraining.lua"/>
|
||||
<Include file="..\api\core-scan-trainer.lua"/>
|
||||
<Include file="..\api\core-scan-spellbookpet.lua"/>
|
||||
<Include file="..\api\core-scan-huntertalents.lua"/>
|
||||
<Include file="..\api\trainer-spell-planner.lua"/>
|
||||
<Include file="..\api\core-level-tracking.lua"/>
|
||||
<Include file="..\api\core.lua"/>
|
||||
<Include file="..\api\version-check.lua"/>
|
||||
<Include file="..\api\localization.lua"/>
|
||||
<Include file="..\api\config.lua"/>
|
||||
</Ui>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
||||
<!-- MetaHunt Compatibility Layer -->
|
||||
<!-- Handles version compatibility with Turtle WoW -->
|
||||
</Ui>
|
||||
@@ -1,4 +0,0 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
||||
<!-- MetaHunt Environment Setup -->
|
||||
<!-- Global tables, localization, and configuration -->
|
||||
</Ui>
|
||||
@@ -1,5 +0,0 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
||||
<!-- MetaHunt Library includes -->
|
||||
<!-- External libraries for enhanced functionality -->
|
||||
<!-- Note: Libraries can be added here as needed -->
|
||||
</Ui>
|
||||
@@ -0,0 +1,38 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
||||
<Include file="..\locales\init.lua"/>
|
||||
|
||||
<Include file="..\locales\ui\enUS.lua"/>
|
||||
<Include file="..\locales\ui\deDE.lua"/>
|
||||
<Include file="..\locales\ui\esES.lua"/>
|
||||
<Include file="..\locales\ui\ptBR.lua"/>
|
||||
<Include file="..\locales\ui\ruRU.lua"/>
|
||||
<Include file="..\locales\ui\zhCN.lua"/>
|
||||
|
||||
<Include file="..\locales\spells\enUS.lua"/>
|
||||
<Include file="..\locales\spells\deDE.lua"/>
|
||||
<Include file="..\locales\spells\esES.lua"/>
|
||||
<Include file="..\locales\spells\ptBR.lua"/>
|
||||
<Include file="..\locales\spells\ruRU.lua"/>
|
||||
<Include file="..\locales\spells\zhCN.lua"/>
|
||||
|
||||
<Include file="..\locales\data\vendors\enUS.lua"/>
|
||||
<Include file="..\locales\data\vendors\deDE.lua"/>
|
||||
<Include file="..\locales\data\vendors\esES.lua"/>
|
||||
<Include file="..\locales\data\vendors\ptBR.lua"/>
|
||||
<Include file="..\locales\data\vendors\ruRU.lua"/>
|
||||
<Include file="..\locales\data\vendors\zhCN.lua"/>
|
||||
|
||||
<Include file="..\locales\data\items\enUS.lua"/>
|
||||
<Include file="..\locales\data\items\deDE.lua"/>
|
||||
<Include file="..\locales\data\items\esES.lua"/>
|
||||
<Include file="..\locales\data\items\ptBR.lua"/>
|
||||
<Include file="..\locales\data\items\ruRU.lua"/>
|
||||
<Include file="..\locales\data\items\zhCN.lua"/>
|
||||
|
||||
<Include file="..\locales\data\beasts\enUS.lua"/>
|
||||
<Include file="..\locales\data\beasts\deDE.lua"/>
|
||||
<Include file="..\locales\data\beasts\esES.lua"/>
|
||||
<Include file="..\locales\data\beasts\ptBR.lua"/>
|
||||
<Include file="..\locales\data\beasts\ruRU.lua"/>
|
||||
<Include file="..\locales\data\beasts\zhCN.lua"/>
|
||||
</Ui>
|
||||
@@ -20,4 +20,13 @@ MetaHunt Module Loader
|
||||
|
||||
<!-- AutoBuy Module - ENABLED -->
|
||||
<Include file="..\modules\autobuy\loader.xml"/>
|
||||
|
||||
<!-- Auto Quest Module - ENABLED -->
|
||||
<Include file="..\modules\autoquest\loader.xml"/>
|
||||
|
||||
<!-- ICU Module - ENABLED (runtime default: disabled) -->
|
||||
<Include file="..\modules\icu\loader.xml"/>
|
||||
|
||||
<!-- Experimental Ammunition Module - ENABLED -->
|
||||
<Include file="..\modules\expammo\loader.xml"/>
|
||||
</Ui>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
||||
<!-- MetaHunt UI Skins and Styling -->
|
||||
<!-- Placeholder for custom UI theming -->
|
||||
</Ui>
|
||||
@@ -0,0 +1,713 @@
|
||||
MTH_LocaleData = MTH_LocaleData or {}
|
||||
MTH_LocaleData.data = MTH_LocaleData.data or {}
|
||||
MTH_LocaleData.data.beasts = MTH_LocaleData.data.beasts or {}
|
||||
|
||||
MTH_LocaleData.data.beasts.deDE = {
|
||||
[30] = "Waldspinne",
|
||||
[43] = "Minenspinne",
|
||||
[69] = "Erkrankter wilder Waldwolf",
|
||||
[113] = "Steinhauereber",
|
||||
[118] = "Streuner",
|
||||
[119] = "Langschnauze",
|
||||
[154] = "Großer Fleischreißer",
|
||||
[157] = "Geiferzahn",
|
||||
[199] = "Junger Fleischreißer",
|
||||
[213] = "Ausgehungerter Terrorwolf",
|
||||
[217] = "Giftweberspinne",
|
||||
[299] = "Junger Wolf",
|
||||
[330] = "Prinzessin",
|
||||
[345] = "Stopfwanst",
|
||||
[390] = "Gefolgsschwein",
|
||||
[428] = "Terrorkondor",
|
||||
[442] = "Tarantel",
|
||||
[454] = "Junger Geiferzahn",
|
||||
[462] = "Vultros",
|
||||
[471] = "Giftzahnbrutmutter",
|
||||
[505] = "Große Tarantel",
|
||||
[521] = "Lupos",
|
||||
[524] = "Steinhauteber",
|
||||
[525] = "Räudiger Wolf",
|
||||
[539] = "Zwerggiftweberspinne",
|
||||
[547] = "Großer Geiferzahn",
|
||||
[565] = "Tollwütiger Terrorwolf",
|
||||
[569] = "Grüner Eremit",
|
||||
[574] = "Naraxis",
|
||||
[616] = "Chatter",
|
||||
[628] = "Schwarzer Verheerer",
|
||||
[681] = "Junger Schlingendorntiger",
|
||||
[682] = "Schlingendorntiger",
|
||||
[683] = "Junger Panther",
|
||||
[684] = "Schattentatzenpanther",
|
||||
[685] = "Schlingendornraptor",
|
||||
[686] = "Schmetterschwanz",
|
||||
[687] = "Dschungelpirscher",
|
||||
[704] = "Struppiger Waldwolf",
|
||||
[705] = "Struppiger junger Wolf",
|
||||
[708] = "Kleiner Klippeneber",
|
||||
[728] = "Bhag'thera",
|
||||
[729] = "Sin'Dall",
|
||||
[730] = "Tethis",
|
||||
[731] = "König Bangalash",
|
||||
[736] = "Panther",
|
||||
[767] = "Sumpfjaguar",
|
||||
[768] = "Schattenpanther",
|
||||
[769] = "Todesstoßtarantel",
|
||||
[772] = "Schlingendorntigerin",
|
||||
[822] = "Junger Waldbär",
|
||||
[830] = "Sandkriecher",
|
||||
[831] = "Meereskriecher",
|
||||
[833] = "Kojotenrudelanführer",
|
||||
[834] = "Kojote",
|
||||
[854] = "Junger Dschungelpirscher",
|
||||
[855] = "Junger Schlingendornraptor",
|
||||
[856] = "Junger Schmetterschwanz",
|
||||
[858] = "Trauerspinner",
|
||||
[922] = "Schlickkriecher",
|
||||
[923] = "Junger schwarzer Verheerer",
|
||||
[930] = "Junges einer Schwarzen Witwe",
|
||||
[949] = "Aaskrabbler",
|
||||
[976] = "Kurzens Kriegstiger",
|
||||
[977] = "Kurzens Kriegspanther",
|
||||
[1015] = "Hochlandraptor",
|
||||
[1016] = "Hochlandschmetterschwanz",
|
||||
[1017] = "Hochlandsensenklaue",
|
||||
[1018] = "Hochlandscharfzahn",
|
||||
[1019] = "Alter Scharfzahn",
|
||||
[1020] = "Scheckiger Raptor",
|
||||
[1021] = "Scheckiger Kreischer",
|
||||
[1022] = "Scheckige Sensenklaue",
|
||||
[1023] = "Scheckiger Scharfzahn",
|
||||
[1082] = "Sägezahnkrokilisk",
|
||||
[1084] = "Junger Sägezahnkrokilisk",
|
||||
[1087] = "Sägezahnschnapper",
|
||||
[1088] = "Monströser Kriecher",
|
||||
[1108] = "Nebeltalgorilla",
|
||||
[1109] = "Fleischreißer",
|
||||
[1111] = "Egelpirscher",
|
||||
[1112] = "Egelwitwe",
|
||||
[1114] = "Dschungeldonnerer",
|
||||
[1125] = "Klippeneber",
|
||||
[1126] = "Großer Klippeneber",
|
||||
[1127] = "Alter Klippeneber",
|
||||
[1128] = "Junger Schwarzbär",
|
||||
[1129] = "Schwarzbär",
|
||||
[1130] = "Bjarn",
|
||||
[1131] = "Winterwolf",
|
||||
[1132] = "Holzkopf",
|
||||
[1133] = "Verhungernder Winterwolf",
|
||||
[1138] = "Schneefährtenleserwolf",
|
||||
[1140] = "Scharfzahnmatriarchin",
|
||||
[1150] = "Flusskrokilisk",
|
||||
[1152] = "Schnappkieferkrokilisk",
|
||||
[1184] = "Klippenlauerer",
|
||||
[1185] = "Holzlauerer",
|
||||
[1186] = "Schwarzbär",
|
||||
[1188] = "Ergrauter Schwarzbär",
|
||||
[1189] = "Schwarzbärenpatriarch",
|
||||
[1190] = "Bergeber",
|
||||
[1191] = "Räudiger Bergeber",
|
||||
[1192] = "Alter Bergeber",
|
||||
[1194] = "Bergbussard",
|
||||
[1195] = "Waldlauerer",
|
||||
[1196] = "Eisklauenbär",
|
||||
[1199] = "Halbwüchsiger Schneeleopard",
|
||||
[1201] = "Schneeleopard",
|
||||
[1216] = "Küstenkriecher",
|
||||
[1225] = "Alte Rußtatze",
|
||||
[1258] = "Schwarzer Verheerermastiff",
|
||||
[1400] = "Sumpflandkrokilisk",
|
||||
[1417] = "Junger Sumpflandkrokilisk",
|
||||
[1504] = "Junger Nachtweber",
|
||||
[1505] = "Nachtweber",
|
||||
[1508] = "Junger Aasfresser",
|
||||
[1509] = "Struppiger Aasfresser",
|
||||
[1511] = "Wütender Silberrücken",
|
||||
[1512] = "Nachtsauger",
|
||||
[1513] = "Räudiger Nachtsauger",
|
||||
[1516] = "Konda",
|
||||
[1547] = "Altersschwacher Schattenhund",
|
||||
[1548] = "Verfluchter Schattenhund",
|
||||
[1549] = "Gefräßiger Schattenhund",
|
||||
[1553] = "Großer Nachtsauger",
|
||||
[1554] = "Vampirnachtsauger",
|
||||
[1555] = "Heimtückischer Nachtweber",
|
||||
[1557] = "Alter Nebeltalgorilla",
|
||||
[1558] = "Silberrückenpatriarch",
|
||||
[1688] = "Nachtwebermatriarchin",
|
||||
[1689] = "Vernarbter Klippeneber",
|
||||
[1693] = "Lochkrokilisk",
|
||||
[1713] = "Alter Schattentatzenpanther",
|
||||
[1765] = "Worg",
|
||||
[1766] = "Tollwütiger Worg",
|
||||
[1778] = "Wilder ergrauter Bär",
|
||||
[1780] = "Netzhuscherhetzer",
|
||||
[1781] = "Netzhuscherlauerer",
|
||||
[1797] = "Tollwütiger Riesenbär",
|
||||
[1809] = "Aasgeier",
|
||||
[1815] = "Kranker Schwarzbär",
|
||||
[1816] = "Kranker Grizzly",
|
||||
[1817] = "Kranker Wolf",
|
||||
[1822] = "Giftnebellauerer",
|
||||
[1824] = "Seuchenlauerer",
|
||||
[1922] = "Grauer Forstwolf",
|
||||
[1923] = "Blutschnauzenworg",
|
||||
[1961] = "Marterklaue",
|
||||
[1984] = "Junger Disteleber",
|
||||
[1985] = "Disteleber",
|
||||
[1986] = "Waldweberspinne",
|
||||
[1994] = "Githyiss die Üble",
|
||||
[1995] = "Strigideule",
|
||||
[1996] = "Strigidkreischer",
|
||||
[1997] = "Strigidjäger",
|
||||
[1998] = "Waldweberlauerer",
|
||||
[1999] = "Waldwebergiftzahn",
|
||||
[2000] = "Waldweberseidenspinner",
|
||||
[2001] = "Riesige Waldweberspinne",
|
||||
[2031] = "Junger Nachtsäbler",
|
||||
[2032] = "Räudiger Nachtsäbler",
|
||||
[2033] = "Alter Nachtsäbler",
|
||||
[2034] = "Wilder Nachtsäbler",
|
||||
[2042] = "Nachtsäbler",
|
||||
[2043] = "Pirschender Nachtsäbler",
|
||||
[2069] = "Mondpirscher",
|
||||
[2070] = "Mondpirscherwicht",
|
||||
[2071] = "Mondpirschermatriarchin",
|
||||
[2089] = "Riesiger Sumpflandkrokilisk",
|
||||
[2163] = "Distelbär",
|
||||
[2165] = "Ergrauter Distelbär",
|
||||
[2172] = "Schreitergelegemutter",
|
||||
[2175] = "Schattenklaue",
|
||||
[2231] = "Zwerggezeitenkriecher",
|
||||
[2232] = "Gezeitenkriecher",
|
||||
[2233] = "Verkrusteter Gezeitenkriecher",
|
||||
[2234] = "Junger Riffkriecher",
|
||||
[2235] = "Riffkriecher",
|
||||
[2236] = "Tobender Riffkriecher",
|
||||
[2237] = "Mondpirscherpatriarch",
|
||||
[2275] = "Wütender Stanley",
|
||||
[2321] = "Waldschreiterjunges",
|
||||
[2322] = "Waldschreiter",
|
||||
[2323] = "Riesiger Waldschreiter",
|
||||
[2348] = "Alter Mooskrabbler",
|
||||
[2349] = "Domestizierter Krabbler",
|
||||
[2350] = "Waldkrabbler",
|
||||
[2351] = "Verseuchter Bär",
|
||||
[2354] = "Heimtückischer Graubär",
|
||||
[2356] = "Alter Graubär",
|
||||
[2384] = "Verhungernder Berglöwe",
|
||||
[2385] = "Hügellandpirscher",
|
||||
[2406] = "Berglöwe",
|
||||
[2407] = "Bulliger Berglöwe",
|
||||
[2408] = "Schnappkiefer",
|
||||
[2476] = "Gosh-Haldir",
|
||||
[2505] = "Salzwasserschnappkiefer",
|
||||
[2521] = "Himmelsmähnengorilla",
|
||||
[2522] = "Jagueropirscher",
|
||||
[2544] = "Südlicher Sandkriecher",
|
||||
[2559] = "Hochlandschreiter",
|
||||
[2560] = "Hochlandhauer",
|
||||
[2561] = "Hochlandfleischpirscher",
|
||||
[2563] = "Ebenenkrabbler",
|
||||
[2565] = "Riesiger Ebenenkrabbler",
|
||||
[2578] = "Junger Mesabussard",
|
||||
[2579] = "Mesabussard",
|
||||
[2580] = "Alter Mesabussard",
|
||||
[2635] = "Alter Schnappkieferkrokilisk",
|
||||
[2680] = "Wolfswelpe der Blutfratzen",
|
||||
[2681] = "Raubwolf der Blutfratzen",
|
||||
[2686] = "Brutwache der Bleichborken",
|
||||
[2727] = "Klippenkojote",
|
||||
[2728] = "Wilder Klippenkojote",
|
||||
[2729] = "Alter Klippenkojote",
|
||||
[2730] = "Tollwütiger Klippenkojote",
|
||||
[2731] = "Kammpirscher",
|
||||
[2732] = "Kammjägerin",
|
||||
[2734] = "Kammpirscherpatriarch",
|
||||
[2753] = "Barnabus",
|
||||
[2829] = "Verhungernder Bussard",
|
||||
[2830] = "Ausgedörrter Bussard",
|
||||
[2831] = "Riesenbussard",
|
||||
[2850] = "Zerbrochener Zahn",
|
||||
[2923] = "Räudige Silbermähne",
|
||||
[2924] = "Silbermähnenwolf",
|
||||
[2925] = "Silbermähnenheuler",
|
||||
[2926] = "Silbermähnenpirscher",
|
||||
[2931] = "Zaricotl",
|
||||
[2954] = "Kampfeber der Borstennacken",
|
||||
[2955] = "Ebenenschreiter",
|
||||
[2956] = "Ausgewachsener Ebenenschreiter",
|
||||
[2957] = "Alter Ebenenschreiter",
|
||||
[2958] = "Präriewolf",
|
||||
[2959] = "Präriepirscher",
|
||||
[2960] = "Präriewolfalpha",
|
||||
[2961] = "Bergpuma",
|
||||
[2966] = "Junger Kampfeber",
|
||||
[2969] = "Drahtiger Sturzflieger",
|
||||
[2970] = "Sturzflieger",
|
||||
[2971] = "Kralliger Sturzflieger",
|
||||
[3035] = "Flachlandpuma",
|
||||
[3068] = "Mazzranache",
|
||||
[3098] = "Scheckiger Eber",
|
||||
[3099] = "Scheckiger Terroreber",
|
||||
[3100] = "Alter scheckiger Eber",
|
||||
[3106] = "Brandungskriecher",
|
||||
[3107] = "Ausgewachsener Brandungskriecher",
|
||||
[3108] = "Verkrusteter Brandungskriecher",
|
||||
[3110] = "Schaudermagenkrokilisk",
|
||||
[3121] = "Durotartiger",
|
||||
[3122] = "Blutkrallenschwanzpeitscher",
|
||||
[3123] = "Blutkrallensensenschlund",
|
||||
[3124] = "Skorpidarbeiter",
|
||||
[3125] = "Rasselnder Skorpid",
|
||||
[3126] = "Gepanzerter Skorpid",
|
||||
[3127] = "Siechstachelskorpid",
|
||||
[3225] = "Verderbter scheckiger Eber",
|
||||
[3226] = "Verderbter Skorpid",
|
||||
[3227] = "Verderbter Blutkrallensensenschlund",
|
||||
[3228] = "Verderbter Brandungskriecher",
|
||||
[3231] = "Verderbter Schaudermagenkrokilisk",
|
||||
[3241] = "Savannenpatriarch",
|
||||
[3243] = "Savannenhochmähne",
|
||||
[3244] = "Großer Ebenenschreiter",
|
||||
[3245] = "Störrischer Ebenenschreiter",
|
||||
[3246] = "Flüchtiger Ebenenschreiter",
|
||||
[3247] = "Donnerfalkenjungtier",
|
||||
[3249] = "Großer Donnerfalke",
|
||||
[3250] = "Silithidkrabbler",
|
||||
[3252] = "Silithidschwärmer",
|
||||
[3254] = "Sonnenschuppenschmetterschwanz",
|
||||
[3255] = "Sonnenschuppenkreischer",
|
||||
[3256] = "Sonnenschuppensensenklaue",
|
||||
[3257] = "Ishamuhale",
|
||||
[3281] = "Sarkoth",
|
||||
[3415] = "Savannenjägerin",
|
||||
[3416] = "Savannenmatriarchin",
|
||||
[3424] = "Donnerfalkenwolkenkitzler",
|
||||
[3425] = "Savannenstreuner",
|
||||
[3461] = "Oasenschnappkiefer",
|
||||
[3472] = "Washte Pawne",
|
||||
[3475] = "Echeyakee",
|
||||
[3503] = "Silithidbeschützer",
|
||||
[3566] = "Flachlandstreuner",
|
||||
[3581] = "Kanalbestie",
|
||||
[3619] = "Geistersäbler",
|
||||
[3630] = "Deviatwinder",
|
||||
[3631] = "Deviatdornenpeitscher",
|
||||
[3632] = "Deviatkrabbler",
|
||||
[3633] = "Deviattöter",
|
||||
[3634] = "Deviatpirscher",
|
||||
[3636] = "Deviatverheerer",
|
||||
[3637] = "Deviatwächter",
|
||||
[3653] = "Kresh",
|
||||
[3809] = "Eschentalbär",
|
||||
[3810] = "Alter Eschentalbär",
|
||||
[3811] = "Riesiger Eschentalbär",
|
||||
[3812] = "Rasselnder Kriecher",
|
||||
[3814] = "Stachelkriecher",
|
||||
[3819] = "Wilddornpirscher",
|
||||
[3820] = "Wilddorngiftspucker",
|
||||
[3821] = "Wilddornlauerer",
|
||||
[3823] = "Geisterpfotenläufer",
|
||||
[3824] = "Geisterpfotenheuler",
|
||||
[3825] = "Geisterpfotenalpha",
|
||||
[3861] = "Düsterer Worg",
|
||||
[3862] = "Sabbernder Worg",
|
||||
[3866] = "Üble Fledermaus",
|
||||
[3868] = "Blutsucher",
|
||||
[4005] = "Kluftmooskrabbler",
|
||||
[4006] = "Kluftmoosnetzweber",
|
||||
[4007] = "Kluftmoosgiftspucker",
|
||||
[4040] = "Höhlenpirscher",
|
||||
[4067] = "Mondlichtläufer",
|
||||
[4117] = "Wolkenschlange",
|
||||
[4118] = "Giftige Wolkenschlange",
|
||||
[4119] = "Alte Wolkenschlange",
|
||||
[4124] = "Nadelpuma",
|
||||
[4126] = "Klippenpirscher",
|
||||
[4127] = "Fleischreißerhyäne",
|
||||
[4128] = "Fleischreißerpirscher",
|
||||
[4129] = "Fleischreißerkläffer",
|
||||
[4139] = "Skorpidschrecken",
|
||||
[4140] = "Skorpidhäscher",
|
||||
[4142] = "Spiegelpanzerschildkröte",
|
||||
[4143] = "Spiegelpanzerschnapper",
|
||||
[4144] = "Spiegelpanzerbohrer",
|
||||
[4154] = "Salzwüstenaasfresser",
|
||||
[4158] = "Salzwüstengeier",
|
||||
[4248] = "Pestfleckenhyäne",
|
||||
[4249] = "Pestfleckenkläffer",
|
||||
[4250] = "Rudelhund der Galak",
|
||||
[4263] = "Kluftmoosjungtier",
|
||||
[4264] = "Kluftmoosmatriarchin",
|
||||
[4304] = "Scharlachroter Fährtenleserhund",
|
||||
[4316] = "Rudelhund der Kolkar",
|
||||
[4341] = "Staubsuhlerkrokilisk",
|
||||
[4342] = "Staubsuhlerzwingenkiefer",
|
||||
[4343] = "Staubsuhlerschnapper",
|
||||
[4344] = "Scheckiger Staubsuhlerkrokilisk",
|
||||
[4345] = "Staubsuhlerdolchrachen",
|
||||
[4351] = "Blutsumpfraptor",
|
||||
[4352] = "Blutsumpfkreischer",
|
||||
[4355] = "Blutsumpfsensenklaue",
|
||||
[4356] = "Blutsumpfscharfzahn",
|
||||
[4357] = "Blutsumpfschmetterschwanz",
|
||||
[4376] = "Graunebelspinne",
|
||||
[4377] = "Graunebeljungtier",
|
||||
[4378] = "Graunebeleremit",
|
||||
[4379] = "Graunebelseidenspinner",
|
||||
[4380] = "Graunebelwitwe",
|
||||
[4396] = "Schlammpanzerschildkröte",
|
||||
[4397] = "Dornenschlammpanzer",
|
||||
[4398] = "Schlammpanzereinbuddler",
|
||||
[4399] = "Schlammpanzerbohrer",
|
||||
[4400] = "Schlammpanzerschnappkiefer",
|
||||
[4411] = "Dunkelzahnlauerer",
|
||||
[4412] = "Dunkelzahnkrabbler",
|
||||
[4413] = "Dunkelzahnspinne",
|
||||
[4414] = "Dunkelzahngiftspucker",
|
||||
[4415] = "Riesige Dunkelzahnspinne",
|
||||
[4425] = "Blinder Jäger",
|
||||
[4511] = "Agam'ar",
|
||||
[4512] = "Faulender Agam'ar",
|
||||
[4514] = "Tobender Agam'ar",
|
||||
[4538] = "Kralfledermaus",
|
||||
[4539] = "Große Kralfledermaus",
|
||||
[4548] = "Stahlbiss",
|
||||
[4660] = "Skelettpfote der Maraudine",
|
||||
[4662] = "Skelettpfote der Magram",
|
||||
[4688] = "Skelettpfotenhyäne",
|
||||
[4689] = "Verhungernde Skelettpfote",
|
||||
[4690] = "Tollwütige Skelettpfote",
|
||||
[4692] = "Schreckenssturzflieger",
|
||||
[4693] = "Schreckensflieger",
|
||||
[4694] = "Schreckensschlitzer",
|
||||
[4695] = "Aasschrecken",
|
||||
[4696] = "Skorpashischnapper",
|
||||
[4697] = "Skorpashipeitscher",
|
||||
[4699] = "Skorpashigiftpeitscher",
|
||||
[4821] = "Huschendes Krustentier",
|
||||
[4822] = "Schnappendes Krustentier",
|
||||
[4823] = "Stacheliges Krustentier",
|
||||
[4824] = "Fischschnapper von Aku'mai",
|
||||
[4825] = "Schnappkiefer von Aku'mai",
|
||||
[4841] = "Schlammwühler",
|
||||
[4861] = "Würgerfledermaus",
|
||||
[4887] = "Ghamoo-Ra",
|
||||
[4950] = "Spot",
|
||||
[5048] = "Deviatotter",
|
||||
[5053] = "Deviatkrokilisk",
|
||||
[5056] = "Deviatschreckensfang",
|
||||
[5224] = "Düstergleiter",
|
||||
[5225] = "Düsterspeier",
|
||||
[5260] = "Groddocaffe",
|
||||
[5262] = "Groddocdonnerer",
|
||||
[5268] = "Eisenfellbär",
|
||||
[5272] = "Grauer Eisenfellbär",
|
||||
[5274] = "Eisenfellpatriarch",
|
||||
[5286] = "Langzahnläufer",
|
||||
[5287] = "Langzahnheuler",
|
||||
[5288] = "Tollwütiger Langzahn",
|
||||
[5291] = "Frostflügler der Hakkari",
|
||||
[5307] = "Talkreischer",
|
||||
[5308] = "Wilder Talkreischer",
|
||||
[5349] = "Arash-ethis",
|
||||
[5352] = "Silbergrimm der Weise",
|
||||
[5356] = "Knurrer",
|
||||
[5422] = "Skorpidjäger",
|
||||
[5423] = "Skorpidschwanzpeitscher",
|
||||
[5424] = "Skorpiddünenpirscher",
|
||||
[5425] = "Verhungernde Eiterpfote",
|
||||
[5426] = "Eiterpfotenhyäne",
|
||||
[5427] = "Tollwütige Eiterpfote",
|
||||
[5428] = "Roc",
|
||||
[5429] = "Feuerroc",
|
||||
[5430] = "Sengender Roc",
|
||||
[5431] = "Brandungsgleiter",
|
||||
[5432] = "Riesiger Brandungsgleiter",
|
||||
[5708] = "Brut von Hakkar",
|
||||
[5755] = "Deviatviper",
|
||||
[5756] = "Deviatgiftschwinge",
|
||||
[5762] = "Deviatmokassinschlange",
|
||||
[5766] = "Savannenjunges",
|
||||
[5807] = "Der Kratzer",
|
||||
[5823] = "Todesschinder",
|
||||
[5828] = "Humar der Rudellord",
|
||||
[5829] = "Snort der Spucker",
|
||||
[5834] = "Azzere die Himmelsklinge",
|
||||
[5842] = "Takk der Springer",
|
||||
[5856] = "Glasnetzspinne",
|
||||
[5857] = "Sengende Lavaspinne",
|
||||
[5858] = "Große Lavaspinne",
|
||||
[5865] = "Dishu",
|
||||
[5937] = "Übelstich",
|
||||
[5982] = "Schwarzer Töter",
|
||||
[5984] = "Verhungernder Kicherfang",
|
||||
[5985] = "Kicherfanghyäne",
|
||||
[5988] = "Skorpokstecher",
|
||||
[5992] = "Aschmähneneber",
|
||||
[6013] = "Entflogener Bussard",
|
||||
[6352] = "Korallenpanzerschleicher",
|
||||
[6369] = "Korallenpanzerschildkröte",
|
||||
[6505] = "Ravasaurus",
|
||||
[6506] = "Ravasaurusläufer",
|
||||
[6507] = "Ravasaurusjäger",
|
||||
[6508] = "Gifthautravasaurus",
|
||||
[6513] = "Stampfer von Un'Goro",
|
||||
[6514] = "Gorilla von Un'Goro",
|
||||
[6516] = "Donnerer von Un'Goro",
|
||||
[6581] = "Ravasaurusmatriarchin",
|
||||
[6585] = "Uhk'loc",
|
||||
[6788] = "Höhlenmutter",
|
||||
[6789] = "Distelbärjunges",
|
||||
[7022] = "Giftpeitscherskorpid",
|
||||
[7055] = "Worg des Schwarzfels",
|
||||
[7078] = "Kluftskorpid",
|
||||
[7097] = "Eisenschnabeleule",
|
||||
[7098] = "Eisenschnabelkreischer",
|
||||
[7099] = "Eisenschnabeljäger",
|
||||
[7268] = "Wächter der Sandwüter",
|
||||
[7319] = "Lady Sathrah",
|
||||
[7405] = "Tödlicher Kluftskorpid",
|
||||
[7430] = "Junger Frostsäbler",
|
||||
[7431] = "Frostsäbler",
|
||||
[7432] = "Frostsäblerpirscherin",
|
||||
[7433] = "Frostsäblerjägerin",
|
||||
[7434] = "Rudelbehüterin der Frostsäbler",
|
||||
[7443] = "Splitterzahnraufer",
|
||||
[7444] = "Splitterzahnbär",
|
||||
[7445] = "Alter Splitterzahn",
|
||||
[7446] = "Tollwütiger Splitterzahn",
|
||||
[7455] = "Winterquelleule",
|
||||
[7456] = "Winterquellkreischer",
|
||||
[7803] = "Dünenbuddlerskorpid",
|
||||
[8207] = "Glutschwinge",
|
||||
[8208] = "Mordlustige Eiterpfote",
|
||||
[8211] = "Klippenspringer",
|
||||
[8213] = "Eisenpanzer",
|
||||
[8277] = "Rekk'tilac",
|
||||
[8299] = "Fledderschnabel",
|
||||
[8300] = "Verheerer",
|
||||
[8301] = "Clack der Häscher",
|
||||
[8303] = "Suhlaman",
|
||||
[8336] = "Pionier der Hakkari",
|
||||
[8600] = "Seuchenfledermaus",
|
||||
[8601] = "Giftige Seuchenfledermaus",
|
||||
[8602] = "Monströse Seuchenfledermaus",
|
||||
[8762] = "Baumspinnereremit",
|
||||
[8926] = "Tiefstecher",
|
||||
[8927] = "Dunkelkreischer",
|
||||
[8933] = "Höhlenkrabbler",
|
||||
[8956] = "Wutklauenbär",
|
||||
[8958] = "Wutklauenraufer",
|
||||
[8959] = "Teufelspfotenwolf",
|
||||
[8960] = "Teufelspfotenaasfresser",
|
||||
[8961] = "Teufelspfotenverheerer",
|
||||
[9416] = "Worg der Schmetterschilde",
|
||||
[9622] = "U'cha",
|
||||
[9683] = "Lar'korwis Weibchen",
|
||||
[9684] = "Lar'korwi",
|
||||
[9690] = "Glutworg",
|
||||
[9691] = "Giftstichskorpid",
|
||||
[9694] = "Sabbernder Glutworg",
|
||||
[9695] = "Todespeitschenskorpid",
|
||||
[9696] = "Worg der Blutäxte",
|
||||
[9697] = "Riesiger Glutworg",
|
||||
[9698] = "Feuerschwanzskorpid",
|
||||
[10077] = "Totenreißer",
|
||||
[10200] = "Rak'shiri",
|
||||
[10221] = "Worgwelpe der Blutäxte",
|
||||
[10356] = "Bayne",
|
||||
[10357] = "Ressan der Aufstachler",
|
||||
[10359] = "Sri'skulk",
|
||||
[10375] = "Spitzenspinnling",
|
||||
[10644] = "Nebelheuler",
|
||||
[10737] = "Shy-Rotam",
|
||||
[10741] = "Sian-Rotam",
|
||||
[10806] = "Ursius",
|
||||
[10882] = "Arikara",
|
||||
[10981] = "Frostwolf",
|
||||
[11357] = "Sohn von Hakkar",
|
||||
[11360] = "Zulianisches Junges",
|
||||
[11361] = "Zulianischer Tiger",
|
||||
[11365] = "Zulianischer Panther",
|
||||
[11368] = "Blutsucherfledermaus",
|
||||
[11370] = "Razzashibrutwitwe",
|
||||
[11371] = "Razzashischlange",
|
||||
[11372] = "Razzashiotter",
|
||||
[11735] = "Steinpanzerskorpid",
|
||||
[11736] = "Steinpanzerzange",
|
||||
[11737] = "Steinpanzerschinder",
|
||||
[11738] = "Sandhuscher",
|
||||
[11739] = "Felspirscher",
|
||||
[11921] = "Besseleth",
|
||||
[12037] = "Ursol'lok",
|
||||
[12431] = "Blutmaul",
|
||||
[12432] = "Zwingenkiefer",
|
||||
[12433] = "Krethis Schattennetz",
|
||||
[13599] = "Phlegmatischer Schnappkiefer",
|
||||
[14123] = "Stahlkieferschnapper",
|
||||
[14222] = "Araga",
|
||||
[14223] = "Benj der Missmutige",
|
||||
[14228] = "Kicherer",
|
||||
[14232] = "Pfeil",
|
||||
[14233] = "Reißerschuppe",
|
||||
[14234] = "Hayoc",
|
||||
[14266] = "Shanda die Weberin",
|
||||
[14268] = "Lord Condar",
|
||||
[14279] = "Kriechfänger",
|
||||
[14280] = "Der Große Samras",
|
||||
[14283] = "Eule der Sturmlanzen",
|
||||
[14339] = "Todesheuler",
|
||||
[14343] = "Olm der Weise",
|
||||
[14344] = "Mongress",
|
||||
[14472] = "Gretheer",
|
||||
[14476] = "Krellack",
|
||||
[14491] = "Kurmokk",
|
||||
[14532] = "Razzashigiftbrut",
|
||||
[14821] = "Razzashiraptor",
|
||||
[14965] = "Rasende Blutsucherfledermaus",
|
||||
[15041] = "Brut von Mar'li",
|
||||
[15043] = "Zulianischer Krokilisk",
|
||||
[15101] = "Zulianischer Streuner",
|
||||
[16117] = "Verseuchtes Schwein",
|
||||
[36507] = "Fleck",
|
||||
[36514] = "Frostsäblerjunges",
|
||||
[37007] = "Black Widow Hatchling",
|
||||
[40000] = "Roter Fuchs",
|
||||
[50624] = "Silithidschwärmer",
|
||||
[50627] = "Silithidschwärmer",
|
||||
[50630] = "Silithidschwärmer",
|
||||
[60494] = "Kummerklaue",
|
||||
[60697] = "Beruhigungshund",
|
||||
[60848] = "Eber der Borstennacken",
|
||||
[61074] = "Gorilla der Hochtalfeste",
|
||||
[61075] = "Felsenschalenkrabbler",
|
||||
[61076] = "Sandschalenkrabbler",
|
||||
[61077] = "Schimpanse der Hochtalfeste",
|
||||
[61078] = "Affe der Hochtalfeste",
|
||||
[61080] = "Älterer Gorilla des Hochtals",
|
||||
[61082] = "Donnerschreiter der Hochtalfeste",
|
||||
[61090] = "Bürstenschwanz Kobra",
|
||||
[61091] = "Bürstenschwanz Natter",
|
||||
[61095] = "Giftpeitscher Schreier",
|
||||
[61096] = "Giftpeitscher Schlange",
|
||||
[61098] = "König Morogo Donnerfuß",
|
||||
[61206] = "Spinnenkrabbler Kriecher",
|
||||
[61218] = "Deviat Windschlangenschlüpfling",
|
||||
[61227] = "Dämmerpelz Schleicher",
|
||||
[61228] = "Dämmerpelz Pirscher",
|
||||
[61229] = "Ausgehungerter Dämmerpelz",
|
||||
[61230] = "Pestschwingen Fledermausjunges",
|
||||
[61231] = "Pestschwingen Fledermaus",
|
||||
[61232] = "Düsternetz Spinne",
|
||||
[61233] = "Düsternetz Giftspucker",
|
||||
[61336] = "Großhorn Eule",
|
||||
[61337] = "Großhorn Jäger",
|
||||
[61401] = "Große Schreckensschwinge",
|
||||
[61488] = "Azurenschnabel",
|
||||
[61500] = "Silberrücken von Hochtal",
|
||||
[61552] = "Abendkrabbler",
|
||||
[61554] = "Morgengeheul",
|
||||
[61599] = "Dunkelsäbel",
|
||||
[61656] = "Aschenfeder Bussard",
|
||||
[61657] = "Aschenfeder Stürmer",
|
||||
[61658] = "Hyäne der Aschenpfoten",
|
||||
[61659] = "Jäger der Aschenpfoten",
|
||||
[61660] = "Steinschwanz Skorpid",
|
||||
[61661] = "Steinschwanz Peitscher",
|
||||
[61662] = "Schwarzgift Skorpid",
|
||||
[61680] = "Schlammriff Kriecher",
|
||||
[61681] = "Junger Schlammriff Kriecher",
|
||||
[61686] = "Aschenfeder Plünderer",
|
||||
[61689] = "Junger Thalassischer Eber",
|
||||
[61690] = "Junger Karmesinroter Luchs",
|
||||
[61691] = "Wald Falkenschreiter",
|
||||
[61692] = "Thalassischer Eber",
|
||||
[61693] = "Karmesinroter Luchs",
|
||||
[61695] = "Elfenbein Falkenschreiter",
|
||||
[61696] = "Älterer Thalassischer Eber",
|
||||
[61697] = "Leuchtluchs",
|
||||
[61698] = "Leuchtluchs Matriarchin",
|
||||
[61699] = "Shar'lan",
|
||||
[61700] = "Älterer Karmesinroter Luchs",
|
||||
[61704] = "Kriecher der Weltenwanderer",
|
||||
[61716] = "Karmesinroter Falkenschreiter",
|
||||
[61717] = "Thalassischer Fuchs",
|
||||
[61774] = "Brillanter Manawyrm",
|
||||
[61775] = "Smaragd Manawyrm",
|
||||
[61776] = "Lavendel Manawyrm",
|
||||
[61789] = "Mondfeder",
|
||||
[61975] = "Hochhauer Eber",
|
||||
[62066] = "Höhlenweber Brutmutter",
|
||||
[62073] = "Höhlenweber Spinne",
|
||||
[62074] = "Höhlenweber Kriecher",
|
||||
[62075] = "Höhlenweber Giftspucker",
|
||||
[62121] = "Goldborsten Eber",
|
||||
[62122] = "Stämmiger Goldborsten Eber",
|
||||
[62123] = "Goldpelz Bär",
|
||||
[62124] = "Goldpelz Grizzly",
|
||||
[62125] = "Älterer Timberwolf",
|
||||
[62126] = "Timberwolf Alpha",
|
||||
[62223] = "Grimmschuppenraptor",
|
||||
[62224] = "Grimmschuppenkreischer",
|
||||
[62225] = "Grimmschuppenprügler",
|
||||
[62226] = "Steinhautkeiler",
|
||||
[62227] = "Alter Steinhauteber",
|
||||
[62247] = "Braune Einsiedlerspinne",
|
||||
[62248] = "Große Braune Einsiedlerspinne",
|
||||
[62256] = "Klarwasser Schnappkiefer",
|
||||
[62257] = "Kranker Klarwasser Schnappkiefer",
|
||||
[62258] = "Scharfzahn Krokilisk",
|
||||
[62259] = "Riesiger Scharfzahn Krokilisk",
|
||||
[62260] = "Waldeber",
|
||||
[62261] = "Alter Waldeber",
|
||||
[62262] = "Hochebenen Eber",
|
||||
[62271] = "Junger Goldborsten Eber",
|
||||
[62272] = "Junger Waldwolf",
|
||||
[62338] = "Sturmflügel Bussard",
|
||||
[62339] = "Sturmflügel Aasgeier",
|
||||
[62340] = "Zerzauster Felsspitzenkoyote",
|
||||
[62341] = "Sabbernder Felsspitzenkoyote",
|
||||
[62342] = "Nebelrinden Spinne",
|
||||
[62343] = "Nebelrinden Netzwirkerin",
|
||||
[62344] = "Mutter Nebelrinde",
|
||||
[62345] = "Trauermoor Krokilisk",
|
||||
[62642] = "Klingenschuppe",
|
||||
[80250] = "Karmesinroter Luchs",
|
||||
[80252] = "Junger Arktisfuchs",
|
||||
[80254] = "Junger Brauner Fuchs",
|
||||
[80256] = "Silberfuchs",
|
||||
[80257] = "Roter Fuchs",
|
||||
[80258] = "Junger Nebelwolf",
|
||||
[80259] = "Nebelwolf",
|
||||
[80260] = "Geisterfuchs",
|
||||
[80466] = "Tirisfal Pestbär",
|
||||
[80815] = "Amaniadler",
|
||||
[80816] = "Hinterlandadler",
|
||||
[80920] = "Amani Adler",
|
||||
[90980] = "Zulianischer Panther",
|
||||
[90981] = "Zulianischer Panther",
|
||||
[91819] = "Hazzuri Tiger",
|
||||
[91825] = "Junger Bengal Tiger",
|
||||
[91826] = "Bengal Matriarchin",
|
||||
[91827] = "Bengal Tiger",
|
||||
[91828] = "Bengal Alpha",
|
||||
[91829] = "Dschungelrücken Stampfer",
|
||||
[91830] = "Dschungelrücken Schläger",
|
||||
[91831] = "Leuchtkrabbler",
|
||||
[91832] = "Inselkneifer",
|
||||
[91833] = "Felskrabbler",
|
||||
[91838] = "Tiefenschalen Schnapper",
|
||||
[91960] = "Dschungelpranken Tiger",
|
||||
[91961] = "Dschungelpranken Schattentiger",
|
||||
[91962] = "Salzschnapp Krokilisk",
|
||||
[91966] = "Giftige Dschungelschlange",
|
||||
[91984] = "Tirisklaue Bär",
|
||||
[91985] = "Tirisklaue Junges",
|
||||
[91987] = "Felshauer Eber",
|
||||
[91989] = "Graupfoten Wolf",
|
||||
[91990] = "Graupfoten Alpha",
|
||||
[92115] = "Rasende Scharfklaue",
|
||||
[92146] = "Strandkrabbler",
|
||||
[92147] = "Silberküstenkrabbler",
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
MTH_LocaleData = MTH_LocaleData or {}
|
||||
MTH_LocaleData.data = MTH_LocaleData.data or {}
|
||||
MTH_LocaleData.data.beasts = MTH_LocaleData.data.beasts or {}
|
||||
|
||||
-- Intentionally left empty by default.
|
||||
-- enUS beast names are lazy-bootstrapped from MTH_DS_Beasts at runtime,
|
||||
-- and this table can still be used for manual ID overrides when needed.
|
||||
MTH_LocaleData.data.beasts.enUS = {
|
||||
}
|
||||
@@ -0,0 +1,713 @@
|
||||
MTH_LocaleData = MTH_LocaleData or {}
|
||||
MTH_LocaleData.data = MTH_LocaleData.data or {}
|
||||
MTH_LocaleData.data.beasts = MTH_LocaleData.data.beasts or {}
|
||||
|
||||
MTH_LocaleData.data.beasts.esES = {
|
||||
[30] = "Araña del Bosque",
|
||||
[43] = "Araña Minera",
|
||||
[69] = "Lobo Maderero",
|
||||
[113] = "Jabalí Colmipétreo",
|
||||
[118] = "Merodeador",
|
||||
[119] = "Morrolargo",
|
||||
[154] = "Desgarrador Mayor",
|
||||
[157] = "Dentosangre",
|
||||
[199] = "Desgarrador Joven",
|
||||
[213] = "Lobo Feroz Hambriento",
|
||||
[217] = "Telarácnida Venenosa",
|
||||
[299] = "Lobo Joven",
|
||||
[330] = "Princesa",
|
||||
[345] = "Panzallena",
|
||||
[390] = "Séquito Porcino",
|
||||
[428] = "Cóndor Temible",
|
||||
[442] = "Tarantula",
|
||||
[454] = "Dentosangre Joven",
|
||||
[462] = "Vultros",
|
||||
[471] = "Madre Colmillo",
|
||||
[505] = "Tarántula Superior",
|
||||
[521] = "Lupos",
|
||||
[524] = "Jabalí Pielroca",
|
||||
[525] = "Lobo Sarnoso",
|
||||
[539] = "Telarácnida Pigmeovenenosa",
|
||||
[547] = "Gran Dentosangre",
|
||||
[565] = "Lobo Temible Rabioso",
|
||||
[569] = "Ermitaña Verde",
|
||||
[574] = "Naraxis",
|
||||
[616] = "Parloteador",
|
||||
[628] = "Devastador Negro",
|
||||
[681] = "Tigre Joven de Tuercespina",
|
||||
[682] = "Tigre de Tuercespina",
|
||||
[683] = "Pantera Joven",
|
||||
[684] = "Pantera Faucesombría",
|
||||
[685] = "Raptor de Tuercespina",
|
||||
[686] = "Raptor Colazote",
|
||||
[687] = "Acechador de la Selva",
|
||||
[704] = "Lobo Gris Pulgoso",
|
||||
[705] = "Lobo Joven Harapiento",
|
||||
[708] = "Jabalí del Peñasco Pequeño",
|
||||
[728] = "Bhag'thera",
|
||||
[729] = "Sin'Dall",
|
||||
[730] = "Tethis",
|
||||
[731] = "Rey Bangalash",
|
||||
[736] = "Pantera",
|
||||
[767] = "Jaguar del Pantano",
|
||||
[768] = "Pantera de las Sombras",
|
||||
[769] = "Tarántula Golpe Mortal",
|
||||
[772] = "Tigresa de Tuercespina",
|
||||
[822] = "Oso del Bosque Joven",
|
||||
[830] = "Reptador de Arena",
|
||||
[831] = "Reptador Marino",
|
||||
[833] = "Coyote Líder",
|
||||
[834] = "Coyote",
|
||||
[854] = "Acechador de la Jungla Joven",
|
||||
[855] = "Raptor de la Vega de Tuercespina Joven",
|
||||
[856] = "Raptor Cola de Látigo Joven",
|
||||
[858] = "Tejedor de la Penumbra",
|
||||
[922] = "Reptador de Cieno",
|
||||
[923] = "Devastador Negro Joven",
|
||||
[930] = "Prole de Viuda Negra",
|
||||
[949] = "Recluso Carroñero",
|
||||
[976] = "Médico Brujo Kurzen",
|
||||
[977] = "Tigre de Guerra Kurzen",
|
||||
[1015] = "Raptor de las Tierras Altas",
|
||||
[1016] = "Colazote de las Tierras Altas",
|
||||
[1017] = "Garfiohoz de las Tierras Altas",
|
||||
[1018] = "Fauzafilo de las Tierras Altas",
|
||||
[1019] = "Fauces Navaja Ancianas",
|
||||
[1020] = "Raptor Moteado",
|
||||
[1021] = "Chillón Moteado",
|
||||
[1022] = "Garfiohoz Moteado",
|
||||
[1023] = "Fauzafilada Moteado",
|
||||
[1082] = "Crocolisco Dientesierra",
|
||||
[1084] = "Crocolisco Dientes de Sierra Joven",
|
||||
[1087] = "Triturador Dientesierra",
|
||||
[1088] = "Reptador Monstruoso",
|
||||
[1108] = "Gorila del Valle de la Niebla",
|
||||
[1109] = "Desgarrador",
|
||||
[1111] = "Acechador de Sanguijuelas",
|
||||
[1112] = "Viuda Sanguijuela",
|
||||
[1114] = "Tronador de la Jungla",
|
||||
[1125] = "Jabalí del Risco",
|
||||
[1126] = "Jabalí de Peñasco Grande",
|
||||
[1127] = "Jabalí del Risco Anciano",
|
||||
[1128] = "Oso Negro Joven",
|
||||
[1129] = "Oso Negro",
|
||||
[1130] = "Bjarn",
|
||||
[1131] = "Lobo Invernal",
|
||||
[1132] = "Maderero",
|
||||
[1133] = "Lobo Invernal Hambriento",
|
||||
[1138] = "Lobo Rastreador de Nieve",
|
||||
[1140] = "Matriarca Tajobuche",
|
||||
[1150] = "Crocolisco de Río",
|
||||
[1152] = "Cocodrilo Quijada",
|
||||
[1184] = "Acechador del Risco",
|
||||
[1185] = "Acechador de Madera",
|
||||
[1186] = "Oso Negro Anciano",
|
||||
[1188] = "Oso Negro Canoso",
|
||||
[1189] = "Patriarca Oso Negro",
|
||||
[1190] = "Jabalí de Montaña",
|
||||
[1191] = "Jabalí de Montaña Harapiento",
|
||||
[1192] = "Jabalí Montés Mayor",
|
||||
[1194] = "Buitre de Montaña",
|
||||
[1195] = "Acechador del Bosque",
|
||||
[1196] = "Oso Garra de Hielo",
|
||||
[1199] = "Leopardo de las Nieves Joven",
|
||||
[1201] = "Leopardo de Nieve",
|
||||
[1216] = "Reptador de la Costa",
|
||||
[1225] = "Viejo Sooty",
|
||||
[1258] = "Masadura Nicroyermo",
|
||||
[1400] = "Crocolisco de Humedales",
|
||||
[1417] = "Crocolisco Joven de los Humedales",
|
||||
[1504] = "Nocturácnida Joven",
|
||||
[1505] = "Araña Nocturácnida",
|
||||
[1508] = "Carroñero Joven",
|
||||
[1509] = "Carroñero Andrajoso",
|
||||
[1511] = "Gorila Lomoplata Iracundo",
|
||||
[1512] = "Murciumbrío",
|
||||
[1513] = "Murciumbrío Sarnoso",
|
||||
[1516] = "Konda",
|
||||
[1547] = "Can Oscuro Decrépito",
|
||||
[1548] = "Can Oscuro Maldito",
|
||||
[1549] = "Can Oscuro Voraz",
|
||||
[1553] = "Murciumbrío Superior",
|
||||
[1554] = "Murciumbrío Vampírico",
|
||||
[1555] = "Nocturácnida Sañosa",
|
||||
[1557] = "Gorila Del Valle Velo de Bruma Viejo",
|
||||
[1558] = "Patriarca Lomoplata",
|
||||
[1688] = "Matriarca Nocturácnida",
|
||||
[1689] = "Jabalí Del Risco Cicatríceo",
|
||||
[1693] = "Crocolisco del Lago",
|
||||
[1713] = "Pantera Faucesombría Vieja",
|
||||
[1765] = "Huargo",
|
||||
[1766] = "Huargo Jaspeado",
|
||||
[1778] = "Oso Grisáceo Feroz",
|
||||
[1780] = "Acechamusgo",
|
||||
[1781] = "Trepanieblas",
|
||||
[1797] = "Oso Grisáceo Gigante",
|
||||
[1809] = "Buitre Carroñero",
|
||||
[1815] = "Oso Negro Malsano",
|
||||
[1816] = "Pardo Malsano",
|
||||
[1817] = "Lobo Malsano",
|
||||
[1822] = "Rondador NeboVenenoso",
|
||||
[1824] = "Rondador de Peste",
|
||||
[1922] = "Lobo Grisáceo Del Bosque",
|
||||
[1923] = "Huargo Sangrocico",
|
||||
[1961] = "Sarnagarra",
|
||||
[1984] = "Joven Jabalí Cardo",
|
||||
[1985] = "Jabalí Cardo",
|
||||
[1986] = "Araña Tejemadera",
|
||||
[1994] = "Githyiss la Vil",
|
||||
[1995] = "Lechuza Estrígida",
|
||||
[1996] = "Estridador Estrígido",
|
||||
[1997] = "Cazador Estrígido",
|
||||
[1998] = "Rondador Tejemadera",
|
||||
[1999] = "Venolmillo Tejemadera",
|
||||
[2000] = "Virasedal Tejemadera",
|
||||
[2001] = "Araña Tejemadera Gigante",
|
||||
[2031] = "Sable de la Noche Joven",
|
||||
[2032] = "Sable de la Noche Sarnoso",
|
||||
[2033] = "Sable de la Noche Mayor",
|
||||
[2034] = "Sable de la Noche Salvaje",
|
||||
[2042] = "Sable de la Noche",
|
||||
[2043] = "Acechador Sable de la Noche",
|
||||
[2069] = "Acechalunas",
|
||||
[2070] = "Cachorro Acechalunas",
|
||||
[2071] = "Matriarca Acechalunas",
|
||||
[2089] = "Crocolisco Gigante de Los Humedales",
|
||||
[2163] = "Oso Cardo",
|
||||
[2165] = "Oso Cardo Grisáceo",
|
||||
[2172] = "Zancador Pedalar",
|
||||
[2175] = "Garrasombría",
|
||||
[2231] = "Corremareas Marepígmeo",
|
||||
[2232] = "Corremareas",
|
||||
[2233] = "Corremareas Escamado",
|
||||
[2234] = "Joven Reptador Correarrecifes",
|
||||
[2235] = "Correarrecifes",
|
||||
[2236] = "Reptador Correarrecifes Enfurecido",
|
||||
[2237] = "Señor Acechalunas",
|
||||
[2275] = "Stanley Enfurecido",
|
||||
[2321] = "Fugalante Zancudo Del Bosque",
|
||||
[2322] = "Zancudo Del Bosque",
|
||||
[2323] = "Zancudo Del Bosque Gigante",
|
||||
[2348] = "Trepamusgo Mayor",
|
||||
[2349] = "Trepamusgo Gigante",
|
||||
[2350] = "Trepamusgo Del Bosque",
|
||||
[2351] = "Oso Gris",
|
||||
[2354] = "Oso Gris Sañoso",
|
||||
[2356] = "Oso Gris Viejo",
|
||||
[2384] = "León de Montaña Famélico",
|
||||
[2385] = "León de Montaña Feral",
|
||||
[2406] = "León de Montaña",
|
||||
[2407] = "León de Montaña Descomunal",
|
||||
[2408] = "Quijaforte",
|
||||
[2476] = "Gran Crocolisco del Lago",
|
||||
[2505] = "Quijaforte Marino",
|
||||
[2521] = "Gorila Cieloleno",
|
||||
[2522] = "Acechador Jaguero",
|
||||
[2544] = "Reptador de Las Arenas Del Sur",
|
||||
[2559] = "Zancudo de las Tierras Altas",
|
||||
[2560] = "Vándalo de las Tierras Altas",
|
||||
[2561] = "Acechacarne de las Tierras Altas",
|
||||
[2563] = "Trepador de Las Llanuras",
|
||||
[2565] = "Trepador de Las Llanuras Gigante",
|
||||
[2578] = "Águila Ratonera de Mesa Joven",
|
||||
[2579] = "Águila Ratonera Mesa",
|
||||
[2580] = "Águila Ratonera de Mesa Mayor",
|
||||
[2635] = "Crocolisco Marino Mayor",
|
||||
[2680] = "Lobato Vilrama",
|
||||
[2681] = "Lobo de Montar Vilrama",
|
||||
[2686] = "Guardia Del Linaje Secacorteza",
|
||||
[2727] = "Coyote del Risco",
|
||||
[2728] = "Coyote de Risco Feral",
|
||||
[2729] = "Coyote del Risco Mayor",
|
||||
[2730] = "Coyote del Risco Rabioso",
|
||||
[2731] = "Acechador de la Cresta",
|
||||
[2732] = "Cazadora de la Cresta",
|
||||
[2734] = "Patriarca Acechador de la Cresta",
|
||||
[2753] = "Barnabus",
|
||||
[2829] = "Águila Ratonera Hambrienta",
|
||||
[2830] = "Águila Ratonera",
|
||||
[2831] = "Águila Ratonera Gigante",
|
||||
[2850] = "Diente Partido",
|
||||
[2923] = "Melenargenta Sarnoso",
|
||||
[2924] = "Lobo Melenargenta",
|
||||
[2925] = "Aullador Melenargenta",
|
||||
[2926] = "Acechador Melenargenta",
|
||||
[2931] = "Zaricotl",
|
||||
[2954] = "Jabaguerrero Lomopelo",
|
||||
[2955] = "Zancudo de Llanura",
|
||||
[2956] = "Zancudo de Llanura Adulto",
|
||||
[2957] = "Zancudo de Llanura Viejo",
|
||||
[2958] = "Lobo de Pradera",
|
||||
[2959] = "Acechador de Pradera",
|
||||
[2960] = "Lobo Alfa de Pradera",
|
||||
[2961] = "Puma Montés",
|
||||
[2966] = "Jabaguerrero",
|
||||
[2969] = "Aguilón Nervudo",
|
||||
[2970] = "Aguilón",
|
||||
[2971] = "Aguilón Garrudo",
|
||||
[3035] = "Puma de las Estepas",
|
||||
[3068] = "Mazzranache",
|
||||
[3098] = "Jabalí Jaspeado",
|
||||
[3099] = "Jabalí Jaspeado Temible",
|
||||
[3100] = "Jabalí Jaspeado Mayor",
|
||||
[3106] = "Reptador de la Superficie Pigmeo",
|
||||
[3107] = "Reptador de Espuma",
|
||||
[3108] = "Reptador de Espuma Escamado",
|
||||
[3110] = "Crocolisco Fauzatroz",
|
||||
[3121] = "Tigre de Durotar",
|
||||
[3122] = "Colazote Garrasangre",
|
||||
[3123] = "Rojomorro Garrasangre",
|
||||
[3124] = "Trabajador Escórpido",
|
||||
[3125] = "Escórpido Castañeante",
|
||||
[3126] = "Escórpido Acorazado",
|
||||
[3127] = "Escórpido Venocola",
|
||||
[3225] = "Jabalí Jaspeado Corrupto",
|
||||
[3226] = "Escórpido Corrupto",
|
||||
[3227] = "Rojomorro Garrasangre Corrupto",
|
||||
[3228] = "Reptador de Oleaje Corrupto",
|
||||
[3231] = "Crocolisco Fauzatroz Corrupto",
|
||||
[3241] = "Patriarca de Sabana",
|
||||
[3243] = "Crinalta de la Sabana",
|
||||
[3244] = "Zancudo de Llanura Superior",
|
||||
[3245] = "Zancudo de Llanura Arisco",
|
||||
[3246] = "Zancudo Veloz de Llanura",
|
||||
[3247] = "Prole de Halcón Trueno",
|
||||
[3249] = "Halcón Trueno Superior",
|
||||
[3250] = "Trepador Silítido",
|
||||
[3252] = "Enjambre Silítido",
|
||||
[3254] = "Colazote Solescama",
|
||||
[3255] = "Estridador Solescama",
|
||||
[3256] = "Segador Solescama",
|
||||
[3257] = "Ishamuhale",
|
||||
[3281] = "Sarkoth",
|
||||
[3415] = "Cazadora de Sabana",
|
||||
[3416] = "Matriarca de Sabana",
|
||||
[3424] = "Halcón Trueno Desechanube",
|
||||
[3425] = "Merodeador de Sabana",
|
||||
[3461] = "Quijaforte del Oasis",
|
||||
[3472] = "Washte Pawne",
|
||||
[3475] = "Echeyakee",
|
||||
[3503] = "Protector Silítido",
|
||||
[3566] = "Merodeador de Llanuras",
|
||||
[3581] = "Bestia de Alcantarilla",
|
||||
[3619] = "Sable Fantasma",
|
||||
[3630] = "Volutador Descarriado",
|
||||
[3631] = "Aguijolatigador Descarriado",
|
||||
[3632] = "Trepador Descarriado",
|
||||
[3633] = "Destripador Descarriado",
|
||||
[3634] = "Acechador Descarriado",
|
||||
[3636] = "Devastador Descarriado",
|
||||
[3637] = "Guardián Descarriado",
|
||||
[3653] = "Kresh",
|
||||
[3809] = "Oso de Vallefresno",
|
||||
[3810] = "Oso Viejo de Vallefresno",
|
||||
[3811] = "Oso Gigante de Vallefresno",
|
||||
[3812] = "Reptador Castañeante",
|
||||
[3814] = "Reptador Espinado",
|
||||
[3819] = "Acechador Espinaferal",
|
||||
[3820] = "EscupeVeneno Espinaferal",
|
||||
[3821] = "Rondador Espinaferal",
|
||||
[3823] = "Corredor Garraespectro",
|
||||
[3824] = "Aullador Garraespectro",
|
||||
[3825] = "Alfa Garraespectro",
|
||||
[3861] = "Huargo Lóbrego",
|
||||
[3862] = "Huargo Babeante",
|
||||
[3866] = "Murciovil",
|
||||
[3868] = "Rastreasangre",
|
||||
[4005] = "Trepador Musgondo",
|
||||
[4006] = "Tejerred Musgondo",
|
||||
[4007] = "EscupeVeneno Musgondo",
|
||||
[4040] = "Acechador de Cuevas",
|
||||
[4067] = "Corredor Crepuscular",
|
||||
[4117] = "Serpiente Nubosa",
|
||||
[4118] = "Serpiente Nubosa Venenosa",
|
||||
[4119] = "Serpiente Nubosa Anciana",
|
||||
[4124] = "Puma de las Agujas",
|
||||
[4126] = "Acechador del Risco",
|
||||
[4127] = "Hiena Dentecenado",
|
||||
[4128] = "Acechador Dentecenado",
|
||||
[4129] = "Gruñidor Dentecenado",
|
||||
[4139] = "Terror Escórpido",
|
||||
[4140] = "Atracador Escórpido",
|
||||
[4142] = "Tortuga Caparallante",
|
||||
[4143] = "Sacudidor Caparallante",
|
||||
[4144] = "Cavapozos Caparallante",
|
||||
[4154] = "Carroñero del Desierto de Sal",
|
||||
[4158] = "Buitre del Desierto de Sal",
|
||||
[4248] = "Hiena Pestillejo",
|
||||
[4249] = "Gruñidor Pestillejo",
|
||||
[4250] = "Can de Manada Galak",
|
||||
[4263] = "Prole de Musgondo",
|
||||
[4264] = "Matriarca Musgondo",
|
||||
[4304] = "Sabueso Escarlata",
|
||||
[4316] = "Can de Manada Kolkar",
|
||||
[4341] = "Crocolisco Secorrón",
|
||||
[4342] = "Mentovilo Secorrón",
|
||||
[4343] = "Sacudidor Secorrón",
|
||||
[4344] = "Crocolisco Secorrón Jaspeado",
|
||||
[4345] = "Faucedaga Secorrón",
|
||||
[4351] = "Raptor Cienorrojo",
|
||||
[4352] = "Estridador Cienorrojo",
|
||||
[4355] = "Segador Cienorrojo",
|
||||
[4356] = "Tajobuche Cienorrojo",
|
||||
[4357] = "Colazote Cienorrojo",
|
||||
[4376] = "Araña Niebla Negra",
|
||||
[4377] = "Rondadora Niebla Negra",
|
||||
[4378] = "Ermitaña Niebla Negra",
|
||||
[4379] = "Virasedal Niebla Negra",
|
||||
[4380] = "Viuda Niebla Negra",
|
||||
[4396] = "Tortuga Fangorroca",
|
||||
[4397] = "Caparapico Fangorroca",
|
||||
[4398] = "Hurgador Fangorroca",
|
||||
[4399] = "Cavapozos Fangorroca",
|
||||
[4400] = "Quijaforte Fangorroca",
|
||||
[4411] = "Rondador Colmilloumbrío",
|
||||
[4412] = "Trepador Colmilloumbrío",
|
||||
[4413] = "Araña Colmilloumbrío",
|
||||
[4414] = "EscupeVeneno Colmilloumbrío",
|
||||
[4415] = "Araña Colmilloumbrío Gigante",
|
||||
[4425] = "Cazador ciego",
|
||||
[4511] = "Agam'ar",
|
||||
[4512] = "Agam'ar Putrefacto",
|
||||
[4514] = "Agam'ar Enfurecido",
|
||||
[4538] = "Murciélago del Horado",
|
||||
[4539] = "Murciélago del Horado Superior",
|
||||
[4548] = "Metaleador",
|
||||
[4660] = "Zarpósea Maraudine",
|
||||
[4662] = "Zarpósea Maggram",
|
||||
[4688] = "Hiena Zarpósea",
|
||||
[4689] = "Zarpósea Famélica",
|
||||
[4690] = "Zarpósea Rabiosa",
|
||||
[4692] = "Aguilón Aterrador",
|
||||
[4693] = "Volater Aterrador",
|
||||
[4694] = "Desgarrador Aterrador",
|
||||
[4695] = "Horror Carroñero",
|
||||
[4696] = "Sacudidor Scorpashi",
|
||||
[4697] = "Azotador Scorpashi",
|
||||
[4699] = "LatiVenenoso Scorpashi",
|
||||
[4821] = "Crustarácnido",
|
||||
[4822] = "Crustáceo Mordedor",
|
||||
[4823] = "Crustáceo Espinoso",
|
||||
[4824] = "Pescador Aku'mai",
|
||||
[4825] = "Quijaforte Aku'mai",
|
||||
[4841] = "Lodomuerto",
|
||||
[4861] = "Alcaudociélago",
|
||||
[4887] = "Ghamoo-ra",
|
||||
[4950] = "Mancha",
|
||||
[5048] = "Víbora Descarriada",
|
||||
[5053] = "Crocolisco Descarriado",
|
||||
[5056] = "Colminferno Descarriado",
|
||||
[5224] = "Deslizador de la Oscuridad",
|
||||
[5225] = "Escupidor de la Oscuridad",
|
||||
[5260] = "Simio Groddoc",
|
||||
[5262] = "Tronador Groddoc",
|
||||
[5268] = "Oso Cueracero",
|
||||
[5272] = "Oso Cueracero Grisáceo",
|
||||
[5274] = "Patriarca Cueracero",
|
||||
[5286] = "Corredor Dientelargo",
|
||||
[5287] = "Aullador Dientelargo",
|
||||
[5288] = "Dientelargo Rabioso",
|
||||
[5291] = "Alaescarcha Hakkari",
|
||||
[5307] = "Estridador del Valle",
|
||||
[5308] = "Estridador del Valle Huido",
|
||||
[5349] = "Arash-ethis",
|
||||
[5352] = "Viejo Tripasgrises",
|
||||
[5356] = "Gruñidor",
|
||||
[5422] = "Cazador Escórpido",
|
||||
[5423] = "Colazote Escórpido",
|
||||
[5424] = "Acechadunas Escórpido",
|
||||
[5425] = "Llagapata Hambrienta",
|
||||
[5426] = "Hiena Llagapata",
|
||||
[5427] = "Llagapata Rabiosa",
|
||||
[5428] = "Roc",
|
||||
[5429] = "Roc de Fuego",
|
||||
[5430] = "Roc Abrasador",
|
||||
[5431] = "Planoleador",
|
||||
[5432] = "Planoleador Gigante",
|
||||
[5708] = "Engendro de Hakkar",
|
||||
[5755] = "Víbora Descarriada",
|
||||
[5756] = "Venala Descarriada",
|
||||
[5762] = "Mocasin Descarriado",
|
||||
[5766] = "Cachorro de la Sabana",
|
||||
[5807] = "El Rastrillo",
|
||||
[5823] = "Despellejador de la Muerte",
|
||||
[5828] = "Humar el Señor Orgulloso",
|
||||
[5829] = "Bufo el Molesto",
|
||||
[5834] = "Azzere el Filo del Cielo",
|
||||
[5842] = "Takk el Saltarín",
|
||||
[5856] = "Araña Cristalderma",
|
||||
[5857] = "Lavaraña Abrasadora",
|
||||
[5858] = "Lavaraña Superior",
|
||||
[5865] = "Dishu",
|
||||
[5937] = "Aguijón Vil",
|
||||
[5982] = "Destripador Negro",
|
||||
[5984] = "Hiena Risitas Hambrienta",
|
||||
[5985] = "Hiena Risitas",
|
||||
[5988] = "Aguijonero Scorpok",
|
||||
[5992] = "Jabalí Cenicrín",
|
||||
[6013] = "Águila Ratonera Guardapaso",
|
||||
[6352] = "Rondador Corazacoral",
|
||||
[6369] = "Tortuga Corazacoral",
|
||||
[6505] = "Ravasaurio",
|
||||
[6506] = "Corredor Ravasaurio",
|
||||
[6507] = "Cazador Ravasaurio",
|
||||
[6508] = "Ravasaurio Pellejo Venenoso",
|
||||
[6513] = "Vapuleador de Un'Goro",
|
||||
[6514] = "Gorila de Un'Goro",
|
||||
[6516] = "Tronador de Un'Goro",
|
||||
[6581] = "Matriarca Ravasaurio",
|
||||
[6585] = "Uhk'loc",
|
||||
[6788] = "Madre del Cubil",
|
||||
[6789] = "Cachorro Cardo",
|
||||
[7022] = "Escórpido LatiVenenoso",
|
||||
[7055] = "Huargo Roca Negra",
|
||||
[7078] = "Escórpido de la Grieta",
|
||||
[7097] = "Lechuza Cortezaférrea",
|
||||
[7098] = "Estridador Cortezaférrea",
|
||||
[7099] = "Cazador Cortezaférrea",
|
||||
[7268] = "Guardián Furiarena",
|
||||
[7319] = "Lady Sathrah",
|
||||
[7405] = "Escórpido Del Risco Mortal",
|
||||
[7430] = "Cachorro de Sable de Escarcha",
|
||||
[7431] = "Sable de Hielo",
|
||||
[7432] = "Acechador Sable de Hielo",
|
||||
[7433] = "Cazadora Sable de Hielo",
|
||||
[7434] = "Vigía Del Orgullo Sable de Hielo",
|
||||
[7443] = "Aplastador Dentoesquirla",
|
||||
[7444] = "Oso Dentoesquirla",
|
||||
[7445] = "Ancestro Dentoesquirla",
|
||||
[7446] = "Dentoesquirla Rabioso",
|
||||
[7455] = "Lechuza de Cuna Del Invierno",
|
||||
[7456] = "Estridador de Cuna Del Invierno",
|
||||
[7803] = "Perforadunas Escórpido",
|
||||
[8207] = "Pájaro de Fuego Mayor",
|
||||
[8208] = "Llagapata Mortífera",
|
||||
[8211] = "Viejo Saltariscos",
|
||||
[8213] = "Espaldacerada",
|
||||
[8277] = "Rekk'tilac",
|
||||
[8299] = "Escupetripas",
|
||||
[8300] = "Devastatia",
|
||||
[8301] = "Clack el Atracador",
|
||||
[8303] = "Gruñón",
|
||||
[8336] = "Zapador Hakkari",
|
||||
[8600] = "Murcipeste",
|
||||
[8601] = "Murcipeste Nocivo",
|
||||
[8602] = "Murcipeste Monstruoso",
|
||||
[8762] = "Ermitaña Telamadera",
|
||||
[8926] = "Aguijonero Profundo",
|
||||
[8927] = "Estridador Sombrío",
|
||||
[8933] = "Trepador de Cuevas",
|
||||
[8956] = "Oso Zarpira",
|
||||
[8958] = "Aplastador Zarpira",
|
||||
[8959] = "Lobo Zarpayel",
|
||||
[8960] = "Carroñero Zarpayel",
|
||||
[8961] = "Devastador Zarpayel",
|
||||
[9416] = "Huargo del Escudo del Estigma",
|
||||
[9622] = "U'cha",
|
||||
[9683] = "Compañera de Lar'Korwi",
|
||||
[9684] = "Lar'korwi",
|
||||
[9690] = "Huargo Brasal",
|
||||
[9691] = "Escórpido Venopunta",
|
||||
[9694] = "Huargo Brasal Esclavizante",
|
||||
[9695] = "Escórpido Latimorte",
|
||||
[9696] = "Huargo Hacha de Sangre",
|
||||
[9697] = "Huargo Brasal Gigante",
|
||||
[9698] = "Escórpido Colafuego",
|
||||
[10077] = "Morifauces",
|
||||
[10200] = "Rak'shiri",
|
||||
[10221] = "Mascolupo Hacha de Sangre",
|
||||
[10356] = "Bayne",
|
||||
[10357] = "Ressan el Enfilador",
|
||||
[10359] = "Sri'skulk",
|
||||
[10375] = "Arañita de la Cumbre",
|
||||
[10644] = "Aullanieblas",
|
||||
[10737] = "Vergonzoso-Rotam",
|
||||
[10741] = "Sian-Rotam",
|
||||
[10806] = "Ursius",
|
||||
[10882] = "Arikara",
|
||||
[10981] = "Lobo Gélido",
|
||||
[11357] = "Hijo de Hakkar",
|
||||
[11360] = "Cachorro Zulian",
|
||||
[11361] = "Tigre Zulian",
|
||||
[11365] = "Tigre Zuliano",
|
||||
[11368] = "Murciélago Buscasangre",
|
||||
[11370] = "Proleviuda Razzashi",
|
||||
[11371] = "Serpiente Razzashi",
|
||||
[11372] = "Víbora Razzashi",
|
||||
[11735] = "Escórpido Latipiedra",
|
||||
[11736] = "Tenazario Latipiedra",
|
||||
[11737] = "Despellejador Latipiedra",
|
||||
[11738] = "Arácnido de Arena",
|
||||
[11739] = "Acecharrocas",
|
||||
[11921] = "Besseleth",
|
||||
[12037] = "Ursol'lok",
|
||||
[12431] = "Mandisangre",
|
||||
[12432] = "Viejo Malafauce",
|
||||
[12433] = "Krethis Sombravolta",
|
||||
[13599] = "Quijaforte Imperturbable",
|
||||
[14123] = "Sacudidor Quijacero",
|
||||
[14222] = "Araga",
|
||||
[14223] = "Cascarrabias Ben",
|
||||
[14228] = "Giggler",
|
||||
[14232] = "Dardo",
|
||||
[14233] = "Rasgascama",
|
||||
[14234] = "Hayoc",
|
||||
[14266] = "Shanda la Giratoria",
|
||||
[14268] = "Lord Condar",
|
||||
[14279] = "Trepazoso",
|
||||
[14280] = "Gran Samras",
|
||||
[14283] = "Lechuza Pico Tormenta",
|
||||
[14339] = "Aullador de la Muerte",
|
||||
[14343] = "Olm el Sabio",
|
||||
[14344] = "Mongress",
|
||||
[14472] = "Gretheer",
|
||||
[14476] = "Krellack",
|
||||
[14491] = "Kurmokk",
|
||||
[14532] = "Venenoso Razzashi",
|
||||
[14821] = "Raptor Razzashi",
|
||||
[14965] = "Murciélago Procurasangre Demenciado",
|
||||
[15041] = "Alevín de Mar'li",
|
||||
[15043] = "Crocolisco Zulian",
|
||||
[15101] = "Merodeador Zulian",
|
||||
[16117] = "Cerdo Infestado",
|
||||
[36507] = "Mancha",
|
||||
[36514] = "Cachorro de Sable de Escarcha",
|
||||
[37007] = "Prole de Viuda Negra",
|
||||
[40000] = "Zorro Rojo",
|
||||
[50624] = "Enjambre Silítido",
|
||||
[50627] = "Enjambre Silítido",
|
||||
[50630] = "Enjambre Silítido",
|
||||
[60494] = "Zarpa de Pesar",
|
||||
[60697] = "Can de la Calma",
|
||||
[60848] = "Jabalí Lomopelo",
|
||||
[61074] = "Gorila de Valalto",
|
||||
[61075] = "Reptador Caparazón de Roca",
|
||||
[61076] = "Reptador Caparazón de Arena",
|
||||
[61077] = "Chimpancé de Valalto",
|
||||
[61078] = "Mono de Valalto",
|
||||
[61080] = "Anciano Gorila de Valalto",
|
||||
[61082] = "Tronador de Valalto",
|
||||
[61090] = "Cobra Colamatorral",
|
||||
[61091] = "Serpiente Colamatorral",
|
||||
[61095] = "Chillador Venodesuellador",
|
||||
[61096] = "Serpiente Venodesuellador",
|
||||
[61098] = "Rey Morogo Pies de Trueno",
|
||||
[61206] = "Reptador de Telaraña Escurridiza",
|
||||
[61218] = "Cría de Serpiente Descarriado",
|
||||
[61227] = "Acechador Fauceumbría",
|
||||
[61228] = "Acechador Fauceumbría",
|
||||
[61229] = "Piel Oscura Hambriento",
|
||||
[61230] = "Cría de Murciélago Ala Vil",
|
||||
[61231] = "Murciélago Ala Vil",
|
||||
[61232] = "Araña de Red Hueca",
|
||||
[61233] = "Escupidor de Veneno de Red Hueca",
|
||||
[61336] = "Búho Grancuerno",
|
||||
[61337] = "Cazador Grancuerno",
|
||||
[61401] = "Murciélago Alavil Superior",
|
||||
[61488] = "Picoazul",
|
||||
[61500] = "Espalda Plateada de Valalto",
|
||||
[61552] = "Correanoche",
|
||||
[61554] = "Aullaalba",
|
||||
[61599] = "Sable Oscuro",
|
||||
[61656] = "Buitre Pluma de Ceniza",
|
||||
[61657] = "Planeador Pluma de Ceniza",
|
||||
[61658] = "Hiena Pataceniza",
|
||||
[61659] = "Cazador Pataceniza",
|
||||
[61660] = "Escórpido Colapétrea",
|
||||
[61661] = "Azotador Colapétrea",
|
||||
[61662] = "Escórpido Veneno Negro",
|
||||
[61680] = "Reptador de Arrecife de Fango",
|
||||
[61681] = "Reptador Arrecife de Lodo Joven",
|
||||
[61686] = "Viajero Pluma de Ceniza",
|
||||
[61689] = "Jabalí Thalassiano Jóven",
|
||||
[61690] = "Lince Carmesí Jóven",
|
||||
[61691] = "Jinete de Águila del Bosque",
|
||||
[61692] = "Jabalí Thalassiano",
|
||||
[61693] = "Lince Carmesí",
|
||||
[61695] = "Halcón Thalassiano Marfil",
|
||||
[61696] = "Jabalí Thalassiano Anciano",
|
||||
[61697] = "Lynx Brillante",
|
||||
[61698] = "Lynx Brillante Matriarca",
|
||||
[61699] = "Shar'lan",
|
||||
[61700] = "Lynx Carmesí Viejo",
|
||||
[61704] = "Reptador Errante",
|
||||
[61716] = "Corcel alado Carmesí",
|
||||
[61717] = "Zorro Thalassiano",
|
||||
[61774] = "Maldición de Maná Brillante",
|
||||
[61775] = "Cachorro de Maná Esmeralda",
|
||||
[61776] = "Serpiente de Maná Lavanda",
|
||||
[61789] = "Pluma Lunar",
|
||||
[61975] = "Jabalí Colmilloalto",
|
||||
[62066] = "Madre Criadora Tejecaverna",
|
||||
[62073] = "Araña Tejecaverna",
|
||||
[62074] = "Escalador Tejecaverna",
|
||||
[62075] = "Escupidor de Veneno Tejecaverna",
|
||||
[62121] = "Jabalí Espinofirme ",
|
||||
[62122] = "Jabalí Espinofirme Grande",
|
||||
[62123] = "Oso Pelodoro",
|
||||
[62124] = "Oso Pardo Pelodoro",
|
||||
[62125] = "Lobo del Bosque Anciano",
|
||||
[62126] = "Alfa del Bosque Anciano",
|
||||
[62223] = "Raptor Escama Sombría",
|
||||
[62224] = "Chillón Escama Sombría",
|
||||
[62225] = "Azotador Escama Sombría",
|
||||
[62226] = "Jabalí Piel de Piedra",
|
||||
[62227] = "Jabalí de Piel de Piedra Anciano",
|
||||
[62247] = "Reclusa Marrón",
|
||||
[62248] = "Reclusa Marrón Superior",
|
||||
[62256] = "Quijaforte de Agua Dulce",
|
||||
[62257] = "Quijada de Agua Clara Enfermizo",
|
||||
[62258] = "Crocolisco Dientefilo",
|
||||
[62259] = "Cocodrilo Denteserra Gigante",
|
||||
[62260] = "Jabalí del Bosque",
|
||||
[62261] = "Jabalí del Bosque Viejo",
|
||||
[62262] = "Jabalí de la Mesa",
|
||||
[62271] = "Jabalí Dientebrillo Joven",
|
||||
[62272] = "Lobo Maderero Joven",
|
||||
[62338] = "Buitre Ala Tormenta",
|
||||
[62339] = "Buitre Ala Tormenta",
|
||||
[62340] = "Coyote Rocoso Andrajoso",
|
||||
[62341] = "Coyote Rocoso Babeante",
|
||||
[62342] = "Araña Cortezaniebla",
|
||||
[62343] = "Tejedora Cortezaniebla",
|
||||
[62344] = "Madre Cortezaniebla",
|
||||
[62345] = "Crocolisco Lamentoso",
|
||||
[62642] = "Faucefilosa",
|
||||
[80250] = "Lince Carmesí",
|
||||
[80252] = "Zorro Ártico Joven",
|
||||
[80254] = "Zorro Marrón Joven",
|
||||
[80256] = "Zorro Plateado",
|
||||
[80257] = "Zorro Rojo",
|
||||
[80258] = "Zorro de Niebla Joven",
|
||||
[80259] = "Zorro de la Niebla",
|
||||
[80260] = "Zorro Espiritual",
|
||||
[80466] = "Oso Apestado de Tirisfal",
|
||||
[80815] = "Águila Amani",
|
||||
[80816] = "Águila de Las Tierras del Interior",
|
||||
[80920] = "Águila Amani",
|
||||
[90980] = "Tigre Zuliano",
|
||||
[90981] = "Tigre Zuliano",
|
||||
[91819] = "Tigre Hazzuri",
|
||||
[91825] = "Tigre de Bengala Joven",
|
||||
[91826] = "Matriarca de Bengala",
|
||||
[91827] = "Tigre de Bengala",
|
||||
[91828] = "Alfa de Bengala",
|
||||
[91829] = "Pisoteador Lomo de Jungla",
|
||||
[91830] = "Azotador Lomo de Jungla",
|
||||
[91831] = "Reptador Luminoso",
|
||||
[91832] = "Pinza de la Isla",
|
||||
[91833] = "Reptador de Rocas",
|
||||
[91838] = "Sacudidor Caparazhondo",
|
||||
[91960] = "Pantera Zarpa de Jungla",
|
||||
[91961] = "Pantera de las Sombras Zarpa de Jungla",
|
||||
[91962] = "Crocolisco Rompesal",
|
||||
[91966] = "Serpiente de la Selva Venenosa",
|
||||
[91984] = "Oso Tirisgarra",
|
||||
[91985] = "Cachorro Tirisgarra",
|
||||
[91987] = "Jabalí Riscolmillo",
|
||||
[91989] = "Lobo Zarpa Gris",
|
||||
[91990] = "Alfa Zarpa Gris",
|
||||
[92115] = "Garrafilada Enfurecida",
|
||||
[92146] = "Reptador de la Playa",
|
||||
[92147] = "Reptador de la Costa de Plata",
|
||||
}
|
||||
@@ -0,0 +1,713 @@
|
||||
MTH_LocaleData = MTH_LocaleData or {}
|
||||
MTH_LocaleData.data = MTH_LocaleData.data or {}
|
||||
MTH_LocaleData.data.beasts = MTH_LocaleData.data.beasts or {}
|
||||
|
||||
MTH_LocaleData.data.beasts.ptBR = {
|
||||
[30] = "Aranha da Floresta",
|
||||
[43] = "Aranha da Mina",
|
||||
[69] = "Lobo Silvestre",
|
||||
[113] = "Javali Pedratusco",
|
||||
[118] = "Predador",
|
||||
[119] = "Fuçalonga",
|
||||
[154] = "Ripa-Carne Maior",
|
||||
[157] = "Goretusk",
|
||||
[199] = "Ripa-carne Jovem",
|
||||
[213] = "Lobo Hediondo Faminto",
|
||||
[217] = "Venaracna",
|
||||
[299] = "Lobo Jovem",
|
||||
[330] = "Princesa",
|
||||
[345] = "Ronquifuça",
|
||||
[390] = "Séquito Porcino",
|
||||
[428] = "Condor Hediondo",
|
||||
[442] = "Tarântula",
|
||||
[454] = "Gorestusco Jovem",
|
||||
[462] = "Vultros",
|
||||
[471] = "Mãe Veneno",
|
||||
[505] = "Grande Tarântula",
|
||||
[521] = "Lupos",
|
||||
[524] = "Javali Couropedra",
|
||||
[525] = "Lobo Sarnento",
|
||||
[539] = "Veneracna Pigmeia",
|
||||
[547] = "Grande Goretusk",
|
||||
[565] = "Lobo Hediondo Raivoso",
|
||||
[569] = "Recluso Verde",
|
||||
[574] = "Naraxis",
|
||||
[616] = "Palpos",
|
||||
[628] = "Devastador Negro",
|
||||
[681] = "Tigre-do-espinhaço Jovem",
|
||||
[682] = "Tigre da Selva do Espinhaço",
|
||||
[683] = "Pantera Jovem",
|
||||
[684] = "Pantera Umbragorja",
|
||||
[685] = "Raptor da Selva do Espinhaço",
|
||||
[686] = "Raptor Batecauda",
|
||||
[687] = "Perseguidor da Selva",
|
||||
[704] = "Lobo Silvestre Sarnento",
|
||||
[705] = "Lobo Jovem Roto",
|
||||
[708] = "Rochetusco Pequeno",
|
||||
[728] = "Bhag'thera",
|
||||
[729] = "Sin'dall",
|
||||
[730] = "Tethis",
|
||||
[731] = "Rei Bangalash",
|
||||
[736] = "Pantera",
|
||||
[767] = "Onça do Pântano",
|
||||
[768] = "Pantera das Sombras",
|
||||
[769] = "Tarântula Mataca",
|
||||
[772] = "Tigresa da Selva do Espinhaço",
|
||||
[822] = "Ursinho da Floresta",
|
||||
[830] = "Rastreador de Areia",
|
||||
[831] = "Rastreador do Mar",
|
||||
[833] = "Coiote-alfa",
|
||||
[834] = "Coiote",
|
||||
[854] = "Espreitador da Selva Jovem",
|
||||
[855] = "Raptor-do-espinhaço Jovem",
|
||||
[856] = "Raptor Batecauda Jovem",
|
||||
[858] = "Girador de Tristeza",
|
||||
[922] = "Rastrejante do Lodo",
|
||||
[923] = "Assolador Negro Jovem",
|
||||
[930] = "Filhote de Viúva Negra",
|
||||
[949] = "Reclusa Carniceira",
|
||||
[976] = "Tigre de Guerra Kurzen",
|
||||
[977] = "Pantera de Guerra Kurzen",
|
||||
[1015] = "Raptor Montanhês",
|
||||
[1016] = "Batecauda Montanhês",
|
||||
[1017] = "Garrafoice Montanhês",
|
||||
[1018] = "Rasgaqueixo Montanhês",
|
||||
[1019] = "Rasgaqueixo Ancião",
|
||||
[1020] = "Raptor Mosqueado",
|
||||
[1021] = "Guinchador Mosqueado",
|
||||
[1022] = "Garrafoice Mosqueado",
|
||||
[1023] = "Rasgaqueixo Mosqueado",
|
||||
[1082] = "Crocolisco Dentesserra",
|
||||
[1084] = "Crocolisco Dentesserra Jovem",
|
||||
[1087] = "Tartalisca Dentesserra",
|
||||
[1088] = "Rastejante Monstruoso",
|
||||
[1108] = "Gorila do Vale da Névoa",
|
||||
[1109] = "Ripa-carne",
|
||||
[1111] = "Espreitadora Sanguessuga",
|
||||
[1112] = "Viúva Negra Sanguessuga",
|
||||
[1114] = "Trovejador da Selva",
|
||||
[1125] = "Rochetusco",
|
||||
[1126] = "Rochetusco Grande",
|
||||
[1127] = "Rochetusco Ancião",
|
||||
[1128] = "Urso Preto Jovem",
|
||||
[1129] = "Urso Preto",
|
||||
[1130] = "Bjarn",
|
||||
[1131] = "Lobo Invernal",
|
||||
[1132] = "Lenho",
|
||||
[1133] = "Lobo Invernal Faminto",
|
||||
[1138] = "Lobo Rastreador da Neve",
|
||||
[1140] = "Matriarca Rasgaqueixo",
|
||||
[1150] = "Crocolisco do Rio",
|
||||
[1152] = "Crocolisco Arapuca",
|
||||
[1184] = "Tocaieira do Penhasco",
|
||||
[1185] = "Tocaieira da Mata",
|
||||
[1186] = "Urso Preto Ancião",
|
||||
[1188] = "Urso Pardo Preto",
|
||||
[1189] = "Urso Preto Patriarca",
|
||||
[1190] = "Javali Montanhês",
|
||||
[1191] = "Javali Montanhês Sarnento",
|
||||
[1192] = "Javali Montanhês Ancião",
|
||||
[1194] = "Urubu Montanhês",
|
||||
[1195] = "Tocaieira da Floresta",
|
||||
[1196] = "Urso Garra de Gelo",
|
||||
[1199] = "Leopardo da Neve Juvenil",
|
||||
[1201] = "Leopardo da Neve",
|
||||
[1216] = "Orlatisco",
|
||||
[1225] = "Velho Fumaça",
|
||||
[1258] = "Mastim Assolador Negro",
|
||||
[1400] = "Crocolisco do Pantanal",
|
||||
[1417] = "Crocolisco do Pantanal Jovem",
|
||||
[1504] = "Trevateia Jovem",
|
||||
[1505] = "Trevateia",
|
||||
[1508] = "Carniceiro Jovem",
|
||||
[1509] = "Carniceiro Roto",
|
||||
[1511] = "Gorila Costalva Enfurecido",
|
||||
[1512] = "Quiropúsculo",
|
||||
[1513] = "Quiropúsculo Sarnento",
|
||||
[1516] = "Konda",
|
||||
[1547] = "Cão das Trevas Decrépito",
|
||||
[1548] = "Cão das Trevas Amaldiçoado",
|
||||
[1549] = "Cão das Trevas Voraz",
|
||||
[1553] = "Grande Quiropúsculo",
|
||||
[1554] = "Quiropúsculo Vampírico",
|
||||
[1555] = "Trevateia Malévola",
|
||||
[1557] = "Gorila Ancião do Vale da Névoa",
|
||||
[1558] = "Patriarca Costalva",
|
||||
[1688] = "Matriarca Trevateia",
|
||||
[1689] = "Rochetusco Cicatrizado",
|
||||
[1693] = "Crocolisco Lacustre",
|
||||
[1713] = "Pantera Umbragorja Anciã",
|
||||
[1765] = "Worg",
|
||||
[1766] = "Worg Mosqueado",
|
||||
[1778] = "Urso Cinzento Feroz",
|
||||
[1780] = "Espreitadora do Musgo",
|
||||
[1781] = "Rastejante da Bruma",
|
||||
[1797] = "Urso Cinzento Gigante",
|
||||
[1809] = "Abutre Carniceiro",
|
||||
[1815] = "Urso Preto Doente",
|
||||
[1816] = "Urso-cinzento Doente",
|
||||
[1817] = "Lobo Doente",
|
||||
[1822] = "Tocaieira Rasterruça",
|
||||
[1824] = "Tocaieira Pestilenta",
|
||||
[1922] = "Lobo Cinza da Floresta",
|
||||
[1923] = "Worg Rinossangue",
|
||||
[1961] = "Ronhagarra",
|
||||
[1984] = "Javali-espinho Jovem",
|
||||
[1985] = "Javali-espinho",
|
||||
[1986] = "Aranha Lenhateia",
|
||||
[1994] = "Githyiss, a Torpe",
|
||||
[1995] = "Coruja Estrígida",
|
||||
[1996] = "Guinchadora Estrígida",
|
||||
[1997] = "Caçadora Estrígida",
|
||||
[1998] = "Tocaieira Lenhateia",
|
||||
[1999] = "Lenhateia Peçonhenta",
|
||||
[2000] = "Lenhateia Fiandeira",
|
||||
[2001] = "Aranha Lenhateia Gigante",
|
||||
[2031] = "Sabre-da-noite Jovem",
|
||||
[2032] = "Sabre-da-noite Sarnento",
|
||||
[2033] = "Sabre-da-noite Ancião",
|
||||
[2034] = "Sabre-da-noite Feral",
|
||||
[2042] = "Sabre-da-noite",
|
||||
[2043] = "Espreitador Sabre-da-noite",
|
||||
[2069] = "Espreitaluna",
|
||||
[2070] = "Espreitaluna Raquítico",
|
||||
[2071] = "Matriarca Espreitaluna",
|
||||
[2089] = "Crocolisco do Pantanal Gigante",
|
||||
[2163] = "Ursocardo",
|
||||
[2165] = "Ursocardo Grisalho",
|
||||
[2172] = "Mamãe Moa da Floresta",
|
||||
[2175] = "Garrassombra",
|
||||
[2231] = "Maretisco Pigmeu",
|
||||
[2232] = "Maretisco",
|
||||
[2233] = "Maretisco Incrustado",
|
||||
[2234] = "Tiscoral Jovem",
|
||||
[2235] = "Tiscoral",
|
||||
[2236] = "Tiscoral Furioso",
|
||||
[2237] = "Espreitaluna Macho",
|
||||
[2275] = "Fido Enraivecido",
|
||||
[2321] = "Filhote de Florestruz",
|
||||
[2322] = "Florestruz",
|
||||
[2323] = "Florestruz Gigante",
|
||||
[2348] = "Rastejamusgo Anciã",
|
||||
[2349] = "Rastejante do Musgo Gigante",
|
||||
[2350] = "Rastejante do Musgo da Floresta",
|
||||
[2351] = "Urso Cinzento",
|
||||
[2354] = "Urso-cinzento Feroz",
|
||||
[2356] = "Urso Cinzento Ancião",
|
||||
[2384] = "Leão da Montanha Esfomeado",
|
||||
[2385] = "Leão da Montanha Selvagem",
|
||||
[2406] = "Leão da Montanha",
|
||||
[2407] = "Leão da Montanha Imponente",
|
||||
[2408] = "Mordelisca",
|
||||
[2476] = "Crocolisco Lacustre Grande",
|
||||
[2505] = "Mordelisca de Água Salgada",
|
||||
[2521] = "Gorila Grenhacéu",
|
||||
[2522] = "Espreitador Jaguara",
|
||||
[2544] = "Aretisco Austral",
|
||||
[2559] = "Andarilho do Planalto",
|
||||
[2560] = "Espancador do Planalto",
|
||||
[2561] = "Fareja-carne do Planalto",
|
||||
[2563] = "Rastejante das Planícies",
|
||||
[2565] = "Rastejante Gigante das Planícies",
|
||||
[2578] = "Filhote de Urubu da Chapada",
|
||||
[2579] = "Urubu da Chapada",
|
||||
[2580] = "Urubu Ancião da Chapada",
|
||||
[2635] = "Crocolisco de Água Salgada Ancião",
|
||||
[2680] = "Filhote de Lobo Ramatorpe",
|
||||
[2681] = "Lobo Saqueador Ramatorpe",
|
||||
[2686] = "Guarda-prole Cascasseca",
|
||||
[2727] = "Coiote do Rochedo",
|
||||
[2728] = "Coiote Feroz do Rochedo",
|
||||
[2729] = "Coiote Ancião do Rochedo",
|
||||
[2730] = "Coiote Raivoso do Rochedo",
|
||||
[2731] = "Espreitador da Serra",
|
||||
[2732] = "Caçadora da Serra",
|
||||
[2734] = "Patriarca Espreitador da Serra",
|
||||
[2753] = "Barnabus",
|
||||
[2829] = "Urubu Faminto",
|
||||
[2830] = "Urubu",
|
||||
[2831] = "Urubu Gigante",
|
||||
[2850] = "Dente Quebrado",
|
||||
[2923] = "Jubaprata Sarnento",
|
||||
[2924] = "Lobo Jubaprata",
|
||||
[2925] = "Uivador Jubaprata",
|
||||
[2926] = "Espreitador Jubaprata",
|
||||
[2931] = "Zaricotl",
|
||||
[2954] = "Javaliço Costagulha",
|
||||
[2955] = "Pinote",
|
||||
[2956] = "Pinote Adulto",
|
||||
[2957] = "Pinote Ancião",
|
||||
[2958] = "Lobo-da-pradaria",
|
||||
[2959] = "Espreitador-da-pradaria",
|
||||
[2960] = "Lobo-da-pradaria Alfa",
|
||||
[2961] = "Puma da Montanha",
|
||||
[2966] = "Javaliço",
|
||||
[2969] = "Rapineiro Esguio",
|
||||
[2970] = "Rapineiro",
|
||||
[2971] = "Rapineiro de Gadanho",
|
||||
[3035] = "Puma das Estepes",
|
||||
[3068] = "Mazzranache",
|
||||
[3098] = "Mosquetusco",
|
||||
[3099] = "Mosquetusco Hediondo",
|
||||
[3100] = "Mosquetusco Ancião",
|
||||
[3106] = "Surfatisco Pigmeu",
|
||||
[3107] = "Surfatisco",
|
||||
[3108] = "Surfatisco Incrustado",
|
||||
[3110] = "Crocolisco Bocarrão",
|
||||
[3121] = "Tigre de Durotar",
|
||||
[3122] = "Açoitacauda Garrassangre",
|
||||
[3123] = "Bocalfanje Garrassangre",
|
||||
[3124] = "Escorpídeo Operário",
|
||||
[3125] = "Escorpídeo Estalador",
|
||||
[3126] = "Escorpídeo Encouraçado",
|
||||
[3127] = "Escorpídeo Caudaçonha",
|
||||
[3225] = "Mosquetusco Corrompido",
|
||||
[3226] = "Escorpídeo Corrompido",
|
||||
[3227] = "Bocalfanje Garrassangre Corrompido",
|
||||
[3228] = "Surfatisco Corrompido",
|
||||
[3231] = "Crocolisco Bocarrão Corrompido",
|
||||
[3241] = "Patriarca da Savana",
|
||||
[3243] = "Jubalta da Savana",
|
||||
[3244] = "Pinote Grande",
|
||||
[3245] = "Pinote Turrão",
|
||||
[3246] = "Pinote Fugaz",
|
||||
[3247] = "Filhote de Falcotrom",
|
||||
[3249] = "Grande Falcotrom",
|
||||
[3250] = "Rastejante Silitídeo",
|
||||
[3252] = "Enxameador Silitídeo",
|
||||
[3254] = "Batecauda Helióscamo",
|
||||
[3255] = "Uivador Helióscamo",
|
||||
[3256] = "Garrafoice Helióscamo",
|
||||
[3257] = "Ishamuhale",
|
||||
[3281] = "Sarkoth",
|
||||
[3415] = "Caçadora da Savana",
|
||||
[3416] = "Matriarca da Savana",
|
||||
[3424] = "Falcotrom Raspa-nuvens",
|
||||
[3425] = "Predador da Savana",
|
||||
[3461] = "Mordelisca do Oásis",
|
||||
[3472] = "Anhan Guera",
|
||||
[3475] = "Echeyaki",
|
||||
[3503] = "Protetor Silitídeo",
|
||||
[3566] = "Predador das Estepes",
|
||||
[3581] = "Fera do Esgoto",
|
||||
[3619] = "Sabre Fantasma",
|
||||
[3630] = "Espiríleo Anormal",
|
||||
[3631] = "Cravaçoite Anormal",
|
||||
[3632] = "Rastejante Anormal",
|
||||
[3633] = "Matador Anormal",
|
||||
[3634] = "Espreitador Anormal",
|
||||
[3636] = "Assolador Anormal",
|
||||
[3637] = "Guardião Anormal",
|
||||
[3653] = "Cresh",
|
||||
[3809] = "Urso do Vale Gris",
|
||||
[3810] = "Urso Ancião do Vale Gris",
|
||||
[3811] = "Urso Gigante do Vale Gris",
|
||||
[3812] = "Rastetisco Espocador",
|
||||
[3814] = "Espetisco",
|
||||
[3819] = "Espreitadora Cardagreste",
|
||||
[3820] = "Cospeçonha Cardagreste",
|
||||
[3821] = "Tocaieira Cardagreste",
|
||||
[3823] = "Corredor Patalmas",
|
||||
[3824] = "Patalmas Uivador",
|
||||
[3825] = "Patalmas Alfa",
|
||||
[3861] = "Worg Sinistro",
|
||||
[3862] = "Worg Salivante",
|
||||
[3866] = "Morcego Torpe",
|
||||
[3868] = "Busca-sangue",
|
||||
[4005] = "Rastejante de Fundolimo",
|
||||
[4006] = "Teceteia de Fundolimo",
|
||||
[4007] = "Cospeçonha de Fundolimo",
|
||||
[4040] = "Espreitadora da Caverna",
|
||||
[4067] = "Corredor do Crepúsculo",
|
||||
[4117] = "Serpente das Nuvens",
|
||||
[4118] = "Serpente das Nuvens Venenosa",
|
||||
[4119] = "Serpente das Nuvens Anciã",
|
||||
[4124] = "Puma de Mil Agulhas",
|
||||
[4126] = "Espreitador dos Penhascos",
|
||||
[4127] = "Hiena Presavaia",
|
||||
[4128] = "Espreitador Presavaia",
|
||||
[4129] = "Rosnador Presavaia",
|
||||
[4139] = "Terror Escorpídeo",
|
||||
[4140] = "Aniquilador Escorpídeo",
|
||||
[4142] = "Tartaruga Quelobrilho",
|
||||
[4143] = "Tartalisca Quelobrilho",
|
||||
[4144] = "Perfurador Quelobrilho",
|
||||
[4154] = "Catador da Planície Salgada",
|
||||
[4158] = "Abutre da Planície Salgada",
|
||||
[4248] = "Hiena Courirrita",
|
||||
[4249] = "Rosnadora Courirrita",
|
||||
[4250] = "Galath Alcateio",
|
||||
[4263] = "Filhote de Aranha de Fundolimo",
|
||||
[4264] = "Matriarca de Fundolimo",
|
||||
[4304] = "Cão Farejador Escarlate",
|
||||
[4316] = "Kolkar Alcateio",
|
||||
[4341] = "Crocolísco Secâmoro",
|
||||
[4342] = "Mandivil Secâmoro",
|
||||
[4343] = "Abocalisco Secâmoro",
|
||||
[4344] = "Crocolísco Secâmoro Mosqueado",
|
||||
[4345] = "Secâmoro Gorjadaga",
|
||||
[4351] = "Raptor Dinossangue",
|
||||
[4352] = "Gritador Dinossangue",
|
||||
[4355] = "Garrafoice Dinossangue",
|
||||
[4356] = "Rasgaqueixo Dinossangue",
|
||||
[4357] = "Batecauda Dinossangue",
|
||||
[4376] = "Aranha Névoa Negra",
|
||||
[4377] = "Tocaieira Névoa Negra",
|
||||
[4378] = "Reclusa Névoa Negra",
|
||||
[4379] = "Tecesseda Névoa Negra",
|
||||
[4380] = "Viúva Névoa Negra",
|
||||
[4396] = "Quelônio Pedralama",
|
||||
[4397] = "Espicasca Pedralama",
|
||||
[4398] = "Toqueira Pedralama",
|
||||
[4399] = "Escavadora Pedralama",
|
||||
[4400] = "Mordelisca Pedralama",
|
||||
[4411] = "Tocaieira Presanegra",
|
||||
[4412] = "Rastejante Presanegra",
|
||||
[4413] = "Aranha Presanegra",
|
||||
[4414] = "Cospeçonha Presanegra",
|
||||
[4415] = "Aranha Presanegra Gigante",
|
||||
[4425] = "Caçador Cego",
|
||||
[4511] = "Agam'ar",
|
||||
[4512] = "Agam'ar Pútrido",
|
||||
[4514] = "Agam'ar Furioso",
|
||||
[4538] = "Morcego do Urzal",
|
||||
[4539] = "Grande Morcego do Urzal",
|
||||
[4548] = "Estalaço",
|
||||
[4660] = "Pataóssea Maraudino",
|
||||
[4662] = "Pataóssea Magram",
|
||||
[4688] = "Hiena Pataóssea",
|
||||
[4689] = "Pataóssea Esfomeada",
|
||||
[4690] = "Pataóssea Raivosa",
|
||||
[4692] = "Pavor Rapineiro",
|
||||
[4693] = "Pavor Alado",
|
||||
[4694] = "Pavor Estripante",
|
||||
[4695] = "Carniceiro Horrendo",
|
||||
[4696] = "Escorpachi Estalante",
|
||||
[4697] = "Escorpachi Açoitador",
|
||||
[4699] = "Escorpachi Venelátego",
|
||||
[4821] = "Crustáceo Deslizante",
|
||||
[4822] = "Crustáceo Mordedor",
|
||||
[4823] = "Crustáceo Farpado",
|
||||
[4824] = "Aku'mai Pescadora",
|
||||
[4825] = "Mordelisca Aku'mai",
|
||||
[4841] = "Mortocharco",
|
||||
[4861] = "Morcego Corvoide",
|
||||
[4887] = "Ghamoo-ra",
|
||||
[4950] = "Manchinha",
|
||||
[5048] = "Áspide Anormal",
|
||||
[5053] = "Crocolisco Anormal",
|
||||
[5056] = "Presátera Anormal",
|
||||
[5224] = "Coleante do Lodo",
|
||||
[5225] = "Cospelodo",
|
||||
[5260] = "Gorila Groddoc",
|
||||
[5262] = "Trovejante Groddoc",
|
||||
[5268] = "Urso Veloférreo",
|
||||
[5272] = "Urso Veloférreo Cinzento",
|
||||
[5274] = "Patriarca Veloférreo",
|
||||
[5286] = "Corredor Longodente",
|
||||
[5287] = "Uivador Longodente",
|
||||
[5288] = "Longodente Raivoso",
|
||||
[5291] = "Asa Gélida Hakkari",
|
||||
[5307] = "Guinchador do Vale",
|
||||
[5308] = "Guinchador Errante do Vale",
|
||||
[5349] = "Arash-ethis",
|
||||
[5352] = "Velho Pançagris",
|
||||
[5356] = "Rosnador",
|
||||
[5422] = "Caçador Escorpídeo",
|
||||
[5423] = "Caudaçoite Escorpídeo",
|
||||
[5424] = "Espretareia Escorpídeo",
|
||||
[5425] = "Patapústula Faminta",
|
||||
[5426] = "Hiena Patapústula",
|
||||
[5427] = "Patapústula Raivoso",
|
||||
[5428] = "Roca",
|
||||
[5429] = "Roca de Fogo",
|
||||
[5430] = "Roca Flamejante",
|
||||
[5431] = "Tartaruga Ondesliza",
|
||||
[5432] = "Tartaruga Ondesliza Gigante",
|
||||
[5708] = "Rebento de Hakkar",
|
||||
[5755] = "Víbora Anormal",
|
||||
[5756] = "Peçonhasa Anormal",
|
||||
[5762] = "Caninana Anormal",
|
||||
[5766] = "Filhote da Savana",
|
||||
[5807] = "O Estraçalhador",
|
||||
[5823] = "Açoita-morte",
|
||||
[5828] = "Humar, o Senhor dos Leões",
|
||||
[5829] = "Roncão, o Importuno",
|
||||
[5834] = "Azzere, o Cortacéu",
|
||||
[5842] = "Takk, o Saltador",
|
||||
[5856] = "Aranha Teia-de-vidro",
|
||||
[5857] = "Aranha de Lava Causticante",
|
||||
[5858] = "Grande Aranha de Lava",
|
||||
[5865] = "Dishu",
|
||||
[5937] = "Torpicada",
|
||||
[5982] = "Matador Negro",
|
||||
[5984] = "Rasgarrindo Faminta",
|
||||
[5985] = "Hiena Rasgarrindo",
|
||||
[5988] = "Aguilhante Escorpóde",
|
||||
[5992] = "Javali Jubacinza",
|
||||
[6013] = "Urubu Errante",
|
||||
[6352] = "Tocaieiro Cascoral",
|
||||
[6369] = "Tartaruga Cascoral",
|
||||
[6505] = "Ravassauro",
|
||||
[6506] = "Ravassauro Corredor",
|
||||
[6507] = "Ravassauro Caçador",
|
||||
[6508] = "Ravassauro Pelenoso",
|
||||
[6513] = "Pisoteador de Un'Goro",
|
||||
[6514] = "Gorila de Un'Goro",
|
||||
[6516] = "Trovejador de Un'Goro",
|
||||
[6581] = "Matriarca Ravassauro",
|
||||
[6585] = "Uhk'loc",
|
||||
[6788] = "Matriarca do Covil",
|
||||
[6789] = "Ursocardinho",
|
||||
[7022] = "Escorpídeo Açoitador",
|
||||
[7055] = "Worg Rocha Negra",
|
||||
[7078] = "Escorpídeo da Fenda",
|
||||
[7097] = "Coruja Bico-de-ferro",
|
||||
[7098] = "Guinchador Bico-de-ferro",
|
||||
[7099] = "Caçador Bico-de-ferro",
|
||||
[7268] = "Guardião Zangareia",
|
||||
[7319] = "Lady Sathrah",
|
||||
[7405] = "Escorpídeo Mortal da Fenda",
|
||||
[7430] = "Filhote de Sabre-de-gelo",
|
||||
[7431] = "Sabre-de-gelo",
|
||||
[7432] = "Espreitador Sabre-de-gelo",
|
||||
[7433] = "Caçadora Sabre-de-gelo",
|
||||
[7434] = "Vigia Sabre-de-gelo",
|
||||
[7443] = "Espancador Dentilhaço",
|
||||
[7444] = "Urso Dentilhaço",
|
||||
[7445] = "Ancião Dentilhaço",
|
||||
[7446] = "Dentilhaço Raivoso",
|
||||
[7455] = "Coruja de Hibérnia",
|
||||
[7456] = "Guinchadora de Hibérnia",
|
||||
[7803] = "Entocador Escorpídeo",
|
||||
[8207] = "Ave Flamejante Maior",
|
||||
[8208] = "Patapústula Matadora",
|
||||
[8211] = "Velho Pula-penhasco",
|
||||
[8213] = "Cascaférrea",
|
||||
[8277] = "Rekk'tilac",
|
||||
[8299] = "Láquila",
|
||||
[8300] = "Rasgarga",
|
||||
[8301] = "Clack, o Aniquilador",
|
||||
[8303] = "Grunhido",
|
||||
[8336] = "Sapador Hakkari",
|
||||
[8600] = "Quiróprago",
|
||||
[8601] = "Quiróprago Nóxio",
|
||||
[8602] = "Quiróprago Monstruoso",
|
||||
[8762] = "Reclusa Xilaracna",
|
||||
[8926] = "Aguilhão Profundo",
|
||||
[8927] = "Uivador Sombrio",
|
||||
[8933] = "Rastejante da Caverna",
|
||||
[8956] = "Urso Garraivoso",
|
||||
[8958] = "Espancador Garraivoso",
|
||||
[8959] = "Lobo Patavil",
|
||||
[8960] = "Catador Patavil",
|
||||
[8961] = "Assolador Patavil",
|
||||
[9416] = "Worg do Escudo Marcado",
|
||||
[9622] = "U'cha",
|
||||
[9683] = "Lar'korwi Fêmea",
|
||||
[9684] = "Lar'korwi",
|
||||
[9690] = "Worg Incandescente",
|
||||
[9691] = "Escorpídeo Furimata",
|
||||
[9694] = "Worg Incandescente Salivante",
|
||||
[9695] = "Escorpídeo Letalátego",
|
||||
[9696] = "Worg Machado Sangrento",
|
||||
[9697] = "Worg Incandescente Gigante",
|
||||
[9698] = "Escorpídeo Cauda-de-fogo",
|
||||
[10077] = "Goela da Morte",
|
||||
[10200] = "Rak'shiri",
|
||||
[10221] = "Filhote de Worg Machado Sangrento",
|
||||
[10356] = "Bayne",
|
||||
[10357] = "Ressan, o Agulheiro",
|
||||
[10359] = "Tok'aya",
|
||||
[10375] = "Filhote de Aranha do Pináculo",
|
||||
[10644] = "Uivador da Névoa",
|
||||
[10737] = "Shy-Rotam",
|
||||
[10741] = "Sian-Rotam",
|
||||
[10806] = "Úrsius",
|
||||
[10882] = "Arikara",
|
||||
[10981] = "Lobo do Gelo",
|
||||
[11357] = "Filho de Hakkar",
|
||||
[11360] = "Cria Zulian",
|
||||
[11361] = "Tigre Zulian",
|
||||
[11365] = "Pantera Zulian",
|
||||
[11368] = "Morcego Sanguinário",
|
||||
[11370] = "Proleviúva Razzashi",
|
||||
[11371] = "Serpente Razzashi",
|
||||
[11372] = "Áspide Razzashi",
|
||||
[11735] = "Escorpídeo Petraçoite",
|
||||
[11736] = "Tenaz Petraçoite",
|
||||
[11737] = "Esfolador Petraçoite",
|
||||
[11738] = "Rastejante da Areia",
|
||||
[11739] = "Espreitadora de Rocha",
|
||||
[11921] = "Besseleth",
|
||||
[12037] = "Ursol'lok",
|
||||
[12431] = "Dilaceros",
|
||||
[12432] = "Velho Tenaz",
|
||||
[12433] = "Krethis Umbrateia",
|
||||
[13599] = "Mordelisca Estólida",
|
||||
[14123] = "Tartalisca Maxilaço",
|
||||
[14222] = "Araga",
|
||||
[14223] = "Benji Rabugento",
|
||||
[14228] = "Risadinha",
|
||||
[14232] = "Dardo",
|
||||
[14233] = "Rancascame",
|
||||
[14234] = "Hayoc",
|
||||
[14266] = "Shanda, a Tecelã",
|
||||
[14268] = "Lorde Condar",
|
||||
[14279] = "Rastejatriz",
|
||||
[14280] = "Zé Comilão",
|
||||
[14283] = "Coruja de Lançatroz",
|
||||
[14339] = "Uivo Mortífero",
|
||||
[14343] = "Olm, o Sábio",
|
||||
[14344] = "Mestiça",
|
||||
[14472] = "Gretheer",
|
||||
[14476] = "Krellack",
|
||||
[14491] = "Kurmokk",
|
||||
[14532] = "Veneracnídea Razzashi",
|
||||
[14821] = "Raptor Razzashi",
|
||||
[14965] = "Morcego Sanguinário Frenético",
|
||||
[15041] = "Prole de Mar'li",
|
||||
[15043] = "Crocolisco Zulian",
|
||||
[15101] = "Predador Zulian",
|
||||
[16117] = "Suíno Pestilento",
|
||||
[36507] = "Manchinha",
|
||||
[36514] = "Filhote de Sabre-de-gelo",
|
||||
[37007] = "Filhote de Viúva Negra",
|
||||
[40000] = "Raposa-vermelha",
|
||||
[50624] = "Enxameador Silitídeo",
|
||||
[50627] = "Enxameador Silitídeo",
|
||||
[50630] = "Enxameador Silitídeo",
|
||||
[60494] = "Pesagarra",
|
||||
[60697] = "Cão de Caça",
|
||||
[60848] = "Javali Costagulha",
|
||||
[61074] = "Gorila do Altovale",
|
||||
[61075] = "Rastreador Pedraconha",
|
||||
[61076] = "Aretisco Conchareia",
|
||||
[61077] = "Chimpanzé do Altovale",
|
||||
[61078] = "Macaco do Altovale",
|
||||
[61080] = "Gorila Ancião do Altovale",
|
||||
[61082] = "Trovejante do Altovale",
|
||||
[61090] = "Cobra Rabo-de-escova",
|
||||
[61091] = "Áspide de Cauda de Escova",
|
||||
[61095] = "Ginchador Esfolavêneno",
|
||||
[61096] = "Serpente Esfolavêneno",
|
||||
[61098] = "Rei Morogo Pé de Trovão",
|
||||
[61206] = "Rastejante Tremeteia",
|
||||
[61218] = "Filhote de Espiríleo Anormal",
|
||||
[61227] = "Predador Crespupele",
|
||||
[61228] = "Espreitador Crespupele",
|
||||
[61229] = "Pele do Crepúsculo Faminto",
|
||||
[61230] = "Morceguinho Asa Vil",
|
||||
[61231] = "Morcego Asa Vil",
|
||||
[61232] = "Aranha Teia Oca",
|
||||
[61233] = "Veneno de Teia Oca",
|
||||
[61336] = "Corujão Grande",
|
||||
[61337] = "Caçador de Chifres Grandes",
|
||||
[61401] = "Morcego Asatorpe Maior",
|
||||
[61488] = "Azurebeak",
|
||||
[61500] = "Highvale Silverback",
|
||||
[61552] = "Crespulizo",
|
||||
[61554] = "Dawnhowl",
|
||||
[61599] = "Sabre Negro",
|
||||
[61656] = "Urubu Penagris",
|
||||
[61657] = "Caçador Penagris",
|
||||
[61658] = "Hiena Patacinza",
|
||||
[61659] = "Caçador Patacinza",
|
||||
[61660] = "Escorpídeo Cauda-de-pedra",
|
||||
[61661] = "Açoitador Cauda-de-pedra",
|
||||
[61662] = "Escorpídeo Peçonha-negra",
|
||||
[61680] = "Rastejante Coralodo",
|
||||
[61681] = "Tiscoral Sujo Jovem",
|
||||
[61686] = "Catador Penagris",
|
||||
[61689] = "Javali Thalassiano Jovem ",
|
||||
[61690] = "Lince-carmesim Jovem ",
|
||||
[61691] = "Falcostruz da Floresta",
|
||||
[61692] = "Javali Thalassiano",
|
||||
[61693] = "Lince-carmesim",
|
||||
[61695] = "Falcostruz Marfim",
|
||||
[61696] = "Javali Thalassiano Ancião",
|
||||
[61697] = "Lince-lume",
|
||||
[61698] = "Matriarca Lince-lume",
|
||||
[61699] = "Shar'lan",
|
||||
[61700] = "Lince-carmesim Ancião",
|
||||
[61704] = "Rastejante dos Andarilhos",
|
||||
[61716] = "Falcostruz Carmesim",
|
||||
[61717] = "Raposa Thalassiana",
|
||||
[61774] = "Moreia de Mana Brilhante",
|
||||
[61775] = "Moreia de Mana Esmeralda",
|
||||
[61776] = "Moreia de Mana Lavanda",
|
||||
[61789] = "Lunapluma",
|
||||
[61975] = "Javali Presa Alta",
|
||||
[62066] = "Prolemadre Espeleoteia",
|
||||
[62073] = "Aranha Espeleoteia",
|
||||
[62074] = "Rastejante Espeleoteia",
|
||||
[62075] = "Cospeçonha Espeleoteia",
|
||||
[62121] = "Javali de Cerdas Douradas",
|
||||
[62122] = "Javali Corpulento de Cerdas Douradas",
|
||||
[62123] = "Urso Dourado",
|
||||
[62124] = "Urso-cardo Dourado",
|
||||
[62125] = "Lobo Silvestre Ancião",
|
||||
[62126] = "Lobo Silvestre Alfa",
|
||||
[62223] = "Raptor Escamatroz",
|
||||
[62224] = "Uivador Escamatroz",
|
||||
[62225] = "Espancador Escamatroz",
|
||||
[62226] = "Javali-couropétreo",
|
||||
[62227] = "Javali-couropétreo Ancião",
|
||||
[62247] = "Recluso Castanho",
|
||||
[62248] = "Grande Recluso Marrom",
|
||||
[62256] = "Mordelisca de Águas Claras",
|
||||
[62257] = "Mordelisca de Águas Claras Doente",
|
||||
[62258] = "Crocolisco Dentefino",
|
||||
[62259] = "Crocolisco Gigante Dente-de-navalha",
|
||||
[62260] = "Javali da Floresta",
|
||||
[62261] = "Javali Ancião da Floresta",
|
||||
[62262] = "Javali da Chapada",
|
||||
[62271] = "Jovem Javali Cerdouro",
|
||||
[62272] = "Jovem Lobo Silvestre",
|
||||
[62338] = "Urubu Asatempéria",
|
||||
[62339] = "Abrute Asatempéria",
|
||||
[62340] = "Ragged Crag Coyote",
|
||||
[62341] = "Coiote Penhascoscravo",
|
||||
[62342] = "Aranha Mistbark",
|
||||
[62343] = "Conjurador de teias Mistbark",
|
||||
[62344] = "Mãe Mistbark",
|
||||
[62345] = "Crocolisco Sorrowmore",
|
||||
[62642] = "Filocéfalo",
|
||||
[80250] = "Lince-carmesim",
|
||||
[80252] = "Filhote de Raposa-do-ártico",
|
||||
[80254] = "Filhote de Raposa-do-campo ",
|
||||
[80256] = "Raposa-prateada",
|
||||
[80257] = "Raposa-vermelha",
|
||||
[80258] = "Filhote de Raposa-da-névoa",
|
||||
[80259] = "Raposa-da-névoa",
|
||||
[80260] = "Raposa Espiritual",
|
||||
[80466] = "Urso Pestilento de Tirisfal",
|
||||
[80815] = "Águia Amani",
|
||||
[80816] = "Águia das Terras Agrestes",
|
||||
[80920] = "Águia Amani",
|
||||
[90980] = "Pantera Zulian",
|
||||
[90981] = "Pantera Zulian",
|
||||
[91819] = "Tigre Hazzuri",
|
||||
[91825] = "Tigre-de-bengala Jovem",
|
||||
[91826] = "Tigre de Bengala Matriarca",
|
||||
[91827] = "Tigre de Bengala",
|
||||
[91828] = "Tigre de Bengala Alfa",
|
||||
[91829] = "Pisador da Selva",
|
||||
[91830] = "Debulhador da Selva",
|
||||
[91831] = "Rastreador Brilhante",
|
||||
[91832] = "Tenaz Insular",
|
||||
[91833] = "Rastejante de Rochas",
|
||||
[91838] = "Quebrador Conchafunda",
|
||||
[91960] = "Pantera Pataselva",
|
||||
[91961] = "Pantera das Sombras Pataselva",
|
||||
[91962] = "Crocolisco Arapuca",
|
||||
[91966] = "Serpente da Selva Venenosa",
|
||||
[91984] = "Urso Tirisgarra",
|
||||
[91985] = "Filhote Tirisgarra",
|
||||
[91987] = "Javali Presanhasco",
|
||||
[91989] = "Lobo Pata Cinzenta",
|
||||
[91990] = "Pata Cinzenta Alfa",
|
||||
[92115] = "Garrafiada Enfurecido",
|
||||
[92146] = "Rastejante da Praia",
|
||||
[92147] = "Rastreador da Costa de Prata",
|
||||
}
|
||||
@@ -0,0 +1,573 @@
|
||||
MTH_LocaleData = MTH_LocaleData or {}
|
||||
MTH_LocaleData.data = MTH_LocaleData.data or {}
|
||||
MTH_LocaleData.data.beasts = MTH_LocaleData.data.beasts or {}
|
||||
|
||||
MTH_LocaleData.data.beasts.ruRU = {
|
||||
[30] = "Лесной паук",
|
||||
[43] = "Рудниковый паук",
|
||||
[69] = "Лесной волк",
|
||||
[113] = "Вепрь-камнеклык",
|
||||
[118] = "Лесной хищник",
|
||||
[119] = "Длиннорыл",
|
||||
[154] = "Большой шкуродер",
|
||||
[157] = "Жутеклык",
|
||||
[199] = "Молодой шкуродер",
|
||||
[213] = "Голодный лютый волк",
|
||||
[217] = "Ядовитый кругопряд",
|
||||
[299] = "Молодой волк",
|
||||
[330] = "Принцесса",
|
||||
[345] = "Грубобрюх",
|
||||
[390] = "Кабаний эскорт",
|
||||
[428] = "Ужасающий кондор",
|
||||
[442] = "Тарантул",
|
||||
[454] = "Молодой жутевепрь",
|
||||
[462] = "Сарыч",
|
||||
[471] = "Мать Клык",
|
||||
[505] = "Крупный тарантул",
|
||||
[521] = "Волкус",
|
||||
[524] = "Твердошкурый вепрь",
|
||||
[525] = "Облезлый волк",
|
||||
[539] = "Карликовый ядовитый кругопряд",
|
||||
[547] = "Большой жутевепрь",
|
||||
[565] = "Бешеный лютый волк",
|
||||
[569] = "Зеленый паук-отшельник",
|
||||
[574] = "Нараксис",
|
||||
[616] = "Трещунья",
|
||||
[628] = "Черный опустошитель",
|
||||
[681] = "Молодой тигр Тернистой долины",
|
||||
[682] = "Тигр Тернистой долины",
|
||||
[683] = "Молодая пантера",
|
||||
[684] = "Тенебрюхая пантера",
|
||||
[685] = "Ящер Тернистой долины",
|
||||
[686] = "Ящер-кнутохвост",
|
||||
[687] = "Тропический ловец",
|
||||
[704] = "Косматый лесной волк",
|
||||
[705] = "Косматый молодой волк",
|
||||
[708] = "Малый скальный вепрь",
|
||||
[728] = "Баг'тира",
|
||||
[729] = "Син'Далл",
|
||||
[730] = "Тетис",
|
||||
[731] = "Король Бангалаш",
|
||||
[736] = "Пантера",
|
||||
[767] = "Болотный ягуар",
|
||||
[768] = "Пантера Тени",
|
||||
[769] = "Смертоносный тарантул",
|
||||
[772] = "Тигрица Тернистой долины",
|
||||
[822] = "Молодой лесной медведь",
|
||||
[830] = "Песчаный краб",
|
||||
[831] = "Морской краб",
|
||||
[833] = "Вожак стаи койотов",
|
||||
[834] = "Койот",
|
||||
[854] = "Молодой тропический ловец",
|
||||
[855] = "Молодой ящер Тернистой долины",
|
||||
[856] = "Молодой ящер-кнутохвост",
|
||||
[858] = "Ткач скорби",
|
||||
[922] = "Иловый краб",
|
||||
[923] = "Молодой черный опустошитель",
|
||||
[930] = "Детеныш черной вдовы",
|
||||
[949] = "Падальщик-отшельник",
|
||||
[976] = "Боевой тигр Курцена",
|
||||
[977] = "Боевая пантера Курцена",
|
||||
[1015] = "Ящер нагорья",
|
||||
[1016] = "Кнутохвост нагорья",
|
||||
[1017] = "Смертехват нагорья",
|
||||
[1018] = "Острозуб Нагорья",
|
||||
[1019] = "Старый острозуб",
|
||||
[1020] = "Крапчатый ящер",
|
||||
[1021] = "Крапчатый крикун",
|
||||
[1022] = "Крапчатый смертехват",
|
||||
[1023] = "Крапчатый острозуб",
|
||||
[1082] = "Острозубый кроколиск",
|
||||
[1084] = "Молодой острозубый кроколиск",
|
||||
[1087] = "Острозубый крепкохват",
|
||||
[1088] = "Чудовищный краб",
|
||||
[1108] = "Горилла Туманной Долины",
|
||||
[1109] = "Шкуродер",
|
||||
[1111] = "Ловец-кровопийца",
|
||||
[1112] = "Кровавая Вдова",
|
||||
[1114] = "Джунглевый рокотун",
|
||||
[1125] = "Скальный вепрь",
|
||||
[1126] = "Большой скальный вепрь",
|
||||
[1127] = "Старый скальный вепрь",
|
||||
[1128] = "Молодой черный медведь",
|
||||
[1129] = "Черный медведь",
|
||||
[1130] = "Бьярн",
|
||||
[1131] = "Снежный волк",
|
||||
[1132] = "Серый",
|
||||
[1133] = "Голодный снежный волк",
|
||||
[1138] = "Снежный гончий волк",
|
||||
[1140] = "Острозуб-матриарх",
|
||||
[1150] = "Речной кроколиск",
|
||||
[1152] = "Кроколиск-хрустогрыз",
|
||||
[1184] = "Скальный скрытень",
|
||||
[1185] = "Древесный воронковый паук",
|
||||
[1186] = "Старый черный медведь",
|
||||
[1188] = "Седой черный медведь",
|
||||
[1189] = "Черный медведь-патриарх",
|
||||
[1190] = "Горный вепрь",
|
||||
[1191] = "Облезлый горный вепрь",
|
||||
[1192] = "Старый горный вепрь",
|
||||
[1194] = "Горный канюк",
|
||||
[1195] = "Лесной паук-скрытень",
|
||||
[1196] = "Медведь-ледолап",
|
||||
[1199] = "Молодой снежный леопард",
|
||||
[1201] = "Снежный леопард",
|
||||
[1216] = "Береговой краб",
|
||||
[1225] = "Старина Черношкур",
|
||||
[1258] = "Черный мастиф-опустошитель",
|
||||
[1400] = "Болотный кроколиск",
|
||||
[1417] = "Молодой болотный кроколиск",
|
||||
[1504] = "Молодой ночной кругопряд",
|
||||
[1505] = "Ночной кругопряд",
|
||||
[1508] = "Молодой падальщик",
|
||||
[1509] = "Косматый падальщик",
|
||||
[1511] = "Разъяренная седоспиная горилла",
|
||||
[1512] = "Сумеречница",
|
||||
[1513] = "Облезлая сумеречница",
|
||||
[1516] = "Конда",
|
||||
[1547] = "Дряхлый пес Тьмы",
|
||||
[1548] = "Проклятый пес тьмы",
|
||||
[1549] = "Прожорливый пес Тьмы",
|
||||
[1553] = "Большая сумеречница",
|
||||
[1554] = "Сумеречница-вампир",
|
||||
[1555] = "Злобный ночной кругопряд",
|
||||
[1557] = "Старая горилла Мглистой долины",
|
||||
[1558] = "Среброспин-патриарх",
|
||||
[1688] = "Ночной кругопряд-матриарх",
|
||||
[1689] = "Покрытый шрамами скальный вепрь",
|
||||
[1693] = "Озерный кроколиск",
|
||||
[1713] = "Старая тенебрюхая пантера",
|
||||
[1765] = "Ворг",
|
||||
[1766] = "Крапчатый ворг",
|
||||
[1778] = "Лютый седой медведь",
|
||||
[1780] = "Мшистый паук-ловец",
|
||||
[1781] = "Мглистый ползун",
|
||||
[1797] = "Гигантский седой медведь",
|
||||
[1809] = "Падальщик",
|
||||
[1815] = "Больной черный медведь",
|
||||
[1816] = "Болезненный гризли",
|
||||
[1817] = "Больной волк",
|
||||
[1822] = "Воронковый паук Ядовитого Тумана",
|
||||
[1824] = "Чумной воронковый паук",
|
||||
[1922] = "Серый лесной волк",
|
||||
[1923] = "Кровожадный ворг",
|
||||
[1961] = "Паршивый Коготь",
|
||||
[1984] = "Молодой щетинистый вепрь",
|
||||
[1985] = "Щетинистый вепрь",
|
||||
[1986] = "Чащобный тенетник",
|
||||
[1994] = "Гитьис Зловещая",
|
||||
[1995] = "Сова",
|
||||
[1996] = "Сова-крикун",
|
||||
[1997] = "Сова-охотник",
|
||||
[1998] = "Чащобный тенетник",
|
||||
[1999] = "Чащобный ядозуб",
|
||||
[2000] = "Чащобный паук-балдахинник",
|
||||
[2001] = "Гигантский чащобный тенетник",
|
||||
[2031] = "Молодой ночной саблезуб",
|
||||
[2032] = "Облезлый ночной саблезуб",
|
||||
[2033] = "Старый ночной саблезуб",
|
||||
[2034] = "Яростный ночной саблезуб",
|
||||
[2042] = "Ночной саблезуб",
|
||||
[2043] = "Ночной саблезуб-охотник",
|
||||
[2069] = "Лунопард",
|
||||
[2070] = "Малорослый лунопард",
|
||||
[2071] = "Лунопард-матриарх",
|
||||
[2089] = "Огромный болотный кроколиск",
|
||||
[2163] = "Колючешерстный медведь",
|
||||
[2165] = "Седой колючешерстный медведь",
|
||||
[2172] = "Долгоног-несушка",
|
||||
[2175] = "Тенекоготь",
|
||||
[2231] = "Карликовый прибрежный краб",
|
||||
[2232] = "Прибрежный краб",
|
||||
[2233] = "Покрытый илом краб",
|
||||
[2234] = "Молодой рифовый краб",
|
||||
[2235] = "Рифовый краб",
|
||||
[2236] = "Гневный рифовый краб",
|
||||
[2237] = "Взрослый лунопард",
|
||||
[2275] = "Разъяренный",
|
||||
[2321] = "Птенец лесного долгохода",
|
||||
[2322] = "Лесной долгоход",
|
||||
[2323] = "Гигантский лесной долгоног",
|
||||
[2348] = "Старый моховой ползун",
|
||||
[2349] = "Гигантский моховой ползун",
|
||||
[2350] = "Лесной моховой ползун",
|
||||
[2351] = "Серый медведь",
|
||||
[2354] = "Злобный серый медведь",
|
||||
[2356] = "Старый серый медведь",
|
||||
[2384] = "Голодный горный лев",
|
||||
[2385] = "Яростный горный лев",
|
||||
[2406] = "Горный лев",
|
||||
[2407] = "Крупный горный лев",
|
||||
[2408] = "Хрустогрыз",
|
||||
[2476] = "Большой озерный кроколиск",
|
||||
[2505] = "Морской хрустогрыз",
|
||||
[2521] = "Небогривая горилла",
|
||||
[2522] = "Жагуар",
|
||||
[2544] = "Южный песчаный краб",
|
||||
[2559] = "Долгоног нагорья",
|
||||
[2560] = "Твердозуб нагорья",
|
||||
[2561] = "Плотоглот нагорья",
|
||||
[2563] = "Равнинный ползун",
|
||||
[2565] = "Гигантский равнинный ползун",
|
||||
[2578] = "Молодой равнинный канюк",
|
||||
[2579] = "Равнинный канюк",
|
||||
[2580] = "Старый равнинный канюк",
|
||||
[2635] = "Старый солоноводный кроколиск",
|
||||
[2680] = "Волчонок Порочной Ветви",
|
||||
[2681] = "Ездовой волк Порочной Ветви",
|
||||
[2686] = "Паук-нянька племени Сухокожих",
|
||||
[2727] = "Скальный койот",
|
||||
[2728] = "Яростный скальный койот",
|
||||
[2729] = "Старый скальный койот",
|
||||
[2730] = "Бешеный скальный койот",
|
||||
[2731] = "Горный охотник",
|
||||
[2732] = "Горная охотница",
|
||||
[2734] = "Патриарх горных охотинков",
|
||||
[2753] = "Барнабус",
|
||||
[2829] = "Голодный канюк",
|
||||
[2830] = "Канюк",
|
||||
[2831] = "Гигантский канюк",
|
||||
[2850] = "Сломанный Клык",
|
||||
[2923] = "Облезлый среброгрив",
|
||||
[2924] = "Среброгривый волк",
|
||||
[2925] = "Среброгривый ревун",
|
||||
[2926] = "Среброгривый бродяга",
|
||||
[2931] = "Зарикотль",
|
||||
[2954] = "Боевой вепрь-дыбогрив",
|
||||
[2955] = "Равнинный долгоног",
|
||||
[2956] = "Взрослый равнинный долгоног",
|
||||
[2957] = "Старый полевой долгоног",
|
||||
[2958] = "Волк прерий",
|
||||
[2959] = "Охотник прерий",
|
||||
[2960] = "Могучий волк прерий",
|
||||
[2961] = "Горная пума",
|
||||
[2966] = "Боевой вепрь",
|
||||
[2969] = "Тощий перепелятник",
|
||||
[2970] = "Перепелятник",
|
||||
[2971] = "Когтистый перепелятник",
|
||||
[3035] = "Пума плоскогорья",
|
||||
[3068] = "Маззранач",
|
||||
[3098] = "Пятнистый вепрь",
|
||||
[3099] = "Лютый пятнистый вепрь",
|
||||
[3100] = "Старый крапчатый вепрь",
|
||||
[3106] = "Карликовый приливный краб",
|
||||
[3107] = "Приливный краб",
|
||||
[3108] = "Поросший водорослями краб",
|
||||
[3110] = "Крупнозубый кроколиск",
|
||||
[3121] = "Дуротарский тигр",
|
||||
[3122] = "Кровопалый кнутохвост",
|
||||
[3123] = "Лезвезуб Кровавого Когтя",
|
||||
[3124] = "Рабочий скорпид",
|
||||
[3125] = "Гремучий скорпид",
|
||||
[3126] = "Бронескорп",
|
||||
[3127] = "Ядохвостый скорпид",
|
||||
[3225] = "Оскверненный крапчатый вепрь",
|
||||
[3226] = "Оскверненный скорпид",
|
||||
[3227] = "Оскверненный лезвезуб Кровавого Когтя",
|
||||
[3228] = "Порочный приливный краб",
|
||||
[3231] = "Оскверненный крупнозубый кроколиск",
|
||||
[3241] = "Патриарх саванны",
|
||||
[3243] = "Высокогрив саванны",
|
||||
[3244] = "Большой равнинный долгоног",
|
||||
[3245] = "Злобный равнинный долгоног",
|
||||
[3246] = "Проворный равнинный долгоног",
|
||||
[3247] = "Птенец грозового змея",
|
||||
[3249] = "Большой грозовой змей",
|
||||
[3250] = "Ползучий силитид",
|
||||
[3252] = "Членистоног Силитида",
|
||||
[3254] = "Яркочешуйчатый кнутохвост",
|
||||
[3255] = "Яркочешуйчатый крикун",
|
||||
[3256] = "Яркочешуйчатый смертехват",
|
||||
[3257] = "Ишамухал",
|
||||
[3281] = "Саркот",
|
||||
[3415] = "Охотница саванны",
|
||||
[3416] = "Матриарх саванны",
|
||||
[3424] = "Грозовой змей-облачник",
|
||||
[3425] = "Хищник саванны",
|
||||
[3461] = "Оазисный хрустогрыз",
|
||||
[3472] = "Ваште Пауни",
|
||||
[3475] = "Ичияки",
|
||||
[3503] = "Заступник-силитид",
|
||||
[3566] = "Хищник плоскогорья",
|
||||
[3581] = "Тварь из Клоак",
|
||||
[3619] = "Саблезубый призрак",
|
||||
[3630] = "Загадочный крылозмей",
|
||||
[3631] = "Загадочный жалохлест",
|
||||
[3632] = "Загадочный ползун",
|
||||
[3633] = "Загадочный душегуб",
|
||||
[3634] = "Загадочный ловец",
|
||||
[3636] = "Загадочный опустошитель",
|
||||
[3637] = "Загадочный страж",
|
||||
[3653] = "Криг",
|
||||
[3809] = "Медведь Ясеневого леса",
|
||||
[3810] = "Старый медведь Ясеневого леса",
|
||||
[3811] = "Гигантский медведь Ясеневого леса",
|
||||
[3812] = "Краб-щелкун",
|
||||
[3814] = "Шипастый краб",
|
||||
[3819] = "Паук-скакун Дикого Терна",
|
||||
[3820] = "Ядоплюй Дикого Терна",
|
||||
[3821] = "Воронковый паук Дикого Терна",
|
||||
[3823] = "Светлолапый бегун",
|
||||
[3824] = "Светлолапый ревун",
|
||||
[3825] = "Светлолапый вожак",
|
||||
[3861] = "Бесцветный ворг",
|
||||
[3862] = "Слюнявый ворг",
|
||||
[3866] = "Злобный нетопырь",
|
||||
[3868] = "Кровожад",
|
||||
[4005] = "Ползун из густого мха",
|
||||
[4006] = "Моховой паук-вязальщик",
|
||||
[4007] = "Моховой ядоплюй",
|
||||
[4040] = "Пещерный ловец",
|
||||
[4067] = "Сумеречный бегун",
|
||||
[4117] = "Облачный змей",
|
||||
[4118] = "Ядовитый облачный змей",
|
||||
[4119] = "Старый облачный змей",
|
||||
[4124] = "Игольчатая пума",
|
||||
[4126] = "Скальный ловец",
|
||||
[4127] = "Зубастая гиена",
|
||||
[4128] = "Многозубый ловец",
|
||||
[4129] = "Многозубый ворчун",
|
||||
[4139] = "Скорпид-убийца",
|
||||
[4140] = "Скорпид-разоритель",
|
||||
[4142] = "Блестопанцирная черепаха",
|
||||
[4143] = "Блестопанцирный крепкохват",
|
||||
[4144] = "Блестопанцирный бурильщик",
|
||||
[4154] = "Солончаковый падальщик",
|
||||
[4158] = "Стервятник соляных равнин",
|
||||
[4248] = "Паршивая гиена",
|
||||
[4249] = "Паршивый рыкун",
|
||||
[4250] = "Гиена племени Галак",
|
||||
[4263] = "Детеныш густого мха",
|
||||
[4264] = "Моховая паучиха",
|
||||
[4304] = "Ищейка из Алого ордена",
|
||||
[4316] = "Гиена племени Колкар",
|
||||
[4341] = "Кроколиск-грязекуп",
|
||||
[4342] = "Грязекуп-злобокус",
|
||||
[4343] = "Грязекуп-крепкохват",
|
||||
[4344] = "Крапчатый кроколиск-грязекуп",
|
||||
[4345] = "Грязекуп-острозуб",
|
||||
[4351] = "Ящер Кровавой Топи",
|
||||
[4352] = "Крикун Кровавой Топи",
|
||||
[4355] = "Смертехват Кровавой Топи",
|
||||
[4356] = "Острозуб Кровавой Топи",
|
||||
[4357] = "Кнутохвост Кровавой Топи",
|
||||
[4376] = "Паук Мглистой пещеры",
|
||||
[4377] = "Паучонок Мглистой пещеры",
|
||||
[4378] = "Паук-отшельник Мглистой пещеры",
|
||||
[4379] = "Паук-шелкопряд Мглистой пещеры",
|
||||
[4380] = "Черная вдова Мглистой пещеры",
|
||||
[4396] = "Глинистая черепаха",
|
||||
[4397] = "Глинистая колючая черепаха",
|
||||
[4398] = "Глинистый землеглот",
|
||||
[4399] = "Глинистый бурильщик",
|
||||
[4400] = "Глинистый хрустогрыз",
|
||||
[4411] = "Скрытень-темножвал",
|
||||
[4412] = "Ползун-темножвал",
|
||||
[4413] = "Паук-темножвал",
|
||||
[4414] = "Ядоплюй-темножвал",
|
||||
[4415] = "Гигантский паук-темножвал",
|
||||
[4425] = "Слепой охотник",
|
||||
[4511] = "Агам'ар",
|
||||
[4512] = "Гниющий вурдалак",
|
||||
[4514] = "Разъяренный Агам'ар",
|
||||
[4538] = "Лабиринтовый нетопырь",
|
||||
[4539] = "Большой лабиринтовый нетопырь",
|
||||
[4548] = "Сталезуб",
|
||||
[4660] = "Костелап клана Мародин",
|
||||
[4662] = "Костелап племени Маграм",
|
||||
[4688] = "Костелапая гиена",
|
||||
[4689] = "Голодный костелап",
|
||||
[4690] = "Бешеный костелап",
|
||||
[4692] = "Жуткий перепелятник",
|
||||
[4693] = "Жуткий летун",
|
||||
[4694] = "Жуткий потрошитель",
|
||||
[4695] = "Ужасный падальщик",
|
||||
[4696] = "Крепкохват cкорпаши",
|
||||
[4697] = "Плеточник cкорпаши",
|
||||
[4699] = "Ядохлыст cкорпаши",
|
||||
[4821] = "Стремительное ракообразное",
|
||||
[4822] = "Ракообразный клешнехват",
|
||||
[4823] = "Колючий членистоног",
|
||||
[4824] = "Рыбоед Аку'май",
|
||||
[4825] = "Хрустогрыз Аку'май",
|
||||
[4841] = "Зыбун",
|
||||
[4861] = "Крылан-охотник",
|
||||
[4887] = "Гхаму-ра",
|
||||
[4950] = "Спот",
|
||||
[5048] = "Загадочный аспид",
|
||||
[5053] = "Загадочный кроколиск",
|
||||
[5056] = "Загадочный страхозуб",
|
||||
[5224] = "Мрачный ползун",
|
||||
[5225] = "Мракоплюй",
|
||||
[5260] = "Гроддокская обезьяна",
|
||||
[5262] = "Гроддокский рокотун",
|
||||
[5268] = "Медведь-сталемех",
|
||||
[5272] = "Седой медведь-сталемех",
|
||||
[5274] = "Сталемех-патриарх",
|
||||
[5286] = "Длиннозуб-охотник",
|
||||
[5287] = "Длиннозубый ревун",
|
||||
[5288] = "Бешеный длиннозуб",
|
||||
[5291] = "Ледокрыл из племени Хаккари",
|
||||
[5307] = "Дольный крикун",
|
||||
[5308] = "Норовистый дольный крикун",
|
||||
[5349] = "Араш-етис",
|
||||
[5352] = "Старый Серобрюх",
|
||||
[5356] = "Рыкун",
|
||||
[5422] = "Скорпид-охотник",
|
||||
[5423] = "Плетохвостый скорпид",
|
||||
[5424] = "Песчаный скорпид-охотник",
|
||||
[5425] = "Голодный хромоног",
|
||||
[5426] = "Хромоногая гиена",
|
||||
[5427] = "Бешеный стертолап",
|
||||
[5428] = "Рух",
|
||||
[5429] = "Огненный рух",
|
||||
[5430] = "Опаляющий рух",
|
||||
[5431] = "Береговая черепаха",
|
||||
[5432] = "Гигантская береговая черепаха",
|
||||
[5708] = "Порождение Хаккара",
|
||||
[5755] = "Загадочная гадюка",
|
||||
[5756] = "Загадочный крылояд",
|
||||
[5762] = "Загадочная болотная гадюка",
|
||||
[5766] = "Детеныш зищника саванны",
|
||||
[5807] = "Цап-царап",
|
||||
[5823] = "Смертоносный живодер",
|
||||
[5828] = "Вожак стаи Хумар",
|
||||
[5829] = "Фырк Дразнила",
|
||||
[5834] = "Аззира Клинок Небес",
|
||||
[5842] = "Такк Прыгун",
|
||||
[5856] = "Хрустальный паук",
|
||||
[5857] = "Опаляющий лавовый паук",
|
||||
[5858] = "Большой лавовый паук",
|
||||
[5865] = "Дишу",
|
||||
[5937] = "Коварное Жало",
|
||||
[5982] = "Черный душегуб",
|
||||
[5984] = "Голодный хохотун",
|
||||
[5985] = "Хохочущая гиена",
|
||||
[5988] = "Скорпок-острожал",
|
||||
[5992] = "Пеплогривый кабан",
|
||||
[6013] = "Своенравный канюк",
|
||||
[6352] = "Кораллопанцирный скрытень",
|
||||
[6369] = "Кораллопанцирная черепаха",
|
||||
[6505] = "Равазавр",
|
||||
[6506] = "Равазавр-бегун",
|
||||
[6507] = "Равазавр-охотник",
|
||||
[6508] = "Ядовитый равазавр",
|
||||
[6513] = "Ун'Горский топотун",
|
||||
[6514] = "Ун'горская горилла",
|
||||
[6516] = "Ун'Горский рокотун",
|
||||
[6581] = "Равазавр-матриарх",
|
||||
[6585] = "Ак'лок",
|
||||
[6788] = "Мать-медведица",
|
||||
[6789] = "Медвежонок-осотник",
|
||||
[7022] = "Ядоносный скорпид",
|
||||
[7055] = "Ворг из клана Черной Горы",
|
||||
[7078] = "Пещерный скорпид",
|
||||
[7097] = "Железноклюв",
|
||||
[7098] = "Железноклюв-крикун",
|
||||
[7099] = "Железноклюв-охотник",
|
||||
[7268] = "Страж из племени Песчаной Бури",
|
||||
[7319] = "Леди Сатра",
|
||||
[7405] = "Смертоносный пещерный скорпид",
|
||||
[7430] = "Детеныш ледопарда",
|
||||
[7431] = "Ледопард",
|
||||
[7432] = "Ловчий ледопард",
|
||||
[7433] = "Ледопард-охотница",
|
||||
[7434] = "Дозорный прайда ледопардов",
|
||||
[7443] = "Могучий щербозуб",
|
||||
[7444] = "Медведь-щербозуб",
|
||||
[7445] = "Старый щербозуб",
|
||||
[7446] = "Бешеный щербозуб",
|
||||
[7455] = "Сова Зимних Ключей",
|
||||
[7456] = "Крикун Зимних Ключей",
|
||||
[7803] = "Песчаный скорпид",
|
||||
[8207] = "Большой огнекрыл",
|
||||
[8208] = "Безжалостный хромоног",
|
||||
[8211] = "Старый утесный прыгун",
|
||||
[8213] = "Сталеспин",
|
||||
[8277] = "Рекк'тилак",
|
||||
[8299] = "Злобоклюй",
|
||||
[8300] = "Разор",
|
||||
[8301] = "Щелкун Разоритель",
|
||||
[8303] = "Хрюггер",
|
||||
[8336] = "Сапер из племени Хаккари",
|
||||
[8600] = "Чумной нетопырь",
|
||||
[8601] = "Пагубный нетопырь",
|
||||
[8602] = "Чудовищный чумной нетопырь",
|
||||
[8762] = "Древесный отшельник",
|
||||
[8926] = "Глубинный острожал",
|
||||
[8927] = "Черный крикун",
|
||||
[8933] = "Пещерный ползун",
|
||||
[8956] = "Медведь злобных когтей",
|
||||
[8958] = "Злобнопалый изверг",
|
||||
[8959] = "Волк-сквернолап",
|
||||
[8960] = "Падальщик-сквернолап",
|
||||
[8961] = "Опустошитель-сквернолап",
|
||||
[9416] = "Ворг легиона Изрубленного Щита",
|
||||
[9622] = "У'ча",
|
||||
[9683] = "Самка Лар'корви",
|
||||
[9684] = "Лар'корви",
|
||||
[9690] = "Пепельный ворг",
|
||||
[9691] = "Ядожалый скорпид",
|
||||
[9694] = "Слюнявый пепельный ворг",
|
||||
[9695] = "Скорпид-смертехлест",
|
||||
[9696] = "Ворг легиона Кровавого Топора",
|
||||
[9697] = "Огромный пепельный ворг",
|
||||
[9698] = "Огнехвостый скорпид",
|
||||
[10077] = "Гиблопасть",
|
||||
[10200] = "Рак'шири",
|
||||
[10221] = "Щенок ворга легиона Кровавого Топора",
|
||||
[10356] = "Зверр",
|
||||
[10357] = "Куссан Жалящий",
|
||||
[10359] = "Шри'скалк",
|
||||
[10375] = "Скальный паучонок",
|
||||
[10644] = "Ревун из тумана",
|
||||
[10737] = "Ши-Ротам",
|
||||
[10741] = "Сиан-Ротам",
|
||||
[10806] = "Урсиус",
|
||||
[10882] = "Арикара",
|
||||
[10981] = "Северный волк",
|
||||
[11357] = "Сын Хаккара",
|
||||
[11360] = "Зулианский тигренок",
|
||||
[11361] = "Зулианский тигр",
|
||||
[11365] = "Зулианская пантера",
|
||||
[11368] = "Кровососущий нетопырь",
|
||||
[11370] = "Черная вдова Раззаши",
|
||||
[11371] = "Змей Раззаши",
|
||||
[11372] = "Аспид Раззаши",
|
||||
[11735] = "Камнехвостый скорпид",
|
||||
[11736] = "Камнехвостый клешневик",
|
||||
[11737] = "Камнехвостый живодер",
|
||||
[11738] = "Песчаный паук-быстроног",
|
||||
[11739] = "Скальный паук-скакун",
|
||||
[11921] = "Бесселет",
|
||||
[12037] = "Урсол'лок",
|
||||
[12431] = "Жуткоклык",
|
||||
[12432] = "Старый Губач",
|
||||
[12433] = "Кретис Тенеткач",
|
||||
[13599] = "Вялый хрустогрыз",
|
||||
[14123] = "Стальноклювый крепкохват",
|
||||
[14222] = "Арага",
|
||||
[14223] = "Злобный Бенджи",
|
||||
[14228] = "Хохотунья",
|
||||
[14232] = "Дарт",
|
||||
[14233] = "Чешуекус",
|
||||
[14234] = "Хайок",
|
||||
[14266] = "Шанда Прядильщица",
|
||||
[14268] = "Лорд Кондар",
|
||||
[14279] = "Ползух",
|
||||
[14280] = "Большой Самрас",
|
||||
[14283] = "Сова клана Грозовой Вершины",
|
||||
[14339] = "Смертный вой",
|
||||
[14343] = "Олм Мудрый",
|
||||
[14344] = "Полукров",
|
||||
[14472] = "Гретир",
|
||||
[14476] = "Креллак",
|
||||
[14491] = "Курмокк",
|
||||
[14532] = "Ядокус Раззаши",
|
||||
[14821] = "Ящер Раззаши",
|
||||
[14965] = "Бесноватый кровососущий нетопырь",
|
||||
[15041] = "Порождение Мар'ли",
|
||||
[15043] = "Зулианский кроколиск",
|
||||
[15101] = "Зулианский хищник",
|
||||
[16117] = "Чумной кабан",
|
||||
}
|
||||
@@ -0,0 +1,713 @@
|
||||
MTH_LocaleData = MTH_LocaleData or {}
|
||||
MTH_LocaleData.data = MTH_LocaleData.data or {}
|
||||
MTH_LocaleData.data.beasts = MTH_LocaleData.data.beasts or {}
|
||||
|
||||
MTH_LocaleData.data.beasts.zhCN = {
|
||||
[30] = "森林蜘蛛",
|
||||
[43] = "矿坑蜘蛛",
|
||||
[69] = "森林狼",
|
||||
[113] = "石牙野猪",
|
||||
[118] = "觅食的灰狼",
|
||||
[119] = "长鼻野猪",
|
||||
[154] = "大碎尸鸟",
|
||||
[157] = "血牙野猪",
|
||||
[199] = "小碎尸鸟",
|
||||
[213] = "饥饿的恐狼",
|
||||
[217] = "结网毒蜘蛛",
|
||||
[299] = "幼狼",
|
||||
[330] = "公主",
|
||||
[345] = "贝利格拉布",
|
||||
[390] = "公主的随从",
|
||||
[428] = "恐鹫",
|
||||
[442] = "狼蛛",
|
||||
[454] = "幼年血牙野猪",
|
||||
[462] = "乌尔图斯",
|
||||
[471] = "母蜘蛛",
|
||||
[505] = "巨型狼蛛",
|
||||
[521] = "鲁伯斯",
|
||||
[524] = "石皮野猪",
|
||||
[525] = "癞皮狼",
|
||||
[539] = "小型结网毒蜘蛛",
|
||||
[547] = "巨型血牙野猪",
|
||||
[565] = "疯狂的恐狼",
|
||||
[569] = "绿色独行蛛",
|
||||
[574] = "纳拉克西斯",
|
||||
[616] = "查特",
|
||||
[628] = "黑色破坏者",
|
||||
[681] = "小荆棘谷猛虎",
|
||||
[682] = "荆棘谷猛虎",
|
||||
[683] = "小猎豹",
|
||||
[684] = "深喉猎豹",
|
||||
[685] = "荆棘谷迅猛龙",
|
||||
[686] = "鞭尾迅猛龙",
|
||||
[687] = "丛林捕猎者",
|
||||
[704] = "蓬毛森林狼",
|
||||
[705] = "蓬毛幼狼",
|
||||
[708] = "小型峭壁野猪",
|
||||
[728] = "巴尔瑟拉",
|
||||
[729] = "辛达尔",
|
||||
[730] = "泰希斯",
|
||||
[731] = "虎王邦加拉西",
|
||||
[736] = "黑豹",
|
||||
[767] = "沼泽虎",
|
||||
[768] = "暗影黑豹",
|
||||
[769] = "死亡狼蛛",
|
||||
[772] = "荆棘谷雌虎",
|
||||
[822] = "小森林熊",
|
||||
[830] = "沙行蟹",
|
||||
[831] = "海行蟹",
|
||||
[833] = "山狗首领",
|
||||
[834] = "山狗",
|
||||
[854] = "幼年丛林捕猎者",
|
||||
[855] = "幼年荆棘谷迅猛龙",
|
||||
[856] = "幼年鞭尾迅猛龙",
|
||||
[858] = "沼泽纺丝蛛",
|
||||
[922] = "淤泥蟹",
|
||||
[923] = "小黑色破坏者",
|
||||
[930] = "小型黑寡妇蜘蛛",
|
||||
[949] = "食腐独行蛛",
|
||||
[976] = "库尔森战虎",
|
||||
[977] = "库尔森战豹",
|
||||
[1015] = "高地迅猛龙",
|
||||
[1016] = "高地鞭尾龙",
|
||||
[1017] = "高地镰爪龙",
|
||||
[1018] = "高地刺喉龙",
|
||||
[1019] = "老刺喉龙",
|
||||
[1020] = "杂斑迅猛龙",
|
||||
[1021] = "杂斑尖啸龙",
|
||||
[1022] = "杂斑镰爪龙",
|
||||
[1023] = "杂斑刺喉龙",
|
||||
[1082] = "盐齿鳄鱼",
|
||||
[1084] = "小盐齿鳄鱼",
|
||||
[1087] = "盐齿钳嘴鳄",
|
||||
[1088] = "巨型淤泥蟹",
|
||||
[1108] = "迷雾谷猩猩",
|
||||
[1109] = "碎尸鸟",
|
||||
[1111] = "吸血捕猎者",
|
||||
[1112] = "吸血寡妇",
|
||||
[1114] = "丛林大猩猩",
|
||||
[1125] = "峭壁野猪",
|
||||
[1126] = "大峭壁野猪",
|
||||
[1127] = "老峭壁野猪",
|
||||
[1128] = "小黑熊",
|
||||
[1129] = "黑熊",
|
||||
[1130] = "游荡的冰爪熊",
|
||||
[1131] = "冬狼",
|
||||
[1132] = "狂暴的冬狼",
|
||||
[1133] = "饥饿的冬狼",
|
||||
[1138] = "雪狼",
|
||||
[1140] = "刺喉雌龙",
|
||||
[1150] = "淡水鳄",
|
||||
[1152] = "钳嘴鳄鱼",
|
||||
[1184] = "峭壁潜伏者",
|
||||
[1185] = "林木潜伏者",
|
||||
[1186] = "老黑熊",
|
||||
[1188] = "灰斑黑熊",
|
||||
[1189] = "黑熊首领",
|
||||
[1190] = "山猪",
|
||||
[1191] = "癞皮山猪",
|
||||
[1192] = "老山猪",
|
||||
[1194] = "山鹫",
|
||||
[1195] = "森林潜伏者",
|
||||
[1196] = "冰爪熊",
|
||||
[1199] = "雪豹幼崽",
|
||||
[1201] = "雪豹",
|
||||
[1216] = "滩行蟹",
|
||||
[1225] = "老煤灰",
|
||||
[1258] = "巨型黑色破坏者",
|
||||
[1400] = "湿地鳄鱼",
|
||||
[1417] = "小湿地鳄鱼",
|
||||
[1504] = "小夜行蜘蛛",
|
||||
[1505] = "夜行蜘蛛",
|
||||
[1508] = "小食腐狼",
|
||||
[1509] = "蓬毛食腐狼",
|
||||
[1511] = "暴怒的银背猩猩",
|
||||
[1512] = "夜行蝙蝠",
|
||||
[1513] = "癞皮夜行蝙蝠",
|
||||
[1516] = "康达",
|
||||
[1547] = "衰老的黑暗犬",
|
||||
[1548] = "被诅咒的黑暗犬",
|
||||
[1549] = "饥饿的黑暗犬",
|
||||
[1553] = "巨型夜行蝙蝠",
|
||||
[1554] = "吸血夜行蝙蝠",
|
||||
[1555] = "邪恶的夜行蜘蛛",
|
||||
[1557] = "老迈的迷雾谷猩猩",
|
||||
[1558] = "银背雌猩猩",
|
||||
[1688] = "夜行雌蜘蛛",
|
||||
[1689] = "有伤疤的峭壁野猪",
|
||||
[1693] = "洛克鳄",
|
||||
[1713] = "老年深喉猎豹",
|
||||
[1765] = "座狼",
|
||||
[1766] = "杂斑座狼",
|
||||
[1778] = "凶猛的灰斑熊",
|
||||
[1780] = "苔藓阔步者",
|
||||
[1781] = "迷雾爬行者",
|
||||
[1797] = "巨型灰斑熊",
|
||||
[1809] = "食腐秃鹫",
|
||||
[1815] = "生病的黑熊",
|
||||
[1816] = "生病的灰熊",
|
||||
[1817] = "生病的狼",
|
||||
[1822] = "毒雾潜伏者",
|
||||
[1824] = "瘟疫潜伏者",
|
||||
[1922] = "森林灰狼",
|
||||
[1923] = "血牙座狼",
|
||||
[1961] = "癞爪",
|
||||
[1984] = "小草刺野猪",
|
||||
[1985] = "草刺野猪",
|
||||
[1986] = "树林蜘蛛",
|
||||
[1994] = "邪恶的基塞伊斯",
|
||||
[1995] = "巨翼枭",
|
||||
[1996] = "巨翼恶枭",
|
||||
[1997] = "巨翼猎枭",
|
||||
[1998] = "树林潜伏者",
|
||||
[1999] = "树林毒蜘蛛",
|
||||
[2000] = "树林结网蛛",
|
||||
[2001] = "巨型树林蜘蛛",
|
||||
[2031] = "夜刃豹幼崽",
|
||||
[2032] = "癞皮夜刃豹",
|
||||
[2033] = "老夜刃豹",
|
||||
[2034] = "野生夜刃豹",
|
||||
[2042] = "夜刃豹",
|
||||
[2043] = "夜刃捕食者",
|
||||
[2069] = "月夜猛虎",
|
||||
[2070] = "月夜猛虎幼崽",
|
||||
[2071] = "月夜雌虎",
|
||||
[2089] = "巨型湿地鳄鱼",
|
||||
[2163] = "蓟熊",
|
||||
[2165] = "灰斑蓟熊",
|
||||
[2172] = "雌性森林陆行鸟",
|
||||
[2175] = "影爪",
|
||||
[2231] = "小潮行蟹",
|
||||
[2232] = "潮行蟹",
|
||||
[2233] = "硬壳潮行蟹",
|
||||
[2234] = "小暗礁蟹",
|
||||
[2235] = "暗礁蟹",
|
||||
[2236] = "狂暴暗礁蟹",
|
||||
[2237] = "月夜雄虎",
|
||||
[2275] = "暴怒的斯坦利",
|
||||
[2321] = "森林陆行鸟雏鸟",
|
||||
[2322] = "森林陆行鸟",
|
||||
[2323] = "凶猛的森林陆行鸟",
|
||||
[2348] = "老食苔蛛",
|
||||
[2349] = "巨型食苔蛛",
|
||||
[2350] = "森林食苔蛛",
|
||||
[2351] = "灰熊",
|
||||
[2354] = "邪恶的灰熊",
|
||||
[2356] = "老灰熊",
|
||||
[2384] = "饥饿的山地狮",
|
||||
[2385] = "野生山地狮",
|
||||
[2406] = "山地狮",
|
||||
[2407] = "巨型山地狮",
|
||||
[2408] = "钳嘴龟",
|
||||
[2476] = "大型洛克鳄",
|
||||
[2505] = "海水钳嘴龟",
|
||||
[2521] = "蓝鬃猩猩",
|
||||
[2522] = "丛林猎豹",
|
||||
[2544] = "南部沙蟹",
|
||||
[2559] = "高地阔步龙",
|
||||
[2560] = "高地长尾龙",
|
||||
[2561] = "高地猎兽龙",
|
||||
[2563] = "平原狼蛛",
|
||||
[2565] = "巨型平原狼蛛",
|
||||
[2578] = "小山地秃鹰",
|
||||
[2579] = "山地秃鹰",
|
||||
[2580] = "老山地秃鹰",
|
||||
[2635] = "老海鳄",
|
||||
[2680] = "邪枝幼狼",
|
||||
[2681] = "邪枝巨狼",
|
||||
[2686] = "枯木巢穴守卫",
|
||||
[2727] = "峭壁山狗",
|
||||
[2728] = "野生峭壁山狗",
|
||||
[2729] = "老峭壁山狗",
|
||||
[2730] = "疯狂的峭壁山狗",
|
||||
[2731] = "山脊巡行者",
|
||||
[2732] = "山脊雌豹",
|
||||
[2734] = "山脊雄豹",
|
||||
[2753] = "巴纳布斯",
|
||||
[2829] = "饥饿的秃鹫",
|
||||
[2830] = "秃鹫",
|
||||
[2831] = "巨型秃鹫",
|
||||
[2850] = "断牙",
|
||||
[2923] = "癞皮银鬃狼",
|
||||
[2924] = "银鬃狼",
|
||||
[2925] = "银鬃嗥狼",
|
||||
[2926] = "银鬃捕猎者",
|
||||
[2931] = "札里科特",
|
||||
[2954] = "刺背斗猪",
|
||||
[2955] = "平原陆行鸟",
|
||||
[2956] = "成年平原陆行鸟",
|
||||
[2957] = "老平原陆行鸟",
|
||||
[2958] = "草原狼",
|
||||
[2959] = "草原捕食者",
|
||||
[2960] = "草原狼前锋",
|
||||
[2961] = "山狮",
|
||||
[2966] = "斗猪",
|
||||
[2969] = "消瘦的猛鹫",
|
||||
[2970] = "猛鹫",
|
||||
[2971] = "长爪猛鹫",
|
||||
[3035] = "平原狮",
|
||||
[3068] = "马兹拉纳其",
|
||||
[3098] = "杂斑野猪",
|
||||
[3099] = "可怕的杂斑野猪",
|
||||
[3100] = "老杂斑野猪",
|
||||
[3106] = "小海浪蟹",
|
||||
[3107] = "海浪蟹",
|
||||
[3108] = "硬壳海浪蟹",
|
||||
[3110] = "巨齿鳄鱼",
|
||||
[3121] = "杜隆塔尔猛虎",
|
||||
[3122] = "血爪鞭尾龙",
|
||||
[3123] = "血爪锐齿龙",
|
||||
[3124] = "蝎子",
|
||||
[3125] = "巨钳蝎",
|
||||
[3126] = "硬甲蝎",
|
||||
[3127] = "毒尾蝎",
|
||||
[3225] = "堕落的杂斑野猪",
|
||||
[3226] = "堕落蝎",
|
||||
[3227] = "堕落的血爪锐齿龙",
|
||||
[3228] = "堕落海浪蟹",
|
||||
[3231] = "堕落的巨齿鳄鱼",
|
||||
[3241] = "草原狮王",
|
||||
[3243] = "长鬃草原狮",
|
||||
[3244] = "巨型平原陆行鸟",
|
||||
[3245] = "暴躁的平原陆行鸟",
|
||||
[3246] = "敏捷的平原陆行鸟",
|
||||
[3247] = "雷鹰雏鸟",
|
||||
[3249] = "巨型雷鹰",
|
||||
[3250] = "异种爬行者",
|
||||
[3252] = "异种群居蝎",
|
||||
[3254] = "赤鳞鞭尾龙",
|
||||
[3255] = "赤鳞尖啸龙",
|
||||
[3256] = "赤鳞镰爪龙",
|
||||
[3257] = "伊沙姆哈尔",
|
||||
[3281] = "萨科斯",
|
||||
[3415] = "雌性草原狮",
|
||||
[3416] = "草原狮后",
|
||||
[3424] = "雷鹰破云者",
|
||||
[3425] = "草原徘徊者",
|
||||
[3461] = "绿洲钳嘴龟",
|
||||
[3472] = "瓦希塔帕恩",
|
||||
[3475] = "埃其亚基",
|
||||
[3503] = "异种护卫者",
|
||||
[3566] = "平原徘徊者",
|
||||
[3581] = "下水道鳄鱼",
|
||||
[3619] = "幽灵豹",
|
||||
[3630] = "变异曲蛇",
|
||||
[3631] = "变异刺鞭蛇",
|
||||
[3632] = "变异爬行者",
|
||||
[3633] = "变异屠戮者",
|
||||
[3634] = "变异捕猎者",
|
||||
[3636] = "变异破坏者",
|
||||
[3637] = "变异守护者",
|
||||
[3653] = "克雷什",
|
||||
[3809] = "灰谷熊",
|
||||
[3810] = "老灰谷熊",
|
||||
[3811] = "灰谷巨熊",
|
||||
[3812] = "巨钳蟹",
|
||||
[3814] = "刺壳蟹",
|
||||
[3819] = "野棘捕猎者",
|
||||
[3820] = "野棘毒蜘蛛",
|
||||
[3821] = "野棘潜伏者",
|
||||
[3823] = "幽爪奔跑者",
|
||||
[3824] = "幽爪嗥叫者",
|
||||
[3825] = "幽爪前锋",
|
||||
[3861] = "灰色座狼",
|
||||
[3862] = "流涎的座狼",
|
||||
[3866] = "邪蝠",
|
||||
[3868] = "觅血者",
|
||||
[4005] = "深苔爬行者",
|
||||
[4006] = "深苔结网蛛",
|
||||
[4007] = "深苔毒蜘蛛",
|
||||
[4040] = "洞穴捕猎者",
|
||||
[4067] = "夜行虎",
|
||||
[4117] = "风蛇",
|
||||
[4118] = "毒性风蛇",
|
||||
[4119] = "老风蛇",
|
||||
[4124] = "千针石林山狮",
|
||||
[4126] = "峭壁捕猎者",
|
||||
[4127] = "乱齿土狼",
|
||||
[4128] = "乱齿捕猎者",
|
||||
[4129] = "乱齿狂吠者",
|
||||
[4139] = "恐蝎",
|
||||
[4140] = "恐蝎劫掠者",
|
||||
[4142] = "盐壳龟",
|
||||
[4143] = "盐壳钳嘴龟",
|
||||
[4144] = "盐壳掘地龟",
|
||||
[4154] = "盐湖食腐鹫",
|
||||
[4158] = "盐湖秃鹫",
|
||||
[4248] = "杂毛土狼",
|
||||
[4249] = "杂毛嚎叫者",
|
||||
[4250] = "加拉克猎犬",
|
||||
[4263] = "深苔幼蛛",
|
||||
[4264] = "深苔雌蜘蛛",
|
||||
[4304] = "血色捕猎犬",
|
||||
[4316] = "科卡尔猎犬",
|
||||
[4341] = "尘泥鳄鱼",
|
||||
[4342] = "尘泥锐爪鳄鱼",
|
||||
[4343] = "尘泥钳嘴鳄鱼",
|
||||
[4344] = "尘泥杂斑鳄鱼",
|
||||
[4345] = "尘泥利齿鳄鱼",
|
||||
[4351] = "血沼迅猛龙",
|
||||
[4352] = "血沼尖啸龙",
|
||||
[4355] = "血沼镰爪龙",
|
||||
[4356] = "血沼刺喉龙",
|
||||
[4357] = "血沼鞭尾龙",
|
||||
[4376] = "黑雾蜘蛛",
|
||||
[4377] = "黑雾潜伏者",
|
||||
[4378] = "黑雾独行蛛",
|
||||
[4379] = "黑雾纺丝者",
|
||||
[4380] = "黑雾寡妇蛛",
|
||||
[4396] = "泥石海龟",
|
||||
[4397] = "泥石刺壳龟",
|
||||
[4398] = "泥石穴居龟",
|
||||
[4399] = "泥石掘洞龟",
|
||||
[4400] = "泥石钳嘴龟",
|
||||
[4411] = "暗牙潜伏者",
|
||||
[4412] = "暗牙爬行者",
|
||||
[4413] = "暗牙蜘蛛",
|
||||
[4414] = "暗牙毒蜘蛛",
|
||||
[4415] = "巨型暗牙蜘蛛",
|
||||
[4425] = "盲眼猎手",
|
||||
[4511] = "阿迦玛",
|
||||
[4512] = "腐烂的阿迦玛",
|
||||
[4514] = "暴怒的阿迦玛",
|
||||
[4538] = "沼泽蝙蝠",
|
||||
[4539] = "巨型沼泽蝙蝠",
|
||||
[4548] = "钢齿土狼",
|
||||
[4660] = "玛洛迪骨爪土狼",
|
||||
[4662] = "玛格拉姆骨爪土狼",
|
||||
[4688] = "骨爪土狼",
|
||||
[4689] = "饥饿的骨爪土狼",
|
||||
[4690] = "疯狂的骨爪土狼",
|
||||
[4692] = "恐怖飞扑者",
|
||||
[4693] = "恐怖飞鸟",
|
||||
[4694] = "恐怖撕裂者",
|
||||
[4695] = "恐怖腐化鸟",
|
||||
[4696] = "荒土巨钳蝎",
|
||||
[4697] = "荒土鞭尾蝎",
|
||||
[4699] = "荒土毒尾蝎",
|
||||
[4821] = "滑腻的甲壳蟹",
|
||||
[4822] = "巨钳甲壳蟹",
|
||||
[4823] = "刺毛甲壳蟹",
|
||||
[4824] = "阿库麦尔食鱼龟",
|
||||
[4825] = "阿库麦尔钳嘴龟",
|
||||
[4841] = "死沼巨鳄",
|
||||
[4861] = "利齿蝙蝠",
|
||||
[4887] = "加摩拉",
|
||||
[4950] = "斑斑",
|
||||
[5048] = "无毒飞蛇",
|
||||
[5053] = "变异鳄鱼",
|
||||
[5056] = "变异尖牙风蛇",
|
||||
[5224] = "黑暗滑行虫",
|
||||
[5225] = "黑暗粘液虫",
|
||||
[5260] = "格罗多克猿人",
|
||||
[5262] = "格罗多克大猩猩",
|
||||
[5268] = "铁鬃熊",
|
||||
[5272] = "铁鬃灰熊",
|
||||
[5274] = "铁鬃熊王",
|
||||
[5286] = "长牙奔跑者",
|
||||
[5287] = "长牙嚎叫者",
|
||||
[5288] = "疯狂的长牙狼",
|
||||
[5291] = "哈卡莱霜翼飞蛇",
|
||||
[5307] = "山谷尖啸者",
|
||||
[5308] = "游荡的山谷尖啸者",
|
||||
[5349] = "阿拉瑟希斯",
|
||||
[5352] = "灰腹老熊",
|
||||
[5356] = "咆哮者",
|
||||
[5422] = "沙漠猎食蝎",
|
||||
[5423] = "沙漠鞭尾蝎",
|
||||
[5424] = "沙漠疾行蝎",
|
||||
[5425] = "饥饿的疱爪土狼",
|
||||
[5426] = "疱爪土狼",
|
||||
[5427] = "疯狂的疱爪土狼",
|
||||
[5428] = "大鹏",
|
||||
[5429] = "火鹏",
|
||||
[5430] = "炎鹏",
|
||||
[5431] = "海浪龟",
|
||||
[5432] = "巨型滑浪龟",
|
||||
[5708] = "哈卡的后代",
|
||||
[5755] = "剧毒飞蛇",
|
||||
[5756] = "变异剧毒风蛇",
|
||||
[5762] = "弱毒飞蛇",
|
||||
[5766] = "草原幼狮",
|
||||
[5807] = "扫荡者",
|
||||
[5823] = "死亡毒蝎",
|
||||
[5828] = "狮王休玛",
|
||||
[5829] = "土狼斯诺特",
|
||||
[5834] = "天空之刃艾泽里",
|
||||
[5842] = "跳跃者塔克",
|
||||
[5856] = "晶网蜘蛛",
|
||||
[5857] = "灼热熔岩蜘蛛",
|
||||
[5858] = "巨型熔岩蜘蛛",
|
||||
[5865] = "迪舒",
|
||||
[5937] = "邪刺恐蝎",
|
||||
[5982] = "黑色屠戮者",
|
||||
[5984] = "饥饿的弯牙土狼",
|
||||
[5985] = "弯牙土狼",
|
||||
[5988] = "厚甲毒刺蝎",
|
||||
[5992] = "灰鬃野猪",
|
||||
[6013] = "游荡的秃鹫",
|
||||
[6352] = "珊瑚潜伏者",
|
||||
[6369] = "珊瑚海龟",
|
||||
[6505] = "暴掠龙",
|
||||
[6506] = "疾奔暴掠龙",
|
||||
[6507] = "狩猎暴掠龙",
|
||||
[6508] = "毒皮暴掠龙",
|
||||
[6513] = "安戈洛巨猩猩",
|
||||
[6514] = "安戈洛猩猩",
|
||||
[6516] = "安戈洛大猩猩",
|
||||
[6581] = "暴掠龙女王",
|
||||
[6585] = "乌卡洛克",
|
||||
[6788] = "雌蓟熊",
|
||||
[6789] = "小蓟熊",
|
||||
[7022] = "毒鞭蝎",
|
||||
[7055] = "黑石座狼",
|
||||
[7078] = "裂隙沙漠蝎",
|
||||
[7097] = "铁喙猫头鹰",
|
||||
[7098] = "铁喙尖啸者",
|
||||
[7099] = "铁喙狩猎者",
|
||||
[7268] = "沙怒守护者",
|
||||
[7319] = "萨丝拉",
|
||||
[7405] = "致命的裂隙沙漠蝎",
|
||||
[7430] = "小霜刃豹",
|
||||
[7431] = "霜刃豹",
|
||||
[7432] = "霜刃捕食者",
|
||||
[7433] = "霜刃雌豹",
|
||||
[7434] = "霜刃守卫者",
|
||||
[7443] = "碎齿暴熊",
|
||||
[7444] = "碎齿熊",
|
||||
[7445] = "老碎齿熊",
|
||||
[7446] = "疯狂的碎齿熊",
|
||||
[7455] = "冬泉巨枭",
|
||||
[7456] = "冬泉鸣枭",
|
||||
[7803] = "沙漠掘洞蝎",
|
||||
[8207] = "巨型火鸟",
|
||||
[8208] = "残忍的疱爪土狼",
|
||||
[8211] = "海崖奔跳者",
|
||||
[8213] = "铁背龟",
|
||||
[8277] = "雷克提拉克",
|
||||
[8299] = "斯比弗雷尔",
|
||||
[8300] = "毁灭",
|
||||
[8301] = "掠夺者科拉克",
|
||||
[8303] = "格朗特",
|
||||
[8336] = "哈卡莱挖掘者",
|
||||
[8600] = "瘟疫蝙蝠",
|
||||
[8601] = "毒性瘟疫蝙蝠",
|
||||
[8602] = "巨型瘟疫蝙蝠",
|
||||
[8762] = "林木隐匿者",
|
||||
[8926] = "深渊钉刺者",
|
||||
[8927] = "黑暗尖啸者",
|
||||
[8933] = "洞穴爬行者",
|
||||
[8956] = "怒爪熊",
|
||||
[8958] = "怒爪巨熊",
|
||||
[8959] = "魔爪狼",
|
||||
[8960] = "魔爪食腐狼",
|
||||
[8961] = "魔爪掠夺者",
|
||||
[9416] = "裂盾座狼",
|
||||
[9622] = "尤尔查",
|
||||
[9683] = "拉克维的配偶",
|
||||
[9684] = "拉克维",
|
||||
[9690] = "灰烬座狼",
|
||||
[9691] = "毒尖蝎",
|
||||
[9694] = "流涎的灰烬座狼",
|
||||
[9695] = "死鞭蝎",
|
||||
[9696] = "血斧座狼",
|
||||
[9697] = "巨型灰烬座狼",
|
||||
[9698] = "火尾蝎",
|
||||
[10077] = "死亡之喉",
|
||||
[10200] = "拉克西里",
|
||||
[10221] = "血斧座狼幼崽",
|
||||
[10356] = "贝恩",
|
||||
[10357] = "毒针雷萨恩",
|
||||
[10359] = "瑟斯库克",
|
||||
[10375] = "尖塔小蜘蛛",
|
||||
[10644] = "迷雾嚎叫者",
|
||||
[10737] = "希洛塔姆",
|
||||
[10741] = "赛洛塔姆",
|
||||
[10806] = "尤西乌斯",
|
||||
[10882] = "阿利卡拉",
|
||||
[10981] = "霜狼",
|
||||
[11357] = "哈卡之子",
|
||||
[11360] = "祖利安幼虎",
|
||||
[11361] = "祖利安猛虎",
|
||||
[11365] = "祖利安雌猎虎",
|
||||
[11368] = "觅血蝙蝠",
|
||||
[11370] = "拉札什寡妇蜘蛛",
|
||||
[11371] = "拉扎什毒蛇",
|
||||
[11372] = "拉札什奎蛇",
|
||||
[11735] = "石鞭蝎",
|
||||
[11736] = "石鞭巨钳蝎",
|
||||
[11737] = "石鞭掠夺者",
|
||||
[11738] = "掠沙蜘蛛",
|
||||
[11739] = "石行蜘蛛",
|
||||
[11921] = "贝瑟莱斯",
|
||||
[12037] = "乌索洛克",
|
||||
[12431] = "血牙狼人",
|
||||
[12432] = "维斯迦尔",
|
||||
[12433] = "克雷希斯",
|
||||
[13599] = "迟钝的钳嘴龟",
|
||||
[14123] = "钢腭钳嘴龟",
|
||||
[14222] = "阿拉加",
|
||||
[14223] = "暴躁的本希",
|
||||
[14228] = "怪笑者",
|
||||
[14232] = "飞镖",
|
||||
[14233] = "裂鳞",
|
||||
[14234] = "哈尤克",
|
||||
[14266] = "纺织者杉达",
|
||||
[14268] = "康达尔老爷",
|
||||
[14279] = "爬行的塞斯",
|
||||
[14280] = "大个子萨姆拉斯",
|
||||
[14283] = "雷矛猫头鹰",
|
||||
[14339] = "死亡之嚎",
|
||||
[14343] = "智者奥尔姆",
|
||||
[14344] = "莫戈雷斯",
|
||||
[14472] = "格雷瑟尔",
|
||||
[14476] = "克里拉克",
|
||||
[14491] = "库尔莫克",
|
||||
[14532] = "拉札什毒液蜘蛛",
|
||||
[14821] = "拉札什迅猛龙",
|
||||
[14965] = "疯狂的觅血蝠",
|
||||
[15041] = "玛尔里的爪牙",
|
||||
[15043] = "祖利安鳄鱼",
|
||||
[15101] = "祖利安徘徊者",
|
||||
[16117] = "瘟疫野猪",
|
||||
[36507] = "斯巴特",
|
||||
[36514] = "霜刃豹幼崽",
|
||||
[37007] = "黑寡妇幼蛛",
|
||||
[40000] = "红色狐狸",
|
||||
[50624] = "异种群居蝎",
|
||||
[50627] = "异种群居蝎",
|
||||
[50630] = "异种群居蝎",
|
||||
[60494] = "悲爪",
|
||||
[60697] = "抚慰犬",
|
||||
[60848] = "刺背野猪",
|
||||
[61074] = "高原大猩猩",
|
||||
[61075] = "岩壳爬行者",
|
||||
[61076] = "沙壳爬行者",
|
||||
[61077] = "高原黑猩猩",
|
||||
[61078] = "高原猴子",
|
||||
[61080] = "年迈的高原大猩猩",
|
||||
[61082] = "高原雷霆猩猩",
|
||||
[61090] = "刷尾眼镜蛇",
|
||||
[61091] = "刷尾蝰蛇",
|
||||
[61095] = "毒液掠夺尖啸者",
|
||||
[61096] = "毒液掠夺毒蛇",
|
||||
[61098] = "莫罗戈·雷足国王",
|
||||
[61206] = "掠网爬行者",
|
||||
[61218] = "变异弯曲幼蛇",
|
||||
[61227] = "暮毛徘徊者",
|
||||
[61228] = "暮毛追猎者",
|
||||
[61229] = "饥饿的暮毛",
|
||||
[61230] = "邪翼蝙蝠宝宝",
|
||||
[61231] = "邪翼蝙蝠",
|
||||
[61232] = "空网蜘蛛",
|
||||
[61233] = "空网喷毒者",
|
||||
[61336] = "巨角猫头鹰",
|
||||
[61337] = "巨角猎人",
|
||||
[61401] = "巨型邪翼蝙蝠",
|
||||
[61488] = "蔚蓝喙",
|
||||
[61500] = "高原银背兽",
|
||||
[61552] = "暮掠者",
|
||||
[61554] = "黎明嚎叫",
|
||||
[61599] = "暗豹",
|
||||
[61656] = "灰羽秃鹰",
|
||||
[61657] = "灰羽猎食者",
|
||||
[61658] = "灰爪土狼",
|
||||
[61659] = "灰爪猎手",
|
||||
[61660] = "石尾蝎",
|
||||
[61661] = "石尾鞭笞者",
|
||||
[61662] = "黑毒蝎",
|
||||
[61680] = "泥沼爬行者",
|
||||
[61681] = "幼年泥沼爬行者",
|
||||
[61686] = "灰羽食腐者",
|
||||
[61689] = "幼年萨拉斯野猪",
|
||||
[61690] = "幼年血色山猫",
|
||||
[61691] = "森林陆行鸟",
|
||||
[61692] = "萨拉斯野猪",
|
||||
[61693] = "血色山猫",
|
||||
[61695] = "白色陆行鸟",
|
||||
[61696] = "年迈的萨拉斯野猪",
|
||||
[61697] = "明亮山猫",
|
||||
[61698] = "明亮山猫雌兽",
|
||||
[61699] = "夏尔兰",
|
||||
[61700] = "年迈的血色山猫",
|
||||
[61704] = "远行爬行者",
|
||||
[61716] = "赤红陆行鸟",
|
||||
[61717] = "萨拉斯狐狸",
|
||||
[61774] = "辉煌法力雏龙",
|
||||
[61775] = "翡翠法力雏龙",
|
||||
[61776] = "淡紫法力雏龙",
|
||||
[61789] = "月羽",
|
||||
[61975] = "巨牙野猪",
|
||||
[62066] = "穴织女王",
|
||||
[62073] = "穴织蜘蛛",
|
||||
[62074] = "穴织爬行者",
|
||||
[62075] = "穴织毒蜘蛛",
|
||||
[62121] = "金鬃野猪",
|
||||
[62122] = "雄壮的金鬃野猪",
|
||||
[62123] = "金皮熊",
|
||||
[62124] = "金皮灰熊",
|
||||
[62125] = "年迈的森林狼",
|
||||
[62126] = "森林狼前锋",
|
||||
[62223] = "酷鳞迅猛龙",
|
||||
[62224] = "酷鳞尖啸龙",
|
||||
[62225] = "酷鳞长尾龙",
|
||||
[62226] = "石皮野猪",
|
||||
[62227] = "年迈的石皮野猪",
|
||||
[62247] = "棕色隐匿者",
|
||||
[62248] = "巨型棕色隐匿者",
|
||||
[62256] = "清水钳嘴龟",
|
||||
[62257] = "生病的清水钳嘴龟",
|
||||
[62258] = "剃齿鳄鱼",
|
||||
[62259] = "巨型剃齿鳄鱼",
|
||||
[62260] = "森林野猪",
|
||||
[62261] = "老森林野猪",
|
||||
[62262] = "山地野猪",
|
||||
[62271] = "幼年金鬃野猪",
|
||||
[62272] = "幼年森林狼",
|
||||
[62338] = "雷翼秃鹰",
|
||||
[62339] = "雷翼秃鹫",
|
||||
[62340] = "蓬毛峭壁山狗",
|
||||
[62341] = "流涎的峭壁山狗",
|
||||
[62342] = "雾木蜘蛛",
|
||||
[62343] = "雾木结网蜘蛛",
|
||||
[62344] = "雾木蛛母",
|
||||
[62345] = "悲痛湖鳄鱼",
|
||||
[62642] = "锋鳞",
|
||||
[80250] = "血色山猫",
|
||||
[80252] = "极地狐幼崽",
|
||||
[80254] = "棕狐幼崽",
|
||||
[80256] = "银狐",
|
||||
[80257] = "红狐",
|
||||
[80258] = "雾狐幼崽",
|
||||
[80259] = "雾狐",
|
||||
[80260] = "灵狐",
|
||||
[80466] = "提瑞斯法瘟疫熊",
|
||||
[80815] = "阿曼尼雄鹰",
|
||||
[80816] = "辛特兰雄鹰",
|
||||
[80920] = "阿曼尼雄鹰",
|
||||
[90980] = "祖利安黑豹",
|
||||
[90981] = "祖利安黑豹",
|
||||
[91819] = "海祖瑞猛虎",
|
||||
[91825] = "丛林虎幼崽",
|
||||
[91826] = "丛林母虎",
|
||||
[91827] = "丛林虎",
|
||||
[91828] = "丛林前锋虎",
|
||||
[91829] = "林背践踏猿",
|
||||
[91830] = "林背迅击猿",
|
||||
[91831] = "锐钳蟹",
|
||||
[91832] = "海岛龙虾人",
|
||||
[91833] = "岩石蟹",
|
||||
[91838] = "厚壳钳嘴龟",
|
||||
[91960] = "林爪黑豹",
|
||||
[91961] = "林爪暗影黑豹",
|
||||
[91962] = "盐钳鳄鱼",
|
||||
[91966] = "毒性林蛇",
|
||||
[91984] = "提瑞斯熊",
|
||||
[91985] = "提瑞斯利爪幼熊",
|
||||
[91987] = "峭壁牙野猪",
|
||||
[91989] = "灰爪狼",
|
||||
[91990] = "灰爪阿尔法",
|
||||
[92115] = "被激怒的利爪",
|
||||
[92146] = "沙滩蟹",
|
||||
[92147] = "银岸蟹",
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
MTH_LocaleData = MTH_LocaleData or {}
|
||||
MTH_LocaleData.data = MTH_LocaleData.data or {}
|
||||
MTH_LocaleData.data.items = MTH_LocaleData.data.items or {}
|
||||
|
||||
MTH_LocaleData.data.items.deDE = {
|
||||
[2098] = "Doppelläufige Schrotflinte",
|
||||
[2099] = "Zwergische Handkanone",
|
||||
[2100] = "Präzise kalibriertes Schießeisen",
|
||||
[2504] = "Abgenutzter Kurzbogen",
|
||||
[2505] = "Polierter Kurzbogen",
|
||||
[2506] = "Hornholzdoppelbogen",
|
||||
[2507] = "Laminierter Doppelbogen",
|
||||
[2508] = "Alte Donnerbüchse",
|
||||
[2509] = "Verschnörkelte Donnerbüchse",
|
||||
[2510] = "Robuste Donnerbüchse",
|
||||
[2511] = "Jägerschießeisen",
|
||||
[2512] = "Rauer Pfeil",
|
||||
[2515] = "Scharfer Pfeil",
|
||||
[2516] = "Leichtes Geschoss",
|
||||
[2519] = "Schweres Geschoss",
|
||||
[2550] = "Monster - Bow, Short",
|
||||
[2551] = "Monster - Crossbow",
|
||||
[2552] = "Monster - Gun",
|
||||
[2773] = "Gesprungener Kurzbogen",
|
||||
[2774] = "Rostbedeckte Donnerbüchse",
|
||||
[2777] = "Schwächlicher Kurzbogen",
|
||||
[2778] = "Billige Donnerbüchse",
|
||||
[2780] = "Leichter Jagdbogen",
|
||||
[2781] = "Schmutzige Donnerbüchse",
|
||||
[2782] = "Verschlissener Doppelbogen",
|
||||
[2783] = "Schäbige Donnerbüchse",
|
||||
[2785] = "Steifer Doppelbogen",
|
||||
[2786] = "Geölte Donnerbüchse",
|
||||
[2824] = "Hurrikan",
|
||||
[2825] = "Bogen der Sengpfeile",
|
||||
[2903] = "Daryls Jagdbogen",
|
||||
[2904] = "Daryls Jagdgewehr",
|
||||
[3021] = "Waldläuferbogen",
|
||||
[3023] = "Großkalibrige Donnerbüchse",
|
||||
[3024] = "BKP 2700 \"Vollstrecker\"",
|
||||
[3025] = "BKP 42 \"Ultra\"",
|
||||
[3026] = "Verstärkter Bogen",
|
||||
[3027] = "Schwerer Doppelbogen",
|
||||
[3028] = "Longbow",
|
||||
[3030] = "Schneidenpfeil",
|
||||
[3033] = "Robustes Geschoss",
|
||||
[3034] = "Glyphed Breastplate",
|
||||
[3036] = "Schwerer Kurzbogen",
|
||||
[3037] = "Peitschenholzdoppelbogen",
|
||||
[3039] = "Kurzer Eschenbogen",
|
||||
[3040] = "Vorderlader des Jägers",
|
||||
[3041] = "Donnerbüchse \"Magierauge\"",
|
||||
[3042] = "BKP \"Spatz\" Kleinkaliber",
|
||||
[3078] = "Nagaherzbohrer",
|
||||
[3079] = "Skorns Gewehr",
|
||||
[3430] = "Heckenschützengewehr",
|
||||
[3464] = "Gefiederter Pfeil",
|
||||
[3465] = "Explosivgeschoss",
|
||||
[3493] = "Raptor-Ex",
|
||||
[3567] = "Zwergische Angelrute",
|
||||
[3742] = "Bogen des Plünderns",
|
||||
[3778] = "Gespannter Kompositbogen",
|
||||
[3780] = "Langläufige Muskete",
|
||||
[4025] = "Ausbalancierter Langbogen",
|
||||
[4026] = "Schildwachenmuskete",
|
||||
[4086] = "Blitzgewehr",
|
||||
[4087] = "Volltrefferbogen",
|
||||
[4089] = "Querschlägerbüchse",
|
||||
[4110] = "Meisterjägerbogen",
|
||||
[4111] = "Gewehr des Meisterjägers",
|
||||
[4127] = "Schrapnellblaster",
|
||||
[4362] = "Raues Schießeisen",
|
||||
[4369] = "Tödliche Donnerbüchse",
|
||||
[4372] = "Liebevoll hergestelltes Schießeisen",
|
||||
[4379] = "Versilberte Schrotflinte",
|
||||
[4383] = "Mondsichtgewehr",
|
||||
[4474] = "Rabenholzbogen",
|
||||
[4576] = "Leichter Bogen",
|
||||
[4577] = "Kompakte Schrotflinte",
|
||||
[4763] = "Blackwood Recurve Bow",
|
||||
[4931] = "Hickorykurzbogen",
|
||||
[4960] = "Blitzkügelchen",
|
||||
[5258] = "Monster - Bow, Black",
|
||||
[5259] = "Monster - Bow, Red",
|
||||
[5260] = "Monster - Bow, Brown",
|
||||
[5261] = "Monster - Bow, Gray",
|
||||
[5262] = "Monster - Bow, Dark Brown",
|
||||
[5309] = "Freibeutermuskete",
|
||||
[5346] = "Orcischer Kampfbogen",
|
||||
[5568] = "Glatter Kiesel",
|
||||
[5596] = "Eschenholzbogen",
|
||||
[5748] = "Centaur Longbow",
|
||||
[5817] = "Lunarisbogen",
|
||||
[6315] = "Stahlpfeilarmbrust",
|
||||
[6469] = "Giftschlag",
|
||||
[6696] = "Nachtpirscherbogen",
|
||||
[6739] = "Klippenläufers Ziel",
|
||||
[6798] = "Schmetterprügel",
|
||||
[7729] = "Chesterfallmuskete",
|
||||
[8067] = "Gefertigtes leichtes Geschoss",
|
||||
[8068] = "Gefertigtes schweres Geschoss",
|
||||
[8069] = "Gefertigtes robustes Geschoss",
|
||||
[8179] = "Kadettenbogen",
|
||||
[8180] = "Jagdbogen",
|
||||
[8181] = "Jagdgewehr",
|
||||
[8182] = "Schrotgewehr",
|
||||
[8183] = "Präzisionsbogen",
|
||||
[8188] = "Explosive Schrotflinte",
|
||||
[9399] = "Präzisionspfeil",
|
||||
[9400] = "Baelogs Kurzbogen",
|
||||
[9412] = "Galganns Feuerblaster",
|
||||
[9422] = "Buschmeister der Schattenschmiede",
|
||||
[9426] = "Monolithbogen",
|
||||
[9456] = "Glasschießprügel",
|
||||
[9487] = "Hightech-Superschusswaffe",
|
||||
[10508] = "Mithrildonnerbüchse",
|
||||
[10510] = "Schwerkalibriges Mithrilgewehr",
|
||||
[10512] = "Stark einschlagende Mithrilpatronen",
|
||||
[10513] = "Gyromithrilgeschoss",
|
||||
[10567] = "Stachelschießer",
|
||||
[10579] = "Explosive Arrow",
|
||||
[10624] = "Stechbogen",
|
||||
[11021] = "Monster - Big Sniper Gun",
|
||||
[11284] = "Genaue Patronen",
|
||||
[11285] = "Gezackter Pfeil",
|
||||
[11303] = "Feiner Kurzbogen",
|
||||
[11304] = "Feiner Langbogen",
|
||||
[11305] = "Handfester Kurzbogen",
|
||||
[11306] = "Stabiler Doppelbogen",
|
||||
[11307] = "Massiver Langbogen",
|
||||
[11308] = "Sylvankurzbogen",
|
||||
[11628] = "Hundemeisterbogen",
|
||||
[11629] = "Hundemeisterflinte",
|
||||
[11630] = "Felssplitterkügelchen",
|
||||
[12446] = "Ambossarmuskete",
|
||||
[12447] = "Distelholzbogen",
|
||||
[12448] = "Leichtes Jagdgewehr",
|
||||
[12449] = "Primitiver Bogen",
|
||||
[12523] = "Monster - Gun, Silver Musket",
|
||||
[12651] = "Schwarzkrähe",
|
||||
[12653] = "Zerfetzhaken",
|
||||
[12654] = "Verdammnisgeschoss",
|
||||
[13019] = "Harpyienklauenkurzbogen",
|
||||
[13020] = "Bogen des Himmelsstürmers",
|
||||
[13021] = "Einfädler",
|
||||
[13022] = "Greifenflügellangbogen",
|
||||
[13023] = "Adlerhornlangbogen",
|
||||
[13037] = "Kristallfichtenstecher",
|
||||
[13038] = "Windhauch",
|
||||
[13039] = "Schädelspalterarmbrust",
|
||||
[13040] = "Herzsuchende Armbrust",
|
||||
[13136] = "Klein Timmys Blasrohr",
|
||||
[13137] = "Eisenwirker",
|
||||
[13138] = "Der Verstummer",
|
||||
[13139] = "Bauchripper",
|
||||
[13146] = "Kugelspeiende Schrotflinte",
|
||||
[13147] = "Monster - Bow, White",
|
||||
[13175] = "Voones Zitterbogen",
|
||||
[13248] = "Explosionsschuss-Arkebuse",
|
||||
[13377] = "Miniaturkanonenkugeln",
|
||||
[13380] = "Willeys tragbare Haubitze",
|
||||
[13474] = "Bauer Dalsons Schrotflinte",
|
||||
[13824] = "Doppellangbogen",
|
||||
[13825] = "Scharfe Muskete",
|
||||
[13923] = "Monster - Gun, Tauren Blade Silver",
|
||||
[13924] = "Monster - Gun, Tauren Scope Blade Feathered Silver Deluxe",
|
||||
[14105] = "Monster - Bow, C01/B02 White",
|
||||
[14118] = "Monster - Bow, C02/B02 Black",
|
||||
[14394] = "Durability Bow",
|
||||
[14642] = "Monster - Gun, Tauren Feathers Silver",
|
||||
[15205] = "Gewehr der Eulensicht",
|
||||
[15284] = "Langer Kampfbogen",
|
||||
[15285] = "Langbogen des Bogenschützen",
|
||||
[15286] = "Rotholzlangbogen",
|
||||
[15287] = "Kreuzzüglerbogen",
|
||||
[15288] = "Durchschlagkräftiger Hornbogen",
|
||||
[15289] = "Erzschlagbogen",
|
||||
[15291] = "Harpyienstachler",
|
||||
[15294] = "Belagerungsbogen",
|
||||
[15295] = "Feuerfederbogen",
|
||||
[15296] = "Falkenauges Bogen",
|
||||
[15322] = "Glattlaufschusswaffe",
|
||||
[15323] = "Zündschrotflinte",
|
||||
[15324] = "Uferbachgewehr",
|
||||
[15325] = "Scharfschützenarkebuse",
|
||||
[15460] = "Monster - Gun, Shotgun",
|
||||
[15691] = "Minischrotflinte",
|
||||
[15807] = "Leichte Armbrust",
|
||||
[15808] = "Feine leichte Armbrust",
|
||||
[15809] = "Schwere Armbrust",
|
||||
[15995] = "Thoriumgewehr",
|
||||
[15997] = "Thoriumpatronen",
|
||||
[16004] = "Dunkeleisengewehr",
|
||||
[16007] = "Fehlerloses Arkanitgewehr",
|
||||
[16622] = "Dornenwerfer",
|
||||
[16992] = "Smokeys Sprengstoffschleuder",
|
||||
[16996] = "Blutholzbogen",
|
||||
[17042] = "Nagelspeier",
|
||||
[17069] = "Mal des Schlägers",
|
||||
[17072] = "Blasterwerfer",
|
||||
[17686] = "Meisterjägerbogen",
|
||||
[17687] = "Gewehr des Meisterjägers",
|
||||
[17717] = "Megaschussgewehr",
|
||||
[17753] = "Ziel des tiefgrünen Bewahrers",
|
||||
[18042] = "Thoriumpfeile",
|
||||
[18282] = "Kernscharfschützengewehr",
|
||||
[18323] = "Satyrbogen",
|
||||
[18388] = "Steinzertrümmerer",
|
||||
[18460] = "Unausgereifte Handkanone",
|
||||
[18482] = "Ogerzahnstocherschießer",
|
||||
[18680] = "Uralter Knochenbogen",
|
||||
[18713] = "Rhok'delar, Langbogen der uralten Bewahrer",
|
||||
[18729] = "Kreischender Bogen",
|
||||
[18738] = "Panzerrückgratarmbrust",
|
||||
[18755] = "Xorothianischer Feuerstab",
|
||||
[18833] = "Volltreffer des Großmarschalls",
|
||||
[18835] = "Doppelbogen des Obersten Kriegsfürsten",
|
||||
[18836] = "Repetierarmbrust des Großmarschalls",
|
||||
[18837] = "Armbrust des Obersten Kriegsfürsten",
|
||||
[18855] = "Handkanone des Großmarschalls",
|
||||
[18860] = "Der Straßenfeger des Obersten Kriegsfürsten",
|
||||
[19082] = "Monster - Fire Arrow",
|
||||
[19107] = "Blutsucher",
|
||||
[19114] = "Hochlandbogen",
|
||||
[19316] = "Eisgewirkter Pfeil",
|
||||
[19317] = "Eisgewirkte Kugel",
|
||||
[19350] = "Herzensbrecher",
|
||||
[19361] = "Ashjre'thul, Armbrust der Pein",
|
||||
[19368] = "Drachenodemhandkanone",
|
||||
[19558] = "Bogen der Vorhut",
|
||||
[19559] = "Bogen der Vorhut",
|
||||
[19560] = "Bogen der Vorhut",
|
||||
[19561] = "Bogen der Vorhut",
|
||||
[19562] = "Bogen des Kundschafters",
|
||||
[19563] = "Bogen des Kundschafters",
|
||||
[19564] = "Bogen des Kundschafters",
|
||||
[19565] = "Bogen des Kundschafters",
|
||||
[19853] = "Zwergenzerstörer der Gurubashi",
|
||||
[19993] = "Hoodoojagdbogen",
|
||||
[20038] = "Mandokirs Stachel",
|
||||
[20245] = "90 Green Warrior Gun",
|
||||
[20285] = "63 Green Warrior Gun",
|
||||
[20299] = "90 Green Rogue Bow",
|
||||
[20313] = "63 Green Rogue Bow",
|
||||
[20368] = "Bland Bow of Steadiness",
|
||||
[20437] = "Bogen der Vorhut",
|
||||
[20438] = "Bogen des Kundschafters",
|
||||
[20488] = "Rhok'delar, Langbogen der uralten Bewahrer DEP",
|
||||
[20599] = "Polierte Eisenholzarmbrust",
|
||||
[20646] = "Sandschreitermal",
|
||||
[20663] = "Durchschlagskräftiger Bogen",
|
||||
[20722] = "Kristallprojektilwerfer",
|
||||
[21272] = "Gesegnete Qirajimuskete",
|
||||
[21459] = "Armbrust der bevorstehenden Verdammnis",
|
||||
[21478] = "Bogen der straffen Sehne",
|
||||
[21550] = "Monster - Bow, Kaldorei",
|
||||
[21554] = "Monster - Gun, PvP Horde",
|
||||
[21564] = "Monster - Gun, Kaldorei PVP Alliance",
|
||||
[21616] = "Huhurans Stachel",
|
||||
[21800] = "Silithidhülsenzünder",
|
||||
[22318] = "Malgens Langbogen",
|
||||
[22347] = "Fahrads selbstladende Repetierarmbrust",
|
||||
[22656] = "Der Läuterer",
|
||||
[22810] = "Toxindüse",
|
||||
[22811] = "Seelenstrang",
|
||||
[22812] = "Nerubischer Sklavenjäger",
|
||||
[23557] = "Larve des großen Wurms",
|
||||
[41023] = "Water-logged Musket",
|
||||
[41190] = "Bow of Alah'Thalas",
|
||||
[41725] = "Drachenmal Kampfbogen",
|
||||
[51746] = "Shadowblaster",
|
||||
[51759] = "Siege Bomber",
|
||||
[51768] = "Bow of Quel'Danil",
|
||||
[51780] = "Ghoulslayer Shotgun",
|
||||
[51794] = "Otherworldly Rifle",
|
||||
[51828] = "Hawkwind Rifle",
|
||||
[51844] = "Stunning Crossbow",
|
||||
[51862] = "Brave's Rifle",
|
||||
[55028] = "Kavdan's Patient Watch",
|
||||
[55068] = "Zan's Leperification Blaster",
|
||||
[55096] = "Phase-shifting Crossbow",
|
||||
[55346] = "Rain of Spiders",
|
||||
[55379] = "Slag Slugger",
|
||||
[58016] = "Ashwood Bow",
|
||||
[58083] = "Nippsy's Precision Rifle",
|
||||
[58086] = "Standard Grade Rifle",
|
||||
[58193] = "Demon Hair Bow",
|
||||
[58284] = "Waterlogged Blunderbuss",
|
||||
[60165] = "Wobblefree Fizz-rifle",
|
||||
[60309] = "'Jadewood' Longbow",
|
||||
[60338] = "Lordaeron Rusty Crossbow",
|
||||
[60339] = "High Elven Rotten Bow",
|
||||
[60440] = "Bloodscalp Longbow",
|
||||
[60506] = "Vigilance",
|
||||
[60545] = "Techrifle X-TREME 5200",
|
||||
[60624] = "Goldplated Royal Crossbow",
|
||||
[60782] = "Shieldbreaker Arbalest",
|
||||
[60821] = "Theramore Armbrust",
|
||||
[60882] = "Magram Windstriker",
|
||||
[60953] = "Brackenwall Longbow",
|
||||
[61011] = "Flintlocke's Hand Cannon",
|
||||
[61068] = "Dark Iron Desecrator",
|
||||
[61165] = "Bow of the Night Huntress",
|
||||
[61248] = "Beasthunter's Blunderbuss",
|
||||
[61307] = "Worgen Hunter Musket",
|
||||
[61383] = "Intricate Gnomish Blunderbuss",
|
||||
[61472] = "Brigade Rifle",
|
||||
[61525] = "Nature's Call",
|
||||
[61569] = "Time Frozen Bow",
|
||||
[61629] = "Farmer's Musket",
|
||||
[61683] = "Battered Arbalest",
|
||||
[70049] = "Worn Crossbow",
|
||||
[80106] = "Miscalibrated Rifle",
|
||||
[80127] = "Polished Boomstick",
|
||||
[80207] = "Worn Wooden Bow",
|
||||
[80223] = "Weltenwanderer Jagdbogen",
|
||||
[80503] = "Well-balanced Short Bow",
|
||||
[80546] = "Quel'dorei Ranger's Longbow",
|
||||
[80603] = "Sturdy Short Bow",
|
||||
[80646] = "Revantusk Shadow Hunter's Longbow",
|
||||
[80745] = "Abomination Crossbow",
|
||||
[80795] = "Burstshot Harquebus",
|
||||
[80820] = "Quilflinger",
|
||||
[80825] = "Flamestring Bow",
|
||||
[80876] = "Silvermoon Bow",
|
||||
[83094] = "Monster - Rifle2H, Mechagnome",
|
||||
[83225] = "Bow of the Grove",
|
||||
[83257] = "Caer Darrow Reserve Rifle",
|
||||
[83452] = "Windbreaker",
|
||||
[83517] = "Balanced Light Crossbow",
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user