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 site2 = site
40 .world_site
41 .and_then(|ws| ctx.index.sites.get(ws).site2())?;
42 Some((site_id, site, site2))
43 });
44 let nearest_by_size = sites_iter.clone()
45 .map(|(site_id, site, site2)| {
46 let mut other_sites = sites_iter.clone()
47 .filter(|(other_id, _, other_site2)| *other_id != site_id && other_site2.plots().len() > site2.plots().len())
49 .collect::<Vec<_>>();
50 other_sites.sort_by_key(|(_, other, _)| other.wpos.as_::<i64>().distance_squared(site.wpos.as_::<i64>()));
51 let mut max_size = 0;
52 other_sites.retain(|(_, _, other_site2)| {
54 if other_site2.plots().len() > max_size {
55 max_size = other_site2.plots().len();
56 true
57 } else {
58 false
59 }
60 });
61 let nearest_by_size = other_sites
62 .into_iter()
63 .map(|(site_id, _, _)| site_id)
64 .collect::<Vec<_>>();
65 (site_id, nearest_by_size)
66 })
67 .collect::<Vec<_>>();
68 for (site_id, nearest_by_size) in nearest_by_size {
69 if let Some(site) = data.sites.get_mut(site_id) {
70 site.nearby_sites_by_size = nearest_by_size;
71 }
72 }
73}
74
75fn on_health_change(ctx: EventCtx<SyncNpcs, OnHealthChange>) {
76 let data = &mut *ctx.state.data_mut();
77
78 if ctx.event.new_health_fraction != 0.0
81 && let Actor::Npc(npc_id) = ctx.event.actor
82 {
83 if let Some(npc) = data.npcs.get_mut(npc_id) {
84 npc.health_fraction = ctx.event.new_health_fraction;
85 }
86 }
87}
88
89fn on_death(ctx: EventCtx<SyncNpcs, OnDeath>) {
90 let data = &mut *ctx.state.data_mut();
91
92 if let Actor::Npc(npc_id) = ctx.event.actor {
93 if let Some(npc) = data.npcs.get_mut(npc_id) {
94 npc.health_fraction = 0.0;
96 }
97 }
98}
99
100fn on_tick(ctx: EventCtx<SyncNpcs, OnTick>) {
101 let data = &mut *ctx.state.data_mut();
102 for (npc_id, npc) in data.npcs.npcs.iter_mut() {
103 npc.current_site = ctx
105 .world
106 .sim()
107 .get(npc.wpos.xy().as_().wpos_to_cpos())
108 .and_then(|chunk| {
109 chunk
110 .sites
111 .iter()
112 .find_map(|site| data.sites.world_site_map.get(site).copied())
113 });
114
115 if let Some(current_site) = npc.current_site
118 && Some(current_site) == npc.home
119 {
120 if let Some(site) = data.sites.get_mut(current_site) {
121 site.known_reports.extend(npc.known_reports.iter().copied());
123 npc.inbox.extend(
124 site.known_reports
125 .iter()
126 .copied()
127 .filter(|report| !npc.known_reports.contains(report))
128 .map(NpcInput::Report),
129 );
130 }
131 }
132
133 let chunk_pos = npc.wpos.xy().as_().wpos_to_cpos();
135 if npc.chunk_pos != Some(chunk_pos) {
136 if let Some(cell) = npc
137 .chunk_pos
138 .and_then(|chunk_pos| data.npcs.npc_grid.get_mut(chunk_pos))
139 {
140 if let Some(index) = cell.npcs.iter().position(|id| *id == npc_id) {
141 cell.npcs.swap_remove(index);
142 }
143 }
144 npc.chunk_pos = Some(chunk_pos);
145 if let Some(cell) = data.npcs.npc_grid.get_mut(chunk_pos) {
146 cell.npcs.push(npc_id);
147 }
148 }
149 }
150}