Skip to content

Commit f722aee

Browse files
authored
Merge pull request #59 from AtomicChicken/create-unit
create-unit: added 'quantity' argument and random caste selection
2 parents 7099794 + 028cead commit f722aee

1 file changed

Lines changed: 119 additions & 97 deletions

File tree

modtools/create-unit.lua

Lines changed: 119 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
-- sets baby/child profession and mood for creatures of the appropriate age where relevant
1111
-- properly assigns civ_id to historical_figure to eliminate a number of hostility issues
1212
-- removes the arena-generated string of numbers from the first name of units
13+
-- added 'quantity' arg for spawning multiple creatures simultaneously
14+
-- creature caste is now randomly selected if left unspecified
15+
1316
--[[
1417
TODO
1518
confirm body size is computed appropriately for different ages / life stages
@@ -36,6 +39,7 @@ Creates a unit. Usage::
3639
HUMAN
3740
-caste casteName
3841
specify the caste of the unit to be created
42+
if not specified, the caste is randomly selected
3943
examples:
4044
MALE
4145
FEMALE
@@ -54,14 +58,20 @@ Creates a unit. Usage::
5458
Can be used instead of -civId \\LOCAL and -groupId \\LOCAL
5559
-name entityRawName
5660
set the unit's name to be a random name appropriate for the
57-
given entity. examples:
61+
given entity
62+
examples:
5863
MOUNTAIN
64+
EVIL
5965
-nick nickname
6066
set the unit's nickname directly
6167
-location [ x y z ]
6268
create the unit at the specified coordinates
6369
-age howOld
6470
set the birth date of the unit by current age
71+
chosen randomly if not specified
72+
-quantity howMany
73+
replace "howMany" with the number of creatures you want to create
74+
defaults to 1 if not specified
6575
-flagSet [ flag1 flag2 ... ]
6676
set the specified unit flags in the new unit to true
6777
flags may be selected from df.unit_flags1, df.unit_flags2,
@@ -85,7 +95,7 @@ local utils=require 'utils'
8595
function createUnit(...)
8696
local old_gametype = df.global.gametype
8797
local old_mode = df.global.ui.main.mode
88-
local old_popups = {} --as:df.popup_message[]
98+
local old_popups = {}
8999
for _, popup in pairs(df.global.world.status.popups) do
90100
table.insert(old_popups, popup)
91101
end
@@ -144,8 +154,8 @@ function createUnitInner(race_id, caste_id, location, entityRawName)
144154
local spawnScreen = dfhack.gui.getCurViewscreen()
145155
if dfhack.world.isArena() then
146156
-- Just modify the current screen in arena mode (#994)
147-
spawnScreen.race:insert(0, race_id) --hint:df.viewscreen_layer_arena_creaturest
148-
spawnScreen.caste:insert(0, caste_id) --hint:df.viewscreen_layer_arena_creaturest
157+
spawnScreen.race:insert(0, race_id)
158+
spawnScreen.caste:insert(0, caste_id)
149159
end
150160
gui.simulateInput(spawnScreen, 'SELECT')
151161

@@ -175,12 +185,8 @@ end
175185
--u.population_id = df.historical_entity.find(df.global.ui.civ_id).populations[0]
176186

177187
-- Picking a caste or gender at random
178-
--luacheck: in=number
179-
function getRandomCasteId(race_id)
180-
local cr = df.creature_raw.find(race_id)
181-
local caste_id, casteMax
182-
183-
casteMax = #cr.caste - 1
188+
function getRandomCasteId(race)
189+
local casteMax = #race.caste - 1
184190

185191
if casteMax > 0 then
186192
return math.random(0, casteMax)
@@ -303,9 +309,8 @@ function createNemesis(trgunit,civ_id,group_id)
303309

304310
nem.save_file_id=-1
305311

306-
local he = nil
307312
if civ_id ~= -1 then
308-
he=df.historical_entity.find(civ_id)
313+
local he=df.historical_entity.find(civ_id)
309314
he.nemesis_ids:insert("#",id)
310315
he.nemesis:insert("#",nem)
311316
allocateIds(nem,he)
@@ -318,7 +323,7 @@ function createNemesis(trgunit,civ_id,group_id)
318323
he_group.nemesis_ids:insert("#",id)
319324
he_group.nemesis:insert("#",nem)
320325
end
321-
nem.figure=createFigure(trgunit,he,he_group)
326+
nem.figure = trgunit.hist_figure_id ~= -1 and df.historical_figure.find(trgunit.hist_figure_id) or createFigure(trgunit,he,he_group) -- the histfig check is there just in case this function is called by another script to create nemesis data for a historical figure which somehow lacks it
322327
return nem
323328
end
324329

@@ -352,7 +357,7 @@ function domesticate(uid, group_id)
352357

353358
-- And make them tame (from Dirst)
354359
u.flags1.tame = true
355-
u.training_level = df.animal_training_level.Domesticated
360+
u.training_level = 7
356361
end
357362
end
358363

@@ -378,7 +383,6 @@ function wild(uid)
378383
end
379384
end
380385

381-
--luacheck: in=number,string
382386
function nameUnit(id, entityRawName)
383387
--pick a random appropriate name
384388
--choose three random words in the appropriate things
@@ -460,7 +464,7 @@ function setAgeProfession(unit)
460464
end
461465
end
462466

463-
local validArgs = utils.invert({
467+
validArgs = utils.invert({
464468
'help',
465469
'race',
466470
'caste',
@@ -473,7 +477,8 @@ local validArgs = utils.invert({
473477
'nick',
474478
'location',
475479
'age',
476-
'setUnitToFort' -- added by amostubal to get past an issue with \\LOCAL
480+
'setUnitToFort', -- added by amostubal to get past an issue with \\LOCAL
481+
'quantity'
477482
})
478483

479484
if moduleMode then
@@ -490,8 +495,8 @@ local race
490495
local raceIndex
491496
local casteIndex
492497

493-
if not args.race or not args.caste then
494-
error 'Specfiy a race and caste for the new unit.'
498+
if not args.race then
499+
qerror('Specify a race for the new unit.')
495500
end
496501

497502
--find race
@@ -504,25 +509,27 @@ for i,v in ipairs(df.global.world.raws.creatures.all) do
504509
end
505510

506511
if not race then
507-
error 'Invalid race.'
512+
qerror('Invalid race: '..args.race)
508513
end
509514

510-
for i,v in ipairs(race.caste) do
511-
if v.caste_id == args.caste then
512-
casteIndex = i
513-
break
515+
if args.caste then -- if args.caste is omitted, casteIndex is randomly selected within the spawn loop below
516+
for i,v in ipairs(race.caste) do
517+
if v.caste_id == args.caste then
518+
casteIndex = i
519+
break
520+
end
514521
end
515-
end
516522

517-
if not casteIndex then
518-
error 'Invalid caste.'
523+
if not casteIndex then
524+
qerror('Invalid caste: '..args.caste)
525+
end
519526
end
520527

521528
local age
522529
if args.age then
523530
age = tonumber(args.age)
524531
if not age and not age == 0 then
525-
error('Invalid age: ' .. args.age)
532+
qerror('Invalid age: ' .. args.age)
526533
end
527534
end
528535

@@ -546,87 +553,102 @@ if args.setUnitToFort then
546553
group_id = df.global.ui.group_id
547554
end
548555

549-
local unitId
550-
if civ_id == -1 then
551-
unitId = createUnit(raceIndex, casteIndex, args.location, args.name)
552-
else
553-
unitId = createUnitInCiv(raceIndex, casteIndex, civ_id, group_id, args.location, args.name)
554-
end
555-
556-
if args.domesticate then
557-
domesticate(unitId, group_id)
558-
else
559-
wild(unitId)
556+
local spawnNumber = 1
557+
if args.quantity then
558+
spawnNumber = tonumber(args.quantity)
559+
if not spawnNumber or spawnNumber < 1 then
560+
qerror('Invalid spawn quantity: '..args.quantity)
561+
end
560562
end
561563

562-
local u = df.unit.find(unitId)
563-
u.counters.soldier_mood_countdown = -1
564-
u.enemy.unk_450 = -1
565-
u.enemy.unk_454 = -1
566-
u.enemy.army_controller_id = -1
564+
for n = 1,spawnNumber do
567565

568-
--these flags are an educated guess of how to get the game to compute sizes correctly: use -flagSet and -flagClear arguments to override or supplement
569-
u.flags2.calculated_nerves = false
570-
u.flags2.calculated_bodyparts = false
571-
u.flags3.body_part_relsize_computed = false
572-
u.flags3.size_modifier_computed = false
573-
u.flags3.compute_health = true
574-
u.flags3.weight_computed = false
566+
if not args.caste then -- randomly select caste each time
567+
casteIndex = getRandomCasteId(race)
568+
end
575569

576-
if age or age == 0 then
577-
if age == 0 then
578-
u.birth_time = df.global.cur_year_tick
570+
local unitId
571+
if civ_id == -1 then
572+
unitId = createUnit(raceIndex, casteIndex, args.location, args.name)
573+
else
574+
unitId = createUnitInCiv(raceIndex, casteIndex, civ_id, group_id, args.location, args.name)
579575
end
580-
local u = df.unit.find(unitId)
581-
local oldYearDelta = u.old_year - u.birth_year
582-
u.birth_year = df.global.cur_year - age
583-
if u.old_year ~= -1 then
584-
u.old_year = u.birth_year + oldYearDelta
585-
end
586-
if u.flags1.important_historical_figure == true and u.flags2.important_historical_figure == true then
587-
local hf = df.historical_figure.find(u.hist_figure_id)
588-
hf.born_year = u.birth_year
589-
hf.born_seconds = u.birth_time
590-
hf.old_year = u.old_year
591-
hf.old_seconds = u.old_time
576+
577+
if args.domesticate then
578+
domesticate(unitId, group_id)
579+
else
580+
wild(unitId)
592581
end
593-
end
594-
setAgeProfession(u)
595582

596-
if args.flagSet or args.flagClear then
597583
local u = df.unit.find(unitId)
598-
local flagsToSet = {} --as:bool[]
599-
local flagsToClear = {} --as:bool[]
600-
for _,v in ipairs(args.flagSet or {}) do
601-
flagsToSet[v] = true
602-
end
603-
for _,v in ipairs(args.flagClear or {}) do
604-
flagsToClear[v] = true
605-
end
606-
for _,k in ipairs(df.unit_flags1) do
607-
if flagsToSet[k] then
608-
u.flags1[k] = true;
609-
elseif flagsToClear[k] then
610-
u.flags1[k] = false;
584+
u.counters.soldier_mood_countdown = -1
585+
u.counters.death_cause = -1
586+
u.enemy.unk_450 = -1
587+
u.enemy.unk_454 = -1
588+
u.enemy.army_controller_id = -1
589+
590+
--these flags are an educated guess of how to get the game to compute sizes correctly: use -flagSet and -flagClear arguments to override or supplement
591+
u.flags2.calculated_nerves = false
592+
u.flags2.calculated_bodyparts = false
593+
u.flags3.body_part_relsize_computed = false
594+
u.flags3.size_modifier_computed = false
595+
u.flags3.compute_health = true
596+
u.flags3.weight_computed = false
597+
598+
if age or age == 0 then
599+
if age == 0 then
600+
u.birth_time = df.global.cur_year_tick
611601
end
612-
end
613-
for _,k in ipairs(df.unit_flags2) do
614-
if flagsToSet[k] then
615-
u.flags2[k] = true;
616-
elseif flagsToClear[k] then
617-
u.flags2[k] = false;
602+
local u = df.unit.find(unitId)
603+
local oldYearDelta = u.old_year - u.birth_year
604+
u.birth_year = df.global.cur_year - age
605+
if u.old_year ~= -1 then
606+
u.old_year = u.birth_year + oldYearDelta
607+
end
608+
if u.flags1.important_historical_figure == true and u.flags2.important_historical_figure == true then
609+
local hf = df.historical_figure.find(u.hist_figure_id)
610+
hf.born_year = u.birth_year
611+
hf.born_seconds = u.birth_time
612+
hf.old_year = u.old_year
613+
hf.old_seconds = u.old_time
618614
end
619615
end
620-
for _,k in ipairs(df.unit_flags3) do
621-
if flagsToSet[k] then
622-
u.flags3[k] = true;
623-
elseif flagsToClear[k] then
624-
u.flags3[k] = false;
616+
setAgeProfession(u)
617+
618+
if args.flagSet or args.flagClear then
619+
local u = df.unit.find(unitId)
620+
local flagsToSet = {}
621+
local flagsToClear = {}
622+
for _,v in ipairs(args.flagSet or {}) do
623+
flagsToSet[v] = true
624+
end
625+
for _,v in ipairs(args.flagClear or {}) do
626+
flagsToClear[v] = true
627+
end
628+
for _,k in ipairs(df.unit_flags1) do
629+
if flagsToSet[k] then
630+
u.flags1[k] = true;
631+
elseif flagsToClear[k] then
632+
u.flags1[k] = false;
633+
end
634+
end
635+
for _,k in ipairs(df.unit_flags2) do
636+
if flagsToSet[k] then
637+
u.flags2[k] = true;
638+
elseif flagsToClear[k] then
639+
u.flags2[k] = false;
640+
end
641+
end
642+
for _,k in ipairs(df.unit_flags3) do
643+
if flagsToSet[k] then
644+
u.flags3[k] = true;
645+
elseif flagsToClear[k] then
646+
u.flags3[k] = false;
647+
end
625648
end
626649
end
627-
end
628650

629-
if args.nick and type(args.nick) == 'string' then
630-
dfhack.units.setNickname(df.unit.find(unitId), args.nick)
651+
if args.nick and type(args.nick) == 'string' then
652+
dfhack.units.setNickname(df.unit.find(unitId), args.nick)
653+
end
631654
end
632-

0 commit comments

Comments
 (0)