This repository was archived by the owner on Aug 14, 2026. It is now read-only.
forked from TeJota1337/DramaticShapeVoxelMod
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathModSetting.lua
More file actions
209 lines (191 loc) · 7.51 KB
/
Copy pathModSetting.lua
File metadata and controls
209 lines (191 loc) · 7.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
-- One of this mod's own settings: a ladder of values and where it persists.
--
-- The engine gives a render pipeline all of this for free -- ladder,
-- options row, hotkey, persistence -- but only to something that OWNS a
-- pass of the frame. The voxel wireframe and the world curve do not: they
-- parameterise the voxel pass, so they have nothing to put in drawWorld or
-- present and the registry rightly rejects them. What is left is a plain
-- mod setting, and this is the boilerplate two of them would otherwise
-- each carry a copy of:
--
-- options:define a home in options.modOptions.DRAMATIC_SHAPE, plus a row
-- on this mod's page in the mod manager.
-- row() an optional global OPTIONS row, used only for the
-- headline 3D-BTL switch.
--
-- Both routes read and write the one stored value, so they cannot disagree.
-- Writing through row() mirrors what the manager's page does
-- (ManagerState:setOption):
-- the live save's options table, the loader's copy that mod.options:get
-- reads, and then the file.
-- the mod namespace (see main.lua): V.require loads a sibling module
local V = ...
local ModSetting = {}
ModSetting.__index = ModSetting
local function modId()
local mod = V.mod
return (mod and mod.id) or "DRAMATIC_SHAPE"
end
-- `values` are the stored values in ladder order and `labels` what the row
-- shows for each. `defaultIndex` defaults to the first rung and is also the
-- fallback for an unreadable or unrecognised stored value.
function ModSetting.new(key, label, values, labels, defaultIndex)
return setmetatable({
key = key, label = label, values = values, labels = labels,
defaultIndex = defaultIndex or 1,
index = nil, -- nil = not yet read back from the persisted options
}, ModSetting)
end
local function indexOf(self, value)
for i, v in ipairs(self.values) do
if v == value then return i end
end
return self.defaultIndex
end
-- ------- rungs that are not always there
--
-- A ladder may carry a rung that cannot be selected right now -- STADIUM
-- needs models built out of a ROM the player supplies, and until that has
-- happened there is nothing behind the option. `gate` is asked per rung and
-- decides whether it exists at all this frame.
--
-- Skipped rather than shown-and-refused, deliberately. A row that can be
-- cycled onto and then does nothing is indistinguishable from a broken mod;
-- a row that simply has fewer stops reads as the mod not offering something,
-- which is the truth. What the player is missing, and how to get it, is said
-- once in the row's help text instead of implied by a dead setting.
--
-- values[1] is never gated: it is the default and the fallback, so there is
-- always at least one rung to land on.
function ModSetting:setGate(gate)
self.gate = gate
return self
end
function ModSetting:allows(i)
if i == 1 or not self.gate then return true end
local ok, allowed = pcall(self.gate, self.values[i], i)
return (not ok) or allowed and true or false
end
-- How many rungs are live, for a caller that wants to know whether a row is
-- worth showing at all.
function ModSetting:rungs()
local n = 0
for i = 1, #self.values do
if self:allows(i) then n = n + 1 end
end
return n
end
-- What the player left it at last session. Read lazily rather than at load
-- time: the loader fills modOptions before a mod runs, but reading through
-- the API keeps this honest about where the value lives.
function ModSetting:read()
if self.index then return self.index end
local mod = V.mod
local value
if mod and mod.options then
local ok, got = pcall(mod.options.get, mod.options, self.key)
if ok then value = got end
end
self.index = indexOf(self, value)
return self.index
end
function ModSetting:get()
local i = self:read()
-- a rung that was live when it was stored and is not now -- the player
-- moved the ROM, or opened the same save on another machine -- reads as
-- the default rather than as a mode with nothing behind it. The stored
-- value is left alone, so putting the ROM back restores their choice.
if not self:allows(i) then return self.values[1] end
return self.values[i]
end
function ModSetting:level()
return self:read() - 1
end
function ModSetting:setIndex(i, game)
local n = #self.values
i = ((i - 1) % n + n) % n + 1
self.index = i
local value, id = self.values[i], modId()
local opts = game and game.save and game.save.options
if opts then
opts.modOptions = opts.modOptions or {}
opts.modOptions[id] = opts.modOptions[id] or {}
opts.modOptions[id][self.key] = value
end
local loader = game and game.mods
if loader then
loader.modOptions = loader.modOptions or {}
loader.modOptions[id] = loader.modOptions[id] or {}
loader.modOptions[id][self.key] = value
end
if game and game.writeOptions then pcall(game.writeOptions, game) end
return value
end
-- Set by the STORED VALUE rather than by its place on the ladder, for a
-- caller that knows which setting it wants and not where it sits -- a
-- preset, or an assertion. An unrecognised value lands on values[1], the
-- same default indexOf answers everywhere else, so this can never leave a
-- setting holding something the row cannot display.
--
-- Worth having as its own entry point because a ladder's ORDER is not a
-- promise: 3D-BTL grew a third rung in the middle of itself (see
-- OverworldBattle), and every caller that had counted to two would have
-- silently meant something else afterwards.
function ModSetting:setValue(value, game)
return self:setIndex(indexOf(self, value), game)
end
-- Step to the next rung that is actually live, in `dir`. Bounded by the
-- ladder's length so a gate that refuses everything still terminates on
-- values[1], which allows() never gates.
function ModSetting:cycle(game, dir)
dir = dir or 1
local n = #self.values
local i = self:read()
for _ = 1, n do
i = ((i + dir - 1) % n + n) % n + 1
if self:allows(i) then break end
end
return self:setIndex(i, game)
end
-- Adopt a value set from somewhere else (the mod manager's settings page,
-- which writes and persists on its own). Nothing to store: just move the
-- cached index so the next read agrees with it.
function ModSetting:sync(value)
self.index = indexOf(self, value)
end
-- The descriptor src/ui/OptionRows.lua renders, in the shape the
-- ui.options.rows hook appends.
function ModSetting:row()
local self_ = self
return {
id = "DRAMATIC_SHAPE:" .. self.key,
label = self.label,
-- the label of the rung actually in force, which is not the stored one
-- when that rung has been gated away (see get)
value = function()
local i = self_:read()
return self_.labels[self_:allows(i) and i or 1]
end,
step = function(game, dir)
self_:cycle(game, dir)
if self_.onStep then self_.onStep(self_, game, dir) end
return true
end,
}
end
-- The row the mod manager's own settings page builds for this mod.
function ModSetting:schema(help)
local choices = {}
-- gated rungs are left off the manager's page too, so the two rows agree
-- about what can be chosen
for i, v in ipairs(self.values) do
if self:allows(i) then choices[#choices + 1] = { self.labels[i], v } end
end
if #self.values == 2 and self.values[1] == false then
return { key = self.key, type = "toggle", label = self.label,
default = self.values[self.defaultIndex], help = help }
end
return { key = self.key, type = "choice", label = self.label,
choices = choices, default = self.values[self.defaultIndex], help = help }
end
return ModSetting