Skip to content

Commit a233823

Browse files
committed
Add saitama script for multiplying attribute potential
1 parent af457f9 commit a233823

3 files changed

Lines changed: 299 additions & 0 deletions

File tree

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Template for new versions:
2727
# Future
2828

2929
## New Tools
30+
- `saitama`: multiply the attribute potential (max_value) of a unit, squad, or all citizens
3031

3132
## New Features
3233

docs/saitama.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
saitama
2+
=======
3+
4+
.. dfhack-tool::
5+
:summary: Multiply the attribute potential of units.
6+
:tags: fort armok units
7+
8+
Multiplies the potential (max_value) of every mental and physical attribute
9+
for the selected unit, all citizens, all map creatures, or an entire squad.
10+
The current attribute values are left unchanged -- units must still train to
11+
reach their new potential.
12+
13+
Usage
14+
-----
15+
16+
``saitama <multiplier>``
17+
Multiply the attribute potential of the selected unit.
18+
``saitama --citizens <multiplier>``
19+
Multiply the attribute potential of all fort citizens.
20+
``saitama --all <multiplier>``
21+
Multiply the attribute potential of every creature on the map.
22+
``saitama --squad <number> <multiplier>``
23+
Multiply the attribute potential of every member in the given squad.
24+
Squad numbers start at 1. Use ``saitama --listsquads`` to see them.
25+
``saitama --unit <id> <multiplier>``
26+
Multiply the attribute potential of the unit with the given ID.
27+
``saitama --listsquads``
28+
List all squads and their numbers.
29+
30+
Examples
31+
--------
32+
33+
``saitama 100``
34+
The selected unit's max attributes become 100x their current potential.
35+
``saitama --citizens 10``
36+
All citizens get 10x attribute potential.
37+
``saitama --squad 1 50``
38+
First squad members get 50x attribute potential.

