Skip to main content

banished_game/town/
callbacks.rs

1//! The building state callbacks ([`Callbacks`]) that construction and destruction call.
2
3use super::*;
4
5impl Callbacks for Sim<'_, '_> {
6    fn machine(&mut self, site: usize) -> &mut Machine {
7        &mut self.sites.get_mut(&site).unwrap().machine
8    }
9
10    fn call(&mut self, jobs: &mut Jobs, e: usize, f: Callback) -> bool {
11        match f {
12            Callback::None | Callback::Missing => false,
13            Callback::ClearEnter0 => {
14                // `FUN_14011ce30`: with the description's instant flag (`+0x2d`; or the debug
15                // flag `Game +0xd0` bit 0) the footprint is emptied at once (`FUN_14011d230`).
16                let instant = self
17                    .sites
18                    .get(&e)
19                    .and_then(|s| self.site_templates[s.template].clear.as_ref())
20                    .is_some_and(|d| d.instant);
21                if instant {
22                    self.clear_instantly(jobs, e);
23                }
24                if self.is_clear(e) {
25                    self.machine(e).request(1);
26                } else {
27                    self.register_clear(jobs, e);
28                }
29                true
30            }
31            Callback::ClearUpdate0 => {
32                if self.is_clear(e) {
33                    self.machine(e).request(1);
34                }
35                false
36            }
37            Callback::ClearExit0 => {
38                // `FUN_14011cd60`: remove the ClearArea work.
39                if self.sites[&e].work_id >= 0 && jobs.works.contains_key(&self.sites[&e].work_id) {
40                    self.remove_work(jobs, e);
41                }
42                true
43            }
44            // `FUN_14011cc40`: the ClearArea work goes.
45            Callback::ClearExit15 => {
46                let id = self.sites[&e].work_id;
47                if id >= 0 && jobs.works.contains_key(&id) {
48                    self.remove_work(jobs, e);
49                }
50                true
51            }
52            Callback::OrchardEnter2 => {
53                self.orchard_enter2(e);
54                false
55            }
56            Callback::OrchardUpdate2 => {
57                self.orchard_update2(e);
58                true
59            }
60            Callback::OrchardEnter9 => {
61                self.orchard_enter9(jobs, e);
62                true
63            }
64            Callback::OrchardUpdate9 => {
65                self.orchard_update9(jobs, e);
66                true
67            }
68            Callback::OrchardEnter10 => {
69                self.orchard_enter10(jobs, e);
70                true
71            }
72            Callback::OrchardUpdate10 => {
73                self.orchard_update10(jobs, e);
74                true
75            }
76            Callback::OrchardExitWork => {
77                self.orchard_exit_work(jobs, e);
78                true
79            }
80            Callback::OrchardEnter12 => {
81                self.orchard_enter12(jobs, e);
82                true
83            }
84            Callback::OrchardUpdate12 => {
85                self.orchard_update12(e);
86                true
87            }
88            Callback::OrchardEnter11 => {
89                self.orchard_enter11(jobs, e);
90                true
91            }
92            Callback::OrchardUpdate11 => {
93                self.orchard_update11(jobs, e);
94                true
95            }
96            Callback::OrchardEnter14 => {
97                self.orchard_enter14(jobs, e);
98                true
99            }
100            Callback::OrchardExit14 => {
101                self.orchard_exit14(jobs, e);
102                true
103            }
104            Callback::OrchardUpdate14 => {
105                self.orchard_update14(e);
106                true
107            }
108            Callback::CropEnter2 => {
109                self.crop_enter2(e);
110                false
111            }
112            Callback::CropUpdate2 => {
113                self.crop_update2(e);
114                true
115            }
116            Callback::CropEnter9 => {
117                self.crop_enter9(jobs, e);
118                true
119            }
120            Callback::CropUpdate9 => {
121                self.crop_update9(jobs, e);
122                true
123            }
124            Callback::CropEnter10 => {
125                self.crop_enter10(jobs, e);
126                true
127            }
128            Callback::CropExit10 => {
129                self.crop_exit10(jobs, e);
130                true
131            }
132            Callback::CropUpdate10 => {
133                self.crop_update10(e);
134                true
135            }
136            Callback::CropEnter11 => {
137                self.crop_enter11(jobs, e);
138                true
139            }
140            Callback::CropUpdate11 => {
141                self.crop_update11(jobs, e);
142                true
143            }
144            Callback::ClearEnter1 => {
145                // `FUN_14011ccf0`: the ClearArea stamps the terrain, then on to 2.
146                self.clear_area_stamp(e);
147                self.machine(e).request(2);
148                true
149            }
150            Callback::ClearEnter2 => {
151                // Entity event 1: the MapComponent stamps the footprint; the MultiObject passes
152                // it on to the parts (`FUN_14012a960`); the decal turns opaque.
153                self.stamp(e);
154                self.decal_opaque(e);
155                for part in self.sites[&e].parts.clone() {
156                    self.stamp(part);
157                    self.decal_opaque(part);
158                }
159                true
160            }
161            Callback::AbsorbUpdate0 => {
162                self.absorb_piles(jobs, e);
163                true
164            }
165            // The market's storage joins the town's markets (with its radius).
166            Callback::MarketEnter2 => {
167                self.storage_changes.push((e, true));
168                false
169            }
170            Callback::MarketEnter3 => {
171                self.storage_changes.push((e, false));
172                false
173            }
174            Callback::CemeteryEnter2 => {
175                self.cemetery_enter2(e);
176                false
177            }
178            Callback::CemeteryEnter3 => {
179                self.cemeteries.retain(|&c| c != e);
180                false
181            }
182            Callback::StorageEnter2 => {
183                self.storage_changes.push((e, true));
184                false
185            }
186            Callback::StorageEnter3 => {
187                self.storage_changes.push((e, false));
188                false
189            }
190            Callback::PastureEnter2 => {
191                self.pasture_enter2(e);
192                false
193            }
194            Callback::PastureUpdate2 => {
195                self.pasture_update2(jobs, e);
196                true
197            }
198            Callback::PastureEnter3 => {
199                self.pasture_leave(e);
200                false
201            }
202            Callback::WorkPlaceEnter0 => {
203                self.become_site(jobs, e);
204                false
205            }
206            Callback::WorkPlaceEnter2 => {
207                self.become_building(jobs, e);
208                false
209            }
210            // `FUN_140134230`: every worker leaves and the workplace leaves its profession's.
211            Callback::WorkPlaceEnter3 => {
212                self.refresh_demolishing();
213                let mut workers = self.workers();
214                self.workforce.clear_workers(&mut workers, e);
215                self.set_workers(&workers);
216                if let Some(p) = self.workforce.workplaces.get(&e).and_then(|w| w.profession) {
217                    self.workforce.unregister(e, p);
218                }
219                false
220            }
221            Callback::BuildEnter1 => {
222                // `BuildComponent::EnterGather`: the MultiObject's stage count, else 1; the
223                // ClearArea stamps the terrain.
224                let site = self.sites.get_mut(&e).unwrap();
225                site.stages = site.multi_stages.unwrap_or(1);
226                self.clear_area_stamp(e);
227                if !self.site_list.contains(&e) {
228                    self.site_list.push(e);
229                }
230                if !self.has_materials(e) {
231                    let site = &self.sites[&e];
232                    let t = &self.site_templates[site.template];
233                    let stored: Vec<i32> = t
234                        .materials
235                        .iter()
236                        .map(|&(_, m)| self.storages[site.storage].available(m))
237                        .collect();
238                    let gather = Gather {
239                        transit: vec![0; stored.len()],
240                        stored,
241                        workers: Default::default(),
242                    };
243                    let work = Work::new(
244                        e,
245                        self.rect_cell(e),
246                        self.professions.laborer,
247                        WorkKind::Gather(gather),
248                    );
249                    self.set_work(jobs, e, work, CONSTRUCTION_MASK, true);
250                    let site = &self.sites[&e];
251                    let t = &self.site_templates[site.template];
252                    let (mut capacity, mut accepts) = (0i32, 0u32);
253                    for &(n, m) in &t.materials {
254                        accepts |= self.materials[m].flags;
255                        capacity += n * self.materials[m].size * site.stages;
256                    }
257                    let s = &mut self.storages[site.storage];
258                    s.items.clear();
259                    s.capacity = capacity;
260                    s.accepts = accepts;
261                } else {
262                    self.machine(e).request(8);
263                }
264                true
265            }
266            Callback::BuildUpdate1 => {
267                if self.has_materials(e) {
268                    self.remove_work(jobs, e);
269                    self.machine(e).request(8);
270                }
271                true
272            }
273            Callback::BuildEnter8 => {
274                self.decal_opaque(e);
275                self.set_mesh(e, 0);
276                let work = Work::new(
277                    e,
278                    self.rect_cell(e),
279                    self.professions.builder,
280                    WorkKind::Build(BuildWork {
281                        occupied: vec![false; self.build_point_count(e)],
282                        ..Default::default()
283                    }),
284                );
285                self.set_work(jobs, e, work, CONSTRUCTION_MASK, true);
286                true
287            }
288            Callback::BuildUpdate8 => {
289                let site = &self.sites[&e];
290                let total = self.site_templates[site.template].work * site.stages;
291                let meshes = self.meshes(e);
292                if site.progress < total {
293                    let mut m = ((site.progress as f32 / total as f32) * meshes as f32) as i32;
294                    if m == meshes - 1 {
295                        m = meshes - 2;
296                    }
297                    self.set_mesh(e, m);
298                } else {
299                    self.remove_work(jobs, e);
300                    self.set_mesh(e, meshes - 1);
301                    self.machine(e).request(2);
302                }
303                true
304            }
305            Callback::BuildExit8 => {
306                self.site_list.retain(|&s| s != e);
307                // `FUN_14010cad0`: the build is no longer paused.
308                self.set_icon(e, crate::work::professions::icon::BUILD_PAUSED, false);
309                // Entity event 4 (`FUN_1400c8040`): the pasture's shelter points are cleared;
310                // a TerraformComponent digs, the entity's then its MultiObject parts'
311                // (`FUN_14012a960`).
312                if self.wildlife.pastures.contains_key(&e) {
313                    self.pasture_built(e);
314                }
315                let parts = self.sites[&e].parts.clone();
316                for x in std::iter::once(e).chain(parts) {
317                    if self.templates[self.placed[x].template].terraform.is_some() {
318                        self.terraform_requests
319                            .push(crate::world::terraform::Terraform::Dig(x));
320                    }
321                }
322                // A QuarryComponent digs its pit to the shallow floor (`FUN_140055a50`).
323                if let Some(q) = &self.templates[self.placed[e].template].quarry {
324                    self.terraform_requests
325                        .push(crate::world::terraform::Terraform::Quarry {
326                            entity: e,
327                            floor: q.shallow,
328                            claim: false,
329                        });
330                }
331                true
332            }
333            Callback::BuildFinish => {
334                self.finish_storage(e);
335                self.sites.get_mut(&e).unwrap().complete = true;
336                false
337            }
338            Callback::ResidenceEnter2 => {
339                // `FUN_14012f2b0`: register; the component's family slots come from the
340                // description (it keeps its state while out of the town's).
341                self.workforce.residences.push(e);
342                let site = self.sites.get_mut(&e).unwrap();
343                let kept = site.home.take();
344                let storage = site.storage;
345                if let Some(desc) = self.site_templates[site.template].residence {
346                    let home =
347                        kept.unwrap_or_else(|| crate::people::life::Home::new(desc, storage));
348                    self.homes.entry(e).or_insert(home);
349                }
350                false
351            }
352            Callback::ResidenceEnter3 => {
353                self.residence_leave(e);
354                false
355            }
356            Callback::DestroyEnter3 => {
357                self.destroy_enter3(jobs, e);
358                false
359            }
360            Callback::DestroyEnter4 => {
361                self.destroy_enter4(jobs, e);
362                false
363            }
364            Callback::DestroyUpdate4 => {
365                self.destroy_update4(jobs, e);
366                true
367            }
368            Callback::DestroyEnter5 => {
369                self.destroy_enter5(jobs, e);
370                true
371            }
372            Callback::DestroyUpdate5 => {
373                self.destroy_update5(jobs, e);
374                true
375            }
376            Callback::DestroyEnter6 => {
377                self.destroy_enter6(e);
378                false
379            }
380            Callback::DestroyUpdate6 => {
381                self.destroy_update6(e, self.site_dt);
382                true
383            }
384            Callback::DestroyExit6 => {
385                self.destroy_exit6(e);
386                true
387            }
388            Callback::ImmigrationEnter2 => {
389                self.immigration_idle(e);
390                false
391            }
392            Callback::ImmigrationUpdate2 => {
393                self.immigration_idle(e);
394                true
395            }
396            Callback::ImmigrationEnterF => {
397                self.immigration_wait(e);
398                false
399            }
400            Callback::ImmigrationUpdateF => {
401                self.immigration_waiting(e, self.site_dt);
402                true
403            }
404            Callback::ImmigrationEnter10 => {
405                self.immigration_call(e, true);
406                true
407            }
408            Callback::ImmigrationUpdate10 => {
409                self.immigration_call(e, false);
410                true
411            }
412            Callback::ImmigrationEnter11 => {
413                self.band_arrived(e);
414                true
415            }
416            Callback::ImmigrationUpdate11 => {
417                self.band_waiting(e);
418                true
419            }
420            Callback::ImmigrationLeave => {
421                self.send_band_away(e);
422                true
423            }
424            Callback::TradeEnter2 => {
425                self.trade_enter2(jobs, e);
426                false
427            }
428            Callback::TradeUpdate2 => {
429                self.trade_update2(e, self.site_dt);
430                false
431            }
432            Callback::TradeEnter12 => true,
433            Callback::TradeEnter13 => {
434                self.trade_enter13(e);
435                true
436            }
437            Callback::TradeExit13 => {
438                self.trade_exit13(e);
439                true
440            }
441            Callback::TradeUpdate13 => {
442                self.trade_update13(e, self.site_dt);
443                true
444            }
445            Callback::TradeLeave => {
446                self.trade_leave(e);
447                true
448            }
449            Callback::MultiObjectEnter2 => {
450                // `FUN_14012aa30`: each part's MapComponent activates (its minimap rectangle,
451                // drawn by the town from the placed parts) and its floor is added
452                // (`FUN_140124860`, which takes a floor already added out first): a bridge
453                // piece's again, a dock's or porch's for the first time.
454                let size = self.size;
455                for part in self.sites[&e].parts.clone() {
456                    let p = &self.placed[part];
457                    if let Some(mesh) = self.templates[p.template].floor.clone() {
458                        let floor = crate::world::floor::Floor::placed(&mesh, p);
459                        self.cells.floors.add(size, part, floor);
460                    }
461                }
462                false
463            }
464            Callback::WellEnter2 => {
465                self.well_join(e, true);
466                false
467            }
468            Callback::WellEnter3 => {
469                self.well_join(e, false);
470                false
471            }
472            Callback::AttendanceEnter2 => {
473                self.attendance_enter(jobs, e);
474                false
475            }
476            Callback::AttendanceExit2 => {
477                // `FUN_1401318c0(icon, 2, full)`.
478                let full = self.attendance_full(e);
479                self.set_icon(e, crate::work::professions::icon::LIMIT, full);
480                false
481            }
482            Callback::AttendanceEnter3 => {
483                self.remove_work(jobs, e);
484                self.attendance_leave(e);
485                false
486            }
487            Callback::HealerEnter2 => {
488                if !self.healers.contains(&e) {
489                    self.healers.push(e);
490                }
491                false
492            }
493            Callback::HappinessEnter => {
494                self.happiness_enter(e);
495                false
496            }
497            Callback::HappinessUpdate => {
498                self.happiness_update(e);
499                false
500            }
501            Callback::HappinessLeave => {
502                self.happiness_leave(e);
503                false
504            }
505            Callback::HealerEnter3 => {
506                self.healers.retain(|&x| x != e);
507                false
508            }
509            Callback::ProduceEnter2 => {
510                let off = self.sites[&e].produce.as_ref().is_some_and(|p| p.disabled);
511                self.machine(e).request(if off { 0xe } else { 0xd });
512                false
513            }
514            Callback::ProduceClearPoints => {
515                self.produce_clear_points(e);
516                false
517            }
518            Callback::ProduceEnterD => {
519                if self.sites[&e].produce.as_ref().is_some_and(|p| p.disabled) {
520                    self.machine(e).request(0xe);
521                    return true;
522                }
523                // `FUN_140134130(work, 1, 0, …)` with slot `+0x90`'s work, then
524                // `FUN_1401350d0(1)`.
525                let t = &self.site_templates[self.sites[&e].template];
526                let work = if t.herbalist.is_some() {
527                    self.herbalist_work(e)
528                } else if t.forester.is_some() {
529                    self.forester_work(e)
530                } else if t.fishing.is_some() {
531                    self.fishing_work(e)
532                } else if t.hunt.is_some() {
533                    self.hunt_work(e)
534                } else if t.market.is_some() {
535                    self.market_work(e)
536                } else {
537                    self.produce_work(e)
538                };
539                self.set_work(jobs, e, work, 1, false);
540                self.enable_workplace(e, true);
541                // Slot `+0x98`.
542                self.fishing_register(e);
543                false
544            }
545            Callback::ProduceEnterE => {
546                // `FUN_140134020` when registered, then `FUN_1401350d0(0)`.
547                let id = self.sites[&e].work_id;
548                if id >= 0 && jobs.works.contains_key(&id) {
549                    self.remove_work(jobs, e);
550                }
551                self.enable_workplace(e, false);
552                // Slot `+0xa0`.
553                self.fishing_unregister(e);
554                true
555            }
556            Callback::ProduceUpdateD => {
557                // Slot `+0xa8`.
558                self.fishing_water(e);
559                false
560            }
561            Callback::ProduceLeaveD => {
562                // Slot `+0xa0`.
563                self.fishing_unregister(e);
564                true
565            }
566            Callback::ClearResourceEnter2 => {
567                // `FUN_1400c8370` flags the entity (`+0x40` bit 10) and queues it for the entity
568                // manager's removal.
569                if let Some(site) = self.sites.get_mut(&e) {
570                    site.destroyed = true;
571                }
572                true
573            }
574        }
575    }
576}