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 };
232
233 for action in actions {
234 let j = JoinData::new(
235 &join_struct,
236 &read_data.lazy_update,
237 &read_data.dt,
238 &read_data.time,
239 &read_data.msm,
240 &read_data.ability_map,
241 );
242 let state_update = j.character.handle_event(&j, &mut output_events, action);
243 Self::publish_state_update(&mut join_struct, state_update, &mut output_events);
244 }
245
246 if is_rider.is_some() && !join_struct.char_state.can_perform_mounted() {
249 *join_struct.char_state = CharacterState::Idle(idle::Data::default());
251 return;
252 }
253
254 let j = JoinData::new(
255 &join_struct,
256 &read_data.lazy_update,
257 &read_data.dt,
258 &read_data.time,
259 &read_data.msm,
260 &read_data.ability_map,
261 );
262
263 let state_update = j.character.behavior(&j, &mut output_events);
264 Self::publish_state_update(&mut join_struct, state_update, &mut output_events);
265 });
266
267 local_emitter.append_vec(local_events);
268 }
269}
270
271impl Sys {
272 fn publish_state_update(
273 join: &mut JoinStruct,
274 state_update: StateUpdate,
275 output_events: &mut OutputEvents,
276 ) {
277 if *join.char_state != state_update.character {
285 *join.char_state = state_update.character
286 }
287 if *join.character_activity != state_update.character_activity {
288 *join.character_activity = state_update.character_activity
289 }
290 if *join.density != state_update.density {
291 *join.density = state_update.density
292 }
293 if *join.energy != state_update.energy {
294 *join.energy = state_update.energy;
295 };
296
297 *join.pos = state_update.pos;
299 *join.vel = state_update.vel;
300 *join.ori = state_update.ori;
301
302 for (input, attr) in state_update.queued_inputs {
303 join.controller.queued_inputs.insert(input, attr);
304 }
305 for input in state_update.removed_inputs {
306 join.controller.queued_inputs.remove(&input);
307 }
308 if state_update.swap_equipped_weapons {
309 output_events.emit_server(event::InventoryManipEvent(
310 join.entity,
311 InventoryManip::SwapEquippedWeapons,
312 ));
313 }
314 }
315}