Skip to content

Commit c7054eb

Browse files
Added bodyswap.lua
A revived version of the long defunct "adv-bodyswap" with added functionality. This script allows the player to swap their control over to any unit present on the map in adventure mode only. Targets are specified either via their unit id or through UI selection prior to running the script. Unlike previous iterations, units lacking nemesis or historical figure data (such as wild animals) are valid and safe targets, as the script creates this data when it is missing.
1 parent 9f2231b commit c7054eb

1 file changed

Lines changed: 255 additions & 0 deletions

File tree

bodyswap.lua

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
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+
-- nemesis and historical figure creation uses slightly modified functions from modtools/create-unit
5+
6+
--[====[
7+
8+
bodyswap
9+
========
10+
11+
Shifts player control over to another unit (including wild animals) in adventure mode.
12+
Usage:
13+
14+
:bodyswap:
15+
Swaps into the unit selected in the UI (for example, when viewing the unit's status screen or description).
16+
17+
:bodyswap -unit [unitId]:
18+
Swaps into the unit corresponding to the specified id.
19+
20+
]====]
21+
22+
local utils = require 'utils'
23+
validArgs = validArgs or utils.invert({
24+
'unit',
25+
'help'
26+
})
27+
local args = utils.processArgs({...}, validArgs)
28+
29+
local usage = [====[
30+
31+
bodyswap
32+
========
33+
This script allows the player to gain control over a new unit in adventurer mode
34+
whilst simultaneously loosing control over their current character.
35+
36+
To specify the target unit, simply select it in the user interface,
37+
such as by opening the unit's status screen or viewing its description
38+
and enter "bodyswap" in the DFHack console.
39+
40+
Alternatively, the target unit can be specified by its unit id as shown below.
41+
42+
Arguments::
43+
44+
-unit id
45+
replace "id" with the unit id of your target
46+
example:
47+
bodyswap -unit 42
48+
49+
]====]
50+
51+
if args.help then
52+
print(usage)
53+
return
54+
end
55+
56+
if not dfhack.world.isAdventureMode() then
57+
qerror("This script can only be used in adventure mode!")
58+
end
59+
60+
function setNewAdvNemFlags(nem)
61+
nem.flags.ACTIVE_ADVENTURER = true
62+
nem.flags.RETIRED_ADVENTURER = false
63+
nem.flags.ADVENTURER = true
64+
end
65+
function setOldAdvNemFlags(nem)
66+
nem.flags.ACTIVE_ADVENTURER = false
67+
nem.flags.RETIRED_ADVENTURER = true
68+
nem.unit.idle_area.x = nem.unit.pos.x
69+
nem.unit.idle_area.y = nem.unit.pos.y
70+
nem.unit.idle_area.z = nem.unit.pos.z
71+
end
72+
73+
function clearNemesisFromSite(nem)
74+
-- this is a workaround for a bug which tends to cause duplication of the unit entry in df.global.world.units.active when the site to which a historical figure is linked is reloaded with the unit present
75+
-- appears to fix the problem without causing any noticeable issues
76+
if not nem.figure then
77+
return
78+
end
79+
for _,link in ipairs(nem.figure.site_links) do
80+
local site = df.world_site.find(link.site)
81+
for i = #site.unk_1.nemesis-1,0,-1 do
82+
if site.unk_1.nemesis[i] == nem.id then
83+
site.unk_1.nemesis:erase(i)
84+
end
85+
end
86+
end
87+
end
88+
89+
function swapAdvUnit()
90+
91+
local newUnit
92+
if args.unit then
93+
newUnit = df.unit.find(tonumber(args.unit))
94+
else
95+
newUnit = dfhack.gui.getSelectedUnit()
96+
end
97+
if not newUnit then
98+
print("Enter the following if you require assistance: bodyswap -help")
99+
if args.unit then
100+
qerror("Invalid unit id: "..args.unit)
101+
else
102+
qerror("Target unit not specified!")
103+
end
104+
end
105+
106+
local oldUnit = df.nemesis_record.find(df.global.ui_advmode.player_id).unit
107+
if newUnit == oldUnit then
108+
return
109+
end
110+
111+
local activeUnits = df.global.world.units.active
112+
local oldUnitIndex
113+
if activeUnits[0] == oldUnit then
114+
oldUnitIndex = 0
115+
else
116+
for i,u in pairs(activeUnits) do
117+
if u == oldUnit then
118+
oldUnitIndex = i
119+
break
120+
end
121+
end
122+
end
123+
local newUnitIndex
124+
for i,u in pairs(activeUnits) do
125+
if u == newUnit then
126+
newUnitIndex = i
127+
break
128+
end
129+
end
130+
131+
if not newUnitIndex then
132+
qerror("Target unit index not found!")
133+
end
134+
135+
activeUnits[newUnitIndex] = oldUnit
136+
activeUnits[oldUnitIndex] = newUnit
137+
138+
local newNem = dfhack.units.getNemesis(newUnit) or createNemesis(newUnit)
139+
if newNem then
140+
local oldNem = dfhack.units.getNemesis(oldUnit)
141+
if oldNem then
142+
setOldAdvNemFlags(oldNem)
143+
end
144+
setNewAdvNemFlags(newNem)
145+
clearNemesisFromSite(newNem)
146+
df.global.ui_advmode.player_id = newNem.id
147+
end
148+
end
149+
150+
local function allocateNewChunk(hist_entity)
151+
hist_entity.save_file_id = df.global.unit_chunk_next_id
152+
df.global.unit_chunk_next_id = df.global.unit_chunk_next_id+1
153+
hist_entity.next_member_idx = 0
154+
end
155+
156+
local function allocateIds(nemesis_record,hist_entity)
157+
if hist_entity.next_member_idx == 100 then
158+
allocateNewChunk(hist_entity)
159+
end
160+
nemesis_record.save_file_id = hist_entity.save_file_id
161+
nemesis_record.member_idx = hist_entity.next_member_idx
162+
hist_entity.next_member_idx = hist_entity.next_member_idx+1
163+
end
164+
165+
function createFigure(unit,he,he_group)
166+
local hf = df.historical_figure:new()
167+
hf.id = df.global.hist_figure_next_id
168+
hf.race = unit.race
169+
hf.caste = unit.caste
170+
hf.profession = unit.profession
171+
hf.sex = unit.sex
172+
df.global.hist_figure_next_id=df.global.hist_figure_next_id+1
173+
hf.appeared_year = df.global.cur_year
174+
175+
hf.born_year = unit.birth_year
176+
hf.born_seconds = unit.birth_time
177+
hf.curse_year = unit.curse_year
178+
hf.curse_seconds = unit.curse_time
179+
hf.birth_year_bias = unit.birth_year_bias
180+
hf.birth_time_bias = unit.birth_time_bias
181+
hf.old_year = unit.old_year
182+
hf.old_seconds = unit.old_time
183+
hf.died_year = -1
184+
hf.died_seconds = -1
185+
hf.name:assign(unit.name)
186+
hf.civ_id = unit.civ_id
187+
hf.population_id = unit.population_id
188+
hf.breed_id = -1
189+
hf.unit_id = unit.id
190+
hf.unit_id2 = unit.id
191+
192+
hf.flags.never_cull = true
193+
194+
df.global.world.history.figures:insert("#",hf)
195+
196+
hf.info = df.historical_figure_info:new()
197+
hf.info.unk_14 = df.historical_figure_info.T_unk_14:new()
198+
hf.info.unk_14.unk_18 = -1; hf.info.unk_14.unk_1c = -1
199+
hf.info.skills = {new=true}
200+
201+
unit.flags1.important_historical_figure = true
202+
unit.flags2.important_historical_figure = true
203+
unit.hist_figure_id = hf.id
204+
unit.hist_figure_id2 = hf.id
205+
206+
if he then
207+
he.histfig_ids:insert('#',hf.id)
208+
he.hist_figures:insert('#',hf)
209+
210+
hf.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=unit.civ_id,link_strength=100})
211+
212+
local hf_event_id = df.global.hist_event_next_id
213+
df.global.hist_event_next_id = df.global.hist_event_next_id+1
214+
df.global.world.history.events:insert("#",{new=df.history_event_add_hf_entity_linkst,year=unit.birth_year,
215+
seconds=unit.birth_time,id = hf_event_id,civ=hf.civ_id,histfig=hf.id,link_type=0})
216+
end
217+
return hf
218+
end
219+
220+
function createNemesis(unit)
221+
local id = df.global.nemesis_next_id
222+
local nem = df.nemesis_record:new()
223+
224+
nem.id = id
225+
nem.unit_id = unit.id
226+
nem.unit = unit
227+
nem.flags:resize(4)
228+
nem.flags[4] = true
229+
nem.flags[5] = true
230+
nem.flags[6] = true
231+
nem.flags[7] = true
232+
nem.flags[8] = true
233+
nem.flags[9] = true
234+
nem.unk10 = -1
235+
nem.unk11 = -1
236+
nem.unk12 = -1
237+
df.global.world.nemesis.all:insert("#",nem)
238+
df.global.nemesis_next_id = id+1
239+
unit.general_refs:insert("#",{new=df.general_ref_is_nemesisst,nemesis_id=id})
240+
241+
nem.save_file_id = -1
242+
243+
local civ_id = unit.civ_id
244+
local he
245+
if civ_id ~= -1 then
246+
he = df.historical_entity.find(civ_id)
247+
he.nemesis_ids:insert("#",id)
248+
he.nemesis:insert("#",nem)
249+
allocateIds(nem,he)
250+
end
251+
nem.figure = createFigure(unit,he)
252+
return nem
253+
end
254+
255+
swapAdvUnit()

0 commit comments

Comments
 (0)