banished_game/production/
hunt.rs1use crate::behaviour::character::{Action, MoveTo};
13use crate::pathfind::WALKABLE;
14use crate::production::herbalist::HerbalistWork;
15use crate::scheduler::{seconds, ticks};
16use crate::town::Sim;
17use crate::work::jobs::{Work, WorkKind};
18
19impl Sim<'_, '_> {
20 pub(crate) fn hunt_work(&self, e: usize) -> Work {
22 let profession = self
23 .workforce
24 .workplaces
25 .get(&e)
26 .and_then(|w| w.profession)
27 .unwrap_or(self.professions.laborer);
28 Work::new(
29 e,
30 self.rect_cell(e),
31 profession,
32 WorkKind::Hunt(HerbalistWork::default()),
33 )
34 }
35
36 pub fn update_hunts(&mut self, visits: &[(usize, i64)]) {
41 for &(e, dt) in visits {
42 if !self.sites.get(&e).is_some_and(|s| s.hunt_time.is_some()) {
43 continue;
44 }
45 let workers = self
46 .workforce
47 .workplaces
48 .get(&e)
49 .map_or(0, |w| w.workers.len()) as f32;
50 let max = self.work_max(e) as f32;
51 if max <= 0.0 {
53 continue;
54 }
55 let add = ticks(seconds(dt) * (workers / max));
56 if let Some(t) = self.sites.get_mut(&e).and_then(|s| s.hunt_time.as_mut()) {
57 *t += add;
58 }
59 }
60 }
61
62 pub(crate) fn hunt_try(&mut self, work: &mut Work, c: usize) -> u8 {
64 let e = work.entity;
65 if self.states[c].carried.used(self.materials) >= 1 {
66 self.drop_off(c);
67 return 2;
68 }
69 let Some(site) = self.sites.get(&e) else {
70 return 0;
71 };
72 let t = &self.site_templates[site.template];
73 let Some(d) = t.hunt else { return 0 };
74 let radius = t.radius.unwrap_or(0);
75 let (count, limit) = self.stock[d.group.clamp(0, 20) as usize];
76 self.set_icon(e, crate::work::professions::icon::LIMIT, limit <= count);
77 if limit <= count {
78 return 1;
79 }
80 let WorkKind::Hunt(h) = &work.kind else {
81 return 0;
82 };
83 if self.work_max(e) < h.workers.len() as i32 {
84 return 1;
85 }
86 let n = self.size as i32;
87 let (cx, cy) = (work.cell % n, work.cell / n);
88 let (x, y) = loop {
89 let (dx, dy) = loop {
90 let dx = self.rng.rand_tol(radius);
91 let dy = self.rng.rand_tol(radius);
92 if dx * dx + dy * dy <= radius * radius {
93 break (dx, dy);
94 }
95 };
96 let (x, y) = (cx + dx, cy + dy);
98 if 0 <= y && y < n {
99 break (x, y);
100 }
101 };
102 let from = self.citizen_cell(c);
103 let entrance = self.entrance_cell(e);
104 let cell = n * y + x;
105 let walk = 0 <= cell
107 && cell < n * n
108 && entrance >= 0
109 && (1u32 << (self.cells.types[cell as usize].0 & 0x1f)) & 0x1e0 == 0
110 && self.geometry_rank(entrance, cell) <= radius * 2
111 && self.cells.objects[cell as usize].is_none()
112 && self.reachable(cell, from);
113 let point = walk.then(|| self.ground_at(cell));
114 self.hunt_sequence(c, work, e, point);
115 self.took_job(c, work);
116 if let WorkKind::Hunt(h) = &mut work.kind {
117 h.workers.insert(c);
118 }
119 2
120 }
121
122 fn hunt_sequence(&mut self, c: usize, work: &mut Work, lodge: usize, point: Option<[f32; 3]>) {
125 self.clear_citizen(c, Some(work));
126 self.on_empty_think(c);
127 self.push(
128 c,
129 Action::CompleteTask {
130 work: work.id,
131 param: 0,
132 },
133 );
134 let action = match point {
135 Some(p) => Action::move_to(MoveTo::new(p, 1, 0, WALKABLE, 0, -1.0, self.rng)),
136 None => self.perform(c, -1000.0, 8, 5.0, true),
137 };
138 self.push(c, action);
139 self.characters[c]
140 .stack
141 .attach(Action::HuntAnimal { lodge, done: false });
142 }
143}