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