banished_formats/game/construction.rs
1//! Placing, building and removing things: placed and fenced structures, roads, bridges, floors, terraforming, building, destroying and clearing.
2
3use super::*;
4
5/// `CreatePlacedDescription`: how a building is placed; the start structures take their size from
6/// it.
7///
8/// Original: `FUN_14011eda0`, `FUN_1400a7490`.
9#[derive(Debug, Clone)]
10pub struct CreatePlacedDescription {
11 // +0x20
12 /// Not identified yet.
13 pub flags: u32,
14 // +0x30
15 /// Not identified yet.
16 pub values: Vec<u32>,
17 // +0x28
18 /// Not identified yet.
19 pub name: String,
20 // +0x44, +0x48
21 /// The footprint's width in cells.
22 pub width: i32,
23 /// The footprint's height in cells.
24 pub height: i32,
25 // +0x40
26 /// Not identified yet.
27 pub value: i32,
28 // +0x50
29 /// Not identified yet.
30 pub related: Option<String>,
31}
32
33/// Read a [`CreatePlacedDescription`] from the bytes of its resource.
34pub fn create_placed_description(data: &[u8]) -> Result<CreatePlacedDescription> {
35 let (_, value) = crs::parse(data, |r: &mut Reader, h| {
36 if h.type_id != type_id("CreatePlacedDescription") {
37 return format_err(format!(
38 "expected CreatePlacedDescription, found {:#010x}",
39 h.type_id
40 ));
41 }
42 r.block(|r, _| {
43 let flags = r.u32()?;
44 let values = (0..r.count()?).map(|_| r.u32()).collect::<Result<_>>()?;
45 Ok(CreatePlacedDescription {
46 flags,
47 values,
48 name: r.string()?,
49 width: r.i32()?,
50 height: r.i32()?,
51 value: r.i32()?,
52 related: r.external_ref()?,
53 })
54 })
55 })?;
56 Ok(value)
57}
58
59// vtable 0x140166a10; +0x20; +0x28..+0x50; +0x58; +0x5c; +0x60; +0x64; +0x68; +0x70
60/// `CreateFencedDescription`: the fenced tool's (FencedTool) cell-type mask, its six part templates
61/// (shelter, end, corner, straight, long straight, gate), the largest, and smallest, size, the
62/// shelter centred on its side, and the template whose entities may stand in the way.
63///
64/// Original: `FUN_140048250`.
65#[derive(Debug, Clone, Default, PartialEq)]
66pub struct CreateFencedDescription {
67 /// The cell types it may cover.
68 pub mask: u32,
69 /// The part templates: shelter, end, corner, straight, long straight, gate.
70 pub parts: Vec<Option<String>>,
71 /// The largest size.
72 pub max: (i32, i32),
73 /// The smallest size.
74 pub min: (i32, i32),
75 /// Whether the shelter is centred on its side.
76 pub centered: bool,
77 /// The template whose entities may stand in the way.
78 pub clear: Option<String>,
79}
80
81/// Read a [`CreateFencedDescription`] from the bytes of its resource.
82pub fn create_fenced_description(data: &[u8]) -> Result<CreateFencedDescription> {
83 let (_, value) = crs::parse(data, |r: &mut Reader, h| {
84 if h.type_id != type_id("CreateFencedDescription") {
85 return format_err(format!(
86 "expected CreateFencedDescription, found {:#010x}",
87 h.type_id
88 ));
89 }
90 r.block(|r, _| {
91 let mask = r.u32()?;
92 let parts = (0..6).map(|_| r.external_ref()).collect::<Result<_>>()?;
93 Ok(CreateFencedDescription {
94 mask,
95 parts,
96 max: (r.i32()?, r.i32()?),
97 min: (r.i32()?, r.i32()?),
98 centered: r.bool()?,
99 clear: r.external_ref()?,
100 })
101 })
102 })?;
103 Ok(value)
104}
105
106// +0x20; +0x28
107/// `CreateRoadDescription`: the road kind (0 stone, 1 dirt; a lower kind replaces a higher), the
108/// raw material a cell takes (none for dirt).
109///
110/// Original: `FUN_14011f150`.
111#[derive(Debug, Clone, Default, PartialEq, Eq)]
112pub struct CreateRoadDescription {
113 /// The road kind: 0 stone, 1 dirt. A lower kind replaces a higher one.
114 pub kind: u8,
115 /// The raw material a cell takes (none for dirt).
116 pub material: Option<String>,
117}
118
119/// Read a [`CreateRoadDescription`] from the bytes of its resource.
120pub fn create_road_description(data: &[u8]) -> Result<CreateRoadDescription> {
121 let (_, value) = crs::parse(data, |r: &mut Reader, h| {
122 if h.type_id != type_id("CreateRoadDescription") {
123 return format_err(format!(
124 "expected CreateRoadDescription, found {:#010x}",
125 h.type_id
126 ));
127 }
128 r.block(|r, _| {
129 Ok(CreateRoadDescription {
130 kind: r.u8()?,
131 material: r.external_ref()?,
132 })
133 })
134 })?;
135 Ok(value)
136}
137
138// vtable 0x140167f20; +0x20; +0x24; +0x28; +0x2c; +0x30; +0x34; +0x38
139/// `CreateBridgeDescription`: the bridge tool's (BridgeTool) cell-type masks: the cells it spans
140/// (water), its banks, further cells drawn as valid, at least one spanned cell of these, the cells
141/// beside it within (its half width); the part templates (start, 1, 2, 4 and 8 cells long).
142///
143/// Original: `FUN_14011eaa0`.
144#[derive(Debug, Clone, Default, PartialEq, Eq)]
145pub struct CreateBridgeDescription {
146 /// The cell types it spans (water).
147 pub span: u32,
148 /// The cell types of its banks.
149 pub banks: u32,
150 /// Further cell types drawn as valid.
151 pub shown: u32,
152 /// The cell types of which at least one spanned cell is needed.
153 pub needs: u32,
154 /// The cell types allowed beside it.
155 pub sides: u32,
156 /// Its half width, in cells.
157 pub half_width: i32,
158 /// The part templates: the start, then 1, 2, 4 and 8 cells long.
159 pub parts: Vec<Option<String>>,
160}
161
162/// Read a [`CreateBridgeDescription`] from the bytes of its resource.
163pub fn create_bridge_description(data: &[u8]) -> Result<CreateBridgeDescription> {
164 let (_, value) = crs::parse(data, |r: &mut Reader, h| {
165 if h.type_id != type_id("CreateBridgeDescription") {
166 return format_err(format!(
167 "expected CreateBridgeDescription, found {:#010x}",
168 h.type_id
169 ));
170 }
171 r.block(|r, _| {
172 let span = r.u32()?;
173 let shown = r.u32()?;
174 let needs = r.u32()?;
175 let banks = r.u32()?;
176 let sides = r.u32()?;
177 let half_width = r.i32()?;
178 let parts = (0..5).map(|_| r.external_ref()).collect::<Result<_>>()?;
179 Ok(CreateBridgeDescription {
180 span,
181 banks,
182 shown,
183 needs,
184 sides,
185 half_width,
186 parts,
187 })
188 })
189 })?;
190 Ok(value)
191}
192
193// +0x20; +0x28
194/// `FloorDescription`: the picking mesh walked on, added on activation.
195///
196/// Original: `FUN_140124b50`.
197#[derive(Debug, Clone, Default, PartialEq, Eq)]
198pub struct FloorDescription {
199 /// The picking mesh walked on.
200 pub mesh: Option<String>,
201 /// Whether the floor is added on activation.
202 pub active: bool,
203}
204
205/// Read a [`FloorDescription`] from the bytes of its resource.
206pub fn floor_description(data: &[u8]) -> Result<FloorDescription> {
207 let (_, value) = crs::parse(data, |r: &mut Reader, h| {
208 if h.type_id != type_id("FloorDescription") {
209 return format_err(format!(
210 "expected FloorDescription, found {:#010x}",
211 h.type_id
212 ));
213 }
214 r.block(|r, _| {
215 Ok(FloorDescription {
216 mesh: r.external_ref()?,
217 active: r.bool()?,
218 })
219 })
220 })?;
221 Ok(value)
222}
223
224// +0x20; +0x28; +0x30; +0x34
225/// `TerraformDescription`: a tunnel's floor and ceiling picking meshes, each with the cell types
226/// its vertices may change.
227///
228/// Original: `FUN_140058000`.
229#[derive(Debug, Clone, Default, PartialEq, Eq)]
230pub struct TerraformDescription {
231 /// The floor's picking mesh.
232 pub floor: Option<String>,
233 /// The cell types the floor's vertices may change.
234 pub floor_mask: u32,
235 /// The ceiling's picking mesh.
236 pub ceiling: Option<String>,
237 /// The cell types the ceiling's vertices may change.
238 pub ceiling_mask: u32,
239}
240
241/// Read a [`TerraformDescription`] from the bytes of its resource.
242pub fn terraform_description(data: &[u8]) -> Result<TerraformDescription> {
243 let (_, value) = crs::parse(data, |r: &mut Reader, h| {
244 if h.type_id != type_id("TerraformDescription") {
245 return format_err(format!(
246 "expected TerraformDescription, found {:#010x}",
247 h.type_id
248 ));
249 }
250 r.block(|r, _| {
251 Ok(TerraformDescription {
252 floor: r.external_ref()?,
253 floor_mask: r.u32()?,
254 ceiling: r.external_ref()?,
255 ceiling_mask: r.u32()?,
256 })
257 })
258 })?;
259 Ok(value)
260}
261
262/// `CreateRemovalDescription` (`Template\ClearX.rsc:createremoval`): what a RemovalTool drag does
263/// to each cell.
264///
265/// Original: `FUN_14011ef90`, `FUN_140108ab0`.
266#[derive(Debug, Clone, Default)]
267pub struct CreateRemovalDescription {
268 // +0x20
269 /// Mark natural resources for removal.
270 pub resources: bool,
271 // +0x28/+0x30
272 /// The resource templates it marks (empty: every one).
273 pub filters: Vec<String>,
274 // +0x38
275 /// The marker entity's template (`Template\Clear.rsc:resource`).
276 pub template: Option<String>,
277 // +0x40; +0x41; +0x42; +0x43
278 /// Remove roads.
279 pub roads: bool,
280 /// Demolish buildings.
281 pub buildings: bool,
282 /// Cancel.
283 pub cancel: bool,
284 /// Prioritise.
285 pub priority: bool,
286}
287
288/// Read a [`CreateRemovalDescription`] from the bytes of its resource.
289pub fn create_removal_description(data: &[u8]) -> Result<CreateRemovalDescription> {
290 let (_, value) = crs::parse(data, |r: &mut Reader, h| {
291 if h.type_id != type_id("CreateRemovalDescription") {
292 return format_err(format!(
293 "expected CreateRemovalDescription, found {:#010x}",
294 h.type_id
295 ));
296 }
297 r.block(|r, _| {
298 let resources = r.bool()?;
299 let n = r.count()?;
300 let filters = (0..n)
301 .map(|_| Ok(r.external_ref()?.unwrap_or_default()))
302 .collect::<Result<_>>()?;
303 Ok(CreateRemovalDescription {
304 resources,
305 filters,
306 template: r.external_ref()?,
307 roads: r.bool()?,
308 buildings: r.bool()?,
309 cancel: r.bool()?,
310 priority: r.bool()?,
311 })
312 })
313 })?;
314 Ok(value)
315}
316
317/// `CreateDragDescription` (`Template\X.rsc:createdrag`).
318///
319/// Original: `FUN_14011ec20`.
320#[derive(Debug, Clone, Default)]
321pub struct CreateDragDescription {
322 // +0x20
323 /// The cell types the drag may cover, as a mask of type bits.
324 ///
325 /// Original: read by `FUN_1400a4aa0`.
326 pub mask: u32,
327 /// Not identified yet.
328 pub value_28: i32,
329 /// Not identified yet.
330 pub value_2c: i32,
331 /// Not identified yet.
332 pub value_30: i32,
333 /// Not identified yet.
334 pub value_34: i32,
335 // +0x24
336 /// Ignore cell occupants.
337 pub override_occupant: bool,
338 // +0x38
339 /// An external reference.
340 pub reference: Option<String>,
341}
342
343/// Read a [`CreateDragDescription`] from the bytes of its resource.
344pub fn create_drag_description(data: &[u8]) -> Result<CreateDragDescription> {
345 let (_, value) = crs::parse(data, |r: &mut Reader, h| {
346 if h.type_id != type_id("CreateDragDescription") {
347 return format_err(format!(
348 "expected CreateDragDescription, found {:#010x}",
349 h.type_id
350 ));
351 }
352 r.block(|r, _| {
353 Ok(CreateDragDescription {
354 mask: r.u32()?,
355 value_28: r.i32()?,
356 value_2c: r.i32()?,
357 value_30: r.i32()?,
358 value_34: r.i32()?,
359 override_occupant: r.bool()?,
360 reference: r.external_ref()?,
361 })
362 })
363 })?;
364 Ok(value)
365}
366
367/// `BuildDescription`: what constructing a building costs.
368///
369/// Original: `FUN_14010e610`.
370#[derive(Debug, Clone)]
371pub struct BuildDescription {
372 // +0x20
373 /// `(count, raw material)` requirements.
374 pub materials: Vec<(i32, Option<String>)>,
375 // +0x30
376 /// Construction work.
377 pub work: i32,
378 // +0x34
379 /// Shows `labelBuildMulti` in the CreateBar (costs per area).
380 pub multi: bool,
381}
382
383/// Read a [`BuildDescription`] from the bytes of its resource.
384pub fn build_description(data: &[u8]) -> Result<BuildDescription> {
385 let (_, value) = crs::parse(data, |r: &mut Reader, h| {
386 if h.type_id != type_id("BuildDescription") {
387 return format_err(format!(
388 "expected BuildDescription, found {:#010x}",
389 h.type_id
390 ));
391 }
392 r.block(|r, _| {
393 let materials = (0..r.count()?)
394 .map(|_| Ok((r.i32()?, r.external_ref()?)))
395 .collect::<Result<_>>()?;
396 Ok(BuildDescription {
397 materials,
398 work: r.i32()?,
399 multi: r.bool()?,
400 })
401 })
402 })?;
403 Ok(value)
404}
405
406// +0x20; +0x28; +0x2c; +0x30
407/// `DestroyDescription`: the template left in the building's place once it is torn down (the
408/// quarry's and mine's hole; none for most), the ModelComponent's material set while damaged, the
409/// decal while damaged, the lowest construction mesh a collapse steps down to.
410///
411/// Original: `FUN_140123340`, `FUN_14012a4f0`, `FUN_140122aa0`.
412#[derive(Debug, Clone, Default, PartialEq, Eq)]
413pub struct DestroyDescription {
414 /// The template left in the building's place once it is torn down (the quarry's and mine's hole; none for most).
415 pub replace: Option<String>,
416 /// The ModelComponent's material set while damaged.
417 pub material: i32,
418 /// The decal while damaged.
419 pub decal: i32,
420 /// The lowest construction mesh a collapse steps down to.
421 pub min_mesh: i32,
422}
423
424/// Read a [`DestroyDescription`] from the bytes of its resource.
425pub fn destroy_description(data: &[u8]) -> Result<DestroyDescription> {
426 let (_, value) = crs::parse(data, |r: &mut Reader, h| {
427 if h.type_id != type_id("DestroyDescription") {
428 return format_err(format!(
429 "expected DestroyDescription, found {:#010x}",
430 h.type_id
431 ));
432 }
433 r.block(|r, _| {
434 Ok(DestroyDescription {
435 replace: r.external_ref()?,
436 material: r.i32()?,
437 decal: r.i32()?,
438 min_mesh: r.i32()?,
439 })
440 })
441 })?;
442 Ok(value)
443}
444
445/// `ClearAreaDescription` (`Template\X.rsc:cleararea`).
446///
447/// Original: `FUN_14011d5c0`.
448#[derive(Debug, Clone, Default)]
449pub struct ClearAreaDescription {
450 // +0x20
451 /// An external reference handed to the ClearArea work.
452 ///
453 /// Original: `FUN_1400ebe30`.
454 pub reference: Option<String>,
455 // +0x28..+0x2a
456 /// The ClearArea work clears natural resources.
457 pub clear_resources: bool,
458 /// The ClearArea work clears empty cells.
459 pub clear_cells: bool,
460 /// The ClearArea work clears piles.
461 pub clear_piles: bool,
462 // +0x2b
463 /// Register the work keeping its priority.
464 pub keep_priority: bool,
465 // +0x2c
466 /// Stamp the footprint once cleared.
467 ///
468 /// Original: `FUN_14011d740`.
469 pub stamp: bool,
470 // +0x2d
471 /// Remove everything in the footprint at once.
472 ///
473 /// Original: `FUN_14011d230`.
474 pub instant: bool,
475 /// Not identified yet.
476 pub value_30: i32,
477}
478
479/// Read a [`ClearAreaDescription`] from the bytes of its resource.
480pub fn clear_area_description(data: &[u8]) -> Result<ClearAreaDescription> {
481 let (_, value) = crs::parse(data, |r: &mut Reader, h| {
482 if h.type_id != type_id("ClearAreaDescription") {
483 return format_err(format!(
484 "expected ClearAreaDescription, found {:#010x}",
485 h.type_id
486 ));
487 }
488 r.block(|r, _| {
489 Ok(ClearAreaDescription {
490 reference: r.external_ref()?,
491 clear_resources: r.bool()?,
492 clear_cells: r.bool()?,
493 clear_piles: r.bool()?,
494 keep_priority: r.bool()?,
495 stamp: r.bool()?,
496 instant: r.bool()?,
497 value_30: r.i32()?,
498 })
499 })
500 })?;
501 Ok(value)
502}