Skip to content

Commit 42919fb

Browse files
committed
cleanup
1 parent 032cac0 commit 42919fb

File tree

2 files changed

+145
-196
lines changed

2 files changed

+145
-196
lines changed

bodyswap.lua

+126-153
Original file line numberDiff line numberDiff line change
@@ -1,199 +1,172 @@
1-
-- Shifts player control over to another unit in adventure mode.
2-
-- author: Atomic Chicken
3-
-- based on "assumecontrol.lua" by maxthyme, as well as the defunct advtools plugin "adv-bodyswap"
4-
-- calls "modtools/create-unit" for nemesis and histfig creation
5-
61
--@ module = true
72

83
local utils = require 'utils'
94
local validArgs = utils.invert({
10-
'unit',
11-
'help'
5+
'unit',
6+
'help'
127
})
13-
local args = utils.processArgs({...}, validArgs)
14-
15-
local usage = [====[
16-
17-
bodyswap
18-
========
19-
This script allows the player to take direct control of any unit present in
20-
adventure mode whilst giving up control of their current player character.
21-
22-
To specify the target unit, simply select it in the user interface,
23-
such as by opening the unit's status screen or viewing its description,
24-
and enter "bodyswap" in the DFHack console.
25-
26-
Alternatively, the target unit can be specified by its unit id as shown below.
27-
28-
Arguments::
29-
30-
-unit id
31-
replace "id" with the unit id of your target
32-
example:
33-
bodyswap -unit 42
34-
35-
]====]
8+
local args = utils.processArgs({ ... }, validArgs)
369

3710
if args.help then
38-
print(usage)
39-
return
11+
print(dfhack.script_help())
12+
return
4013
end
4114

4215
function setNewAdvNemFlags(nem)
43-
nem.flags.ACTIVE_ADVENTURER = true
44-
nem.flags.RETIRED_ADVENTURER = false
45-
nem.flags.ADVENTURER = true
16+
nem.flags.ACTIVE_ADVENTURER = true
17+
nem.flags.RETIRED_ADVENTURER = false
18+
nem.flags.ADVENTURER = true
4619
end
20+
4721
function setOldAdvNemFlags(nem)
48-
nem.flags.ACTIVE_ADVENTURER = false
22+
nem.flags.ACTIVE_ADVENTURER = false
4923
end
5024

5125
function clearNemesisFromLinkedSites(nem)
52-
-- omitting this step results in duplication of the unit entry in df.global.world.units.active when the site to which the historical figure is linked is reloaded with said figure present as a member of the player party
53-
-- this can be observed as part of the normal recruitment process when the player adds a site-linked historical figure to their party
54-
if not nem.figure then
55-
return
56-
end
57-
for _,link in ipairs(nem.figure.site_links) do
58-
local site = df.world_site.find(link.site)
59-
for i = #site.unk_1.nemesis-1, 0, -1 do
60-
if site.unk_1.nemesis[i] == nem.id then
61-
site.unk_1.nemesis:erase(i)
62-
end
26+
-- omitting this step results in duplication of the unit entry in df.global.world.units.active when the site to which the historical figure is linked is reloaded with said figure present as a member of the player party
27+
-- this can be observed as part of the normal recruitment process when the player adds a site-linked historical figure to their party
28+
if not nem.figure then
29+
return
30+
end
31+
for _, link in ipairs(nem.figure.site_links) do
32+
local site = df.world_site.find(link.site)
33+
for i = #site.unk_1.nemesis - 1, 0, -1 do
34+
if site.unk_1.nemesis[i] == nem.id then
35+
site.unk_1.nemesis:erase(i)
36+
end
37+
end
6338
end
64-
end
6539
end
6640

6741
function createNemesis(unit)
68-
local nemesis = reqscript('modtools/create-unit').createNemesis(unit,unit.civ_id)
69-
nemesis.figure.flags.never_cull = true
70-
return nemesis
42+
local nemesis = reqscript('modtools/create-unit').createNemesis(unit, unit.civ_id)
43+
nemesis.figure.flags.never_cull = true
44+
return nemesis
7145
end
7246

7347
function isPet(nemesis)
74-
if nemesis.unit then
75-
if nemesis.unit.relationship_ids.Pet ~= -1 then
76-
return true
77-
end
78-
elseif nemesis.figure then -- in case the unit is offloaded
79-
for _, link in ipairs(nemesis.figure.histfig_links) do
80-
if link._type == df.histfig_hf_link_pet_ownerst then
81-
return true
82-
end
48+
if nemesis.unit then
49+
if nemesis.unit.relationship_ids.Pet ~= -1 then
50+
return true
51+
end
52+
elseif nemesis.figure then -- in case the unit is offloaded
53+
for _, link in ipairs(nemesis.figure.histfig_links) do
54+
if link._type == df.histfig_hf_link_pet_ownerst then
55+
return true
56+
end
57+
end
8358
end
84-
end
85-
return false
59+
return false
8660
end
8761

