veloren_rtsim/rule/
sync_npcs.rs1use crate::{
2 RtState, Rule, RuleError,
3 event::{EventCtx, OnDeath, OnHealthChange, OnSetup, OnTick},
4};
5use common::{
6 grid::Grid,
7 rtsim::{Actor, NpcInput},
8 terrain::CoordinateConversions,
9};
10
11pub struct SyncNpcs;
12
13impl Rule for SyncNpcs {
14 fn start(rtstate: &mut RtState) -> Result<Self, RuleError> {
15 rtstate.bind::<Self, OnSetup>(on_setup);
16 rtstate.bind::<Self, OnDeath>(on_death);
17 rtstate.bind::<Self, OnHealthChange>(on_health_change);
18 rtstate.bind::<Self, OnTick>(on_tick);
19
20 Ok(Self)
21 }
22}
23
24fn on_setup(ctx: EventCtx<SyncNpcs, OnSetup>) {
25 let data = &mut *ctx.state.data_mut();
26
27 data.npcs.npc_grid = Grid::new(ctx.world.sim().get_size().as_(), Default::default());
29
30 for (npc_id, npc) in data.npcs.npcs.iter() {
32 if let Some(home) = npc.home.and_then(|home| data.sites.get_mut(home)) {
33 home.population.insert(npc_id);
34 }
35 }
36
37 let sites_iter = data.sites.iter().filter_map(|(site_id, site)| {
39 let world_site = site.world_site.map(|ws| ctx.index.sites.get(ws))?;
40 Some((site_id, site, world_site))
41 });
42 let nearest_by_size = sites_iter.clone()
43 .map(|(site_id, site, world_site)| {
44 let mut other_sites = sites_iter.clone()
45 .filter(|(other_id, _, other_site)| *other_id != site_id && other_site.plots().len() > world_site.plots().len())
47 .collect::<Vec<_>>();
48 other_sites.sort_by_key(|(_, other, _)| other.wpos.as_::<i64>().distance_squared(site.wpos.as_::<i64>()));
49 let mut max_size = 0;
50 other_sites.retain(|(_, _, other_site)| {
52 if other_site.plots().len() > max_size {
53 max_size = other_site.plots().len();
54 true
55 } else {
56 false
57 }
58 });
59 let nearest_by_size = other_sites
60 .into_iter()
61 .map(|(site_id, _, _)| site_id)
62 .collect::<Vec<_>>();
63 (site_id, nearest_by_size)
64 })
65 .collect::<Vec<_>>();
66 for (site_id, nearest_by_size) in nearest_by_size {
67 if let Some(site) = data.sites.get_mut(site_id) {
68 site.nearby_sites_by_size = nearest_by_size;
69 }
70 }
71}
72
73fn on_health_change(ctx: EventCtx<SyncNpcs, OnHealthChange>) {
74 let data = &mut *ctx.state.data_mut();
75
76 if ctx.event.new_health_fraction != 0.0
79 && let Actor::Npc(npc_id) = ctx.event.actor
80 && let Some(npc) = data.npcs.get_mut(npc_id)
81 {
82 npc.health_fraction = ctx.event.new_health_fraction;
83 }
84}
85
86fn on_death(ctx: EventCtx<SyncNpcs, OnDeath>) {
87 let data = &mut *ctx.state.data_mut();
88
89 if let Actor::Npc(npc_id) = ctx.event.actor
90 && let Some(npc) = data.npcs.get_mut(npc_id)
91 {
92 npc.health_fraction = 0.0;
94 }
95}
96
97fn on_tick(ctx: EventCtx<SyncNpcs, OnTick>) {
98 let data = &mut *ctx.state.data_mut();
99 for (npc_id, npc) in data.npcs.npcs.iter_mut() {
100 npc.current_site = ctx
102 .world
103 .sim()
104 .get(npc.wpos.xy().as_().wpos_to_cpos())
105 .and_then(|chunk| {
106 chunk
107 .sites
108 .iter()
109 .find_map(|site| data.sites.world_site_map.get(site).copied())
110 });
111
112 if let Some(current_site) = npc.current_site
115 && Some(current_site) == npc.home
116 && let Some(site) = data.sites.get_mut(current_site)
117 {
118 site.known_reports.extend(npc.known_reports.iter().copied());
120 npc.inbox.extend(
121 site.known_reports
122 .iter()
123 .copied()
124 .filter(|report| !npc.known_reports.contains(report))
125 .map(NpcInput::Report),
126 );
127 }
128
129 let chunk_pos = npc.wpos.xy().as_().wpos_to_cpos();
131 if npc.chunk_pos != Some(chunk_pos) {
132 if let Some(cell) = npc
133 .chunk_pos
134 .and_then(|chunk_pos| data.npcs.npc_grid.get_mut(chunk_pos))
135 && let Some(index) = cell.npcs.iter().position(|id| *id == npc_id)
136 {
137 cell.npcs.swap_remove(index);
138 }
139 npc.chunk_pos = Some(chunk_pos);
140 if let Some(cell) = data.npcs.npc_grid.get_mut(chunk_pos) {
141 cell.npcs.push(npc_id);
142 }
143 }
144 }
145}