saitama.lua

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
-- Multiply the potential (max_value) of all attributes of a unit
2+
--[====[
3+
4+
saitama
5+
=======
6+
Multiplies the potential (max_value) of every mental and physical attribute
7+
for the selected unit, all citizens, all map creatures, or an entire squad.
8+
9+
Usage::
10+
11+
saitama <multiplier>
12+
Multiplies the attribute potential of the selected unit.
13+
14+
saitama --all <multiplier>
15+
Multiplies the attribute potential of every creature on the map.
16+
17+
saitama --citizens <multiplier>
18+
Multiplies the attribute potential of all fort citizens.
19+
20+
saitama --squad <number> <multiplier>
21+
Multiplies the attribute potential of every member in squad <number>.
22+
Squad numbers start at 1. Use ``saitama --listsquads`` to see them.
23+
24+
saitama --unit <id> <multiplier>
25+
Multiplies the attribute potential of the unit with the given ID.
26+
27+
saitama --listsquads
28+
Lists all squads and their IDs.
29+
30+
Examples::
31+
32+
saitama 100
33+
Selected unit's max attributes become 100x their current potential.
34+
35+
saitama --citizens 10
36+
All citizens get 10x attribute potential.
37+
38+
saitama --squad 1 50
39+
First squad members get 50x attribute potential.
40+
41+
]====]
42+
43+
-- Manual arg parsing to support: --squad 1 100 (flag + value + positional)
44+
local raw_args = {...}
45+
local args = {}
46+
local positional = {}
47+
local i = 1
48+
while i <= #raw_args do
49+
local v = raw_args[i]
50+
if v == '--help' or v == '-help' then
51+
args.help = true
52+
elseif v == '--all' or v == '-all' then
53+
args.all = true
54+
elseif v == '--citizens' or v == '-citizens' then
55+
args.citizens = true
56+
elseif v == '--listsquads' or v == '-listsquads' then
57+
args.listsquads = true
58+
elseif v == '--squad' or v == '-squad' then
59+
i = i + 1
60+
args.squad = raw_args[i]
61+
elseif v == '--unit' or v == '-unit' then
62+
i = i + 1
63+
args.unit = raw_args[i]
64+
else
65+
table.insert(positional, v)
66+
end
67+
i = i + 1
68+
end
69+
70+
if args.help then
71+
print(dfhack.script_help())
72+
return
73+
end
74+
75+
-- ---------------------------------------------------------------------------
76+
-- Core logic: multiply max_value of all attributes for a unit
77+
-- ---------------------------------------------------------------------------
78+
local function saitama_punch(unit, multiplier)
79+
if not unit then return end
80+
81+
local name = dfhack.units.getReadableName(unit)
82+
83+
-- Mental attributes (soul)
84+
if unit.status.current_soul then
85+
for k, v in pairs(unit.status.current_soul.mental_attrs) do
86+
local old = v.max_value
87+
v.max_value = math.floor(old * multiplier)
88+
-- If current value exceeds new max, leave it alone (don't nerf current)
89+
end
90+
end
91+
92+
-- Physical attributes (body)
93+
if unit.body then
94+
for k, v in pairs(unit.body.physical_attrs) do
95+
local old = v.max_value
96+
v.max_value = math.floor(old * multiplier)
97+
end
98+
end
99+
100+
print((' One Punch: %s (x%d)'):format(dfhack.df2console(name), multiplier))
101+
end
102+
103+
-- ---------------------------------------------------------------------------
104+
-- Squad helpers
105+
-- ---------------------------------------------------------------------------
106+
local function get_squads()
107+
local govt = df.historical_entity.find(df.global.plotinfo.group_id)
108+
if not govt then return {} end
109+
local squads = {}
110+
for i, squad_id in ipairs(govt.squads) do
111+
local squad = df.squad.find(squad_id)
112+
if squad then
113+
table.insert(squads, {index = i, squad = squad})
114+
end
115+
end
116+
return squads
117+
end
118+
119+
local function get_squad_units(squad)
120+
local units = {}
121+
for _, sp in ipairs(squad.positions) do
122+
if sp.occupant ~= -1 then
123+
local hf = df.historical_figure.find(sp.occupant)
124+
if hf then
125+
local unit = df.unit.find(hf.unit_id)
126+
if unit then
127+
table.insert(units, unit)
128+
end
129+
end
130+
end
131+
end
132+
return units
133+
end
134+
135+
local function list_squads()
136+
local squads = get_squads()
137+
if #squads == 0 then
138+
print('No squads found.')
139+
return
140+
end
141+
print('Squads:')
142+
for _, entry in ipairs(squads) do
143+
local squad = entry.squad
144+
local name = dfhack.military.getSquadName(squad.id)
145+
local member_count = 0
146+
for _, sp in ipairs(squad.positions) do
147+
if sp.occupant ~= -1 then member_count = member_count + 1 end
148+
end
149+
print((' [%d] %s (%d members)'):format(entry.index + 1, dfhack.df2console(name), member_count))
150+
end
151+
end
152+
153+
-- ---------------------------------------------------------------------------
154+
-- Parse multiplier from remaining args
155+
-- ---------------------------------------------------------------------------
156+
local function get_multiplier(raw_args)
157+
-- The multiplier is the last positional argument
158+
local val = nil
159+
for _, v in ipairs(raw_args) do
160+
local n = tonumber(v)
161+
if n then val = n end
162+
end
163+
if not val then
164+
qerror('No multiplier provided. Usage: saitama <multiplier>')
165+
end
166+
if val < 1 then
167+
qerror('Multiplier must be >= 1.')
168+
end
169+
return math.floor(val)
170+
end
171+
172+
-- ---------------------------------------------------------------------------
173+
-- Main
174+
-- ---------------------------------------------------------------------------
175+
if args.listsquads then
176+
list_squads()
177+
return
178+
end
179+
180+
181+
182+
183+
if #positional == 0 then
184+
qerror('No multiplier provided.\n\nUsage: saitama <multiplier>\n saitama --all <multiplier>\n saitama --citizens <multiplier>\n saitama --squad <id> <multiplier>\n saitama --listsquads\n\nRun "saitama --help" for details.')
185+
end
186+
187+
local multiplier = tonumber(positional[#positional])
188+
if not multiplier or multiplier < 1 then
189+
qerror('Multiplier must be a number >= 1.')
190+
end
191+
multiplier = math.floor(multiplier)
192+
193+
if args.all then
194+
-- All creatures on map
195+
local count = 0
196+
for _, unit in ipairs(df.global.world.units.active) do
197+
saitama_punch(unit, multiplier)
198+
count = count + 1
199+
end
200+
print(('Saitama punched %d creatures (x%d).'):format(count, multiplier))
201+
202+
elseif args.citizens then
203+
-- All fort citizens
204+
local count = 0
205+
for _, unit in ipairs(dfhack.units.getCitizens()) do
206+
saitama_punch(unit, multiplier)
207+
count = count + 1
208+
end
209+
print(('Saitama punched %d citizens (x%d).'):format(count, multiplier))
210+
211+
elseif args.squad then
212+
-- Specific squad by index
213+
local squad_num = tonumber(args.squad)
214+
if not squad_num or squad_num < 1 then
215+
qerror('Invalid squad number: ' .. tostring(args.squad) .. '\nUse "saitama --listsquads" to see available squads.')
216+
end
217+
local squads = get_squads()
218+
local target_squad = nil
219+
for _, entry in ipairs(squads) do
220+
if entry.index + 1 == squad_num then
221+
target_squad = entry.squad
222+
break
223+
end
224+
end
225+
if not target_squad then
226+
qerror('Squad ' .. squad_num .. ' not found.\nUse "saitama --listsquads" to see available squads.')
227+
end
228+
local squad_name = dfhack.df2console(dfhack.military.getSquadName(target_squad.id))
229+
print(('Targeting squad: %s'):format(squad_name))
230+
local units = get_squad_units(target_squad)
231+
if #units == 0 then
232+
print(' No active members in this squad.')
233+
else
234+
for _, unit in ipairs(units) do
235+
saitama_punch(unit, multiplier)
236+
end
237+
print(('Saitama punched %d members of %s (x%d).'):format(#units, squad_name, multiplier))
238+
end
239+
240+
elseif args.unit then
241+
-- Specific unit by ID
242+
local unit_id = tonumber(args.unit)
243+
if not unit_id then
244+
qerror('Invalid unit ID: ' .. tostring(args.unit))
245+
end
246+
local unit = df.unit.find(unit_id)
247+
if not unit then
248+
qerror('Unit not found: ' .. tostring(unit_id))
249+
end
250+
saitama_punch(unit, multiplier)
251+
252+
else
253+
-- Default: selected unit
254+
local unit = dfhack.gui.getSelectedUnit()
255+
if not unit then
256+
qerror('No unit selected. Select a unit or use --all, --citizens, --squad, or --unit.')
257+
end
258+
saitama_punch(unit, multiplier)
259+
print(('Saitama punched (x%d).'):format(multiplier))
260+
end

0 commit comments

Comments
 (0)