8862
function processNemesisParty(nemesis, targetUnitID, alreadyProcessed)
89-
-- configures the target and any leaders/companions to behave as cohesive adventure mode party members
90-
local alreadyProcessed = alreadyProcessed or {}
91-
alreadyProcessed[tostring(nemesis.id)] = true
92-
93-
local nemUnit = nemesis.unit
94-
if nemesis.unit_id == targetUnitID then -- the target you're bodyswapping into
95-
df.global.adventure.interactions.party_core_members:insert('#', nemesis.figure.id)
96-
nemUnit.relationship_ids.GroupLeader = -1
97-
elseif isPet(nemesis) then -- pets belonging to the target or to their companions
98-
df.global.adventure.interactions.party_pets:insert('#', nemesis.figure.id)
99-
else
100-
df.global.adventure.interactions.party_core_members:insert('#', nemesis.figure.id) -- placing all non-pet companions into the core party list to enable tactical mode swapping
101-
nemesis.flags.ADVENTURER = true
102-
if nemUnit then -- check in case the companion is offloaded
103-
nemUnit.relationship_ids.GroupLeader = targetUnitID
63+
-- configures the target and any leaders/companions to behave as cohesive adventure mode party members
64+
local alreadyProcessed = alreadyProcessed or {}
65+
alreadyProcessed[tostring(nemesis.id)] = true
66+
67+
local nemUnit = nemesis.unit
68+
if nemesis.unit_id == targetUnitID then -- the target you're bodyswapping into
69+
df.global.adventure.interactions.party_core_members:insert('#', nemesis.figure.id)
70+
nemUnit.relationship_ids.GroupLeader = -1
71+
elseif isPet(nemesis) then -- pets belonging to the target or to their companions
72+
df.global.adventure.interactions.party_pets:insert('#', nemesis.figure.id)
73+
else
74+
df.global.adventure.interactions.party_core_members:insert('#', nemesis.figure.id) -- placing all non-pet companions into the core party list to enable tactical mode swapping
75+
nemesis.flags.ADVENTURER = true
76+
if nemUnit then -- check in case the companion is offloaded
77+
nemUnit.relationship_ids.GroupLeader = targetUnitID
78+
end
10479
end
105-
end
106-
-- the hierarchy of nemesis-level leader/companion relationships appears to be left untouched when the player character is changed using the inbuilt "tactical mode" party system
80+
-- the hierarchy of nemesis-level leader/companion relationships appears to be left untouched when the player character is changed using the inbuilt "tactical mode" party system
10781

108-
clearNemesisFromLinkedSites(nemesis)
82+
clearNemesisFromLinkedSites(nemesis)
10983

110-
if nemesis.group_leader_id ~= -1 and not alreadyProcessed[tostring(nemesis.group_leader_id)] then
111-
local leader = df.nemesis_record.find(nemesis.group_leader_id)
112-
if leader then
113-
processNemesisParty(leader, targetUnitID, alreadyProcessed)
84+
if nemesis.group_leader_id ~= -1 and not alreadyProcessed[tostring(nemesis.group_leader_id)] then
85+
local leader = df.nemesis_record.find(nemesis.group_leader_id)
86+
if leader then
87+
processNemesisParty(leader, targetUnitID, alreadyProcessed)
88+
end
11489
end
115-
end
116-
for _, id in ipairs(nemesis.companions) do
117-
if not alreadyProcessed[tostring(id)] then
118-
local companion = df.nemesis_record.find(id)
119-
if companion then
120-
processNemesisParty(companion, targetUnitID, alreadyProcessed)
121-
end
90+
for _, id in ipairs(nemesis.companions) do
91+
if not alreadyProcessed[tostring(id)] then
92+
local companion = df.nemesis_record.find(id)
93+
if companion then
94+
processNemesisParty(companion, targetUnitID, alreadyProcessed)
95+
end
96+
end
12297
end
123-
end
12498
end
12599

126100
function configureAdvParty(targetNemesis)
127-
local party = df.global.adventure.interactions
128-
party.party_core_members:resize(0)
129-
party.party_pets:resize(0)
130-
party.party_extra_members:resize(0)
131-
processNemesisParty(targetNemesis, targetNemesis.unit_id)
101+
local party = df.global.adventure.interactions
102+
party.party_core_members:resize(0)
103+
party.party_pets:resize(0)
104+
party.party_extra_members:resize(0)
105+
processNemesisParty(targetNemesis, targetNemesis.unit_id)
132106
end
133107

