1use common_net::synced_components::Heads;
2use specs::{
3 Entities, LazyUpdate, LendJoin, Read, ReadExpect, ReadStorage, SystemData, WriteStorage, shred,
4};
5
6use common::{
7 comp::{
8 self, ActiveAbilities, Beam, Body, CharacterActivity, CharacterState, Combo, Controller,
9 Density, Energy, Health, Inventory, InventoryManip, Mass, Melee, Ori, PhysicsState, Poise,
10 Pos, PreviousPhysCache, Scale, SkillSet, Stance, StateUpdate, Stats, Vel,
11 character_state::{CharacterStateEvents, OutputEvents},
12 inventory::item::{MaterialStatManifest, tool::AbilityMap},
13 },
14 event::{self, EventBus, KnockbackEvent, LocalEvent},
15 link::Is,
16 mounting::{Rider, VolumeRider},
17 outcome::Outcome,
18 resources::{DeltaTime, Time},
19 states::{
20 behavior::{JoinData, JoinStruct},
21 idle,
22 },
23 terrain::TerrainGrid,
24 uid::{IdMaps, Uid},
25};
26use common_ecs::{Job, Origin, Phase, System};
27
28#[derive(SystemData)]
29pub struct ReadData<'a> {
30 entities: Entities<'a>,
31 events: CharacterStateEvents<'a>,
32 local_bus: Read<'a, EventBus<LocalEvent>>,
33 dt: Read<'a, DeltaTime>,
34 time: Read<'a, Time>,
35 lazy_update: Read<'a, LazyUpdate>,
36 healths: ReadStorage<'a, Health>,
37 heads: ReadStorage<'a, Heads>,
38 bodies: ReadStorage<'a, Body>,
39 masses: ReadStorage<'a, Mass>,
40 scales: ReadStorage<'a, Scale>,
41 physics_states: ReadStorage<'a, PhysicsState>,
42 melee_attacks: ReadStorage<'a, Melee>,
43 beams: ReadStorage<'a, Beam>,
44 uids: ReadStorage<'a, Uid>,
45 is_riders: ReadStorage<'a, Is<Rider>>,
46 is_volume_riders: ReadStorage<'a, Is<VolumeRider>>,
47 stats: ReadStorage<'a, Stats>,
48 skill_sets: ReadStorage<'a, SkillSet>,
49 active_abilities: ReadStorage<'a, ActiveAbilities>,
50 msm: ReadExpect<'a, MaterialStatManifest>,
51 ability_map: ReadExpect<'a, AbilityMap>,
52 combos: ReadStorage<'a, Combo>,
53 alignments: ReadStorage<'a, comp::Alignment>,
54 terrain: ReadExpect<'a, TerrainGrid>,
55 inventories: ReadStorage<'a, Inventory>,
56 stances: ReadStorage<'a, Stance>,
57 prev_phys_caches: ReadStorage<'a, PreviousPhysCache>,
58}
59
60#[derive(Default)]
64pub struct Sys;
65
66impl<'a> System<'a> for Sys {
67 type SystemData = (
68 ReadData<'a>,
69 WriteStorage<'a, CharacterState>,
70 WriteStorage<'a, CharacterActivity>,
71 WriteStorage<'a, Pos>,
72 WriteStorage<'a, Vel>,
73 WriteStorage<'a, Ori>,
74 WriteStorage<'a, Density>,
75 WriteStorage<'a, Energy>,
76 WriteStorage<'a, Controller>,
77 WriteStorage<'a, Poise>,
78 Read<'a, EventBus<Outcome>>,
79 Read<'a, IdMaps>,
80 );
81
82 const NAME: &'static str = "character_behavior";
83 const ORIGIN: Origin = Origin::Common;
84 const PHASE: Phase = Phase::Create;
85
86 fn run(
87 _job: &mut Job<Self>,
88 (
89 read_data,
90 mut character_states,
91 mut character_activities,
92 mut positions,
93 mut velocities,
94 mut orientations,
95 mut densities,
96 mut energies,
97 mut controllers,
98 mut poises,
99 outcomes,
100 id_maps,
101 ): Self::SystemData,
102 ) {
103 let mut local_emitter = read_data.local_bus.emitter();
104 let mut outcomes_emitter = outcomes.emitter();
105 let mut emitters = read_data.events.get_emitters();
106
107 let mut local_events = Vec::new();
108 let mut output_events = OutputEvents::new(&mut local_events, &mut emitters);
109
110 let join = (
111 &read_data.entities,
112 &read_data.uids,
113 &mut character_states,
114 &mut character_activities,
115 &mut positions,
116 &mut velocities,
117 &mut orientations,
118 &read_data.masses,
119 &mut densities,
120 &mut energies,
121 read_data.inventories.maybe(),
122 &mut controllers,
123 read_data.healths.maybe(),
124 read_data.heads.maybe(),
125 (
126 &read_data.bodies,
127 &read_data.physics_states,
128 read_data.scales.maybe(),
129 &read_data.stats,
130 &read_data.skill_sets,
131 read_data.active_abilities.maybe(),
132 read_data.is_riders.maybe(),
133 ),
134 read_data.combos.maybe(),
135 )
136 .lend_join();
137 join.for_each(|comps| {
138 let (
139 entity,
140 uid,
141 mut char_state,
142 character_activity,
143 pos,
144 vel,
145 ori,
146 mass,
147 density,
148 energy,
149 inventory,
150 controller,
151 health,
152 heads,
153 (body, physics, scale, stat, skill_set, active_abilities, is_rider),
154 combo,
155 ) = comps;
156 if health.is_some_and(|h| h.is_dead) {
158 return;
160 }
161
162 if !char_state.is_melee_attack() {
164 read_data.lazy_update.remove::<Melee>(entity);
165 }
166 if !char_state.is_beam_attack() {
167 read_data.lazy_update.remove::<Beam>(entity);
168 }
169
170 if let Some(mut poise) = poises.get_mut(entity) {
172 let was_wielded = char_state.is_wield();
173 let poise_state = poise.poise_state();
174 let pos = pos.0;
175 if let (Some((stunned_state, stunned_duration)), impulse_strength) =
176 poise_state.poise_effect(was_wielded)
177 {
178 poise.reset(*read_data.time, stunned_duration);
180 if !comp::is_downed(health, Some(&char_state)) {
181 *char_state = stunned_state;
182 }
183 outcomes_emitter.emit(Outcome::PoiseChange {
184 pos,
185 state: poise_state,
186 });
187 if let Some(impulse_strength) = impulse_strength {
188 output_events.emit_server(KnockbackEvent {
189 entity,
190 impulse: impulse_strength * *poise.knockback(),
191 });
192 }
193 }
194 }
195
196 let actions = std::mem::take(&mut controller.actions);
198
199 let mut join_struct = JoinStruct {
200 entity,
201 uid,
202 char_state,
203 character_activity,
204 pos,
205 vel,
206 ori,
207 scale,
208 mass,
209 density,
210 energy,
211 inventory,
212 controller,
213 health,
214 heads,
215 body,
216 physics,
217 melee_attack: read_data.melee_attacks.get(entity),
218 beam: read_data.beams.get(entity),
219 stat,
220 skill_set,
221 active_abilities,
222 combo,
223 alignment: read_data.alignments.get(entity),
224 terrain: &read_data.terrain,
225 mount_data: read_data.is_riders.get(entity),
226 volume_mount_data: read_data.is_volume_riders.get(entity),
227 stance: read_data.stances.get(entity),
228 id_maps: &id_maps,
229 alignments: &read_data.alignments,
230 prev_phys_caches: &read_data.prev_phys_caches,
231 bodies: &read_data.bodies,
232 };
233
234 for action in actions {
235 let j = JoinData::new(
236 &join_struct,
237 &read_data.lazy_update,
238 &read_data.dt,
239 &read_data.time,
240 &read_data.msm,
241 &read_data.ability_map,
242 );
243 let state_update = j.character.handle_event(&j, &mut output_events, action);
244 Self::publish_state_update(&mut join_struct, state_update, &mut output_events);
245 }
246
247 if is_rider.is_some() && !join_struct.char_state.can_perform_mounted() {
250 *join_struct.char_state = CharacterState::Idle(idle::Data::default());
252 return;
253 }
254
255 let j = JoinData::new(
256 &join_struct,
257 &read_data.lazy_update,
258 &read_data.dt,
259 &read_data.time,
260 &read_data.msm,
261 &read_data.ability_map,
262 );
263
264 let state_update = j.character.behavior(&j, &mut output_events);
265 Self::publish_state_update(&mut join_struct, state_update, &mut output_events);
266 });
267
268 local_emitter.append_vec(local_events);
269 }
270}
271
272impl Sys {
273 fn publish_state_update(
274 join: &mut JoinStruct,
275 state_update: StateUpdate,
276 output_events: &mut OutputEvents,
277 ) {
278 if *join.char_state != state_update.character {
286 *join.char_state = state_update.character
287 }
288 if *join.character_activity != state_update.character_activity {
289 *join.character_activity = state_update.character_activity
290 }
291 if *join.density != state_update.density {
292 *join.density = state_update.density
293 }
294 if *join.energy != state_update.energy {
295 *join.energy = state_update.energy;
296 };
297
298 *join.pos = state_update.pos;
300 *join.vel = state_update.vel;
301 *join.ori = state_update.ori;
302
303 for (input, attr) in state_update.queued_inputs {
304 join.controller.queued_inputs.insert(input, attr);
305 }
306 for input in state_update.removed_inputs {
307 join.controller.queued_inputs.remove(&input);
308 }
309 if state_update.swap_equipped_weapons {
310 output_events.emit_server(event::InventoryManipEvent(
311 join.entity,
312 InventoryManip::SwapEquippedWeapons,
313 ));
314 }
315 }
316}