1use super::*;
4
5impl Sim<'_, '_> {
6 pub(crate) fn enable_workplace(&mut self, e: usize, on: bool) {
8 let building = self.sites.get(&e).is_some_and(|s| {
11 let state = if s.machine.pending < 0 {
12 s.machine.state
13 } else {
14 s.machine.pending
15 } as u8;
16 state & 0xf6 == 0 && state != 9
17 });
18 self.with_search(|w, workers, search| w.set_enabled(workers, e, on, building, search));
19 }
20
21 pub(crate) fn set_icon(&mut self, e: usize, bit: u8, on: bool) {
23 if let Some(w) = self.workforce.workplaces.get_mut(&e) {
24 w.set_icon(bit, on);
25 }
26 }
27
28 pub(crate) fn is_clear(&self, entity: usize) -> bool {
32 let p = &self.placed[entity];
33 let n = self.size as i32;
34 let (x, y) = p.cell;
35 let (w, h) = p.footprint;
36 let cells =
37 || (y..y + h).flat_map(move |cy| (x..x + w).map(move |cx| (cy * n + cx) as usize));
38 let desc = self
39 .sites
40 .get(&entity)
41 .and_then(|s| self.site_templates[s.template].clear.clone());
42 let roads_clear = !desc.as_ref().is_some_and(|d| d.clear_cells)
44 || cells().all(|i| {
45 let t = self.cells.types[i].0;
46 t != 0
47 && t != 1
48 && self.cells.road_kind[i] == crate::work::road::NONE
49 && !self.pending_roads.contains_key(&(i as i32))
50 });
51 if !roads_clear {
52 return false;
53 }
54 match desc {
55 Some(d) if d.clear_resources && !d.clear_piles => cells().all(|i| {
57 self.cells.objects[i].is_none_or(|o| {
58 let t = &self.templates[self.placed[o].template];
59 t.natural.is_none()
60 || d.reference.as_deref().is_some_and(|f| {
61 !t.reference.eq_ignore_ascii_case(&f.replace('/', "\\"))
62 })
63 })
64 }),
65 _ => cells().all(|i| self.cells.objects[i].is_none()),
67 }
68 }
69
70 pub(crate) fn refresh_demolishing(&mut self) {
72 let ids: Vec<usize> = self.workforce.workplaces.keys().copied().collect();
73 for e in ids {
74 if self.sites.contains_key(&e) && self.is_demolishing(e) {
75 self.workforce.demolishing.insert(e);
76 } else {
77 self.workforce.demolishing.remove(&e);
78 }
79 }
80 }
81
82 pub(crate) fn become_site(&mut self, jobs: &mut Jobs, e: usize) {
85 self.refresh_demolishing();
86 let builder = self.professions.builder;
87 let mut workers = self.workers();
88 self.workforce.clear_workers(&mut workers, e);
89 let id = self.sites[&e].work_id;
90 if let Some(old) = self.workforce.workplaces.get(&e).and_then(|w| w.profession) {
91 jobs.change_profession(id, old, builder);
92 }
93 if let Some(w) = self.workforce.workplaces.get_mut(&e) {
94 w.profession = Some(builder);
95 self.workforce.register(e, builder);
96 let max = self.build_point_count(e) as i32;
97 self.set_workers(&workers);
99 self.with_search(|w, ws, search| w.set_max_workers(ws, e, max, search));
100 workers = self.workers();
101 let w = self.workforce.workplaces.get_mut(&e).unwrap();
104 w.enabled = true;
105 w.set_icon(crate::work::professions::icon::WORK_DISABLED, false);
106 w.set_icon(crate::work::professions::icon::BUILD_PAUSED, false);
107 }
108 self.set_workers(&workers);
109 }
110
111 pub(crate) fn become_building(&mut self, jobs: &mut Jobs, e: usize) {
113 let builder = self.professions.builder;
114 let mut workers = self.workers();
115 let template = self.sites[&e].template;
116 let id = self.sites[&e].work_id;
117 if let Some(w) = self.workforce.workplaces.get(&e) {
118 if w.profession == Some(builder) {
119 self.workforce.unregister(e, builder);
120 let desc = self.site_templates[template].profession;
121 let footprint = self.placed[e].footprint;
122 let max = self.site_templates[template].default_max_workers(footprint);
123 if let Some(p) = desc {
124 jobs.change_profession(id, builder, p);
125 }
126 let w = self.workforce.workplaces.get_mut(&e).unwrap();
127 w.max_workers = max;
128 w.profession = desc;
129 self.workforce.clear_workers(&mut workers, e);
130 }
131 if let Some(p) = self.workforce.workplaces[&e].profession {
132 self.workforce.register(e, p);
133 }
134 }
135 self.set_workers(&workers);
136 }
137
138 pub(crate) fn set_work(
140 &mut self,
141 jobs: &mut Jobs,
142 entity: usize,
143 work: Work,
144 mask: u32,
145 keep: bool,
146 ) {
147 self.remove_work(jobs, entity);
148 let keep = if keep {
149 self.sites[&entity].work_priority
150 } else {
151 None
152 };
153 let id = jobs.add(work, mask, keep);
154 let priority = jobs.works[&id].priority;
155 let site = self.sites.get_mut(&entity).unwrap();
156 site.work_id = id;
157 site.work_priority = Some(priority);
158 }
159
160 pub(crate) fn remove_work(&mut self, jobs: &mut Jobs, entity: usize) {
162 let id = self.sites[&entity].work_id;
163 if let Some(w) = jobs.works.get(&id) {
164 self.sites.get_mut(&entity).unwrap().work_priority = Some(w.priority);
165 jobs.remove(id, self);
166 self.set_icon(entity, crate::work::professions::icon::LIMIT, false);
167 self.set_icon(entity, crate::work::professions::icon::NO_WORK, false);
168 }
169 }
170
171 pub(crate) fn clear_area_stamp(&mut self, entity: usize) {
175 let Some(site) = self.sites.get(&entity) else {
176 return;
177 };
178 if !self.site_templates[site.template]
179 .clear
180 .as_ref()
181 .is_some_and(|c| c.stamp)
182 {
183 return;
184 }
185 let p = &self.placed[entity];
186 let (x, y) = p.cell;
187 let (w, h) = p.footprint;
188 self.flatten_requests.push([x, y, x + w, y + h]);
189 }
190
191 pub(crate) fn quarry_message(&mut self, e: usize) {
195 let Some(q) = &self.templates[self.placed[e].template].quarry else {
196 return;
197 };
198 let Some(site) = self.sites.get(&e) else {
199 return;
200 };
201 let produced = site.produce.as_ref().map_or(0, |p| p.produced);
202 let lifetime = self.site_templates[site.template]
203 .produce
204 .as_ref()
205 .map_or(1, |p| p.lifetime);
206 let t = produced as f32 / lifetime as f32;
207 let floor = (1.0 - t) * q.shallow + t * q.deep;
208 self.terraform_requests
209 .push(crate::world::terraform::Terraform::Quarry {
210 entity: e,
211 floor,
212 claim: true,
213 });
214 }
215
216 pub(crate) fn decal_opaque(&mut self, entity: usize) {
218 let d = self.decals.entry(entity).or_default();
219 if !d.opaque {
220 d.opaque = true;
221 *self.models_dirty = true;
222 }
223 }
224
225 pub(super) fn set_mesh(&mut self, entity: usize, mesh: i32) {
226 self.placed[entity].mesh = if mesh < 0 { usize::MAX } else { mesh as usize };
227 }
228
229 pub(super) fn meshes(&self, entity: usize) -> i32 {
230 let site = &self.sites[&entity];
231 let v = self.placed[entity].variant;
232 self.site_templates[site.template]
233 .meshes
234 .get(v)
235 .copied()
236 .unwrap_or(1) as i32
237 }
238
239 pub(super) fn finish_storage(&mut self, entity: usize) {
242 let site = &self.sites[&entity];
243 let items: Vec<(usize, i32)> = self.storages[site.storage]
244 .items
245 .iter()
246 .map(|i| (i.material, i.count))
247 .collect();
248 for (m, n) in items {
249 self.count_used(m, n);
250 }
251 let site = &self.sites[&entity];
252 let (w, h) = self.placed[entity].footprint;
253 let s = &mut self.storages[site.storage];
254 s.items.clear();
255 if let Some((accepts, capacity, per_cell)) = self.site_templates[site.template].storage {
256 s.accepts = accepts;
257 s.capacity = capacity * if per_cell { w * h } else { 1 };
258 } else {
259 s.accepts = 0;
260 s.capacity = 0;
261 }
262 }
263
264 pub(crate) fn stamp(&mut self, entity: usize) {
266 let p = &self.placed[entity];
267 let Some(map) = &self.templates[p.template].map else {
268 return;
269 };
270 if map.no_stamp {
271 return;
272 }
273 let rules = PlacementRules {
274 mask: 0,
275 masks: Vec::new(),
276 mask_chars: String::new(),
277 footprint: map.footprint.clone(),
278 width: 0,
279 height: 0,
280 cell_type: map.cell_type as u8,
281 };
282 let rect = Rect {
283 x0: p.cell.0,
284 y0: p.cell.1,
285 x1: p.cell.0 + p.footprint.0 - 1,
286 y1: p.cell.1 + p.footprint.1 - 1,
287 };
288 let degrees = p.yaw as i32;
289 crate::world::placement::stamp(self.cells, &rules, rect, degrees, entity);
290 }
291
292 pub(crate) fn unstamp(&mut self, entity: usize) {
296 let p = &self.placed[entity];
297 let Some(map) = &self.templates[p.template].map else {
298 return;
299 };
300 let n = self.size as i32;
301 let (x0, y0) = p.cell;
302 let (w, h) = p.footprint;
303 let fp = map.footprint.as_bytes();
304 let degrees = p.yaw as i32;
305 for y in y0..y0 + h {
306 for x in x0..x0 + w {
307 let i = (n * y + x) as usize;
308 let stamped = fp.is_empty() || {
309 let c = fp
310 .get(crate::world::placement::footprint_index(
311 x - x0,
312 y - y0,
313 w,
314 h,
315 degrees,
316 ))
317 .copied()
318 .unwrap_or(b'.');
319 c == b'#' || c == b'_'
320 };
321 if stamped {
322 let mut ty = self.terrain.natural_cell_type(self.size, i);
323 let water = (1u32 << (ty.0 & 0x1f)) & 0x1e0 != 0;
324 if water && self.cells.occupants[i].is_some_and(|o| o != entity) {
325 ty = crate::terrain::CellType(2);
326 }
327 self.cells.set_type(i, ty);
328 }
329 self.cells.objects[i] = None;
330 }
331 }
332 }
333
334 pub fn activate(&mut self, jobs: &mut Jobs, entity: usize) {
338 self.trade_activate(entity);
339 run(self, jobs, entity, false, false);
340 }
341
342 pub fn update_sites(&mut self, jobs: &mut Jobs, dt: i64, visits: &[(usize, i64)]) {
348 let ids: Vec<usize> = self.sites.keys().copied().collect();
349 self.refresh_demolishing();
350 self.site_dt = dt;
351 for e in &ids {
352 run(self, jobs, *e, false, true);
353 }
354 for &(e, elapsed) in visits {
355 if self.sites.get(&e).is_some_and(|s| !s.destroyed) {
356 self.site_dt = elapsed;
357 run(self, jobs, e, true, true);
358 }
359 }
360 self.site_dt = dt;
361 let destroyed: Vec<usize> = self
362 .sites
363 .iter()
364 .filter(|(_, s)| s.destroyed)
365 .map(|(&e, _)| e)
366 .collect();
367 for e in destroyed {
368 self.destroy_entity(jobs, e);
369 }
370 }
371
372 pub fn destroy_entity(&mut self, jobs: &mut Jobs, e: usize) {
378 if !self.sites.contains_key(&e) {
379 return;
380 }
381 self.remove_work(jobs, e);
382 self.happiness_leave(e);
384 if self.workforce.workplaces.contains_key(&e) {
387 let mut workers = self.workers();
388 self.workforce.clear_workers(&mut workers, e);
389 self.set_workers(&workers);
390 if let Some(p) = self.workforce.workplaces.get(&e).and_then(|w| w.profession) {
391 self.workforce.unregister(e, p);
392 }
393 self.workforce.workplaces.remove(&e);
394 }
395 if self.homes.contains_key(&e) {
396 self.residence_leave(e);
397 }
398 if self.wildlife.pastures.contains_key(&e) {
399 self.pasture_leave(e);
400 self.wildlife.pastures.remove(&e);
401 }
402 for part in self.sites[&e].parts.clone() {
405 self.unstamp(part);
406 let size = self.size;
407 self.cells.floors.remove(size, part);
408 self.placed[part].alive = false;
409 }
410 let parts = self.sites[&e].parts.clone();
412 for x in std::iter::once(e).chain(parts) {
413 if self.templates[self.placed[x].template].terraform.is_some() {
414 self.terraform_requests
415 .push(crate::world::terraform::Terraform::Undo(x));
416 }
417 }
418 self.sites.remove(&e);
419 self.decals.remove(&e);
420 self.site_list.retain(|&s| s != e);
421 let n = self.size as i32;
422 let p = &mut self.placed[e];
423 p.alive = false;
424 let (x, y) = p.cell;
425 let (w, h) = p.footprint;
426 for cy in y..y + h {
427 for cx in x..x + w {
428 let i = (cy * n + cx) as usize;
429 if self.cells.occupants[i] == Some(e) {
430 self.cells.occupants[i] = None;
431 }
432 }
433 }
434 *self.models_dirty = true;
435 }
436}