134108
function swapAdvUnit(newUnit)
109+
if not newUnit then
110+
qerror('Target unit not specified!')
111+
end
135112

136-
if not newUnit then
137-
qerror('Target unit not specified!')
138-
end
113+
local oldNem = df.nemesis_record.find(df.global.adventure.player_id)
114+
local oldUnit = oldNem.unit
115+
if newUnit == oldUnit then
116+
return
117+
end
139118

140-
local oldNem = df.nemesis_record.find(df.global.adventure.player_id)
141-
local oldUnit = oldNem.unit
142-
if newUnit == oldUnit then
143-
return
144-
end
145-
146-
local activeUnits = df.global.world.units.active
147-
local oldUnitIndex
148-
if activeUnits[0] == oldUnit then
149-
oldUnitIndex = 0
150-
else -- unlikely; this is just in case
151-
for i,u in ipairs(activeUnits) do
152-
if u == oldUnit then
153-
oldUnitIndex = i
154-
break
155-
end
119+
local activeUnits = df.global.world.units.active
120+
local oldUnitIndex
121+
if activeUnits[0] == oldUnit then
122+
oldUnitIndex = 0
123+
else -- unlikely; this is just in case
124+
for i, u in ipairs(activeUnits) do
125+
if u == oldUnit then
126+
oldUnitIndex = i
127+
break
128+
end
129+
end
130+
end
131+
local newUnitIndex
132+
for i, u in ipairs(activeUnits) do
133+
if u == newUnit then
134+
newUnitIndex = i
135+
break
136+
end
137+
end
138+
139+
if not newUnitIndex then
140+
qerror("Target unit index not found!")
156141
end
157-
end
158-
local newUnitIndex
159-
for i,u in ipairs(activeUnits) do
160-
if u == newUnit then
161-
newUnitIndex = i
162-
break
142+
143+
local newNem = dfhack.units.getNemesis(newUnit) or createNemesis(newUnit)
144+
if not newNem then
145+
qerror("Failed to obtain target nemesis!")
163146
end
164-
end
165-
166-
if not newUnitIndex then
167-
qerror("Target unit index not found!")
168-
end
169-
170-
local newNem = dfhack.units.getNemesis(newUnit) or createNemesis(newUnit)
171-
if not newNem then
172-
qerror("Failed to obtain target nemesis!")
173-
end
174-
175-
setOldAdvNemFlags(oldNem)
176-
setNewAdvNemFlags(newNem)
177-
configureAdvParty(newNem)
178-
df.global.adventure.player_id = newNem.id
179-
activeUnits[newUnitIndex] = oldUnit
180-
activeUnits[oldUnitIndex] = newUnit
181-
oldUnit.idle_area:assign(oldUnit.pos)
147+
148+
setOldAdvNemFlags(oldNem)
149+
setNewAdvNemFlags(newNem)
150+
configureAdvParty(newNem)
151+
df.global.adventure.player_id = newNem.id
152+
activeUnits[newUnitIndex] = oldUnit
153+
activeUnits[oldUnitIndex] = newUnit
154+
oldUnit.idle_area:assign(oldUnit.pos)
182155
end
183156

184157
if not dfhack_flags.module then
185-
if df.global.gamemode ~= df.game_mode.ADVENTURE then
186-
qerror("This script can only be used in adventure mode!")
187-
end
188-
189-
local unit = args.unit and df.unit.find(tonumber(args.unit)) or dfhack.gui.getSelectedUnit()
190-
if not unit then
191-
print("Enter the following if you require assistance: bodyswap -help")
192-
if args.unit then
193-
qerror("Invalid unit id: "..args.unit)
194-
else
195-
qerror("Target unit not specified!")
158+
if df.global.gamemode ~= df.game_mode.ADVENTURE then
159+
qerror("This script can only be used in adventure mode!")
160+
end
161+
162+
local unit = args.unit and df.unit.find(tonumber(args.unit)) or dfhack.gui.getSelectedUnit()
163+
if not unit then
164+
print("Enter the following if you require assistance: bodyswap -help")
165+
if args.unit then
166+
qerror("Invalid unit id: " .. args.unit)
167+
else
168+
qerror("Target unit not specified!")
169+
end
196170
end
197-
end
198-
swapAdvUnit(unit)
171+
swapAdvUnit(unit)
199172
end

0 commit comments

Comments
 (0)