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