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 {
81 if let Some(npc) = data.npcs.get_mut(npc_id) {
82 npc.health_fraction = ctx.event.new_health_fraction;
83 }
84 }
85}
86
87fn on_death(ctx: EventCtx<SyncNpcs, OnDeath>) {
88 let data = &mut *ctx.state.data_mut();
89
90 if let Actor::Npc(npc_id) = ctx.event.actor {
91 if let Some(npc) = data.npcs.get_mut(npc_id) {
92 npc.health_fraction = 0.0;
94 }
95 }
96}
97
98fn on_tick(ctx: EventCtx<SyncNpcs, OnTick>) {
99 let data = &mut *ctx.state.data_mut();
100 for (npc_id, npc) in data.npcs.npcs.iter_mut() {
101 npc.current_site = ctx
103 .world
104 .sim()
105 .get(npc.wpos.xy().as_().wpos_to_cpos())
106 .and_then(|chunk| {
107 chunk
108 .sites
109 .iter()
110 .find_map(|site| data.sites.world_site_map.get(site).copied())
111 });
112
113 if let Some(current_site) = npc.current_site
116 && Some(current_site) == npc.home
117 {
118 if let Some(site) = data.sites.get_mut(current_site) {
119 site.known_reports.extend(npc.known_reports.iter().copied());
121 npc.inbox.extend(
122 site.known_reports
123 .iter()
124 .copied()
125 .filter(|report| !npc.known_reports.contains(report))
126 .map(NpcInput::Report),
127 );
128 }
129 }
130
131 let chunk_pos = npc.wpos.xy().as_().wpos_to_cpos();
133 if npc.chunk_pos != Some(chunk_pos) {
134 if let Some(cell) = npc
135 .chunk_pos
136 .and_then(|chunk_pos| data.npcs.npc_grid.get_mut(chunk_pos))
137 {
138 if let Some(index) = cell.npcs.iter().position(|id| *id == npc_id) {
139 cell.npcs.swap_remove(index);
140 }
141 }
142 npc.chunk_pos = Some(chunk_pos);
143 if let Some(cell) = data.npcs.npc_grid.get_mut(chunk_pos) {
144 cell.npcs.push(npc_id);
145 }
146 }
147 }
148}