1use crate::{
2 ai::Action,
3 data::{Reports, Sentiments, quest::Quest},
4 generate::name,
5};
6pub use common::rtsim::{ActorId, Profession};
7use common::{
8 character::CharacterId,
9 comp::{self, agent::FlightMode, item::ItemDef},
10 grid::Grid,
11 map::Marker,
12 resources::{Time, TimeOfDay},
13 rtsim::{
14 Dialogue, DialogueId, DialogueKind, FactionId, NpcAction, NpcActivity, NpcInput, NpcMsg,
15 Personality, QuestId, ReportId, Response, Role, SiteId, TerrainResource,
16 },
17 store::Id,
18 terrain::CoordinateConversions,
19 time::DayPeriod,
20 util::Dir,
21};
22use hashbrown::{HashMap, HashSet};
23use rand::prelude::*;
24use serde::{Deserialize, Serialize};
25use slotmap::DenseSlotMap;
26use std::{
27 collections::VecDeque,
28 ops::{Deref, DerefMut},
29 sync::{
30 Arc,
31 atomic::{AtomicU32, Ordering},
32 },
33};
34use tracing::error;
35use vek::*;
36use world::{
37 civ::{Track, airship_travel::AirshipFlightPhase},
38 site::Site as WorldSite,
39 util::{LOCALITY, RandomPerm},
40};
41
42#[derive(Copy, Clone, Debug, Default)]
43pub enum SimulationMode {
44 #[default]
46 Simulated,
47 Loaded,
49}
50
51#[derive(Clone)]
52pub struct PathData<P, N> {
53 pub end: N,
54 pub path: VecDeque<P>,
55 pub repoll: bool,
56}
57
58#[derive(Clone, Default)]
59pub struct PathingMemory {
60 pub intrasite_path: Option<(PathData<Vec2<i32>, Vec2<i32>>, Id<WorldSite>)>,
61 pub intersite_path: Option<(PathData<(Id<Track>, bool), SiteId>, usize)>,
62}
63
64#[derive(Default)]
65pub struct Controller {
66 pub actions: Vec<NpcAction>,
67 pub activity: Option<NpcActivity>,
68 pub new_home: Option<Option<SiteId>>,
69 pub look_dir: Option<Dir>,
70 pub job: Option<Job>,
71 pub quests_to_create: Vec<(QuestId, Quest)>,
72
73 pub current_airship_pilot_leg: Option<(usize, AirshipFlightPhase)>,
83}
84
85impl Controller {
86 pub fn reset(&mut self, npc: &Npc) {
89 self.activity = None;
90 self.look_dir = None;
91 self.job = npc.job.clone();
92 }
93
94 pub fn do_idle(&mut self) { self.activity = None; }
95
96 pub fn do_talk(&mut self, tgt: ActorId) { self.activity = Some(NpcActivity::Talk(tgt)); }
97
98 pub fn do_goto(&mut self, wpos: Vec3<f32>, speed_factor: f32) {
99 self.activity = Some(NpcActivity::Goto(wpos, speed_factor));
100 }
101
102 pub fn do_goto_with_height_and_dir(
104 &mut self,
105 wpos: Vec3<f32>,
106 speed_factor: f32,
107 height: Option<f32>,
108 dir: Option<Dir>,
109 flight_mode: FlightMode,
110 ) {
111 self.activity = Some(NpcActivity::GotoFlying(
112 wpos,
113 speed_factor,
114 height,
115 dir,
116 flight_mode,
117 ));
118 }
119
120 pub fn do_gather(&mut self, resources: &'static [TerrainResource]) {
121 self.activity = Some(NpcActivity::Gather(resources));
122 }
123
124 pub fn do_hunt_animals(&mut self) { self.activity = Some(NpcActivity::HuntAnimals); }
125
126 pub fn do_dance(&mut self, dir: Option<Dir>) { self.activity = Some(NpcActivity::Dance(dir)); }
127
128 pub fn do_cheer(&mut self, dir: Option<Dir>) { self.activity = Some(NpcActivity::Cheer(dir)); }
129
130 pub fn do_sit(&mut self, dir: Option<Dir>, pos: Option<Vec3<i32>>) {
131 self.activity = Some(NpcActivity::Sit(dir, pos));
132 }
133
134 pub fn say(&mut self, target: impl Into<Option<ActorId>>, content: comp::Content) {
135 self.actions.push(NpcAction::Say(target.into(), content));
136 }
137
138 pub fn attack(&mut self, target: ActorId) { self.actions.push(NpcAction::Attack(target)); }
139
140 pub fn set_new_home(&mut self, new_home: impl Into<Option<SiteId>>) {
141 self.new_home = Some(new_home.into());
142 }
143
144 pub fn set_newly_hired(&mut self, actor: ActorId, expires: Time) {
145 self.job = Some(Job::Hired(actor, expires));
146 }
147
148 pub fn end_hiring(&mut self) {
149 if matches!(self.job, Some(Job::Hired(..))) {
150 self.job = None;
151 }
152 }
153
154 pub fn end_quest(&mut self) {
155 if matches!(self.job, Some(Job::Quest(..))) {
156 self.job = None;
157 }
158 }
159
160 pub fn send_msg(&mut self, to: ActorId, msg: NpcMsg) {
161 self.actions.push(NpcAction::Msg { to, msg });
162 }
163
164 pub fn dialogue_start(&mut self, target: ActorId) -> DialogueSession {
166 let session = DialogueSession {
167 target,
168 id: DialogueId(rand::rng().random()),
169 };
170
171 self.actions.push(NpcAction::Dialogue(target, Dialogue {
172 id: session.id,
173 kind: DialogueKind::Start,
174 }));
175
176 session
177 }
178
179 pub fn dialogue_end(&mut self, session: DialogueSession) {
181 self.actions
182 .push(NpcAction::Dialogue(session.target, Dialogue {
183 id: session.id,
184 kind: DialogueKind::End,
185 }));
186 }
187
188 pub fn dialogue_response(
189 &mut self,
190 session: DialogueSession,
191 tag: u32,
192 response: &(u16, Response),
193 ) {
194 self.actions
195 .push(NpcAction::Dialogue(session.target, Dialogue {
196 id: session.id,
197 kind: DialogueKind::Response {
198 tag,
199 response: response.1.clone(),
200 response_id: response.0,
201 },
202 }));
203 }
204
205 fn new_dialogue_tag(&self) -> u32 {
206 static TAG_COUNTER: AtomicU32 = AtomicU32::new(0);
207 TAG_COUNTER.fetch_add(1, Ordering::Relaxed)
208 }
209
210 pub fn dialogue_question(
213 &mut self,
214 session: DialogueSession,
215 msg: comp::Content,
216 responses: impl IntoIterator<Item = (u16, Response)>,
217 ) -> u32 {
218 let tag = self.new_dialogue_tag();
219
220 self.actions
221 .push(NpcAction::Dialogue(session.target, Dialogue {
222 id: session.id,
223 kind: DialogueKind::Question {
224 tag,
225 msg,
226 responses: responses.into_iter().collect(),
227 },
228 }));
229
230 tag
231 }
232
233 pub fn dialogue_statement(
236 &mut self,
237 session: DialogueSession,
238 msg: comp::Content,
239 given_item: Option<(Arc<ItemDef>, u32)>,
240 ) -> u32 {
241 let tag = self.new_dialogue_tag();
242
243 self.actions
244 .push(NpcAction::Dialogue(session.target, Dialogue {
245 id: session.id,
246 kind: DialogueKind::Statement {
247 msg,
248 given_item,
249 tag,
250 },
251 }));
252
253 tag
254 }
255
256 pub fn dialogue_marker(&mut self, session: DialogueSession, marker: Marker) {
258 self.actions
259 .push(NpcAction::Dialogue(session.target, Dialogue {
260 id: session.id,
261 kind: DialogueKind::Marker(marker),
262 }));
263 }
264}
265
266#[derive(Copy, Clone)]
268pub struct DialogueSession {
269 pub target: ActorId,
270 pub id: DialogueId,
271}
272
273pub struct Brain {
274 pub action: Box<dyn Action<(), !>>,
275}
276
277#[derive(Serialize, Deserialize)]
278pub struct Npc {
279 pub known_reports: HashSet<ReportId>,
281
282 #[serde(default)]
283 pub personality: Personality,
284 #[serde(default)]
285 pub sentiments: Sentiments,
286
287 #[serde(default)]
288 pub job: Option<Job>,
289
290 #[serde(skip)]
291 pub controller: Controller,
292 #[serde(skip)]
293 pub inbox: VecDeque<NpcInput>,
294
295 #[serde(skip)]
296 pub brain: Option<Brain>,
297}
298
299#[derive(Serialize, Deserialize)]
300pub struct Character {
301 pub id: CharacterId,
302 pub last_present_at: Option<u64>,
305}
306
307#[allow(clippy::large_enum_variant)]
308#[derive(Serialize, Deserialize)]
309pub enum ActorKind {
310 Npc(Npc),
311 Character(Character),
312}
313
314#[derive(Serialize, Deserialize)]
315pub struct Actor {
316 pub kind: ActorKind,
317
318 pub seed: u32,
320 pub wpos: Vec3<f32>,
322 pub dir: Vec2<f32>,
323
324 pub body: comp::Body,
325 pub role: Role,
326 pub home: Option<SiteId>,
327 pub faction: Option<FactionId>,
328 pub presence: Option<Presence>,
329
330 #[serde(skip)]
332 pub chunk_pos: Option<Vec2<i32>>,
333 #[serde(skip)]
334 pub current_site: Option<SiteId>,
335
336 #[serde(skip)]
341 pub mode: SimulationMode,
342}
343
344#[derive(Clone, Serialize, Deserialize, PartialEq)]
348pub enum Job {
349 Hired(ActorId, Time),
352 Quest(QuestId),
354}
355
356#[derive(Clone, Serialize, Deserialize)]
357pub struct Presence {
358 pub health_fraction: f32,
360}
361
362impl Clone for Actor {
363 fn clone(&self) -> Self {
364 Self {
365 kind: match &self.kind {
366 ActorKind::Npc(npc) => ActorKind::Npc(Npc {
367 known_reports: npc.known_reports.clone(),
368 personality: npc.personality,
369 sentiments: npc.sentiments.clone(),
370 job: npc.job.clone(),
371 controller: Default::default(),
372 inbox: Default::default(),
373 brain: Default::default(),
374 }),
375 ActorKind::Character(c) => ActorKind::Character(Character {
376 id: c.id,
377 last_present_at: c.last_present_at,
378 }),
379 },
380 seed: self.seed,
381 wpos: self.wpos,
382 dir: self.dir,
383 role: self.role.clone(),
384 home: self.home,
385 faction: self.faction,
386 presence: self.presence.clone(),
387 body: self.body,
388 chunk_pos: None,
390 current_site: Default::default(),
391 mode: Default::default(),
392 }
393 }
394}
395
396impl Actor {
397 pub const PERM_ENTITY_CONFIG: u32 = 1;
398 const PERM_NAME: u32 = 0;
399 const PERM_TIME: u32 = 2;
400
401 pub fn new_npc(seed: u32, wpos: Vec3<f32>, body: comp::Body, role: Role) -> Self {
402 Self {
403 kind: ActorKind::Npc(Npc {
404 personality: Default::default(),
405 sentiments: Default::default(),
406 job: None,
407 known_reports: Default::default(),
408 controller: Default::default(),
409 inbox: Default::default(),
410 brain: None,
411 }),
412 seed,
413 wpos,
414 dir: Vec2::unit_x(),
415 body,
416 role,
417 home: None,
418 faction: None,
419 presence: Some(Presence {
420 health_fraction: 1.0,
421 }),
422 chunk_pos: None,
423 current_site: None,
424 mode: SimulationMode::Simulated,
425 }
426 }
427
428 pub fn npc(&self) -> Option<&Npc> {
429 match &self.kind {
430 ActorKind::Npc(npc) => Some(npc),
431 _ => None,
432 }
433 }
434
435 pub fn npc_mut(&mut self) -> Option<&mut Npc> {
436 match &mut self.kind {
437 ActorKind::Npc(npc) => Some(npc),
438 _ => None,
439 }
440 }
441
442 pub fn new_character(
443 id: CharacterId,
444 seed: u32,
445 wpos: Vec3<f32>,
446 body: comp::Body,
447 mode: SimulationMode,
448 ) -> Self {
449 Self {
450 kind: ActorKind::Character(Character {
451 id,
452 last_present_at: None,
453 }),
454 seed,
455 wpos,
456 dir: Vec2::unit_x(),
457 body,
458 role: Role::Civilised(None),
459 home: None,
460 faction: None,
461 presence: None,
463 chunk_pos: None,
464 current_site: None,
465 mode,
466 }
467 }
468
469 pub fn character(&self) -> Option<&Character> {
470 match &self.kind {
471 ActorKind::Character(character) => Some(character),
472 _ => None,
473 }
474 }
475
476 pub fn character_mut(&mut self) -> Option<&mut Character> {
477 match &mut self.kind {
478 ActorKind::Character(character) => Some(character),
479 _ => None,
480 }
481 }
482
483 pub fn is_present_and_alive(&self) -> bool {
484 self.presence
485 .as_ref()
486 .map_or(false, |p| p.health_fraction > 0.0)
487 }
488
489 pub fn is_present_and_dead(&self) -> bool {
490 self.presence
491 .as_ref()
492 .map_or(false, |p| p.health_fraction <= 0.0)
493 }
494
495 pub fn with_personality(mut self, personality: Personality) -> Self {
497 if let ActorKind::Npc(npc) = &mut self.kind {
498 npc.personality = personality;
499 } else {
500 panic!("Cannot set personality for non-NPC");
501 }
502 self
503 }
504
505 pub fn with_home(mut self, home: impl Into<Option<SiteId>>) -> Self {
517 self.home = home.into();
518 self
519 }
520
521 pub fn with_faction(mut self, faction: impl Into<Option<FactionId>>) -> Self {
523 self.faction = faction.into();
524 self
525 }
526
527 pub fn rng(&self, perm: u32) -> impl Rng + use<> {
528 RandomPerm::new(self.seed.wrapping_add(perm))
529 }
530
531 pub fn get_name(&self) -> Option<String> {
534 if let comp::Body::Humanoid(_) = &self.body {
535 Some(name::generate_npc(&mut self.rng(Self::PERM_NAME)))
536 } else {
537 None
538 }
539 }
540
541 pub fn get_day_period(&self, time_of_day: TimeOfDay) -> DayPeriod {
542 let offset = self.rng(Self::PERM_TIME).random_range(-3600.0..3600.0);
543 DayPeriod::from(time_of_day.0 + offset)
544 }
545
546 pub fn profession(&self) -> Option<Profession> {
547 match &self.role {
548 Role::Civilised(profession) => *profession,
549 Role::Monster | Role::Wild | Role::Vehicle => None,
550 }
551 }
552
553 pub fn hired(&self) -> Option<(ActorId, Time)> {
554 if let Some(Job::Hired(actor, time)) = self.npc()?.job {
555 Some((actor, time))
556 } else {
557 None
558 }
559 }
560
561 pub fn cleanup(&mut self, reports: &Reports) {
562 if let ActorKind::Npc(npc) = &mut self.kind {
563 npc.sentiments
567 .cleanup(crate::data::sentiment::NPC_MAX_SENTIMENTS);
568 npc.known_reports
570 .retain(|report| reports.contains_key(*report));
571 }
574 }
575}
576
577#[derive(Default, Clone, Serialize, Deserialize)]
578pub struct GridCell {
579 pub actors: Vec<ActorId>,
580}
581
582#[derive(Clone, Serialize, Deserialize, Debug)]
583pub struct ActorLink {
584 pub mount: ActorId,
585 pub rider: ActorId,
586 pub is_steering: bool,
587}
588
589#[derive(Clone, Default, Serialize, Deserialize)]
590struct Riders {
591 steerer: Option<MountId>,
592 riders: Vec<MountId>,
593}
594
595#[derive(Clone, Default, Serialize, Deserialize)]
596#[serde(
597 from = "DenseSlotMap<MountId, ActorLink>",
598 into = "DenseSlotMap<MountId, ActorLink>"
599)]
600pub struct ActorLinks {
601 links: DenseSlotMap<MountId, ActorLink>,
602 mount_map: slotmap::SecondaryMap<ActorId, Riders>,
603 rider_map: HashMap<ActorId, MountId>,
604}
605
606impl ActorLinks {
607 pub fn remove_mount(&mut self, mount: ActorId) {
608 if let Some(riders) = self.mount_map.remove(mount) {
609 for link in riders
610 .riders
611 .into_iter()
612 .chain(riders.steerer)
613 .filter_map(|link_id| self.links.get(link_id))
614 {
615 self.rider_map.remove(&link.rider);
616 }
617 }
618 }
619
620 fn remove_rider(&mut self, id: MountId, link: &ActorLink) {
622 if let Some(riders) = self.mount_map.get_mut(link.mount) {
623 if link.is_steering && riders.steerer == Some(id) {
624 riders.steerer = None;
625 } else if let Some((i, _)) = riders.riders.iter().enumerate().find(|(_, i)| **i == id) {
626 riders.riders.remove(i);
627 }
628
629 if riders.steerer.is_none() && riders.riders.is_empty() {
630 self.mount_map.remove(link.mount);
631 }
632 }
633 }
634
635 pub fn remove_link(&mut self, link_id: MountId) {
636 if let Some(link) = self.links.remove(link_id) {
637 self.rider_map.remove(&link.rider);
638 self.remove_rider(link_id, &link);
639 }
640 }
641
642 pub fn dismount(&mut self, rider: ActorId) {
643 if let Some(id) = self.rider_map.remove(&rider)
644 && let Some(link) = self.links.remove(id)
645 {
646 self.remove_rider(id, &link);
647 }
648 }
649
650 pub fn add_mounting(
653 &mut self,
654 mount: ActorId,
655 rider: ActorId,
656 steering: bool,
657 ) -> Result<MountId, MountingError> {
658 if mount == rider {
659 return Err(MountingError::MountSelf);
660 }
661 if self.mount_map.contains_key(rider) {
662 return Err(MountingError::RiderIsMounted);
663 }
664 if self.rider_map.contains_key(&mount) {
665 return Err(MountingError::MountIsRiding);
666 }
667 if let Some(mount_entry) = self.mount_map.entry(mount) {
668 if let hashbrown::hash_map::Entry::Vacant(rider_entry) = self.rider_map.entry(rider) {
669 let riders = mount_entry.or_insert(Riders::default());
670
671 if steering {
672 if riders.steerer.is_none() {
673 let id = self.links.insert(ActorLink {
674 mount,
675 rider,
676 is_steering: true,
677 });
678 riders.steerer = Some(id);
679 rider_entry.insert(id);
680 Ok(id)
681 } else {
682 Err(MountingError::HasSteerer)
683 }
684 } else {
685 let id = self.links.insert(ActorLink {
687 mount,
688 rider,
689 is_steering: false,
690 });
691 riders.riders.push(id);
692 rider_entry.insert(id);
693 Ok(id)
694 }
695 } else {
696 Err(MountingError::AlreadyRiding)
697 }
698 } else {
699 Err(MountingError::MountDead)
700 }
701 }
702
703 pub fn steer(&mut self, mount: ActorId, rider: ActorId) -> Result<MountId, MountingError> {
704 self.add_mounting(mount, rider, true)
705 }
706
707 pub fn ride(&mut self, mount: ActorId, rider: ActorId) -> Result<MountId, MountingError> {
708 self.add_mounting(mount, rider, false)
709 }
710
711 pub fn get_mount_link(&self, rider: ActorId) -> Option<&ActorLink> {
712 self.rider_map
713 .get(&rider)
714 .and_then(|link| self.links.get(*link))
715 }
716
717 pub fn get_steerer_link(&self, mount: ActorId) -> Option<&ActorLink> {
718 self.mount_map
719 .get(mount)
720 .and_then(|mount| self.links.get(mount.steerer?))
721 }
722
723 pub fn get(&self, id: MountId) -> Option<&ActorLink> { self.links.get(id) }
724
725 pub fn ids(&self) -> impl Iterator<Item = MountId> + '_ { self.links.keys() }
726
727 pub fn iter(&self) -> impl Iterator<Item = &ActorLink> + '_ { self.links.values() }
728
729 pub fn iter_mounts(&self) -> impl Iterator<Item = ActorId> + '_ { self.mount_map.keys() }
730}
731
732impl From<DenseSlotMap<MountId, ActorLink>> for ActorLinks {
733 fn from(mut value: DenseSlotMap<MountId, ActorLink>) -> Self {
734 let mut from_map = slotmap::SecondaryMap::new();
735 let mut to_map = HashMap::with_capacity(value.len());
736 let mut delete = Vec::new();
737 for (id, link) in value.iter() {
738 if let Some(entry) = from_map.entry(link.mount) {
739 let riders = entry.or_insert(Riders::default());
740 if link.is_steering {
741 if let Some(old) = riders.steerer.replace(id) {
742 error!("Replaced steerer {old:?} with {id:?}");
743 }
744 } else {
745 riders.riders.push(id);
746 }
747 } else {
748 delete.push(id);
749 }
750 to_map.insert(link.rider, id);
751 }
752 for id in delete {
753 value.remove(id);
754 }
755 Self {
756 links: value,
757 mount_map: from_map,
758 rider_map: to_map,
759 }
760 }
761}
762
763impl From<ActorLinks> for DenseSlotMap<MountId, ActorLink> {
764 fn from(other: ActorLinks) -> Self { other.links }
765}
766slotmap::new_key_type! {
767 pub struct MountId;
768}
769
770#[derive(Clone, Serialize, Deserialize)]
771pub struct MountData {
772 is_steering: bool,
773}
774
775#[derive(Clone, Serialize, Deserialize)]
776pub struct Actors {
777 pub actors: DenseSlotMap<ActorId, Actor>,
778 pub mounts: ActorLinks,
779 #[serde(skip, default = "construct_actor_grid")]
782 pub actor_grid: Grid<GridCell>,
783}
784
785impl Default for Actors {
786 fn default() -> Self {
787 Self {
788 actors: Default::default(),
789 mounts: Default::default(),
790 actor_grid: construct_actor_grid(),
791 }
792 }
793}
794
795fn construct_actor_grid() -> Grid<GridCell> { Grid::new(Vec2::zero(), Default::default()) }
796
797#[derive(Debug)]
798pub enum MountingError {
799 MountDead,
800 RiderDead,
801 HasSteerer,
802 AlreadyRiding,
803 MountIsRiding,
804 RiderIsMounted,
805 MountSelf,
806}
807
808impl Actors {
809 pub fn create_actor(&mut self, actor: Actor) -> ActorId { self.actors.insert(actor) }
810
811 pub fn nearby(
815 &self,
816 this_actor: Option<ActorId>,
817 wpos: Vec3<f32>,
818 radius: f32,
819 ) -> impl Iterator<Item = ActorId> + '_ {
820 let chunk_pos = wpos.xy().as_().wpos_to_cpos();
821 let r_sqr = radius * radius;
822 LOCALITY
823 .into_iter()
824 .flat_map(move |neighbor| {
825 self.actor_grid.get(chunk_pos + neighbor).map(move |cell| {
826 cell.actors.iter().copied().filter(move |actor_id| {
827 self.actors.get(*actor_id).is_some_and(|actor| {
828 actor.presence.is_some() && actor.wpos.distance_squared(wpos) < r_sqr
829 }) && Some(*actor_id) != this_actor
830 })
831 })
832 })
833 .flatten()
834 }
835}
836
837impl Deref for Actors {
838 type Target = DenseSlotMap<ActorId, Actor>;
839
840 fn deref(&self) -> &Self::Target { &self.actors }
841}
842
843impl DerefMut for Actors {
844 fn deref_mut(&mut self) -> &mut Self::Target { &mut self.actors }
845}