mirror of
https://codeberg.org/Duvelcorp/MetaHunt.git
synced 2026-09-21 23:26:57 +00:00
Unify SavedVariables into two clean roots with one-time migration
Collapse all legacy/scattered SavedVariables from absorbed standalone
addons (FeedOMatic, ZHunter/zButtons, SmartAmmo, AutoQuest, AntiDaze,
AutoStrip, MinimapButton) into two persisted globals with clean per-module
nesting under .modules.<id>.
Migration engine (api/savedvariables.lua):
- MTH_SV_EnsureSchema() with schemaVersion gate, idempotent + non-destructive
- Per-module migrators map old locations -> canonical nested stores
- Driven by an ADDON_LOADED("MetaHunt") handler so it runs only AFTER WoW
loads the real SavedVariables (fixes announce-every-session bug that ran
the migration on empty pre-load defaults)
- One-time player-facing chat announce, gated on real legacy data present
Framework/config:
- core-framework InitSavedVariables no longer runs migration at file-load;
unconditionally strips account-root module aliases
- config.lua stops re-creating root[module] aliases (was duplicating data
onto disk); canonical store stays under .modules.<id>
Modules:
- feedomatic: bind FOM_* globals as runtime aliases into nested store,
drop persisted duplicates and .legacy blob
- zhunter: ZHunterMod_Saved re-pointed at .modules.zhunter
- smartammo/autoquest/antidaze/autostrip/minimapbutton read nested store
TOC trimmed to MTH_SavedVariables + MTH_CharSavedVariables only.
Profiles unchanged (already snapshot/apply the nested .modules structure).
Also includes pet-scan refinements: canonical pet key derivation and
level-independent pet signature (schema v3).
This commit is contained in:
@@ -3,6 +3,17 @@
|
||||
All notable changes to MetaHunt will be documented in this file.
|
||||
|
||||
|
||||
## [2.0.0] - Unreleased
|
||||
|
||||
### Changed
|
||||
|
||||
- **Settings storage tidied into one clean layout**: The settings MetaHunt inherited from the older stand-alone addons it now replaces (Feed-O-Matic, ZHunter/zButtons, SmartAmmo, and the quest/anti-daze/auto-strip/minimap helpers) used to be scattered across many separate saved entries, several of them duplicated on disk. MetaHunt now keeps everything in one organised place, grouped per module. The clean-up runs automatically and once, the first time you log in after updating: all of your existing settings and layouts are carried over unchanged — nothing is reset — and the old duplicate copies are removed so your saved-variables file stays small and consistent.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Pets — duplicate and phantom entries in your pet collection**: MetaHunt could record the same tamed pet more than once — a fresh copy appeared after you logged out and back in, or after your pet leveled up — and a few very early pets you had long since dismissed lingered as though they were still with you. MetaHunt now recognises each pet reliably from one session to the next (and as it levels), automatically merges the duplicate entries it had already created while keeping the correct taming details (where, and at what level, you tamed each one), and tidies the leftover phantom pets away into your pet history. Your full taming history is preserved — nothing is deleted.
|
||||
|
||||
|
||||
## [1.5.2] - 2026-09-08
|
||||
|
||||
Res on Octowow server.
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
## Title: MetaHunt - |cff00ff00Hunter
|
||||
## 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, FOM_Config, FOM_FoodQuality, FOM_AddedFoods, FOM_RemovedFoods, FOM_Cooking, FOM_QuestFood, FOM_LocaleInfo
|
||||
## SavedVariablesPerCharacter: ZHunterMod_Saved, MTH_CharSavedVariables
|
||||
## SavedVariables: MTH_SavedVariables
|
||||
## SavedVariablesPerCharacter: MTH_CharSavedVariables
|
||||
|
||||
|
||||
data\init.lua
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
# MetaHunt
|
||||
|
||||
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 on Twow.
|
||||
It is a modern modular addon compiling brand new features, and improving a few old hunter add-ons that were broken on twow.
|
||||
It provides a bunch of useful tools for all huntards, wheter they are still levelling, lone-wolf, or HL raider.
|
||||
|
||||
### Important notes
|
||||
|
||||
- Altough Nampower isn't mandatory for most of the features, it is a must-have for some.
|
||||
- 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 and is not a replacement for it.
|
||||
- Zero support for Capycraft and Ravencraft mafia.
|
||||
- MetaHunt is fully compatible with QUIVER, and is not a replacement for it.
|
||||
- MetaHunt don't and will never support Capycraft/Ravencraft (mafia) servers, after the DDOS attacks they conducted on other servers, to only name that. Fuck you.
|
||||
|
||||
## Twow Data
|
||||
|
||||
@@ -23,6 +24,14 @@ MetaHunt ships with large Twow datastores :
|
||||
|
||||
You can browse all the beasts in game within the Book of Huntards, but also on the github site : https://DuvelCorp.github.io/MetaHunt-Web/
|
||||
|
||||
## The Great Book of Huntards
|
||||
|
||||
The Book is the main GUI of MetaHunt, it allows to display the data collected about you and your pet, and browse the addon's data to find anything a Hunter needs.
|
||||
|
||||
- Browse the MetaHunt datastores of NPCs (Beast, vendors, masters) 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
|
||||
- Keep an history of all your pets after you abandon them or if they run away.
|
||||
|
||||
## Smart Ammo
|
||||
|
||||
@@ -31,7 +40,7 @@ You can browse all the beasts in game within the Book of Huntards, but also on t
|
||||
- Auto equip the right ammo when you swap from a box/xbow to a gun and the other way around.
|
||||
|
||||
## MM Widget
|
||||
Lets Make MM great again. Tracks the new 3-state of Experimental ammo cycle (Fire → Nature → Arcane), the Lock&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. Tracks the new 3-state of Experimental ammo cycle (Fire → Nature → Arcane), the Lock&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. You can also bind it to turn it into a very efficient one-button rotation. No more excuse to not be amongst top DPS.
|
||||
|
||||
## Tooltips
|
||||
|
||||
@@ -45,11 +54,13 @@ You can browse all the beasts in game within the Book of Huntards, but also on t
|
||||
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
|
||||
- zRanged : All the ranged weapons currently in your bags to swap them fast
|
||||
- But also zBar, zCrafts, zMounts, zToys and zCompanions.
|
||||
- But also zCrafts, zMounts, zToys and zCompanions.
|
||||
|
||||
Additionally, a zBar can regroup all the zButtons above into a single bar for people that like clean and ordered stuff.
|
||||
|
||||
## Feed-O-Matic
|
||||
|
||||
@@ -63,26 +74,22 @@ The good old Chronometer addon, purged of non-necessary things and reconfigured
|
||||
|
||||
Old addon used to display Targets' enhanced information when you click on your minimap tracking things. MetaHunt offers many more customization options for it.
|
||||
|
||||
## Ammo labels
|
||||
|
||||
You can display on all ammo in your bags and/or bank their type and/or dps 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.
|
||||
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 mobs involved.
|
||||
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.
|
||||
|
||||
## Beast model Viewer
|
||||
|
||||
Display the model of all beasts in the world. Always know what you are going to tame exactly before running there ! You can also browse all skin models of the same pet family to find the best-looking pet according to your tastes.
|
||||
|
||||
## The Great Book of Huntards
|
||||
|
||||
The Book is the main GUI of MetaHunt, it allows to display the data constantly collected about you and your pet, and browse the addon's data to find anything a Hunter needs.
|
||||
|
||||
- Browse the MetaHunt datastores of NPCs (Beast, vendors, masters) 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
|
||||
- Keep an history of all your pets after you abandon them or if they run away.
|
||||
|
||||
|
||||
|
||||
|
||||
+16
-3
@@ -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()
|
||||
|
||||
+13
-3
@@ -33,10 +33,20 @@ 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}
|
||||
|
||||
+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
|
||||
|
||||
+18
-11
@@ -1187,8 +1187,6 @@ function MTH:InitSavedVariables()
|
||||
if not MTH_SavedVariables then
|
||||
MTH_SavedVariables = {
|
||||
modules = {},
|
||||
feedomatic = {},
|
||||
zhunter = {},
|
||||
}
|
||||
end
|
||||
|
||||
@@ -1198,12 +1196,6 @@ 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 {}
|
||||
end
|
||||
if not MTH_SavedVariables.modules.zhunter then
|
||||
MTH_SavedVariables.modules.zhunter = MTH_SavedVariables.zhunter or {}
|
||||
end
|
||||
if type(MTH_SavedVariables.messages) ~= "table" then
|
||||
MTH_SavedVariables.messages = {}
|
||||
end
|
||||
@@ -1215,9 +1207,6 @@ function MTH:InitSavedVariables()
|
||||
end
|
||||
end
|
||||
|
||||
MTH_SavedVariables.feedomatic = MTH_SavedVariables.modules.feedomatic
|
||||
MTH_SavedVariables.zhunter = MTH_SavedVariables.modules.zhunter
|
||||
|
||||
if not MTH_CharSavedVariables then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
@@ -1271,6 +1260,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)
|
||||
|
||||
+154
-21
@@ -2,7 +2,7 @@ if not MTH then
|
||||
error("MetaHunt core framework missing: api/core-framework.lua must load before api/core-scan-stablemaster.lua")
|
||||
end
|
||||
|
||||
local MTH_PETS_SCHEMA_VERSION = 2
|
||||
local MTH_PETS_SCHEMA_VERSION = 3
|
||||
local MTH_PETS_CORE_HOOK_BOUNDARY_KEY = "core-pet-rename-hook"
|
||||
local MTH_ST_FULL_DEBUG_TRACE = false
|
||||
MTH_PETS_TRACE_CONSISTENCY = false
|
||||
@@ -914,6 +914,21 @@ local function MTH_PETS_ParseCreatureIdFromGuid(guid)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Canonical pet key: the pet GUID with its low 16 bits (last 4 hex chars) zeroed.
|
||||
-- On this core the low 16 bits change every session while the high portion stays
|
||||
-- stable per physical pet, so the full GUID cannot be used as a durable identity
|
||||
-- key. The canonical key is that durable identity, derived purely from the GUID.
|
||||
function MTH_PETS_CanonicalKey(guid)
|
||||
if type(guid) ~= "string" or guid == "" then
|
||||
return nil
|
||||
end
|
||||
local body = string.gsub(guid, "^0[xX]", "")
|
||||
if string.len(body) < 5 then
|
||||
return nil
|
||||
end
|
||||
return "0x" .. string.sub(body, 1, string.len(body) - 4) .. "0000"
|
||||
end
|
||||
|
||||
local function MTH_PETS_ParseBeastLevelBounds(levelField)
|
||||
local levelText = tostring(levelField or "")
|
||||
if levelText == "" then
|
||||
@@ -1252,10 +1267,11 @@ local function MTH_PETS_ResolveConfirmedTameBeastId(source, snapshot)
|
||||
end
|
||||
|
||||
local function MTH_PETS_MakeSignature(name, family, level)
|
||||
-- Level intentionally excluded: it mutates on level-up, so baking it into the
|
||||
-- identity signature made the same pet look like a new one after each level.
|
||||
local cleanName = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(name))
|
||||
local cleanFamily = MTH_PETS_SafeLower(MTH_PETS_NormalizeText(family))
|
||||
local numericLevel = tonumber(level) or 0
|
||||
return cleanName .. "|" .. cleanFamily .. "|" .. tostring(numericLevel)
|
||||
return cleanName .. "|" .. cleanFamily
|
||||
end
|
||||
|
||||
local function MTH_PETS_RowHasAnyTameMetadata(row)
|
||||
@@ -1546,11 +1562,14 @@ local function MTH_PETS_RepairActiveRowsByGuid(pets)
|
||||
local groupedByGuid = {}
|
||||
for petId, row in pairs(pets.petStore.activeById) do
|
||||
if type(row) == "table" and type(row.guid) == "string" and row.guid ~= "" then
|
||||
local guid = row.guid
|
||||
if type(groupedByGuid[guid]) ~= "table" then
|
||||
groupedByGuid[guid] = {}
|
||||
-- Group by the CANONICAL key, not the full guid: the full guid is volatile
|
||||
-- across sessions, so grouping on it never catches cross-session duplicates
|
||||
-- of the same physical pet (the root cause of the duplicate-row bug).
|
||||
local key = MTH_PETS_CanonicalKey(row.guid) or row.guid
|
||||
if type(groupedByGuid[key]) ~= "table" then
|
||||
groupedByGuid[key] = {}
|
||||
end
|
||||
table.insert(groupedByGuid[guid], tostring(petId))
|
||||
table.insert(groupedByGuid[key], tostring(petId))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1627,6 +1646,9 @@ local function MTH_PETS_EnsurePetStoreSchema(pets)
|
||||
row.tameVerified = true
|
||||
end
|
||||
end
|
||||
-- Re-sign level-free every load so the signature index can never retain a
|
||||
-- level-baked key from older data.
|
||||
row.signature = MTH_PETS_MakeSignature(row.name, row.family)
|
||||
if type(row.signature) == "string" and row.signature ~= "" then
|
||||
if type(petStore.signatureIndex[row.signature]) ~= "table" then
|
||||
petStore.signatureIndex[row.signature] = {}
|
||||
@@ -1634,7 +1656,12 @@ local function MTH_PETS_EnsurePetStoreSchema(pets)
|
||||
table.insert(petStore.signatureIndex[row.signature], resolvedPetId)
|
||||
end
|
||||
if type(row.guid) == "string" and row.guid ~= "" then
|
||||
petStore.guidIndex[row.guid] = resolvedPetId
|
||||
petStore.guidIndex[MTH_PETS_CanonicalKey(row.guid) or row.guid] = resolvedPetId
|
||||
end
|
||||
-- beastId is mislabeled (per-pet number, not creature id); expose petNumber
|
||||
-- additively without breaking existing readers.
|
||||
if row.beastId ~= nil and row.petNumber == nil then
|
||||
row.petNumber = row.beastId
|
||||
end
|
||||
if row.loyaltyLevel == nil and type(row.loyalty) == "string" then
|
||||
row.loyaltyLevel = MTH_PETS_ParseLoyaltyLevelFromText(row.loyalty)
|
||||
@@ -1800,8 +1827,16 @@ local function MTH_PETS_ApplyCurrentPetFromActiveRow(cp, petId, row)
|
||||
end
|
||||
end
|
||||
|
||||
-- Forward declaration: the one-shot orphan cleanup is defined later (it reuses
|
||||
-- MTH_PETS_MoveToHistory, declared further down), but MTH_PETS_EnsureSchema needs
|
||||
-- to reference it here. By call time (a load-time event) it is assigned.
|
||||
local MTH_PETS_ArchiveOrphanActiveRows
|
||||
|
||||
local function MTH_PETS_EnsureSchema(pets)
|
||||
local now = time()
|
||||
-- Capture BEFORE the stamp block rewrites schemaVersion, so we know whether the
|
||||
-- one-time v3 orphan cleanup still needs to run for this character.
|
||||
local needsOrphanCleanup = (tonumber(pets.schemaVersion) or 0) < 3
|
||||
if pets.schemaVersion ~= MTH_PETS_SCHEMA_VERSION then
|
||||
pets.schemaVersion = MTH_PETS_SCHEMA_VERSION
|
||||
pets.schemaMigratedAt = now
|
||||
@@ -1810,7 +1845,7 @@ local function MTH_PETS_EnsureSchema(pets)
|
||||
end
|
||||
pets.schemaMigration.version = MTH_PETS_SCHEMA_VERSION
|
||||
pets.schemaMigration.at = now
|
||||
pets.schemaMigration.note = "beta canonical store"
|
||||
pets.schemaMigration.note = "canonical identity keys + orphan cleanup"
|
||||
end
|
||||
if pets.updatedAt == nil then
|
||||
pets.updatedAt = 0
|
||||
@@ -1839,6 +1874,11 @@ local function MTH_PETS_EnsureSchema(pets)
|
||||
end
|
||||
MTH_PETS_EnsureCurrentPetSchema(pets)
|
||||
MTH_PETS_EnsurePetStoreSchema(pets)
|
||||
-- After dedup + index rebuild (done inside EnsurePetStoreSchema), sweep legacy
|
||||
-- orphan rows (active but neither the current pet nor stabled) into history once.
|
||||
if needsOrphanCleanup and type(MTH_PETS_ArchiveOrphanActiveRows) == "function" then
|
||||
MTH_PETS_ArchiveOrphanActiveRows(pets, "migration-recovered")
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_PETS_MarkStableVisited(pets, source)
|
||||
@@ -2067,19 +2107,22 @@ local function MTH_PETS_SelectPetIdBySnapshot(store, snapshot, previousPetId)
|
||||
|
||||
if previousPetId and store.activeById[previousPetId] then
|
||||
local previousRow = store.activeById[previousPetId]
|
||||
if previousRow.guid and snapshot.guid and previousRow.guid == snapshot.guid then
|
||||
if previousRow.guid and snapshot.guid
|
||||
and MTH_PETS_CanonicalKey(previousRow.guid) == MTH_PETS_CanonicalKey(snapshot.guid) then
|
||||
return previousPetId
|
||||
end
|
||||
end
|
||||
|
||||
if snapshot.guid and store.guidIndex[snapshot.guid] and store.activeById[store.guidIndex[snapshot.guid]] then
|
||||
return store.guidIndex[snapshot.guid]
|
||||
local snapshotKey = snapshot.guid and MTH_PETS_CanonicalKey(snapshot.guid) or nil
|
||||
if snapshotKey and store.guidIndex[snapshotKey] and store.activeById[store.guidIndex[snapshotKey]] then
|
||||
return store.guidIndex[snapshotKey]
|
||||
end
|
||||
|
||||
if snapshot.guid and snapshot.guid ~= "" then
|
||||
if snapshotKey then
|
||||
for petId, row in pairs(store.activeById or {}) do
|
||||
if type(row) == "table" and row.guid == snapshot.guid then
|
||||
store.guidIndex[snapshot.guid] = tostring(petId)
|
||||
if type(row) == "table" and type(row.guid) == "string"
|
||||
and MTH_PETS_CanonicalKey(row.guid) == snapshotKey then
|
||||
store.guidIndex[snapshotKey] = tostring(petId)
|
||||
return tostring(petId)
|
||||
end
|
||||
end
|
||||
@@ -2240,16 +2283,18 @@ local function MTH_PETS_UpsertActivePetFromSnapshot(pets, snapshot, source, opti
|
||||
petStore.stableSlotIndex[row.stableSlot] = nil
|
||||
end
|
||||
row.stableSlot = nil
|
||||
if row.guid and row.guid ~= snapshot.guid and petStore.guidIndex[row.guid] == petId then
|
||||
petStore.guidIndex[row.guid] = nil
|
||||
local oldGuidKey = row.guid and MTH_PETS_CanonicalKey(row.guid) or nil
|
||||
local newGuidKey = snapshot.guid and MTH_PETS_CanonicalKey(snapshot.guid) or nil
|
||||
if oldGuidKey and oldGuidKey ~= newGuidKey and petStore.guidIndex[oldGuidKey] == petId then
|
||||
petStore.guidIndex[oldGuidKey] = nil
|
||||
end
|
||||
|
||||
local context = MTH_PETS_CaptureContext()
|
||||
MTH_PETS_ApplySnapshotToRow(row, snapshot, source, context)
|
||||
row.firstSeen = row.firstSeen or time()
|
||||
|
||||
if snapshot.guid and snapshot.guid ~= "" then
|
||||
petStore.guidIndex[snapshot.guid] = petId
|
||||
if newGuidKey then
|
||||
petStore.guidIndex[newGuidKey] = petId
|
||||
end
|
||||
if snapshot.signature and snapshot.signature ~= "" then
|
||||
MTH_PETS_AddSignatureIndex(petStore, snapshot.signature, petId)
|
||||
@@ -2573,8 +2618,9 @@ local function MTH_PETS_MoveToHistory(pets, petId, source, context, reason)
|
||||
if row.signature then
|
||||
MTH_PETS_RemoveSignatureIndex(petStore, row.signature, resolvedPetId)
|
||||
end
|
||||
if row.guid and petStore.guidIndex[row.guid] == resolvedPetId then
|
||||
petStore.guidIndex[row.guid] = nil
|
||||
local guidKey = row.guid and MTH_PETS_CanonicalKey(row.guid) or nil
|
||||
if guidKey and petStore.guidIndex[guidKey] == resolvedPetId then
|
||||
petStore.guidIndex[guidKey] = nil
|
||||
end
|
||||
if row.stableSlot and petStore.stableSlotIndex[row.stableSlot] == resolvedPetId then
|
||||
petStore.stableSlotIndex[row.stableSlot] = nil
|
||||
@@ -2597,6 +2643,56 @@ local function MTH_PETS_MoveToHistory(pets, petId, source, context, reason)
|
||||
return true
|
||||
end
|
||||
|
||||
-- One-shot legacy cleanup (v3 migration): move "orphan" active rows into history.
|
||||
-- An orphan is an active row that is neither the current pet nor associated with a
|
||||
-- stable slot in any way. In normal play a pet is always either summoned or stabled,
|
||||
-- so such rows are abandoned pets that older code failed to archive. They are moved
|
||||
-- (not deleted): all tame history is preserved. Assigned to the forward-declared
|
||||
-- local so MTH_PETS_EnsureSchema (defined earlier) can call it.
|
||||
function MTH_PETS_ArchiveOrphanActiveRows(pets, reason)
|
||||
local petStore = type(pets) == "table" and pets.petStore or nil
|
||||
if type(petStore) ~= "table" or type(petStore.activeById) ~= "table" then
|
||||
return 0
|
||||
end
|
||||
local currentId = tostring(pets.currentPetId or petStore.activeCurrentId or "")
|
||||
local orphanIds = {}
|
||||
for petId, row in pairs(petStore.activeById) do
|
||||
if type(row) == "table" then
|
||||
local id = tostring(petId)
|
||||
local isCurrent = (currentId ~= "" and id == currentId)
|
||||
local inSlotIndex = false
|
||||
if type(petStore.stableSlotIndex) == "table" then
|
||||
for _, mappedId in pairs(petStore.stableSlotIndex) do
|
||||
if tostring(mappedId or "") == id then
|
||||
inSlotIndex = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
local hasStableSlot = tonumber(row.stableSlot) and tonumber(row.stableSlot) > 0
|
||||
local hasStableInfo = type(row.stableInfo) == "table"
|
||||
if not isCurrent and not inSlotIndex and not hasStableSlot and not hasStableInfo then
|
||||
table.insert(orphanIds, id)
|
||||
end
|
||||
end
|
||||
end
|
||||
local archived = 0
|
||||
for i = 1, table.getn(orphanIds) do
|
||||
local id = orphanIds[i]
|
||||
local row = petStore.activeById[id]
|
||||
if type(row) == "table" then
|
||||
row.migrationNote = "Auto-archived by 2.0 pet migration (orphaned active row)."
|
||||
if MTH_PETS_MoveToHistory(pets, id, "migration-v3", nil, reason or "migration-recovered") then
|
||||
archived = archived + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if archived > 0 and MTH and MTH.DebugPrint then
|
||||
MTH:DebugPrint("Pet migration: archived " .. tostring(archived) .. " orphan pet row(s) to history.")
|
||||
end
|
||||
return archived
|
||||
end
|
||||
|
||||
function MTH_PETS_RecordPetAbandon(source, explicitName)
|
||||
local pets = MTH_PETS_GetRootStore()
|
||||
if type(pets) ~= "table" then
|
||||
@@ -3952,6 +4048,43 @@ function MTH_CommandPetsReset()
|
||||
return true
|
||||
end
|
||||
|
||||
-- Dev-only: force the v3 pet-store migration to run again (dedup by canonical key,
|
||||
-- level-free re-sign, index rebuild, orphan cleanup). Resets the pet schema flag
|
||||
-- and re-runs EnsureSchema. Does NOT touch MTH.version, so nothing is broadcast.
|
||||
-- Non-destructive: history is preserved. Invoke via /mth dev petmigrate or /run.
|
||||
function MTH_CommandPetsMigrate()
|
||||
local pets = MTH_PETS_GetRootStore()
|
||||
if type(pets) ~= "table" then
|
||||
MTH:Print("Pet migration failed: datastore unavailable.")
|
||||
return false
|
||||
end
|
||||
local store = MTH_PETS_GetStoreTables(pets)
|
||||
local function countTable(t)
|
||||
local n = 0
|
||||
if type(t) == "table" then for _ in pairs(t) do n = n + 1 end end
|
||||
return n
|
||||
end
|
||||
local beforeActive = countTable(store and store.activeById)
|
||||
local beforeHistory = countTable(store and store.historyById)
|
||||
|
||||
-- Force the one-shot path to re-run regardless of current stamp.
|
||||
pets.schemaVersion = 2
|
||||
MTH_PETS_EnsureSchema(pets)
|
||||
|
||||
store = MTH_PETS_GetStoreTables(pets)
|
||||
local afterActive = countTable(store and store.activeById)
|
||||
local afterHistory = countTable(store and store.historyById)
|
||||
pets.updatedAt = time()
|
||||
if type(MTH_CharSavedVariables) == "table" then
|
||||
MTH_CharSavedVariables.MTH_Pets = pets
|
||||
MTH_CharSavedVariables.petStore = pets.petStore
|
||||
end
|
||||
MTH:Print("Pet migration (v3) complete: active " .. tostring(beforeActive) .. "->" .. tostring(afterActive)
|
||||
.. ", history " .. tostring(beforeHistory) .. "->" .. tostring(afterHistory)
|
||||
.. ", schemaVersion=" .. tostring(pets.schemaVersion))
|
||||
return true
|
||||
end
|
||||
|
||||
function MTH_CommandPetsDump()
|
||||
local pets = MTH_PETS_GetRootStore()
|
||||
MTH_PETS_RefreshCurrentPet()
|
||||
|
||||
@@ -422,6 +422,16 @@ function SlashCmdList.MTH(msg, editbox)
|
||||
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 == "food" then
|
||||
local sub = lowerArg
|
||||
local function MTH_FoodItemLabel(itemId)
|
||||
|
||||
+12
-3
@@ -25,10 +25,19 @@ 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
|
||||
|
||||
+18
-14
@@ -16,26 +16,30 @@ local function MTH_AQ_EnsureStore()
|
||||
if type(MTH_CharSavedVariables) ~= "table" then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.autoquest) ~= "table" then
|
||||
MTH_CharSavedVariables.autoquest = {}
|
||||
local store = nil
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
store = MTH:GetModuleCharSavedVariables("autoquest")
|
||||
end
|
||||
local store = MTH_CharSavedVariables.autoquest
|
||||
if type(MTH_CharSavedVariables.questautomation) == "table" then
|
||||
local legacy = MTH_CharSavedVariables.questautomation
|
||||
if store.scorpokDrazial == nil and legacy.scorpokDrazial ~= nil then
|
||||
store.scorpokDrazial = legacy.scorpokDrazial and true or false
|
||||
if type(store) ~= "table" then
|
||||
if type(MTH_CharSavedVariables.modules) ~= "table" then
|
||||
MTH_CharSavedVariables.modules = {}
|
||||
end
|
||||
if store.scorpokTooltip == nil and legacy.scorpokTooltip ~= nil then
|
||||
store.scorpokTooltip = legacy.scorpokTooltip and true or false
|
||||
if type(MTH_CharSavedVariables.modules.autoquest) ~= "table" then
|
||||
MTH_CharSavedVariables.modules.autoquest = {}
|
||||
end
|
||||
store = MTH_CharSavedVariables.modules.autoquest
|
||||
end
|
||||
if store.scorpokDrazial == nil then
|
||||
store.scorpokDrazial = false
|
||||
if type(store.settings) ~= "table" then
|
||||
store.settings = {}
|
||||
end
|
||||
if store.arrowsForSissies == nil then
|
||||
store.arrowsForSissies = false
|
||||
local settings = store.settings
|
||||
if settings.scorpokDrazial == nil then
|
||||
settings.scorpokDrazial = false
|
||||
end
|
||||
return store
|
||||
if settings.arrowsForSissies == nil then
|
||||
settings.arrowsForSissies = false
|
||||
end
|
||||
return settings
|
||||
end
|
||||
|
||||
function MTH_SetupAutoQuestOptions()
|
||||
|
||||
@@ -129,7 +129,7 @@ function MTH_SetupCreditsOptions()
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
MTH_CR_L("CREDITS_ABOUT_ZHUNTER",
|
||||
"- 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."),
|
||||
"- 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 zBars, zAmmo, zCompanions, zMount, zToys, zCraft.\nAlso the SmartAmmo feature idea is coming from Zhuntermod: there was a file in it with that functionality, but was like a work-in-progress, totally unfunctional and even \"dangerous\" in its current state, and was not active in the addon. Since it was a fucking great idea, I recoded it mostly from scratch and made it work reliably and safely."),
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
@@ -170,7 +170,7 @@ function MTH_SetupCreditsOptions()
|
||||
-4)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
MTH_CR_L("CREDITS_ABOUT_MAP", "- 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."),
|
||||
MTH_CR_L("CREDITS_ABOUT_MAP", "- The Map/Marker system is the one of pfQuest from Master Shagu. I mostly let it untouched, I just made slight modifications so it can integrate better within Metahunt, and doesn't conflict with pfQuest."),
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
@@ -179,8 +179,8 @@ function MTH_SetupCreditsOptions()
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor, MTH_CR_L("CREDITS_ABOUT_DATA_TITLE", "About data sources"), "GameFontNormal", -14, 1.00, 0.82, 0.00)
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
MTH_CR_L("CREDITS_ABOUT_DATA_INTRO", "Metahunt ships with its own Twow 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:"),
|
||||
MTH_CR_L("CREDITS_ABOUT_DATA_INTRO", "Metahunt ships with its own Twow datastores, limited to hunter stuff only, and most of this data is up-to-date (twow 1.18.1). The beast information is coming directly from a extract of live twow DB, made for MetaHunt by twow dev lead in March 2026.\n\n"
|
||||
.. "Other sources used to build the data include:"),
|
||||
"GameFontNormalSmall", -10)
|
||||
|
||||
cursor = MTH_CreditsCreateText(content, cursor,
|
||||
|
||||
@@ -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
|
||||
@@ -1,6 +1,7 @@
|
||||
<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\constants.lua"/>
|
||||
<Include file="..\api\savedvariables.lua"/>
|
||||
<Include file="..\api\minimap-button.lua"/>
|
||||
<Include file="..\api\AutoStrip.xml"/>
|
||||
<Include file="..\api\AutoStrip.lua"/>
|
||||
|
||||
@@ -56,70 +56,33 @@ local function QA_GetOptionsStore()
|
||||
if type(MTH_CharSavedVariables) ~= "table" then
|
||||
MTH_CharSavedVariables = {}
|
||||
end
|
||||
if type(MTH_CharSavedVariables.autoquest) ~= "table" then
|
||||
MTH_CharSavedVariables.autoquest = {}
|
||||
end
|
||||
local store = MTH_CharSavedVariables.autoquest
|
||||
|
||||
if store._migrated ~= true then
|
||||
if type(MTH_CharSavedVariables.questautomation) == "table" then
|
||||
local legacyStore = MTH_CharSavedVariables.questautomation
|
||||
if legacyStore.scorpokDrazial ~= nil and store.scorpokDrazial == nil then
|
||||
store.scorpokDrazial = legacyStore.scorpokDrazial and true or false
|
||||
end
|
||||
if legacyStore.scorpokTooltip ~= nil and store.scorpokTooltip == nil then
|
||||
store.scorpokTooltip = legacyStore.scorpokTooltip and true or false
|
||||
end
|
||||
end
|
||||
local legacyChar = nil
|
||||
local legacyAccount = nil
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
legacyChar = MTH:GetModuleCharSavedVariables("autoquest")
|
||||
if type(legacyChar) ~= "table" then
|
||||
legacyChar = MTH:GetModuleCharSavedVariables("questautomation")
|
||||
end
|
||||
end
|
||||
if MTH and MTH.GetModuleSavedVariables then
|
||||
legacyAccount = MTH:GetModuleSavedVariables("autoquest")
|
||||
if type(legacyAccount) ~= "table" then
|
||||
legacyAccount = MTH:GetModuleSavedVariables("questautomation")
|
||||
end
|
||||
end
|
||||
if type(legacyChar) == "table" and legacyChar.scorpokDrazial ~= nil and store.scorpokDrazial == nil then
|
||||
store.scorpokDrazial = legacyChar.scorpokDrazial and true or false
|
||||
elseif type(legacyAccount) == "table" and legacyAccount.scorpokDrazial ~= nil and store.scorpokDrazial == nil then
|
||||
store.scorpokDrazial = legacyAccount.scorpokDrazial and true or false
|
||||
elseif store.scorpokDrazial == nil and MTH and MTH.GetModuleCharSavedVariables then
|
||||
local autobuyStore = MTH:GetModuleCharSavedVariables("autobuy")
|
||||
if type(autobuyStore) == "table"
|
||||
and type(autobuyStore.questAutomation) == "table"
|
||||
and autobuyStore.questAutomation.scorpokDrazial ~= nil then
|
||||
store.scorpokDrazial = autobuyStore.questAutomation.scorpokDrazial and true or false
|
||||
end
|
||||
end
|
||||
store._migrated = true
|
||||
end
|
||||
|
||||
if store.scorpokDrazial == nil then
|
||||
store.scorpokDrazial = false
|
||||
end
|
||||
if store.arrowsForSissies == nil then
|
||||
store.arrowsForSissies = false
|
||||
end
|
||||
if store.scorpokTooltip == nil then
|
||||
store.scorpokTooltip = false
|
||||
end
|
||||
|
||||
local store = nil
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
local moduleStore = MTH:GetModuleCharSavedVariables("autoquest")
|
||||
if type(moduleStore) == "table" then
|
||||
moduleStore.scorpokDrazial = store.scorpokDrazial and true or false
|
||||
moduleStore.arrowsForSissies = store.arrowsForSissies and true or false
|
||||
moduleStore.scorpokTooltip = store.scorpokTooltip and true or false
|
||||
end
|
||||
store = MTH:GetModuleCharSavedVariables("autoquest")
|
||||
end
|
||||
|
||||
return store
|
||||
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
|
||||
if settings.scorpokTooltip == nil then
|
||||
settings.scorpokTooltip = false
|
||||
end
|
||||
return settings
|
||||
end
|
||||
|
||||
function MTH_AutoQuest:GetStore()
|
||||
@@ -129,36 +92,18 @@ end
|
||||
function MTH_AutoQuest:SetScorpokDrazialEnabled(enabled)
|
||||
local store = QA_GetOptionsStore()
|
||||
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
|
||||
QA_Debug("option write: scorpokDrazial=" .. tostring(store.scorpokDrazial and true or false))
|
||||
end
|
||||
|
||||
function MTH_AutoQuest:SetArrowsForSissiesEnabled(enabled)
|
||||
local store = QA_GetOptionsStore()
|
||||
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
|
||||
QA_Debug("option write: arrowsForSissies=" .. tostring(store.arrowsForSissies and true or false))
|
||||
end
|
||||
|
||||
function MTH_AutoQuest:SetScorpokTooltipEnabled(enabled)
|
||||
local store = QA_GetOptionsStore()
|
||||
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
|
||||
QA_Debug("option write: scorpokTooltip=" .. tostring(store.scorpokTooltip and true or false))
|
||||
end
|
||||
|
||||
|
||||
@@ -650,58 +650,6 @@ function MTH_GetMerchantFoodsByDiet()
|
||||
return result;
|
||||
end
|
||||
|
||||
local function FOM_ItemQuality(itemIdOrLink)
|
||||
if (type(GetItemInfo) ~= "function" or itemIdOrLink == nil) then
|
||||
return nil;
|
||||
end
|
||||
local _, _, quality = GetItemInfo(itemIdOrLink);
|
||||
return quality;
|
||||
end
|
||||
|
||||
local function FOM_DebugLog(message)
|
||||
if (not (FOM_Config and FOM_Config.Debug)) then
|
||||
return;
|
||||
end
|
||||
local text = "[FOM] " .. tostring(message);
|
||||
if (MTH and MTH.Print) then
|
||||
MTH:Print(text, "debug");
|
||||
elseif (DEFAULT_CHAT_FRAME and DEFAULT_CHAT_FRAME.AddMessage) then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(text);
|
||||
end
|
||||
return;
|
||||
end
|
||||
|
||||
local function FOM_DebugLogMerchantFoods(sourceEvent)
|
||||
local merchantFoods = MTH_GetMerchantFoodsByDiet();
|
||||
local eventLabel = tostring(sourceEvent or "MERCHANT");
|
||||
if (merchantFoods == nil) then
|
||||
FOM_DebugLog(eventLabel .. ": no merchant data available.");
|
||||
return;
|
||||
end
|
||||
|
||||
local knownCount = table.getn(merchantFoods.items or {});
|
||||
local unknownCount = table.getn(merchantFoods.unknown or {});
|
||||
local merchantName = UnitName("npc") or UnitName("target") or "unknown";
|
||||
|
||||
FOM_DebugLog(eventLabel .. ": " .. tostring(merchantName) .. " | known foods=" .. tostring(knownCount) .. " | unknown items=" .. tostring(unknownCount));
|
||||
|
||||
if (type(merchantFoods.byDiet) == "table") then
|
||||
for dietName, foodRows in merchantFoods.byDiet do
|
||||
local dietCount = table.getn(foodRows or {});
|
||||
FOM_DebugLog("diet " .. tostring(dietName) .. ": " .. tostring(dietCount) .. " item(s)");
|
||||
for _, row in foodRows do
|
||||
FOM_DebugLog(" - [" .. tostring(row.id) .. "] " .. tostring(row.name) .. " (merchantIndex=" .. tostring(row.index) .. ")");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (unknownCount > 0) then
|
||||
for _, row in merchantFoods.unknown do
|
||||
FOM_DebugLog("unknown food map: [" .. tostring(row.id) .. "] " .. tostring(row.name) .. " (merchantIndex=" .. tostring(row.index) .. ")");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MTH_FOM_FeedButton_OnClick()
|
||||
if (arg1 == "RightButton") then
|
||||
if (MTH_OpenOptions) then
|
||||
@@ -920,10 +868,6 @@ function FOM_OnEvent(event, arg1)
|
||||
end
|
||||
return;
|
||||
|
||||
elseif ( event == "MERCHANT_SHOW" or event == "MERCHANT_UPDATE" ) then
|
||||
FOM_DebugLogMerchantFoods(event);
|
||||
return;
|
||||
|
||||
elseif ( event == "PET_ATTACK_START" ) then
|
||||
|
||||
-- Set Flag
|
||||
@@ -950,7 +894,6 @@ function FOM_OnEvent(event, arg1)
|
||||
end
|
||||
if (foodID) then
|
||||
FOM_LastFood = GFWUtils.ItemLink(foodID);
|
||||
FOM_DebugLog("Manually fed "..FOM_LastFood);
|
||||
end
|
||||
end
|
||||
return;
|
||||
@@ -1269,31 +1212,24 @@ end
|
||||
function FOM_CanFeed()
|
||||
local petInfo = MTH_FOM_GetCorePetInfo();
|
||||
if ( not (petInfo and petInfo.liveExists) ) then
|
||||
FOM_DebugLog("Can't feed; pet doesn't exist.");
|
||||
return false;
|
||||
end
|
||||
if ( tonumber(petInfo.health) and tonumber(petInfo.health) <= 0 ) then
|
||||
FOM_DebugLog("Can't feed; pet is dead.");
|
||||
return false;
|
||||
end
|
||||
if ( UnitHealth("player") <= 0 ) then
|
||||
FOM_DebugLog("Can't feed; I'm dead.");
|
||||
return false;
|
||||
end
|
||||
if ( CastingBarFrameStatusBar:IsVisible() ) then
|
||||
FOM_DebugLog("Can't feed; casting a spell / tradeksill.");
|
||||
return false;
|
||||
end
|
||||
if ( UnitOnTaxi("player") ) then
|
||||
FOM_DebugLog("Can't feed; flying.");
|
||||
return false;
|
||||
end
|
||||
if ( FOM_State.InCombat ) or ( PlayerFrame.inCombat ) then
|
||||
FOM_DebugLog("Can't feed; in combat.");
|
||||
return false;
|
||||
end
|
||||
if ( LootFrame:IsVisible() ) then
|
||||
FOM_DebugLog("Shouldn't feed; loot window is open.");
|
||||
return false;
|
||||
end
|
||||
|
||||
@@ -1315,14 +1251,12 @@ function FOM_CanFeed()
|
||||
local name = GetSpellRecField(sid, "name")
|
||||
if name then
|
||||
if dontFeedNames[name] then
|
||||
FOM_DebugLog("Can't feed; buff detected: " .. name)
|
||||
return false
|
||||
end
|
||||
if UnitLevel("player") >= 40 then
|
||||
local lname = string.lower(name)
|
||||
for _, mountSub in FOM_MOUNT_NAME_SUBSTRINGS do
|
||||
if string.find(lname, mountSub) then
|
||||
FOM_DebugLog("Can't feed; mounted (" .. name .. ").")
|
||||
return false
|
||||
end
|
||||
end
|
||||
@@ -1351,7 +1285,6 @@ function FOM_CanFeed()
|
||||
if ( buff ~= nil) then
|
||||
for _, buffTexture in dontFeedBuffTextures do
|
||||
if ( buff == buffTexture ) then
|
||||
FOM_DebugLog("Can't feed; currently, eating, drinking, or feigning death.");
|
||||
return false;
|
||||
end
|
||||
end
|
||||
@@ -1364,7 +1297,6 @@ function FOM_CanFeed()
|
||||
msg = string.lower(msg);
|
||||
for _, mountName in FOM_MOUNT_NAME_SUBSTRINGS do
|
||||
if (string.find(msg, mountName)) then
|
||||
FOM_DebugLog("Can't feed; mounted.");
|
||||
return false;
|
||||
end
|
||||
end
|
||||
@@ -2387,17 +2319,6 @@ function FOM_Feed(aFood, options)
|
||||
foodLevel = selectedFoodLevel,
|
||||
})
|
||||
|
||||
FOM_DebugLog("Picked "..tostring(FOM_LastFood)
|
||||
.." id="..tostring(selectedId)
|
||||
.." foodLevel="..tostring(selectedFoodLevel)
|
||||
.." itemQuality="..tostring(FOM_ItemQuality(selectedId))
|
||||
.." (bag "..tostring(foodBag)..", slot "..tostring(foodItem)..")"
|
||||
.." reason="..tostring(FOM_LastChoiceReason));
|
||||
if (FOM_Config.Debug) then
|
||||
-- don't actually feed anything, just show what we would choose
|
||||
return false;
|
||||
end
|
||||
|
||||
-- Actually feed the item to the pet
|
||||
PickupContainerItem(foodBag, foodItem);
|
||||
if ( CursorHasItem() ) then
|
||||
@@ -2610,19 +2531,6 @@ function FOM_FlatFoodList()
|
||||
if (table.getn(foodList) == 0 and table.getn(overflowFoodList) > 0) then
|
||||
foodList = overflowFoodList;
|
||||
end
|
||||
if (FOM_Config and FOM_Config.Debug) then
|
||||
FOM_DebugLog("Candidate foods for pet level "..tostring(petLevel).." ("..tostring(table.getn(foodList)).." in diet):");
|
||||
for i = 1, table.getn(foodList) do
|
||||
local c = foodList[i];
|
||||
FOM_DebugLog(" ["..tostring(i).."] "..tostring(c.link)
|
||||
.." id="..tostring(c.itemId)
|
||||
.." qty="..tostring(c.count)
|
||||
.." foodLevel="..tostring(c.quality)
|
||||
.." itemQuality="..tostring(FOM_ItemQuality(c.itemId))
|
||||
.." known="..tostring(c.knownLevel)
|
||||
.." useful="..tostring(c.useful));
|
||||
end
|
||||
end
|
||||
return foodList;
|
||||
end
|
||||
|
||||
@@ -2786,12 +2694,10 @@ end
|
||||
function FOM_IsUsefulFood(itemID, quantity)
|
||||
local foodName = GetItemInfo(itemID);
|
||||
if (foodName == nil) then
|
||||
FOM_DebugLog("Can't get info for item ID "..itemID..", assuming it's OK to eat.");
|
||||
return false;
|
||||
end
|
||||
if (FOM_Cooking and FOM_Cooking[FOM_RealmPlayer] and FOM_Cooking[FOM_RealmPlayer][itemID]) then
|
||||
if (FOM_Cooking[FOM_RealmPlayer][itemID] >= FOM_Config.SaveForCookingLevel) then
|
||||
FOM_DebugLog("Skipping "..quantity.."x "..foodName.."; is good for cooking.");
|
||||
return true;
|
||||
end
|
||||
end
|
||||
@@ -2803,19 +2709,15 @@ function FOM_IsUsefulFood(itemID, quantity)
|
||||
FOM_Quantity[foodName] = FOM_Quantity[foodName] + quantity;
|
||||
end
|
||||
if (FOM_Quantity[foodName] > FOM_QuestFood[FOM_RealmPlayer][foodName]) then
|
||||
FOM_DebugLog("Not skipping "..quantity.."x "..foodName.."; is needed for quest, but we have more than enough.");
|
||||
return false;
|
||||
else
|
||||
FOM_DebugLog("Skipping "..quantity.."x "..foodName.."; is needed for quest.");
|
||||
return true;
|
||||
end
|
||||
end
|
||||
end
|
||||
if (FOM_Config.AvoidBonusFood and FOM_IsInDiet(itemID, FOM_DIET_BONUS)) then
|
||||
FOM_DebugLog("Skipping "..quantity.."x "..foodName.."; has bonus effect when eaten by player.");
|
||||
return true;
|
||||
end
|
||||
--FOM_DebugLog("Not skipping "..quantity.."x "..foodName.."; doesn't have other uses.");
|
||||
return false;
|
||||
end
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@ local MTH_FeedOMatic = {
|
||||
enabled = false,
|
||||
events = {
|
||||
"VARIABLES_LOADED",
|
||||
"MERCHANT_SHOW",
|
||||
"MERCHANT_UPDATE",
|
||||
"MTH_PET_LIVE_STATE_CHANGED",
|
||||
"PET_ATTACK_START",
|
||||
"PET_ATTACK_STOP",
|
||||
@@ -31,7 +29,6 @@ local MTH_FeedOMatic = {
|
||||
}
|
||||
|
||||
local MTH_PETL_HOOK_BOUNDARY_KEY = "pet-lifecycle-hooks"
|
||||
local MTH_FOM_MerchantProbe = nil
|
||||
|
||||
function MTH_FeedOMatic_OnUpdate(elapsed)
|
||||
if not elapsed then
|
||||
@@ -128,104 +125,34 @@ local function MTH_FeedOMatic_SyncSavedVariables()
|
||||
end
|
||||
|
||||
local moduleStore = MTH:GetModuleSavedVariables("feedomatic")
|
||||
if not moduleStore.legacy then
|
||||
moduleStore.legacy = {}
|
||||
end
|
||||
|
||||
local function bindLegacyTable(globalName)
|
||||
local legacyValue = moduleStore.legacy[globalName]
|
||||
local globalValue = _G and _G[globalName] or nil
|
||||
|
||||
if type(legacyValue) ~= "table" then
|
||||
-- Bind each legacy FeedOMatic global to its clean nested location under
|
||||
-- modules.feedomatic. The globals are runtime aliases only (NOT persisted via
|
||||
-- the TOC any more); the real data lives in these nested tables inside
|
||||
-- MTH_SavedVariables. Adopt any pre-existing runtime global on first bind.
|
||||
local function bindGlobal(globalName, key)
|
||||
local target = moduleStore[key]
|
||||
if type(target) ~= "table" then
|
||||
local globalValue = _G and _G[globalName] or nil
|
||||
if type(globalValue) == "table" then
|
||||
legacyValue = globalValue
|
||||
target = globalValue
|
||||
else
|
||||
legacyValue = {}
|
||||
target = {}
|
||||
end
|
||||
moduleStore.legacy[globalName] = legacyValue
|
||||
moduleStore[key] = target
|
||||
end
|
||||
|
||||
if _G then
|
||||
_G[globalName] = legacyValue
|
||||
_G[globalName] = target
|
||||
end
|
||||
end
|
||||
|
||||
bindLegacyTable("FOM_Config")
|
||||
bindLegacyTable("FOM_FoodQuality")
|
||||
bindLegacyTable("FOM_AddedFoods")
|
||||
bindLegacyTable("FOM_RemovedFoods")
|
||||
bindLegacyTable("FOM_Cooking")
|
||||
bindLegacyTable("FOM_QuestFood")
|
||||
bindLegacyTable("FOM_LocaleInfo")
|
||||
end
|
||||
|
||||
local function MTH_FeedOMatic_Log(message, severity)
|
||||
if type(MTH_Log) == "function" then
|
||||
MTH_Log("[FeedOMatic] " .. tostring(message or ""), severity)
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_FeedOMatic_DebugMerchantScan(sourceEvent)
|
||||
if type(MTH_GetMerchantFoodsByDiet) ~= "function" then
|
||||
MTH_FeedOMatic_Log(tostring(sourceEvent) .. ": MTH_GetMerchantFoodsByDiet is not available yet")
|
||||
return
|
||||
end
|
||||
|
||||
local merchantFoods = MTH_GetMerchantFoodsByDiet()
|
||||
local knownCount = 0
|
||||
local unknownCount = 0
|
||||
if type(merchantFoods) == "table" then
|
||||
if type(merchantFoods.items) == "table" then
|
||||
knownCount = table.getn(merchantFoods.items)
|
||||
end
|
||||
if type(merchantFoods.unknown) == "table" then
|
||||
unknownCount = table.getn(merchantFoods.unknown)
|
||||
end
|
||||
end
|
||||
|
||||
local merchantName = UnitName("npc") or UnitName("target") or "unknown"
|
||||
MTH_FeedOMatic_Log(tostring(sourceEvent) .. ": wrapper scan for " .. tostring(merchantName) .. " | known=" .. tostring(knownCount) .. " | unknown=" .. tostring(unknownCount))
|
||||
|
||||
if type(merchantFoods.byDiet) == "table" then
|
||||
for dietName, rows in merchantFoods.byDiet do
|
||||
local dietCount = 0
|
||||
if type(rows) == "table" then
|
||||
dietCount = table.getn(rows)
|
||||
end
|
||||
if type(rows) == "table" then
|
||||
for _, row in rows do
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if unknownCount > 0 and type(merchantFoods.unknown) == "table" then
|
||||
for _, row in merchantFoods.unknown do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function MTH_FeedOMatic_SetMerchantProbeEnabled(enabled)
|
||||
if not enabled then
|
||||
if MTH_FOM_MerchantProbe then
|
||||
MTH_FOM_MerchantProbe:UnregisterEvent("MERCHANT_SHOW")
|
||||
MTH_FOM_MerchantProbe:UnregisterEvent("MERCHANT_UPDATE")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if not MTH_FOM_MerchantProbe then
|
||||
MTH_FOM_MerchantProbe = CreateFrame("Frame", "MTH_FOM_MerchantProbe")
|
||||
MTH_FOM_MerchantProbe:SetScript("OnEvent", function()
|
||||
local evt = event
|
||||
if evt == "MERCHANT_SHOW" or evt == "MERCHANT_UPDATE" then
|
||||
MTH_FeedOMatic_DebugMerchantScan(evt)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
MTH_FOM_MerchantProbe:RegisterEvent("MERCHANT_SHOW")
|
||||
MTH_FOM_MerchantProbe:RegisterEvent("MERCHANT_UPDATE")
|
||||
bindGlobal("FOM_Config", "settings")
|
||||
bindGlobal("FOM_FoodQuality", "foodQuality")
|
||||
bindGlobal("FOM_AddedFoods", "addedFoods")
|
||||
bindGlobal("FOM_RemovedFoods", "removedFoods")
|
||||
bindGlobal("FOM_Cooking", "cooking")
|
||||
bindGlobal("FOM_QuestFood", "questFood")
|
||||
bindGlobal("FOM_LocaleInfo", "localeInfo")
|
||||
end
|
||||
|
||||
function MTH_FeedOMatic:init()
|
||||
@@ -265,7 +192,6 @@ function MTH_FeedOMatic:setEnabled(enabled)
|
||||
MTH_FeedOMatic_CaptureHookBoundary()
|
||||
MTH_FeedOMatic_SyncSavedVariables()
|
||||
MTH_FeedOMatic_EnsureVariablesLoaded(self)
|
||||
MTH_FeedOMatic_SetMerchantProbeEnabled(false)
|
||||
if MTH_FOM_FeedButton then
|
||||
MTH_FOM_FeedButton:Show()
|
||||
end
|
||||
@@ -275,7 +201,6 @@ function MTH_FeedOMatic:setEnabled(enabled)
|
||||
if type(FOM_State) == "table" then
|
||||
FOM_State.ShouldFeed = false
|
||||
end
|
||||
MTH_FeedOMatic_SetMerchantProbeEnabled(false)
|
||||
if MTH_FOM_FeedButton then
|
||||
MTH_FOM_FeedButton:Hide()
|
||||
end
|
||||
@@ -304,7 +229,6 @@ function MTH_FeedOMatic:cleanup()
|
||||
if type(FOM_State) == "table" then
|
||||
FOM_State.ShouldFeed = false
|
||||
end
|
||||
MTH_FeedOMatic_SetMerchantProbeEnabled(false)
|
||||
if MTH_FOM_FeedButton then
|
||||
MTH_FOM_FeedButton:Hide();
|
||||
end
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
local function MTHSmartAmmo_GetSaved()
|
||||
return MTH_SA_GetSavedTable("MTHSmartAmmo")
|
||||
end
|
||||
|
||||
local function MTHSmartAmmo_GetModuleStore()
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
return MTH:GetModuleCharSavedVariables("smartammo")
|
||||
@@ -9,40 +5,43 @@ local function MTHSmartAmmo_GetModuleStore()
|
||||
return nil
|
||||
end
|
||||
|
||||
local function MTHSmartAmmo_IsSmartEnabled()
|
||||
-- Single source of truth: moduleStore.settings.{smartEnabled,reloadEnabled,weaponSwapEnabled}
|
||||
local function MTHSmartAmmo_GetSettings()
|
||||
local moduleStore = MTHSmartAmmo_GetModuleStore()
|
||||
if type(moduleStore) == "table" and moduleStore.smartEnabled ~= nil then
|
||||
return moduleStore.smartEnabled and true or false
|
||||
if type(moduleStore) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
local saved = MTHSmartAmmo_GetSaved()
|
||||
if saved["enabled"] == nil then
|
||||
return true
|
||||
if type(moduleStore.settings) ~= "table" then
|
||||
moduleStore.settings = {}
|
||||
end
|
||||
return saved["enabled"] and true or false
|
||||
return moduleStore.settings
|
||||
end
|
||||
|
||||
local function MTHSmartAmmo_ReadFlag(key)
|
||||
local settings = MTHSmartAmmo_GetSettings()
|
||||
if settings and settings[key] ~= nil then
|
||||
return settings[key] and true or false
|
||||
end
|
||||
return true -- default enabled
|
||||
end
|
||||
|
||||
local function MTHSmartAmmo_WriteFlag(key, enabled)
|
||||
local settings = MTHSmartAmmo_GetSettings()
|
||||
if settings then
|
||||
settings[key] = enabled and true or false
|
||||
end
|
||||
end
|
||||
|
||||
local function MTHSmartAmmo_IsSmartEnabled()
|
||||
return MTHSmartAmmo_ReadFlag("smartEnabled")
|
||||
end
|
||||
|
||||
local function MTHSmartAmmo_IsReloadEnabled()
|
||||
local moduleStore = MTHSmartAmmo_GetModuleStore()
|
||||
if type(moduleStore) == "table" and moduleStore.reloadEnabled ~= nil then
|
||||
return moduleStore.reloadEnabled and true or false
|
||||
end
|
||||
local saved = MTHSmartAmmo_GetSaved()
|
||||
if saved["reload"] == nil then
|
||||
return true
|
||||
end
|
||||
return saved["reload"] and true or false
|
||||
return MTHSmartAmmo_ReadFlag("reloadEnabled")
|
||||
end
|
||||
|
||||
local function MTHSmartAmmo_IsWeaponSwapEnabled()
|
||||
local moduleStore = MTHSmartAmmo_GetModuleStore()
|
||||
if type(moduleStore) == "table" and moduleStore.weaponSwapEnabled ~= nil then
|
||||
return moduleStore.weaponSwapEnabled and true or false
|
||||
end
|
||||
local saved = MTHSmartAmmo_GetSaved()
|
||||
if saved["weaponSwap"] == nil then
|
||||
return true
|
||||
end
|
||||
return saved["weaponSwap"] and true or false
|
||||
return MTHSmartAmmo_ReadFlag("weaponSwapEnabled")
|
||||
end
|
||||
|
||||
local MTHSmartAmmo_InitializeHooks
|
||||
@@ -58,9 +57,7 @@ local MTH_SA_SPELL_EVENTS = {
|
||||
}
|
||||
|
||||
function MTHSmartAmmo_SetSmartEnabled(enabled, silent)
|
||||
local saved = MTHSmartAmmo_GetSaved()
|
||||
if enabled then
|
||||
saved["enabled"] = 1
|
||||
local evFrame = getglobal("MTH_SA_EventFrame")
|
||||
if evFrame then
|
||||
for _, ev in ipairs(MTH_SA_SPELL_EVENTS) do
|
||||
@@ -77,7 +74,6 @@ function MTHSmartAmmo_SetSmartEnabled(enabled, silent)
|
||||
MTH_SA_Print("Smart Ammo Enabled.")
|
||||
end
|
||||
else
|
||||
saved["enabled"] = false
|
||||
local evFrame = getglobal("MTH_SA_EventFrame")
|
||||
if evFrame then
|
||||
for _, ev in ipairs(MTH_SA_SPELL_EVENTS) do
|
||||
@@ -89,10 +85,7 @@ function MTHSmartAmmo_SetSmartEnabled(enabled, silent)
|
||||
end
|
||||
end
|
||||
|
||||
local moduleStore = MTHSmartAmmo_GetModuleStore()
|
||||
if type(moduleStore) == "table" then
|
||||
moduleStore.smartEnabled = enabled and true or false
|
||||
end
|
||||
MTHSmartAmmo_WriteFlag("smartEnabled", enabled and true or false)
|
||||
end
|
||||
|
||||
function MTHSmartAmmo_GetSmartEnabled()
|
||||
@@ -100,16 +93,7 @@ function MTHSmartAmmo_GetSmartEnabled()
|
||||
end
|
||||
|
||||
function MTHSmartAmmo_SetReloadEnabled(enabled, silent)
|
||||
local saved = MTHSmartAmmo_GetSaved()
|
||||
if enabled then
|
||||
saved["reload"] = 1
|
||||
else
|
||||
saved["reload"] = false
|
||||
end
|
||||
local moduleStore = MTHSmartAmmo_GetModuleStore()
|
||||
if type(moduleStore) == "table" then
|
||||
moduleStore.reloadEnabled = enabled and true or false
|
||||
end
|
||||
MTHSmartAmmo_WriteFlag("reloadEnabled", enabled and true or false)
|
||||
if not silent and DEFAULT_CHAT_FRAME then
|
||||
MTH_SA_Print("Smart Ammo reload fallback " .. (enabled and "Enabled." or "Disabled."))
|
||||
end
|
||||
@@ -120,16 +104,7 @@ function MTHSmartAmmo_GetReloadEnabled()
|
||||
end
|
||||
|
||||
function MTHSmartAmmo_SetWeaponSwapEnabled(enabled, silent)
|
||||
local saved = MTHSmartAmmo_GetSaved()
|
||||
if enabled then
|
||||
saved["weaponSwap"] = 1
|
||||
else
|
||||
saved["weaponSwap"] = false
|
||||
end
|
||||
local moduleStore = MTHSmartAmmo_GetModuleStore()
|
||||
if type(moduleStore) == "table" then
|
||||
moduleStore.weaponSwapEnabled = enabled and true or false
|
||||
end
|
||||
MTHSmartAmmo_WriteFlag("weaponSwapEnabled", enabled and true or false)
|
||||
if not silent and DEFAULT_CHAT_FRAME then
|
||||
MTH_SA_Print("Smart Ammo weapon-swap auto-equip " .. (enabled and "Enabled." or "Disabled."))
|
||||
end
|
||||
@@ -807,15 +782,15 @@ SlashCmdList["MTHSmartAmmo"] = function(msg)
|
||||
return
|
||||
end
|
||||
|
||||
local saved = MTHSmartAmmo_GetSaved()
|
||||
local smartEnabled = MTHSmartAmmo_GetSmartEnabled()
|
||||
|
||||
if msg == "status" then
|
||||
local castHook, actionHook, byNameHook = MTHSmartAmmo_AreHooksInstalled()
|
||||
MTH_SA_Print("Smart Ammo status: smart=" .. tostring(saved["enabled"] and true or false) .. ", module=" .. tostring(MTH_SA_IsModuleEnabled()) .. ", hooks=" .. tostring(castHook) .. "/" .. tostring(actionHook) .. "/" .. tostring(byNameHook))
|
||||
MTH_SA_Print("Smart Ammo status: smart=" .. tostring(smartEnabled) .. ", module=" .. tostring(MTH_SA_IsModuleEnabled()) .. ", hooks=" .. tostring(castHook) .. "/" .. tostring(actionHook) .. "/" .. tostring(byNameHook))
|
||||
return
|
||||
end
|
||||
|
||||
if saved["enabled"] then
|
||||
|
||||
if smartEnabled then
|
||||
MTHSmartAmmo_SetSmartEnabled(nil)
|
||||
else
|
||||
MTHSmartAmmo_SetSmartEnabled(1)
|
||||
|
||||
@@ -15,39 +15,6 @@ local MTH_SmartAmmo = {
|
||||
|
||||
local MTH_SA_BOUNDARY_KEY = "smartammo.core"
|
||||
|
||||
function MTH_SA_GetSavedTable(tableName)
|
||||
if not MTH or not MTH.GetModuleCharSavedVariables then
|
||||
if type(_G[tableName]) ~= "table" then
|
||||
_G[tableName] = {}
|
||||
end
|
||||
return _G[tableName]
|
||||
end
|
||||
|
||||
local moduleStore = MTH:GetModuleCharSavedVariables("smartammo")
|
||||
if type(moduleStore) ~= "table" then
|
||||
moduleStore = {}
|
||||
end
|
||||
|
||||
if not moduleStore.legacy then
|
||||
moduleStore.legacy = {}
|
||||
end
|
||||
if type(moduleStore.legacy[tableName]) ~= "table" then
|
||||
moduleStore.legacy[tableName] = {}
|
||||
end
|
||||
|
||||
if type(_G[tableName]) == "table" then
|
||||
local legacyTable = moduleStore.legacy[tableName]
|
||||
if not next(legacyTable) then
|
||||
for key, value in pairs(_G[tableName]) do
|
||||
legacyTable[key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
_G[tableName] = moduleStore.legacy[tableName]
|
||||
|
||||
return moduleStore.legacy[tableName]
|
||||
end
|
||||
|
||||
function MTH_SA_IsModuleEnabled()
|
||||
if MTH and MTH.IsModuleEnabled then
|
||||
return MTH:IsModuleEnabled("smartammo", true) and true or false
|
||||
@@ -70,43 +37,22 @@ local function MTH_SA_ApplySavedState()
|
||||
return
|
||||
end
|
||||
|
||||
local saved = MTH_SA_GetSavedTable("MTHSmartAmmo")
|
||||
local moduleStore = MTH and MTH.GetModuleCharSavedVariables and MTH:GetModuleCharSavedVariables("smartammo") or nil
|
||||
if type(moduleStore) == "table" then
|
||||
if moduleStore.smartEnabled ~= nil then
|
||||
saved["enabled"] = moduleStore.smartEnabled and 1 or false
|
||||
end
|
||||
if moduleStore.reloadEnabled ~= nil then
|
||||
saved["reload"] = moduleStore.reloadEnabled and 1 or false
|
||||
end
|
||||
if moduleStore.weaponSwapEnabled ~= nil then
|
||||
saved["weaponSwap"] = moduleStore.weaponSwapEnabled and 1 or false
|
||||
end
|
||||
end
|
||||
if saved["enabled"] == nil then
|
||||
saved["enabled"] = 1
|
||||
if type(moduleStore) == "table" then
|
||||
moduleStore.smartEnabled = true
|
||||
end
|
||||
end
|
||||
if saved["reload"] == nil and type(moduleStore) == "table" then
|
||||
moduleStore.reloadEnabled = saved["reload"] ~= false
|
||||
end
|
||||
if saved["weaponSwap"] == nil then
|
||||
saved["weaponSwap"] = 1
|
||||
if type(moduleStore) == "table" then
|
||||
moduleStore.weaponSwapEnabled = true
|
||||
end
|
||||
elseif type(moduleStore) == "table" and moduleStore.weaponSwapEnabled == nil then
|
||||
moduleStore.weaponSwapEnabled = saved["weaponSwap"] ~= false
|
||||
end
|
||||
local settings = (type(moduleStore) == "table" and type(moduleStore.settings) == "table") and moduleStore.settings or {}
|
||||
|
||||
MTHSmartAmmo_SetSmartEnabled(saved["enabled"] and 1 or nil, 1)
|
||||
local smart = settings.smartEnabled
|
||||
if smart == nil then smart = true end
|
||||
local reload = settings.reloadEnabled
|
||||
if reload == nil then reload = true end
|
||||
local swap = settings.weaponSwapEnabled
|
||||
if swap == nil then swap = true end
|
||||
|
||||
MTHSmartAmmo_SetSmartEnabled(smart and 1 or nil, 1)
|
||||
if type(MTHSmartAmmo_SetReloadEnabled) == "function" then
|
||||
MTHSmartAmmo_SetReloadEnabled(saved["reload"] ~= false and 1 or nil, 1)
|
||||
MTHSmartAmmo_SetReloadEnabled(reload and 1 or nil, 1)
|
||||
end
|
||||
if type(MTHSmartAmmo_SetWeaponSwapEnabled) == "function" then
|
||||
MTHSmartAmmo_SetWeaponSwapEnabled(saved["weaponSwap"] ~= false and 1 or nil, 1)
|
||||
MTHSmartAmmo_SetWeaponSwapEnabled(swap and 1 or nil, 1)
|
||||
end
|
||||
if type(MTHSmartAmmo_EnsureHooks) == "function" then
|
||||
MTHSmartAmmo_EnsureHooks("module-apply")
|
||||
@@ -154,7 +100,6 @@ local function MTH_SA_RestoreHooks()
|
||||
end
|
||||
|
||||
function MTH_SmartAmmo:init()
|
||||
MTH_SA_GetSavedTable("MTHSmartAmmo")
|
||||
if self.enabled then
|
||||
MTH_SA_ApplySavedState()
|
||||
end
|
||||
|
||||
+12
-11
@@ -115,23 +115,24 @@ local function MTH_TT_IsOwnPetTooltipsEnabled()
|
||||
end
|
||||
|
||||
local function MTH_TT_GetAutoQuestStore()
|
||||
if type(MTH_CharSavedVariables) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
local store = MTH_CharSavedVariables.autoquest
|
||||
if type(store) ~= "table" then
|
||||
store = MTH_CharSavedVariables.questautomation
|
||||
local store = nil
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
store = MTH:GetModuleCharSavedVariables("autoquest")
|
||||
end
|
||||
if type(store) ~= "table" then
|
||||
return nil
|
||||
end
|
||||
if store.scorpokDrazial == nil then
|
||||
store.scorpokDrazial = false
|
||||
if type(store.settings) ~= "table" then
|
||||
store.settings = {}
|
||||
end
|
||||
if store.scorpokTooltip == nil then
|
||||
store.scorpokTooltip = false
|
||||
local settings = store.settings
|
||||
if settings.scorpokDrazial == nil then
|
||||
settings.scorpokDrazial = false
|
||||
end
|
||||
return store
|
||||
if settings.scorpokTooltip == nil then
|
||||
settings.scorpokTooltip = false
|
||||
end
|
||||
return settings
|
||||
end
|
||||
|
||||
local function MTH_TT_IsScorpokAutomationEnabled()
|
||||
|
||||
@@ -43,10 +43,35 @@ function MTH_ZH_IsModuleEnabled()
|
||||
end
|
||||
|
||||
function MTH_ZH_GetSavedRoot()
|
||||
if type(ZHunterMod_Saved) ~= "table" then
|
||||
ZHunterMod_Saved = {}
|
||||
-- Canonical store is MTH_CharSavedVariables.modules.zhunter. The old global
|
||||
-- ZHunterMod_Saved is kept only as a RUNTIME alias so the many zButton files
|
||||
-- and options-zbuttons.lua can keep referencing it unchanged.
|
||||
local store = nil
|
||||
if MTH and MTH.GetModuleCharSavedVariables then
|
||||
store = MTH:GetModuleCharSavedVariables("zhunter")
|
||||
end
|
||||
return ZHunterMod_Saved
|
||||
|
||||
if type(store) ~= "table" then
|
||||
-- Framework not ready yet: fall back to a temporary global holder;
|
||||
-- MTH_ZH_SyncSavedVariables re-binds to the nested store later.
|
||||
if type(ZHunterMod_Saved) ~= "table" then
|
||||
ZHunterMod_Saved = {}
|
||||
end
|
||||
return ZHunterMod_Saved
|
||||
end
|
||||
|
||||
-- Adopt a pre-existing global (legacy TOC copy) once, only if the nested
|
||||
-- store is still empty, so no layout is lost during the transition.
|
||||
if type(ZHunterMod_Saved) == "table" and ZHunterMod_Saved ~= store then
|
||||
if next(store) == nil then
|
||||
for k, v in pairs(ZHunterMod_Saved) do
|
||||
store[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ZHunterMod_Saved = store -- pure runtime alias into the canonical store
|
||||
return store
|
||||
end
|
||||
|
||||
function MTH_ZH_GetSavedTable(key)
|
||||
|
||||
@@ -165,18 +165,11 @@ MTH_ZH_ApplyEnabledRuntimeState = function(source)
|
||||
end
|
||||
|
||||
MTH_ZH_SyncSavedVariables = function()
|
||||
if not MTH or not MTH.GetModuleCharSavedVariables then
|
||||
return
|
||||
end
|
||||
|
||||
local moduleStore = MTH:GetModuleCharSavedVariables("zhunter")
|
||||
|
||||
if type(ZHunterMod_Saved) == "table" then
|
||||
if MTH_CharSavedVariables and MTH_CharSavedVariables.modules then
|
||||
MTH_CharSavedVariables.modules.zhunter = ZHunterMod_Saved
|
||||
end
|
||||
elseif type(moduleStore) == "table" then
|
||||
ZHunterMod_Saved = moduleStore
|
||||
-- Bind the ZHunterMod_Saved global as a runtime alias into the canonical
|
||||
-- nested store (MTH_CharSavedVariables.modules.zhunter). GetSavedRoot does the
|
||||
-- adoption + aliasing; there is no second on-disk copy anymore.
|
||||
if type(MTH_ZH_GetSavedRoot) == "function" then
|
||||
MTH_ZH_GetSavedRoot()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user