|
| 1 | +--Spawnunit.lua |
| 2 | +--create unit at pointer. Usage e.g. "spawnunit DWARF 0 Dwarfy" |
| 3 | +--author Warmist, Runrusher? |
| 4 | +--edited by expwnent |
| 5 | + |
| 6 | +function findCasteIndex(race_id,casteName) |
| 7 | + local cr=df.creature_raw.find(race_id) |
| 8 | + for casteIndex,caste in ipairs(cr.caste) do |
| 9 | + if caste.caste_id == casteName then |
| 10 | + return casteIndex |
| 11 | + end |
| 12 | + end |
| 13 | + return nil |
| 14 | +end |
| 15 | +function getCaste(race_id,caste_id) |
| 16 | + local cr=df.creature_raw.find(race_id) |
| 17 | + return cr.caste[caste_id] |
| 18 | +end |
| 19 | +function genBodyModifier(body_app_mod) |
| 20 | + local a=math.random(0,#body_app_mod.ranges-2) |
| 21 | + return math.random(body_app_mod.ranges[a],body_app_mod.ranges[a+1]) |
| 22 | +end |
| 23 | +function getBodySize(caste,time) |
| 24 | + --todo real body size... |
| 25 | + return caste.body_size_1[#caste.body_size_1-1] --returns last body size |
| 26 | +end |
| 27 | +function genAttribute(array) |
| 28 | + local a=math.random(0,#array-2) |
| 29 | + return math.random(array[a],array[a+1]) |
| 30 | +end |
| 31 | +function norm() |
| 32 | + return math.sqrt((-2)*math.log(math.random()))*math.cos(2*math.pi*math.random()) |
| 33 | +end |
| 34 | +function normalDistributed(mean,sigma) |
| 35 | + return mean+sigma*norm() |
| 36 | +end |
| 37 | +function clampedNormal(min,median,max) |
| 38 | + local val=normalDistributed(median,math.sqrt(max-min)) |
| 39 | + if val<min then return min end |
| 40 | + if val>max then return max end |
| 41 | + return val |
| 42 | +end |
| 43 | +function makeSoul(unit,caste) |
| 44 | + local tmp_soul=df.unit_soul:new() |
| 45 | + tmp_soul.unit_id=unit.id |
| 46 | + tmp_soul.name:assign(unit.name) |
| 47 | + tmp_soul.race=unit.race |
| 48 | + tmp_soul.sex=unit.sex |
| 49 | + tmp_soul.caste=unit.caste |
| 50 | + --todo skills,preferences,traits. |
| 51 | + local attrs=caste.attributes |
| 52 | + for k,v in pairs(attrs.ment_att_range) do |
| 53 | + local max_percent=attrs.ment_att_cap_perc[k]/100 |
| 54 | + local cvalue=genAttribute(v) |
| 55 | + tmp_soul.mental_attrs[k]={value=cvalue,max_value=cvalue*max_percent} |
| 56 | + end |
| 57 | + for k,v in pairs(tmp_soul.traits) do |
| 58 | + local min,mean,max |
| 59 | + min=caste.personality.a[k] |
| 60 | + mean=caste.personality.b[k] |
| 61 | + max=caste.personality.c[k] |
| 62 | + tmp_soul.traits[k]=clampedNormal(min,mean,max) |
| 63 | + end |
| 64 | + unit.status.souls:insert("#",tmp_soul) |
| 65 | + unit.status.current_soul=tmp_soul |
| 66 | +end |
| 67 | +function CreateUnit(race_id,caste_id) |
| 68 | + local race=df.creature_raw.find(race_id) |
| 69 | + if race==nil then error("Invalid race_id") end |
| 70 | + local caste=getCaste(race_id,caste_id) |
| 71 | + local unit=df.unit:new() |
| 72 | + unit.race=race_id |
| 73 | + unit.caste=caste_id |
| 74 | + unit.id=df.global.unit_next_id |
| 75 | + df.global.unit_next_id=df.global.unit_next_id+1 |
| 76 | + if caste.misc.maxage_max==-1 then |
| 77 | + unit.relations.old_year=-1 |
| 78 | + else |
| 79 | + unit.relations.old_year=math.random(caste.misc.maxage_min,caste.misc.maxage_max) |
| 80 | + end |
| 81 | + unit.sex=caste.gender |
| 82 | + local body=unit.body |
| 83 | + body.body_plan=caste.body_info |
| 84 | + local body_part_count=#body.body_plan.body_parts |
| 85 | + local layer_count=#body.body_plan.layer_part |
| 86 | + --components |
| 87 | + unit.relations.birth_year=df.global.cur_year |
| 88 | + --unit.relations.birth_time=?? |
| 89 | + |
| 90 | + --unit.relations.old_time=?? --TODO add normal age |
| 91 | + local cp=body.components |
| 92 | + cp.body_part_status:resize(body_part_count) |
| 93 | + cp.numbered_masks:resize(#body.body_plan.numbered_masks) |
| 94 | + for num,v in ipairs(body.body_plan.numbered_masks) do |
| 95 | + cp.numbered_masks[num]=v |
| 96 | + end |
| 97 | + |
| 98 | + cp.layer_status:resize(layer_count) |
| 99 | + cp.layer_wound_area:resize(layer_count) |
| 100 | + cp.layer_cut_fraction:resize(layer_count) |
| 101 | + cp.layer_dent_fraction:resize(layer_count) |
| 102 | + cp.layer_effect_fraction:resize(layer_count) |
| 103 | + local attrs=caste.attributes |
| 104 | + for k,v in pairs(attrs.phys_att_range) do |
| 105 | + local max_percent=attrs.phys_att_cap_perc[k]/100 |
| 106 | + local cvalue=genAttribute(v) |
| 107 | + unit.body.physical_attrs[k]={value=cvalue,max_value=cvalue*max_percent} |
| 108 | + --unit.body.physical_attrs:insert(k,{new=true,max_value=genMaxAttribute(v),value=genAttribute(v)}) |
| 109 | + end |
| 110 | + |
| 111 | + body.blood_max=getBodySize(caste,0) --TODO normal values |
| 112 | + body.blood_count=body.blood_max |
| 113 | + body.infection_level=0 |
| 114 | + unit.status2.body_part_temperature:resize(body_part_count) |
| 115 | + for k,v in pairs(unit.status2.body_part_temperature) do |
| 116 | + unit.status2.body_part_temperature[k]={new=true,whole=10067,fraction=0} |
| 117 | + end |
| 118 | + -------------------- |
| 119 | + local stuff=unit.enemy |
| 120 | + stuff.body_part_878:resize(body_part_count) -- all = 3 |
| 121 | + stuff.body_part_888:resize(body_part_count) -- all = 3 |
| 122 | + stuff.body_part_relsize:resize(body_part_count) -- all =0 |
| 123 | + |
| 124 | + --TODO add correct sizes. (calculate from age) |
| 125 | + local size=caste.body_size_2[#caste.body_size_2-1] |
| 126 | + body.size_info.size_cur=size |
| 127 | + body.size_info.size_base=size |
| 128 | + body.size_info.area_cur=math.pow(size,0.666) |
| 129 | + body.size_info.area_base=math.pow(size,0.666) |
| 130 | + body.size_info.area_cur=math.pow(size*10000,0.333) |
| 131 | + body.size_info.area_base=math.pow(size*10000,0.333) |
| 132 | + |
| 133 | + stuff.were_race=race_id |
| 134 | + stuff.were_caste=caste_id |
| 135 | + stuff.normal_race=race_id |
| 136 | + stuff.normal_caste=caste_id |
| 137 | + stuff.body_part_8a8:resize(body_part_count) -- all = 1 |
| 138 | + stuff.body_part_base_ins:resize(body_part_count) |
| 139 | + stuff.body_part_clothing_ins:resize(body_part_count) |
| 140 | + stuff.body_part_8d8:resize(body_part_count) |
| 141 | + unit.recuperation.healing_rate:resize(layer_count) |
| 142 | + --appearance |
| 143 | + |
| 144 | + local app=unit.appearance |
| 145 | + app.body_modifiers:resize(#caste.body_appearance_modifiers) --3 |
| 146 | + for k,v in pairs(app.body_modifiers) do |
| 147 | + app.body_modifiers[k]=genBodyModifier(caste.body_appearance_modifiers[k]) |
| 148 | + end |
| 149 | + app.bp_modifiers:resize(#caste.bp_appearance.modifier_idx) --0 |
| 150 | + for k,v in pairs(app.bp_modifiers) do |
| 151 | + app.bp_modifiers[k]=genBodyModifier(caste.bp_appearance.modifiers[caste.bp_appearance.modifier_idx[k]]) |
| 152 | + end |
| 153 | + --app.unk_4c8:resize(33)--33 |
| 154 | + app.tissue_style:resize(#caste.bp_appearance.style_part_idx) |
| 155 | + app.tissue_style_civ_id:resize(#caste.bp_appearance.style_part_idx) |
| 156 | + app.tissue_style_id:resize(#caste.bp_appearance.style_part_idx) |
| 157 | + app.tissue_style_type:resize(#caste.bp_appearance.style_part_idx) |
| 158 | + app.tissue_length:resize(#caste.bp_appearance.style_part_idx) |
| 159 | + app.genes.appearance:resize(#caste.body_appearance_modifiers+#caste.bp_appearance.modifiers) --3 |
| 160 | + app.genes.colors:resize(#caste.color_modifiers*2) --??? |
| 161 | + app.colors:resize(#caste.color_modifiers)--3 |
| 162 | + |
| 163 | + makeSoul(unit,caste) |
| 164 | + |
| 165 | + df.global.world.units.all:insert("#",unit) |
| 166 | + df.global.world.units.active:insert("#",unit) |
| 167 | + --todo set weapon bodypart |
| 168 | + |
| 169 | + local num_inter=#caste.body_info.interactions |
| 170 | + --used to be anon_5 and anon_6: I guessed at what those were before the df-structures update. It seems to work at least a bit. ~expwnent |
| 171 | + unit.curse.own_interaction:resize(num_inter) |
| 172 | + unit.curse.own_interaction_delay:resize(num_inter) |
| 173 | + return unit |
| 174 | +end |
| 175 | +function findRace(name) |
| 176 | + for k,v in pairs(df.global.world.raws.creatures.all) do |
| 177 | + if v.creature_id==name then |
| 178 | + return k |
| 179 | + end |
| 180 | + end |
| 181 | + qerror("Race:"..name.." not found!") |
| 182 | +end |
| 183 | +function PlaceUnit(raceName,casteName,name,position) |
| 184 | + local pos |
| 185 | + if position.x==-30000 then |
| 186 | + pos = copyall(df.global.cursor) |
| 187 | + else |
| 188 | + pos = position |
| 189 | + end |
| 190 | + if pos.x==-30000 then |
| 191 | + qerror("Spawnunit: specify location or place the cursor where you want the unit to be created.") |
| 192 | + end |
| 193 | + local race=findRace(raceName) |
| 194 | + local caste=findCasteIndex(race,casteName) |
| 195 | + local u=CreateUnit(race,tonumber(caste) or 0) |
| 196 | + u.pos:assign(pos) |
| 197 | + if name then |
| 198 | + u.name.first_name=name |
| 199 | + u.name.has_name=true |
| 200 | + end |
| 201 | + u.civ_id=df.global.ui.civ_id |
| 202 | + |
| 203 | + local desig,ocupan=dfhack.maps.getTileFlags(pos) |
| 204 | + ocupan.unit=true |
| 205 | + --createNemesis(u) |
| 206 | +end |
| 207 | +function createFigure(trgunit) |
| 208 | + local hf=df.historical_figure:new() |
| 209 | + hf.id=df.global.hist_figure_next_id |
| 210 | + hf.race=trgunit.race |
| 211 | + hf.caste=trgunit.caste |
| 212 | + df.global.hist_figure_next_id=df.global.hist_figure_next_id+1 |
| 213 | + hf.name.first_name=trgunit.name.first_name |
| 214 | + hf.name.has_name=true |
| 215 | + df.global.world.history.figures:insert("#",hf) |
| 216 | + return hf |
| 217 | +end |
| 218 | +function createNemesis(trgunit) |
| 219 | + local id=df.global.nemesis_next_id |
| 220 | + local nem=df.nemesis_record:new() |
| 221 | + nem.id=id |
| 222 | + nem.unit_id=trgunit.id |
| 223 | + nem.unit=trgunit |
| 224 | + nem.flags:resize(1) |
| 225 | + nem.flags[4]=true |
| 226 | + nem.flags[5]=true |
| 227 | + nem.flags[6]=true |
| 228 | + nem.flags[7]=true |
| 229 | + nem.flags[8]=true |
| 230 | + nem.flags[9]=true |
| 231 | + --[[for k=4,8 do |
| 232 | + nem.flags[k]=true |
| 233 | + end]] |
| 234 | + df.global.world.nemesis.all:insert("#",nem) |
| 235 | + df.global.nemesis_next_id=id+1 |
| 236 | + trgunit.general_refs:insert("#",{new=df.general_ref_is_nemesisst,nemesis_id=id}) |
| 237 | + trgunit.flags1.important_historical_figure=true |
| 238 | + local gen=df.global.world.worldgen |
| 239 | + nem.save_file_id=gen.next_unit_chunk_id; |
| 240 | + gen.next_unit_chunk_id=gen.next_unit_chunk_id+1 |
| 241 | + gen.next_unit_chunk_offset=gen.next_unit_chunk_offset+1 |
| 242 | + |
| 243 | + --[[ local gen=df.global.world.worldgen |
| 244 | + gen.next_unit_chunk_id |
| 245 | + gen.next_unit_chunk_offset |
| 246 | + ]] |
| 247 | + nem.figure=createFigure(trgunit) |
| 248 | +end |
| 249 | + |
| 250 | +args={...} |
| 251 | + |
| 252 | +pos = df.new(df.coord) |
| 253 | +if #args > 3 then |
| 254 | + pos.x = tonumber(args[4]) or -30000 |
| 255 | + pos.y = tonumber(args[5]) or -30000 |
| 256 | + pos.z = tonumber(args[6]) or -30000 |
| 257 | +end |
| 258 | + |
| 259 | +PlaceUnit(args[1],args[2],args[3],pos) |
| 260 | + |
0 commit comments