DopingControl v0.6.1
This commit is contained in:
+210
@@ -0,0 +1,210 @@
|
||||
-- DopingControl core/config.lua
|
||||
-- SavedVariables defaults + fill-in: single DEFAULTS table, per-field fill-in
|
||||
-- with nil-checks (NEVER 'or' for booleans -- would flip saved false),
|
||||
-- copy-on-assign for table values so editing a live value can never mutate
|
||||
-- the defaults.
|
||||
-- Pure Lua 5.0, no WoW API -- dofile-loadable offline.
|
||||
|
||||
DopingControl = DopingControl or {}
|
||||
local DC = DopingControl
|
||||
|
||||
-- SavedVariables root (the client replaces this global at ADDON_LOADED;
|
||||
-- core/init.lua then calls DC.EnsureDefaults(DopingControlDB)).
|
||||
DopingControlDB = DopingControlDB or {}
|
||||
|
||||
-- ------------------------------------------------------------------
|
||||
-- Defaults
|
||||
--
|
||||
-- Two keys intentionally have NO entry here (a nil value cannot live in a
|
||||
-- Lua table):
|
||||
-- framePos -- default nil = "no saved position, use the anchor
|
||||
-- default"; written by the UI on drag as
|
||||
-- { point=..., relPoint=..., x=..., y=... }.
|
||||
-- expectations -- default nil = "not initialized yet"; EnsureDefaults
|
||||
-- special-cases it below: the saved shape
|
||||
-- is PER CLASS (db.expectations[role][class][slotId]),
|
||||
-- materialized/migrated from the DC.DEFAULT_EXPECT seed
|
||||
-- by DC.EnsureExpectations.
|
||||
-- ------------------------------------------------------------------
|
||||
DC.DEFAULTS = {
|
||||
testMode = false, -- /dc test: simulated raid
|
||||
showMinimapButton = true,
|
||||
minimapAngle = 215, -- degrees around the minimap rim
|
||||
readyCheckScan = true, -- auto-scan on READY_CHECK
|
||||
readyCheckOpen = true, -- auto-open window on READY_CHECK
|
||||
collapse = {}, -- per-role collapsed flags, e.g. collapse.RANGED = true
|
||||
filterGapsOnly = false, -- toolbar filter All | Gaps only
|
||||
roles = {}, -- confirmed roles keyed by player NAME
|
||||
-- Weapon skill books: db.skillBooks[playerName][weaponSkillName] =
|
||||
-- true|false. A quest weapon-skill book (+3 skill) is INVISIBLE on
|
||||
-- other players -- foreign weapon skill cannot be read at all (see
|
||||
-- data/hit.lua) -- so it is an opt-in the user ticks per player and
|
||||
-- per weapon skill; it only shifts that player's melee/ranged hit CAP.
|
||||
-- Unchecking stores an explicit false (never nil), exactly like the
|
||||
-- expectation grid, so "deliberately off" survives any later fill-in.
|
||||
-- The own character ignores these flags: its weapon skill is measured
|
||||
-- and a measured value already contains the book.
|
||||
skillBooks = {},
|
||||
}
|
||||
|
||||
-- Generic deep copy (tables only; keys are copied by reference, values
|
||||
-- recursively). No cycle handling -- config/expectation shapes are trees.
|
||||
function DC.DeepCopy(src)
|
||||
if type(src) ~= "table" then
|
||||
return src
|
||||
end
|
||||
local out = {}
|
||||
for k, v in pairs(src) do
|
||||
if type(v) == "table" then
|
||||
out[k] = DC.DeepCopy(v)
|
||||
else
|
||||
out[k] = v
|
||||
end
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
-- ------------------------------------------------------------------
|
||||
-- Expectations: the saved shape is
|
||||
-- PER CLASS --
|
||||
-- db.expectations[role][class][slotId] = true|nil
|
||||
-- with one class line for every class that can perform the role
|
||||
-- (DC_Roles.ROLE_CLASSES, the ordered inversion of CLASS_ROLES).
|
||||
-- DC.DEFAULT_EXPECT (data/expectations.lua) STAYS the role x slot SEED.
|
||||
--
|
||||
-- EnsureExpectations guarantees the full structure:
|
||||
-- * first load (expectations == nil): every (role, performing class)
|
||||
-- gets a deep copy of the seed's role line.
|
||||
-- * legacy saved tables (one line per role) -- detected by BOOLEANS directly under
|
||||
-- [role] (new-shape values are class tables) -- are MIGRATED: the
|
||||
-- old role line is copied to every performing class, then the
|
||||
-- structure is replaced. User edits survive on every class where
|
||||
-- they are representable.
|
||||
-- * new-shape tables: existing class lines are NEVER touched; missing
|
||||
-- (role,class) combos are filled from the seed. The fill is
|
||||
-- behavior-neutral -- the model resolves a missing combo to the
|
||||
-- DC.DEFAULT_EXPECT role line anyway (runtime fallback) -- it only
|
||||
-- hands the options grid a complete structure.
|
||||
-- Known ambiguity (accepted): an old-shape role line with EVERY slot
|
||||
-- unchecked is {} and indistinguishable from an untouched new-shape
|
||||
-- role table (the boolean marker is gone); it is re-seeded per class.
|
||||
--
|
||||
-- Resolved at CALL time, not file-load time: data/expectations.lua and
|
||||
-- core/roles.lua load after this file (TOC order const -> config ->
|
||||
-- data/* -> model -> roles), but EnsureDefaults only runs at
|
||||
-- ADDON_LOADED when everything is in. Offline callers without DC_Roles
|
||||
-- loaded simply skip the expectations block.
|
||||
-- ------------------------------------------------------------------
|
||||
local function isOldShapeLine(line)
|
||||
for _, v in pairs(line) do
|
||||
if type(v) == "boolean" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Top up ONE class line in place: slots the seed marks true and the line
|
||||
-- has NEVER SEEN (nil) become true. Deliberate unchecks survive (the
|
||||
-- options grid stores those as explicit false, never nil). Shared by both
|
||||
-- EnsureExpectations branches below so a freshly migrated line is already
|
||||
-- complete -- not just on the SECOND call (idempotency: a migration must
|
||||
-- converge in one pass, not two).
|
||||
local function topUpLine(cl, seedLine)
|
||||
for slotId, v in pairs(seedLine) do
|
||||
if v == true and cl[slotId] == nil then
|
||||
cl[slotId] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function DC.EnsureExpectations(db)
|
||||
if DC.DEFAULT_EXPECT == nil or DC_Roles == nil
|
||||
or DC_Roles.ROLE_CLASSES == nil then
|
||||
return
|
||||
end
|
||||
if db.expectations == nil then
|
||||
db.expectations = {}
|
||||
end
|
||||
local exp = db.expectations
|
||||
local nRoles = table.getn(DC.ROLES)
|
||||
for r = 1, nRoles do
|
||||
local role = DC.ROLES[r]
|
||||
local classes = DC_Roles.ROLE_CLASSES[role] or {}
|
||||
local nClasses = table.getn(classes)
|
||||
local line = exp[role]
|
||||
local seedLine = DC.DEFAULT_EXPECT[role] or {}
|
||||
if line ~= nil and isOldShapeLine(line) then
|
||||
-- migration: copy the old role line to every performing
|
||||
-- class, then top up each new class line from the CURRENT
|
||||
-- seed (a slot added since the old line was saved must reach
|
||||
-- it immediately, not only on a later call -- see topUpLine),
|
||||
-- then replace the structure (never lose user edits where
|
||||
-- representable)
|
||||
local byClass = {}
|
||||
for c = 1, nClasses do
|
||||
local cl = DC.DeepCopy(line)
|
||||
topUpLine(cl, seedLine)
|
||||
byClass[classes[c]] = cl
|
||||
end
|
||||
exp[role] = byClass
|
||||
else
|
||||
if line == nil then
|
||||
line = {}
|
||||
exp[role] = line
|
||||
end
|
||||
-- fill missing (role,class) combos from the seed; existing
|
||||
-- class lines keep their values, but slots the seed knows
|
||||
-- and the line has NEVER SEEN (nil) are topped up -- without
|
||||
-- this, a slot added in a later version never reaches saved
|
||||
-- lines (field report: neck/ring columns arrived as "not
|
||||
-- expected" for everyone). Deliberate unchecks survive: the
|
||||
-- options grid stores them as explicit false, never nil.
|
||||
for c = 1, nClasses do
|
||||
local cls = classes[c]
|
||||
if line[cls] == nil then
|
||||
line[cls] = DC.DeepCopy(seedLine)
|
||||
else
|
||||
topUpLine(line[cls], seedLine)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Fill missing fields in db from DC.DEFAULTS. Existing values -- including
|
||||
-- saved booleans that are false -- are never touched. Table defaults are
|
||||
-- deep-copied on assign.
|
||||
function DC.EnsureDefaults(db)
|
||||
for k, v in pairs(DC.DEFAULTS) do
|
||||
if db[k] == nil then
|
||||
if type(v) == "table" then
|
||||
db[k] = DC.DeepCopy(v)
|
||||
else
|
||||
db[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
-- expectations: per-(role,class) shape, materialized/migrated from
|
||||
-- the DC.DEFAULT_EXPECT seed (see EnsureExpectations above)
|
||||
DC.EnsureExpectations(db)
|
||||
return db
|
||||
end
|
||||
|
||||
-- Reset the whole config IN PLACE (keeps the table reference -- the client
|
||||
-- saves the global DopingControlDB, so the reference must survive).
|
||||
function DC.ResetConfig(db)
|
||||
for k in pairs(db) do
|
||||
db[k] = nil
|
||||
end
|
||||
return DC.EnsureDefaults(db)
|
||||
end
|
||||
|
||||
-- Reset only the expectation matrix to the DC.DEFAULT_EXPECT seed,
|
||||
-- materialized per (role, performing class) -- options button
|
||||
-- "Reset expectations" (reseeds ALL classes).
|
||||
function DC.ResetExpectations(db)
|
||||
db.expectations = nil
|
||||
DC.EnsureDefaults(db)
|
||||
return db.expectations
|
||||
end
|
||||
Reference in New Issue
Block a user