1use banished_formats::game::CharacterDescription;
13
14use crate::behaviour::character::{Action, Character, MoveTo};
15use crate::scheduler::ticks;
16use crate::town::Sim;
17use crate::work::construction::Callbacks;
18
19const PERIOD: f32 = 300.0;
21const ARRIVED: i32 = 0xe;
23const MASK: u32 = 0xe07;
25
26#[derive(Debug, Clone, Default, PartialEq)]
30pub struct ImmigrationTemplate {
31 pub nomad: usize,
34 pub population: i32,
37 pub required: Vec<usize>,
40 pub share: (f32, f32),
43 pub wait: (f32, f32),
46 pub months: (i32, i32),
49 pub leave: i32,
52 pub margin: i32,
55 pub risk: f32,
58 pub risk_months: i32,
60}
61
62#[derive(Debug, Clone, Default, PartialEq, Eq)]
65pub struct Immigration {
66 pub timer: i64,
68 pub arrived: i32,
70 pub nomads: Vec<usize>,
72}
73
74#[derive(Debug, Clone)]
80pub struct NomadKind {
81 pub character: CharacterDescription,
83}
84
85pub struct Nomad {
89 pub template: usize,
91 pub character: Character,
93 pub state: i8,
95 pub town_hall: Option<usize>,
97 pub spawn: [f32; 3],
99 pub target: [f32; 3],
101 pub timer: i64,
103 pub variant: usize,
105}
106
107impl Sim<'_, '_> {
108 fn immigration_desc(&self, e: usize) -> Option<&ImmigrationTemplate> {
109 self.site_templates[self.sites.get(&e)?.template]
110 .immigration
111 .as_ref()
112 }
113
114 fn immigration(&mut self, e: usize) -> Option<&mut Immigration> {
115 self.sites.get_mut(&e)?.immigration.as_mut()
116 }
117
118 fn calendar_month(&self) -> i32 {
120 let m = (self.calendar / 3600.0 * 12.0) as i32;
121 if 11 < m {
122 0
123 } else {
124 m
125 }
126 }
127
128 fn immigration_wanted(&self, e: usize) -> bool {
131 let Some(d) = self.immigration_desc(e) else {
132 return false;
133 };
134 if self.population < d.population {
135 return false;
136 }
137 d.required.iter().all(|&t| {
138 self.sites.iter().any(|(&s, site)| {
139 !site.destroyed && self.placed[s].alive && self.placed[s].template == t
140 })
141 })
142 }
143
144 pub(crate) fn immigration_idle(&mut self, e: usize) {
146 if self.immigration_wanted(e) {
147 self.machine(e).request(0xf);
148 }
149 }
150
151 pub(crate) fn immigration_wait(&mut self, e: usize) {
153 let Some(d) = self.immigration_desc(e).cloned() else {
154 return;
155 };
156 let f = self.rng.rand_float();
157 let t = ticks((f * d.wait.1 + d.wait.0) * PERIOD);
158 if let Some(i) = self.immigration(e) {
159 i.timer = t;
160 }
161 }
162
163 pub(crate) fn immigration_waiting(&mut self, e: usize, dt: i64) {
166 let Some(d) = self.immigration_desc(e).cloned() else {
167 return;
168 };
169 let Some(i) = self.immigration(e) else { return };
170 i.timer -= dt;
171 let due = i.timer < 1;
172 let month = self.calendar_month();
173 if due && d.months.0 <= month && month <= d.months.1 {
174 let next = if self.immigration_wanted(e) { 0x10 } else { 2 };
175 self.machine(e).request(next);
176 }
177 }
178
179 pub(crate) fn immigration_call(&mut self, e: usize, enter: bool) {
182 if enter {
183 if let Some(i) = self.immigration(e) {
184 i.arrived = 0;
185 i.nomads.clear();
186 }
187 } else if !self.immigration(e).is_some_and(|i| i.nomads.is_empty()) {
188 return;
189 }
190 self.spawn_band(e);
191 }
192
193 fn edge(&mut self, d: &ImmigrationTemplate, x: i32, y: i32, target: i32) -> Option<i32> {
196 let n = self.size as i32;
197 for _ in 0..50 {
198 let cx = (x + self.rng.rand_tol(d.margin)).clamp(0, n - 1);
199 let cy = (y + self.rng.rand_tol(d.margin)).clamp(0, n - 1);
200 let cell = cy * n + cx;
201 let a = 1u32 << (self.cells.types[cell as usize].0 & 0x1f);
202 if a & MASK == 0 {
203 continue;
204 }
205 let b = 1u32 << (self.cells.types[target as usize].0 & 0x1f);
206 if (a | b) & 0x1e0 != 0 {
207 return Some(cell);
208 }
209 let (ra, rb) = (self.regions[cell as usize], self.regions[target as usize]);
210 if ra != 0 && ra == rb {
211 return Some(cell);
212 }
213 }
214 None
215 }
216
217 fn spawn_band(&mut self, e: usize) {
221 let Some(d) = self.immigration_desc(e).cloned() else {
222 return;
223 };
224 let n = self.size as i32;
225 let target = self.cell_of(self.use_point(e, 0).0);
226 let mut tries = 0;
227 let (x, y, start) = loop {
228 let side = self.rng.rand_below(4);
229 let (x, y) = if side < 2 {
230 let x = if side == 0 {
231 d.margin
232 } else {
233 n - d.margin - 1
234 };
235 (x, self.rng.rand_below(n as u32) as i32)
236 } else {
237 let x = self.rng.rand_below(n as u32) as i32;
238 (
239 x,
240 if side == 2 {
241 d.margin
242 } else {
243 n - d.margin - 1
244 },
245 )
246 };
247 if let Some(start) = self.edge(&d, x, y, target) {
248 break (x, y, start);
249 }
250 tries += 1;
251 if 9 < tries {
252 return;
253 }
254 };
255 let population = self.population;
256 let f = self.rng.rand_float();
257 let free: i32 = self
258 .workforce
259 .residences
260 .iter()
261 .filter_map(|r| self.homes.get(r))
262 .map(|h| h.families.iter().filter(|f| f.is_empty()).count() as i32)
263 .sum();
264 let mut count = self.rng.rand_below((free * 2) as u32) as i32
265 + (population as f32 * (f * (d.share.1 - d.share.0) + d.share.0)) as i32;
266 if count < 2 {
267 count = 2;
268 }
269 let points: usize = self
270 .interact_entities(e)
271 .iter()
272 .map(|&x| self.templates[self.placed[x].template].create_points.len())
273 .sum();
274 for _ in 0..count {
275 let cell = self.edge(&d, x, y, target).unwrap_or(start);
276 let k = self.rng.rand_below(points as u32) as usize;
277 let dest = self.create_point(e, k);
278 let ground = self.ground_at(cell);
279 if let Some(id) = self.add_nomad(d.nomad, e, ground, dest) {
280 if let Some(i) = self.immigration(e) {
281 i.nomads.push(id);
282 }
283 }
284 }
285 if let Some(i) = self.immigration(e) {
286 i.arrived = 0;
287 }
288 }
289
290 fn create_point(&self, e: usize, mut k: usize) -> [f32; 3] {
292 for x in self.interact_entities(e) {
293 let points = &self.templates[self.placed[x].template].create_points;
294 if k < points.len() {
295 return self.world_point(x, points[k]);
296 }
297 k -= points.len();
298 }
299 self.world_point(e, [0.0; 3])
300 }
301
302 fn add_nomad(
306 &mut self,
307 template: usize,
308 town_hall: usize,
309 spawn: [f32; 3],
310 target: [f32; 3],
311 ) -> Option<usize> {
312 let kind = self.wildlife.nomad_kinds.get(&template)?.clone();
313 let variant = self.rng.rand_below(2) as usize;
314 let yaw = self.rng.rand_float_tol() * 180.0;
315 let id = self.placed.len();
316 let t = &self.templates[template];
317 self.placed.push(crate::nature::seeding::Placed {
318 template,
319 bucket: t.bucket,
320 cell: (
321 self.cell_of(spawn) % self.size as i32,
322 self.cell_of(spawn) / self.size as i32,
323 ),
324 footprint: (1, 1),
325 position: spawn,
326 yaw,
327 variant,
328 mesh: usize::MAX,
329 growth: 0.0,
330 growth_state: None,
331 spawn_state: None,
332 alive: true,
333 removal_requested: false,
334 animation: None,
335 scale: [1.0; 4],
336 });
337 let mut character = Character::new(kind.character, spawn, 1.0);
338 character.yaw = yaw;
339 character.tree_slot = self.wildlife.tree.insert(
340 crate::behaviour::character_tree::Who::Nomad(id),
341 spawn,
342 kind.character.radius,
343 );
344 self.wildlife.nomads.insert(
345 id,
346 Nomad {
347 template,
348 character,
349 state: 0,
350 town_hall: Some(town_hall),
351 spawn,
352 target,
353 timer: 0,
354 variant,
355 },
356 );
357 self.nomad_walk(id, target);
358 *self.models_dirty = true;
359 Some(id)
360 }
361
362 fn nomad_walk(&mut self, id: usize, to: [f32; 3]) {
364 let Some(nomad) = self.wildlife.nomads.get(&id) else {
365 return;
366 };
367 let Some(group) = self
368 .wildlife
369 .groups
370 .get(&(nomad.template, nomad.variant))
371 .cloned()
372 else {
373 return;
374 };
375 let walk = MoveTo::new(to, 1, 0, 0xe1f, 0, -1.0, self.rng);
376 let ch = &mut self.wildlife.nomads.get_mut(&id).unwrap().character;
377 let mut stack = std::mem::take(&mut ch.stack);
378 stack.clear(ch, &group, &mut []);
379 stack.push(ch, &group, Action::move_to(walk));
380 ch.stack = stack;
381 }
382
383 fn remove_nomad(&mut self, id: usize) {
384 if let Some(nomad) = self.wildlife.nomads.remove(&id) {
385 self.wildlife.tree.remove(nomad.character.tree_slot);
386 self.placed[id].alive = false;
387 *self.models_dirty = true;
388 }
389 }
390
391 pub fn update_nomads(&mut self, dt: i64) {
396 let ids: Vec<usize> = self.wildlife.nomads.keys().copied().collect();
397 for id in ids {
398 if let Some(nomad) = self.wildlife.nomads.get_mut(&id) {
401 let following = self.wildlife.tree.follows(nomad.character.tree_slot);
402 nomad.character.stack.notify_moves(following);
403 }
404 let Some(nomad) = self.wildlife.nomads.get(&id) else {
405 continue;
406 };
407 if nomad.character.stack.is_active() {
408 continue;
409 }
410 match nomad.state {
411 0 => match nomad.town_hall.filter(|t| self.sites.contains_key(t)) {
412 None => self.remove_nomad(id),
413 Some(t) => {
414 self.immigration_message(t, 6);
415 if let Some(n) = self.wildlife.nomads.get_mut(&id) {
416 n.state = 1;
417 }
418 }
419 },
420 1 => self.nomad_wander(id, dt),
421 _ => self.remove_nomad(id),
422 }
423 }
424 }
425
426 fn nomad_wander(&mut self, id: usize, dt: i64) {
429 let n = self.size as i32;
430 let nomad = self.wildlife.nomads.get_mut(&id).unwrap();
431 nomad.timer -= dt;
432 if 0 <= nomad.timer {
433 return;
434 }
435 let target = nomad.target;
436 let t = self.rng.rand_float_tol() * 15.0 + 30.0;
437 self.wildlife.nomads.get_mut(&id).unwrap().timer = ticks(t);
438 let c = self.cell_of(target);
439 let (x0, y0) = (c % n - 8, c / n - 8);
440 let mut cells = Vec::new();
441 for y in y0..y0 + 16 {
442 for x in x0..x0 + 16 {
443 if 0 <= x
444 && x < n
445 && 0 <= y
446 && y < n
447 && self.cells.types[(y * n + x) as usize].0 < 3
448 {
449 cells.push(y * n + x);
450 }
451 }
452 }
453 if cells.is_empty() {
454 return;
455 }
456 let k = self.rng.rand_below(cells.len() as u32) as usize;
457 let dy = self.rng.rand_float_tol() * 0.33;
458 let dx = self.rng.rand_float_tol() * 0.33;
459 let g = self.ground_at(cells[k]);
460 self.nomad_walk(id, [g[0] + dx, g[1] + dy, g[2]]);
461 }
462
463 fn nomad_message(&mut self, id: usize, msg: i32) {
466 let Some(nomad) = self.wildlife.nomads.get_mut(&id) else {
467 return;
468 };
469 match (nomad.state, msg) {
470 (0 | 1, 1) => {
471 nomad.state = 2;
472 let spawn = nomad.spawn;
473 self.nomad_walk(id, spawn);
474 if let Some(n) = self.wildlife.nomads.get_mut(&id) {
475 n.town_hall = None;
476 }
477 }
478 (1, 0) => self.remove_nomad(id),
479 _ => {}
480 }
481 }
482
483 pub(crate) fn send_band_away(&mut self, e: usize) {
485 let band = self
486 .immigration(e)
487 .map(|i| std::mem::take(&mut i.nomads))
488 .unwrap_or_default();
489 for id in band {
490 self.nomad_message(id, 1);
491 }
492 if let Some(i) = self.immigration(e) {
493 i.arrived = 0;
494 }
495 }
496
497 pub(crate) fn band_arrived(&mut self, e: usize) {
499 self.site_events.push((ARRIVED, 2, e));
500 }
501
502 pub(crate) fn band_waiting(&mut self, e: usize) {
504 let Some(d) = self.immigration_desc(e) else {
505 return;
506 };
507 if d.leave <= self.calendar_month() {
508 self.send_band_away(e);
509 self.machine(e).request(2);
510 }
511 }
512
513 pub fn immigration_message(&mut self, e: usize, msg: i32) {
519 if self.immigration(e).is_none() || self.machine(e).index < 0 {
520 return;
521 }
522 match (self.machine(e).state, msg) {
523 (0x10, 6) => {
524 let i = self.immigration(e).unwrap();
525 i.arrived += 1;
526 if i.arrived == i.nomads.len() as i32 {
527 self.machine(e).request(0x11);
528 }
529 }
530 (0x11, 8) => {
531 self.send_band_away(e);
532 self.machine(e).request(2);
533 }
534 _ => {}
535 }
536 }
537
538 pub fn take_band(&mut self, e: usize) -> Option<Vec<(usize, [f32; 3])>> {
545 if self.machine(e).index < 0 || self.machine(e).state != 0x11 {
546 return None;
547 }
548 let d = self.immigration_desc(e)?.clone();
549 let band = self.immigration(e)?.nomads.clone();
550 if self.visitors.0 < d.risk {
551 self.visitors.0 = d.risk;
552 }
553 let time = ticks(d.risk_months as f32 * PERIOD);
554 if self.visitors.1 < time {
555 self.visitors.1 = time;
556 }
557 Some(
558 band.iter()
559 .filter_map(|&id| Some((id, self.wildlife.nomads.get(&id)?.character.position)))
560 .collect(),
561 )
562 }
563
564 pub fn band_taken(&mut self, e: usize) {
567 let band = self
568 .immigration(e)
569 .map(|i| std::mem::take(&mut i.nomads))
570 .unwrap_or_default();
571 for id in band {
572 self.nomad_message(id, 0);
573 }
574 if let Some(i) = self.immigration(e) {
575 i.arrived = 0;
576 }
577 self.machine(e).request(2);
578 }
579}