Skip to main content

banished_game/work/construction/
site.rs

1//! What a building site holds: its template, production, workers, decals, piles and the work it
2//! offers, and the town's list of sites (`Construction`).
3
4use super::*;
5
6/// The parts of a building's descriptions construction reads.
7#[derive(Debug, Clone, Default)]
8pub struct SiteTemplate {
9    /// `Template\X.rsc:resource`.
10    pub reference: String,
11    /// The controllers in component order.
12    pub controllers: Vec<Controller>,
13    // +0x20; +0x30
14    /// The materials to build it: `(count, raw material)`.
15    pub materials: Vec<(i32, usize)>,
16    /// The building work needed, in work units.
17    pub work: i32,
18    // +0x2c
19    /// ClearAreaDescription: present, flatten/stamp flag.
20    pub clear: Option<banished_formats::game::ClearAreaDescription>,
21    // +0x24
22    /// WorkDescription (most workers once built).
23    pub max_workers: i32,
24    // +0x34; +0x30; +0x24
25    /// Scale the most workers by the footprint's area over this many cells (from the work
26    /// description).
27    pub per_area: Option<i32>,
28    // +0x20; +0x28; +0x2c
29    /// Whether the player may change the most workers in the building's window.
30    pub adjustable: bool,
31    /// The fewest workers the player may set.
32    pub min_jobs: i32,
33    /// The most workers the player may set.
34    pub max_jobs: i32,
35    // +0x20
36    /// WorkPlaceDescription.
37    pub profession: Option<usize>,
38    /// ConsumeProduceDescription, resolved.
39    pub produce: Option<ProduceTemplate>,
40    /// StorageDescription: accepted flags, capacity, per footprint cell.
41    pub storage: Option<(u32, i32, bool)>,
42    /// ModelDescription meshes per variant (construction stages, the last finished).
43    pub meshes: Vec<usize>,
44    /// ResidenceDescription.
45    pub residence: Option<banished_formats::game::ResidenceDescription>,
46    /// AttendanceDescription capacity.
47    pub attendance: Option<i32>,
48    /// HerbalistDescription (gatherer, herbalist), resolved.
49    pub herbalist: Option<crate::production::herbalist::HerbalistTemplate>,
50    // +0x28
51    /// RadiusDescription.
52    pub radius: Option<i32>,
53    // +0x20
54    /// AutoPickupDescription.
55    pub auto_pickup: Option<bool>,
56    /// ForesterDescription, resolved.
57    pub forester: Option<crate::production::forester::ForesterTemplate>,
58    /// HappinessDescription.
59    pub happiness: Option<banished_formats::game::HappinessDescription>,
60    /// FishingDescription, resolved.
61    pub fishing: Option<crate::production::fishing::FishingTemplate>,
62    /// HuntDescription.
63    pub hunt: Option<banished_formats::game::HuntDescription>,
64    /// CropFieldDescription.
65    pub crop_field: Option<crate::production::farm::CropFieldTemplate>,
66    /// OrchardDescription.
67    pub orchard: Option<crate::production::orchard::OrchardTemplate>,
68    // +0x20
69    /// PastureDescription: the stock slot of its products' limit.
70    pub pasture: Option<i32>,
71    /// DiseaseDescription (fields, orchards, pastures).
72    pub disease: Option<banished_formats::game::DiseaseDescription>,
73    /// CemeteryDescription's tombstone template and its TombstoneDescription.
74    pub cemetery: Option<crate::people::cemetery::CemeteryTemplate>,
75    /// MarketDescription.
76    pub market: Option<banished_formats::game::MarketDescription>,
77    // ModelComponent +0x5c
78    /// StatusIconDescription (presentation), and the finished model's height its icons stand on
79    /// (`ModelComponent`).
80    pub status_icon: Option<banished_formats::game::StatusIconDescription>,
81    /// The height of the status icon above the building, if it has one.
82    pub icon_height: Option<f32>,
83    /// DestroyDescription, resolved.
84    pub destroy: Option<crate::work::destroy::DestroyTemplate>,
85    /// FireDescription (`Template\Common.rsc:fire*`).
86    pub fire: Option<banished_formats::game::FireDescription>,
87    /// ImmigrationDescription, resolved.
88    pub immigration: Option<crate::people::immigration::ImmigrationTemplate>,
89    /// TradeDescription, resolved.
90    pub trade: Option<crate::commerce::trade::TradeTemplate>,
91    // +0x68; +0x6c
92    /// TradeDescription /: the visitors' disease risk trading brings, and its months.
93    pub trade_risk: Option<(f32, i32)>,
94    /// The pile template of each BuildDescription material (the refunds' piles).
95    pub material_templates: Vec<Option<usize>>,
96}
97
98/// A resolved [`banished_formats::game::ConsumeProduceDescription`].
99#[derive(Debug, Clone, Default)]
100pub struct ProduceTemplate {
101    /// The recipes.
102    pub recipes: Vec<Recipe>,
103    // +0x40
104    /// Work cycles per production.
105    pub cycles: i32,
106    // +0x50
107    /// Work type (tool mesh, animation, walk).
108    pub work_type: u32,
109    // +0x38/+0x3c
110    /// A message to the building's component of that type after a production.
111    pub message: (u32, i32),
112    // +0x44
113    /// Seconds per cycle.
114    pub duration: f32,
115    // +0x48
116    /// Productions before exhaustion (−1 never).
117    pub lifetime: i32,
118    // +0x4c
119    /// Initial selector.
120    pub selector: i32,
121}
122
123/// A production alternative: inputs (raw material, count), the output pile template and its
124/// raw material, and the stock group the limit check reads.
125#[derive(Debug, Clone, Default)]
126pub struct Recipe {
127    /// What it uses: `(raw material, count)`.
128    pub inputs: Vec<(usize, i32)>,
129    /// The template of the output pile.
130    pub output: usize,
131    /// The raw material of the output.
132    pub output_material: usize,
133    /// The stock group whose limit is checked.
134    pub group: i32,
135}
136
137// +0x48; +0x50; +0x58; +0x68
138/// ConsumeProduceComponent state (switched off, selector, weights, productions so far).
139#[derive(Debug, Clone, Default)]
140pub struct ProduceState {
141    /// Whether production is switched off.
142    pub disabled: bool,
143    /// The recipe chosen in the window.
144    pub selector: i32,
145    /// The weight of each recipe, for choosing what to make.
146    pub weights: Vec<f32>,
147    /// The productions so far.
148    pub produced: i32,
149}
150
151// +0x58; +0x68; +0x48; +0x4c
152/// `ConsumeProduce` work state: the worker at each use point, each output point's state (0 free, 1
153/// a production is under way, 2 holds output), every output point was taken, work cycles towards
154/// the next production.
155#[derive(Debug, Clone, Default)]
156pub struct ProduceWork {
157    /// The worker at each use point.
158    pub workers: Vec<Option<usize>>,
159    /// The state of each output point: 0 free, 1 a production is under way, 2 holds output.
160    pub points: Vec<u8>,
161    /// Whether every output point was taken.
162    pub full: bool,
163    /// The work cycles towards the next production.
164    pub cycles: i32,
165}
166
167// vtable 0x140169400; +0x30; +0x28; +0x2c
168/// DecalComponent (0x38 bytes, serialized alpha, material): setup takes the description's alpha;
169/// entity event 1 (the ClearArea's state 2) and the build's state 8 make it opaque; a collapse
170/// switches to the DestroyDescription's material. Each change re-adds the map decal. See
171/// docs/DECALS.md.
172///
173/// Original: `FUN_140120ca0`, `FUN_14011ccc0`, `FUN_14010cfb0`, `FUN_140122aa0`, `FUN_140120c30`.
174#[derive(Debug, Clone, Copy, Default, PartialEq)]
175pub struct Decal {
176    // +0x30
177    /// is 1.
178    pub opaque: bool,
179    // +0x28
180    /// Index into the description's materials.
181    pub material: i32,
182}
183
184/// One building under construction (and, once built, its WorkComponent).
185#[derive(Debug, Clone)]
186pub struct Site {
187    /// The building's entity.
188    pub entity: usize,
189    /// Index into [`Construction::templates`].
190    pub template: usize,
191    /// The building's state machine.
192    pub machine: Machine,
193    // +0x48; +0x4c
194    /// BuildComponent progress, stages.
195    pub progress: i32,
196    /// The construction stages.
197    pub stages: i32,
198    // +0x28; +0x2c
199    /// WorkComponent registered work id (−1) and kept priority.
200    pub work_id: i32,
201    /// The priority to keep for its work, if any.
202    pub work_priority: Option<i32>,
203    /// The building's StorageComponent (index into the town's storages).
204    pub storage: usize,
205    /// State 2 reached.
206    pub complete: bool,
207    // +0x48
208    /// ClearAreaComponent: objects on the footprint when clearing began.
209    pub clear_total: i32,
210    /// The entity was destroyed during a state callback; the site goes once the machine has run.
211    ///
212    /// Original: `FUN_1400c8370`.
213    pub destroyed: bool,
214    // +0x48
215    /// ConsumeProduceComponent (ResourceProduceComponent's for the others).
216    pub produce: Option<ProduceState>,
217    /// AutoPickupComponent.
218    pub auto_pickup: Option<crate::production::herbalist::AutoPickup>,
219    /// ForesterComponent's toggles.
220    pub forester: Option<crate::production::forester::Forester>,
221    // +0x28
222    /// HappinessComponent: registered.
223    pub happiness_registered: bool,
224    /// FishingComponent.
225    pub fishing: Option<crate::production::fishing::Fishing>,
226    // +0x50
227    /// HuntComponent: the hunting time gathered.
228    pub hunt_time: Option<i64>,
229    /// CropFieldComponent.
230    pub crop_field: Option<crate::production::farm::CropField>,
231    /// OrchardComponent.
232    pub orchard: Option<crate::production::orchard::Orchard>,
233    // +0x48
234    /// MultiObjectComponent: the entities placed with this one (a pasture's shelter and fences), in
235    /// order.
236    pub parts: Vec<usize>,
237    // +0x58; +0x4c
238    /// MultiObjectComponent: the stages its construction counts (a bridge's cells, a fenced area's
239    /// border cells); `BuildComponent::EnterGather` takes it for.
240    pub multi_stages: Option<i32>,
241    /// DiseaseComponent.
242    pub disease: Option<crate::disasters::blight::Blight>,
243    // +0x48
244    /// CemeteryComponent: the grave cells.
245    ///
246    /// Original: `FUN_140047b60`.
247    pub graves: Vec<i32>,
248    /// DestroyComponent.
249    pub destroy: Option<crate::work::destroy::Destroy>,
250    /// FireComponent.
251    pub fire: Option<crate::disasters::fire::Fire>,
252    /// ImmigrationComponent.
253    pub immigration: Option<crate::people::immigration::Immigration>,
254    /// TradeComponent.
255    pub trade: Option<crate::commerce::trade::Trade>,
256    /// The ResidenceComponent while the residence is out of the town's (its states 3 to 7).
257    pub home: Option<crate::people::life::Home>,
258    // +0x28; +0x30; +0x20
259    /// UIComponent (bytes, the UIDescription's): the window's remembered settings
260    /// (`DialogInstance::SaveSettings`), empty until it is first closed.
261    pub ui_settings: Vec<u8>,
262}
263
264/// The job manager, professions, workforce and every construction site.
265#[derive(Debug, Clone, Default)]
266pub struct Construction {
267    // +0x1b8; +0x190
268    /// The town's commute optimisation (StockManager).
269    pub commute: crate::behaviour::commute::Commute,
270    /// The job manager.
271    pub jobs: Jobs,
272    /// The professions.
273    pub professions: Professions,
274    /// The citizens' workplaces and professions.
275    pub workforce: Workforce,
276    /// The building templates, as construction reads them.
277    pub templates: Vec<SiteTemplate>,
278    /// Entity → site.
279    pub sites: BTreeMap<usize, Site>,
280    // Game +0xf0 → +0xd0
281    /// `Game →`: sites in their gathering and building states.
282    pub site_list: Vec<usize>,
283    /// Sites placed since the last iteration (activated before the next one).
284    pub placed: Vec<usize>,
285    /// Player commands for the next iteration.
286    pub commands: Vec<Command>,
287    /// Loose raw-material piles (RawMaterialComponent), by entity.
288    pub piles: BTreeMap<usize, Pile>,
289    /// NaturalResourceComponent flag `0x100000`: a removal sequence holds the resource.
290    pub claimed_resources: BTreeSet<usize>,
291    /// Piles created since the last iteration (registered with the entity manager and
292    /// activated after the citizens' update).
293    pub new_piles: Vec<usize>,
294    /// Entities were created or removed (the models need refreshing).
295    pub models_dirty: bool,
296    /// Entities the player destroyed (RemovalTool cancel), before the next iteration.
297    pub destroy: Vec<usize>,
298    /// Finished residences' ResidenceComponents, by entity.
299    pub homes: BTreeMap<usize, crate::people::life::Home>,
300    // +0x60
301    /// AttendanceComponents, by entity, and the town manager's registry.
302    pub attendance: BTreeMap<usize, Attendance>,
303    /// The town manager's list of schools, chapels and doctors.
304    pub attendance_list: Vec<usize>,
305    // +0x70
306    /// The town manager's HealerComponents.
307    pub healers: Vec<usize>,
308    // +0x50
309    /// The town manager's registered HappinessComponents, by entity.
310    pub happiness: Vec<usize>,
311    // Game +0x128 → +8; +0
312    /// The calendar's seconds into the year, set before each iteration.
313    pub calendar: f32,
314    /// The calendar's year, set before each iteration.
315    pub year: u32,
316    /// FruitComponents of orchard trees, by entity.
317    pub fruits: BTreeMap<usize, crate::production::orchard::Fruit>,
318    // +0x2d
319    /// NaturalResourceComponent set by a crop field: whether harvesting leaves a pile.
320    pub keep_piles: BTreeMap<usize, bool>,
321    /// Event-log entries the sites posted: type, severity, entity; the game formats them with the
322    /// entity's toolbar name.
323    ///
324    /// Original: `FUN_14009a2d0`.
325    pub site_events: Vec<(i32, i32, usize)>,
326    // +0x00
327    /// StorageLocationComponents joining (`true`) or leaving the town's storages, in order; the
328    /// town applies them after the iteration's update.
329    pub storage_changes: Vec<(usize, bool)>,
330    // +0x40
331    /// The town's cemeteries, in order.
332    pub cemeteries: Vec<usize>,
333    // +0x28
334    /// TombstoneComponents and the time each has left.
335    pub tombstones: BTreeMap<usize, i64>,
336    /// Burials decided at a death (cemetery, grave cell), placed after the citizens' update.
337    pub burials: Vec<(usize, i32)>,
338    /// Entities a DestroyComponent puts in a torn-down building's place, created by the town after
339    /// the update.
340    ///
341    /// Original: `FUN_1400c9d70`.
342    pub rebuilds: Vec<crate::work::destroy::Rebuild>,
343    /// The town's water sources (built with the map).
344    ///
345    /// Original: `FUN_1400a43f0`.
346    pub water: crate::disasters::fire::Water,
347    // Game +0xd0; FUN_14009d530 sets 0xf0 with disasters off, else 0.
348    /// The game's flags (for example, disasters off).
349    pub game_flags: u32,
350    /// The sky's state is precipitation, set before each iteration.
351    pub precipitation: bool,
352    // +0xa8
353    /// Town manager: the population, set before each update.
354    pub population: i32,
355    // +0x1a8; +0x1a0
356    /// Town manager /: the visitors' disease risk and how long it lasts.
357    pub visitors: (f32, i64),
358    // +0x1b0
359    /// Town manager: nomads taken in.
360    pub nomads_admitted: i32,
361    // +0x30
362    /// Town manager: the trading posts.
363    pub trading_posts: Vec<usize>,
364    // +0x1ac
365    /// Town manager: the value traded away.
366    pub trade_value: i32,
367    // +0xe8
368    /// Map: the cells with a planned road, and their BuildRoad works.
369    pub pending_roads: BTreeMap<i32, i32>,
370    /// Tunnels to dig or fill back (TerraformComponent event 4 and removal), for the terrain's
371    /// owner, and each terraformed entity's kept heights and cells.
372    pub terraform_requests: Vec<crate::world::terraform::Terraform>,
373    /// Requests to flatten the ground: cell rectangles (`x0, y0, x1, y1`, inclusive) whose
374    /// cells' low corners go to height 0. They are applied to the terrain after the iteration.
375    ///
376    /// Original: `FUN_1400a35b0`.
377    pub flatten_requests: Vec<[i32; 4]>,
378    /// The tornadoes by entity, and the citizens they killed this iteration (death code 5; the
379    /// citizens' pass applies it).
380    pub tornadoes: BTreeMap<usize, crate::disasters::tornado::Tornado>,
381    /// The citizens killed by tornadoes this iteration.
382    pub tornado_kills: Vec<usize>,
383    /// TrackerComponents by entity, and the stock manager's made/used counters.
384    pub trackers: BTreeMap<usize, crate::work::tracker::Tracker>,
385    /// DecalComponents that left their setup state, by entity (the others show at their
386    /// description's alpha with material 0).
387    pub decals: BTreeMap<usize, Decal>,
388    /// What the town made and used since the last sample.
389    pub counters: crate::work::tracker::StockCounters,
390    /// The terraforming state of tunnels, by entity.
391    pub terraforms: BTreeMap<usize, crate::world::terraform::TerraformState>,
392    /// Built road cells Remove Roads marked, for the town to place their markers (cell,
393    /// marker template).
394    pub road_markers: Vec<(i32, usize)>,
395    /// The goods with a ValueDescription, in the factory's order (`GameResources`).
396    pub trade_goods: Vec<crate::commerce::trade::TradeGood>,
397    // +0x1e0
398    /// MapComponent 's first river centre line, the trader boats' way.
399    pub river: Vec<[f32; 2]>,
400    /// `Game/Names/Male` and `Female`, for the merchants' names.
401    pub names: Option<
402        std::sync::Arc<(
403            banished_formats::names::NameDatabase,
404            banished_formats::names::NameDatabase,
405        )>,
406    >,
407    /// The town manager's known crops, orchard trees and livestock, as placed-object templates.
408    ///
409    /// Original: `FUN_140096ef0`.
410    pub known: Vec<usize>,
411    // +0x110
412    /// The stock manager's known raw materials: the start profile's, then each as its first pile is
413    /// set up or it is traded for.
414    ///
415    /// Original: `FUN_140097330`.
416    pub known_materials: Vec<usize>,
417}
418
419// vtable 0x14015ee00
420/// AttendanceComponent: who attends a school, chapel or doctor.
421#[derive(Debug, Clone, PartialEq)]
422pub struct Attendance {
423    // +0x20
424    /// AttendanceDescription.
425    pub capacity: i32,
426    // +0x28
427    /// Attendees in arrival order.
428    pub attendees: Vec<usize>,
429    /// The workplace's profession (teacher, priest, doctor).
430    pub profession: Option<usize>,
431}
432
433// vtable 0x14016a5a0
434/// `RawMaterialComponent`: a loose pile.
435///
436/// Original: `FUN_14012e8d0`.
437#[derive(Debug, Clone, PartialEq)]
438pub struct Pile {
439    /// The raw material (RawMaterialDescription of the pile's template).
440    pub material: usize,
441    // +0x28
442    /// Units.
443    pub count: i32,
444    // +0x38
445    /// Age; after 3600 s the pile is destroyed.
446    ///
447    /// Original: `FUN_14012e600`.
448    pub age: i64,
449    /// The Pickup work its WorkComponent registered.
450    ///
451    /// Original: `FUN_14012e6a0`.
452    pub work: i32,
453    /// The work's category mask.
454    ///
455    /// Original: `FUN_14012e950`.
456    pub mask: u32,
457    /// A priority to keep from the work at its cell, applied on activation.
458    ///
459    /// Original: `FUN_1400fe220`.
460    pub priority: Option<i32>,
461    /// The workplace whose AutoPickup gets the Pickup work once registered.
462    pub auto_pickup: Option<usize>,
463    // +0x48; +0x50
464    /// The Pickup work's (0 free, 1 claimed, 2 picked up) and (claimer). Kept on the pile: one
465    /// Pickup try claims several piles' works.
466    pub state: i32,
467    /// The citizen who claimed the pile, if any.
468    pub claimer: Option<usize>,
469}
470
471// +0x48; +0x58; +0x68; +0x70..+0x72
472/// `ClearArea` state: claimed resources (entity → citizen), claimed cells, template filter,
473/// resources / cells / piles.
474#[derive(Debug, Clone, Default)]
475pub struct ClearWork {
476    /// The claimed resources: entity → citizen.
477    pub resources: BTreeMap<usize, usize>,
478    /// The claimed cells: cell → citizen.
479    pub cells: BTreeMap<i32, usize>,
480    /// Only resources of this template are cleared, when set.
481    pub filter: Option<String>,
482    /// Whether natural resources are cleared.
483    pub clear_resources: bool,
484    /// Whether cells are cleared.
485    pub clear_cells: bool,
486    /// Whether piles are cleared.
487    pub clear_piles: bool,
488}
489
490// +0x48; +0x50
491/// `Pickup` work: its state lives on the [`Pile`] it belongs to.
492#[derive(Debug, Clone, Default)]
493pub struct PickupWork;
494
495// +0x48; +0x58; +0x68
496/// `GatherRawMaterials` state (delivered per requirement, in transit, workers).
497#[derive(Debug, Clone, Default)]
498pub struct Gather {
499    /// The materials delivered, for each requirement.
500    pub stored: Vec<i32>,
501    /// The materials on their way, for each requirement.
502    pub transit: Vec<i32>,
503    /// The workers.
504    pub workers: BTreeSet<usize>,
505}
506
507// +0x48; +0x4c; +0x50; +0x60
508/// `BuildStructure` state (destroy flag, point cursor, occupied build points, workers).
509#[derive(Debug, Clone, Default)]
510pub struct BuildWork {
511    /// Whether this is demolition instead of construction.
512    pub destroy: bool,
513    /// The next build point to try.
514    pub cursor: i32,
515    /// Which build points are taken.
516    pub occupied: Vec<bool>,
517    /// The workers.
518    pub workers: BTreeSet<usize>,
519}
520
521impl SiteTemplate {
522    /// The default number of workers of a building of `footprint` cells.
523    ///
524    /// Original: `WorkComponent::MaxWorkers`, `FUN_140133a90`.
525    pub fn default_max_workers(&self, footprint: (i32, i32)) -> i32 {
526        let Some(area) = self.per_area else {
527            return self.max_workers;
528        };
529        let n = self.max_workers;
530        // `DAT_14017884c` = 1.0.
531        let scaled = ((footprint.0 * footprint.1) as f32 / area as f32 * n as f32 + 1.0) as i32;
532        if scaled <= n {
533            scaled.max(1)
534        } else {
535            n
536        }
537    }
538}