Skip to main content

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}