1use crate::{
2 combat::AttackSource,
3 comp::{
4 ControlAction, Density, Energy, InputAttr, InputKind, Ori, Pos, Vel, ability::Capability,
5 inventory::item::armor::Friction, item::ConsumableKind,
6 },
7 event::{self, EmitExt, LocalEvent},
8 event_emitters,
9 resources::Time,
10 states::{
11 self,
12 behavior::{CharacterBehavior, JoinData},
13 utils::{AbilityInfo, StageSection},
14 *,
15 },
16 util::Dir,
17};
18use serde::{Deserialize, Serialize};
19use specs::{Component, DerefFlaggedStorage};
20use std::{collections::BTreeMap, time::Duration};
21use strum::Display;
22
23pub struct StateUpdate {
25 pub character: CharacterState,
26 pub pos: Pos,
27 pub vel: Vel,
28 pub ori: Ori,
29 pub density: Density,
30 pub energy: Energy,
31 pub swap_equipped_weapons: bool,
32 pub should_strafe: bool,
33 pub queued_inputs: BTreeMap<InputKind, InputAttr>,
34 pub removed_inputs: Vec<InputKind>,
35 pub character_activity: CharacterActivity,
36}
37
38event_emitters! {
39 pub struct CharacterStateEvents[CharacterStateEventEmitters] {
40 combo: event::ComboChangeEvent,
41 event: event::AuraEvent,
42 shoot: event::ShootEvent,
43 teleport_to: event::TeleportToEvent,
44 shockwave: event::ShockwaveEvent,
45 explosion: event::ExplosionEvent,
46 buff: event::BuffEvent,
47 inventory_manip: event::InventoryManipEvent,
48 sprite_summon: event::CreateSpriteEvent,
49 change_stance: event::ChangeStanceEvent,
50 create_npc: event::CreateNpcEvent,
51 energy_change: event::EnergyChangeEvent,
52 knockback: event::KnockbackEvent,
53 sprite_light: event::ToggleSpriteLightEvent,
54 transform: event::TransformEvent,
55 regrow_head: event::RegrowHeadEvent,
56 create_aura_entity: event::CreateAuraEntityEvent,
57 help_downed: event::HelpDownedEvent,
58 }
59}
60
61pub struct OutputEvents<'a, 'b> {
62 local: &'a mut Vec<LocalEvent>,
63 server: &'a mut CharacterStateEventEmitters<'b>,
64}
65
66impl<'a, 'b: 'a> OutputEvents<'a, 'b> {
67 pub fn new(
68 local: &'a mut Vec<LocalEvent>,
69 server: &'a mut CharacterStateEventEmitters<'b>,
70 ) -> Self {
71 Self { local, server }
72 }
73
74 pub fn emit_local(&mut self, event: LocalEvent) { self.local.push(event); }
75
76 pub fn emit_server<E>(&mut self, event: E)
77 where
78 CharacterStateEventEmitters<'b>: EmitExt<E>,
79 {
80 self.server.emit(event);
81 }
82}
83
84impl From<&JoinData<'_>> for StateUpdate {
85 fn from(data: &JoinData) -> Self {
86 StateUpdate {
87 pos: *data.pos,
88 vel: *data.vel,
89 ori: *data.ori,
90 density: *data.density,
91 energy: *data.energy,
92 swap_equipped_weapons: false,
93 should_strafe: data.inputs.strafing,
94 character: data.character.clone(),
95 queued_inputs: BTreeMap::new(),
96 removed_inputs: Vec::new(),
97 character_activity: data.character_activity.clone(),
98 }
99 }
100}
101#[derive(Clone, Debug, Display, PartialEq, Serialize, Deserialize)]
102pub enum CharacterState {
103 Idle(idle::Data),
104 Crawl,
105 Climb(climb::Data),
106 Sit,
107 Dance,
108 Talk(talk::Data),
109 Glide(glide::Data),
110 GlideWield(glide_wield::Data),
111 Stunned(stunned::Data),
113 BasicBlock(basic_block::Data),
115 Equipping(equipping::Data),
117 Wielding(wielding::Data),
119 Roll(roll::Data),
121 BasicMelee(basic_melee::Data),
123 BasicRanged(basic_ranged::Data),
125 Boost(boost::Data),
127 DashMelee(dash_melee::Data),
129 ComboMelee2(combo_melee2::Data),
131 LeapMelee(leap_melee::Data),
133 LeapShockwave(leap_shockwave::Data),
135 ChargedRanged(charged_ranged::Data),
137 ChargedMelee(charged_melee::Data),
139 RepeaterRanged(repeater_ranged::Data),
141 Shockwave(shockwave::Data),
143 BasicBeam(basic_beam::Data),
146 BasicAura(basic_aura::Data),
148 StaticAura(static_aura::Data),
151 Blink(blink::Data),
153 BasicSummon(basic_summon::Data),
155 SelfBuff(self_buff::Data),
157 SpriteSummon(sprite_summon::Data),
159 UseItem(use_item::Data),
161 Interact(interact::Data),
164 Wallrun(wallrun::Data),
166 Skate(skate::Data),
168 Music(music::Data),
170 FinisherMelee(finisher_melee::Data),
172 DiveMelee(dive_melee::Data),
175 RiposteMelee(riposte_melee::Data),
177 RapidMelee(rapid_melee::Data),
180 Transform(transform::Data),
182 RegrowHead(regrow_head::Data),
184}
185
186impl CharacterState {
187 pub fn is_wield(&self) -> bool {
188 matches!(
189 self,
190 CharacterState::Wielding(_)
191 | CharacterState::BasicMelee(_)
192 | CharacterState::BasicRanged(_)
193 | CharacterState::DashMelee(_)
194 | CharacterState::ComboMelee2(_)
195 | CharacterState::BasicBlock(_)
196 | CharacterState::LeapMelee(_)
197 | CharacterState::LeapShockwave(_)
198 | CharacterState::ChargedMelee(_)
199 | CharacterState::ChargedRanged(_)
200 | CharacterState::RepeaterRanged(_)
201 | CharacterState::Shockwave(_)
202 | CharacterState::BasicBeam(_)
203 | CharacterState::BasicAura(_)
204 | CharacterState::SelfBuff(_)
205 | CharacterState::Blink(_)
206 | CharacterState::Music(_)
207 | CharacterState::BasicSummon(_)
208 | CharacterState::SpriteSummon(_)
209 | CharacterState::Roll(roll::Data {
210 was_wielded: true,
211 ..
212 })
213 | CharacterState::Wallrun(wallrun::Data { was_wielded: true })
214 | CharacterState::Stunned(stunned::Data {
215 was_wielded: true,
216 ..
217 })
218 | CharacterState::FinisherMelee(_)
219 | CharacterState::DiveMelee(_)
220 | CharacterState::RiposteMelee(_)
221 | CharacterState::RapidMelee(_)
222 | CharacterState::StaticAura(_)
223 )
224 }
225
226 pub fn can_interact(&self) -> bool {
228 match self {
229 CharacterState::Idle(_)
230 | CharacterState::Sit
231 | CharacterState::Dance
232 | CharacterState::Talk(_)
233 | CharacterState::Equipping(_)
234 | CharacterState::Wielding(_)
235 | CharacterState::GlideWield(_) => true,
236 CharacterState::Crawl
237 | CharacterState::Climb(_)
238 | CharacterState::Glide(_)
239 | CharacterState::Stunned(_)
240 | CharacterState::BasicBlock(_)
241 | CharacterState::Roll(_)
242 | CharacterState::BasicMelee(_)
243 | CharacterState::BasicRanged(_)
244 | CharacterState::Boost(_)
245 | CharacterState::DashMelee(_)
246 | CharacterState::ComboMelee2(_)
247 | CharacterState::LeapMelee(_)
248 | CharacterState::LeapShockwave(_)
249 | CharacterState::ChargedRanged(_)
250 | CharacterState::ChargedMelee(_)
251 | CharacterState::RepeaterRanged(_)
252 | CharacterState::Shockwave(_)
253 | CharacterState::BasicBeam(_)
254 | CharacterState::BasicAura(_)
255 | CharacterState::StaticAura(_)
256 | CharacterState::Blink(_)
257 | CharacterState::BasicSummon(_)
258 | CharacterState::SelfBuff(_)
259 | CharacterState::SpriteSummon(_)
260 | CharacterState::UseItem(_)
261 | CharacterState::Interact(_)
262 | CharacterState::Wallrun(_)
263 | CharacterState::Skate(_)
264 | CharacterState::Music(_)
265 | CharacterState::FinisherMelee(_)
266 | CharacterState::DiveMelee(_)
267 | CharacterState::RiposteMelee(_)
268 | CharacterState::RapidMelee(_)
269 | CharacterState::Transform(_)
270 | CharacterState::RegrowHead(_) => false,
271 }
272 }
273
274 pub fn was_wielded(&self) -> bool {
275 match self {
276 CharacterState::Roll(data) => data.was_wielded,
277 CharacterState::Stunned(data) => data.was_wielded,
278 CharacterState::Interact(data) => data.static_data.was_wielded,
279 CharacterState::UseItem(data) => data.static_data.was_wielded,
280 CharacterState::Wallrun(data) => data.was_wielded,
281 _ => false,
282 }
283 }
284
285 pub fn is_glide_wielded(&self) -> bool {
286 matches!(
287 self,
288 CharacterState::Glide { .. } | CharacterState::GlideWield { .. }
289 )
290 }
291
292 pub fn is_stealthy(&self) -> bool {
293 matches!(
294 self,
295 CharacterState::Idle(idle::Data {
296 is_sneaking: true,
297 footwear: _,
298 time_entered: _,
299 }) | CharacterState::Wielding(wielding::Data {
300 is_sneaking: true,
301 ..
302 }) | CharacterState::Roll(roll::Data {
303 is_sneaking: true,
304 ..
305 })
306 )
307 }
308
309 pub fn should_follow_look(&self) -> bool {
310 matches!(self, CharacterState::Boost(_)) || self.is_attack()
311 }
312
313 pub fn is_attack(&self) -> bool {
314 matches!(
315 self,
316 CharacterState::BasicMelee(_)
317 | CharacterState::BasicRanged(_)
318 | CharacterState::DashMelee(_)
319 | CharacterState::ComboMelee2(_)
320 | CharacterState::LeapMelee(_)
321 | CharacterState::LeapShockwave(_)
322 | CharacterState::ChargedMelee(_)
323 | CharacterState::ChargedRanged(_)
324 | CharacterState::RepeaterRanged(_)
325 | CharacterState::Shockwave(_)
326 | CharacterState::BasicBeam(_)
327 | CharacterState::BasicAura(_)
328 | CharacterState::SelfBuff(_)
329 | CharacterState::Blink(_)
330 | CharacterState::BasicSummon(_)
331 | CharacterState::SpriteSummon(_)
332 | CharacterState::FinisherMelee(_)
333 | CharacterState::DiveMelee(_)
334 | CharacterState::RiposteMelee(_)
335 | CharacterState::RapidMelee(_)
336 | CharacterState::StaticAura(_)
337 )
338 }
339
340 pub fn is_aimed(&self) -> bool {
341 matches!(
342 self,
343 CharacterState::BasicMelee(_)
344 | CharacterState::BasicRanged(_)
345 | CharacterState::DashMelee(_)
346 | CharacterState::ComboMelee2(_)
347 | CharacterState::BasicBlock(_)
348 | CharacterState::LeapMelee(_)
349 | CharacterState::LeapShockwave(_)
350 | CharacterState::ChargedMelee(_)
351 | CharacterState::ChargedRanged(_)
352 | CharacterState::RepeaterRanged(_)
353 | CharacterState::Shockwave(_)
354 | CharacterState::BasicBeam(_)
355 | CharacterState::Stunned(_)
356 | CharacterState::Wielding(_)
357 | CharacterState::Talk(_)
358 | CharacterState::FinisherMelee(_)
359 | CharacterState::DiveMelee(_)
360 | CharacterState::RiposteMelee(_)
361 | CharacterState::RapidMelee(_)
362 )
363 }
364
365 pub fn is_using_hands(&self) -> bool {
366 matches!(
367 self,
368 CharacterState::Climb(_)
369 | CharacterState::Equipping(_)
370 | CharacterState::Dance
371 | CharacterState::Glide(_)
372 | CharacterState::GlideWield(_)
373 | CharacterState::Talk(_)
374 | CharacterState::Roll(_),
375 )
376 }
377
378 pub fn is_parry(&self, attack_source: AttackSource) -> bool {
379 let melee = matches!(attack_source, AttackSource::Melee);
380 let from_capability_melee = melee
381 && self
382 .ability_info()
383 .map(|a| a.ability_meta.capabilities)
384 .is_some_and(|c| {
385 c.contains(Capability::PARRIES_MELEE)
386 && matches!(
387 self.stage_section(),
388 Some(StageSection::Buildup | StageSection::Action)
389 )
390 });
391 let from_capability = matches!(
392 attack_source,
393 AttackSource::Melee
394 | AttackSource::Projectile
395 | AttackSource::Beam
396 | AttackSource::AirShockwave
397 | AttackSource::Explosion
398 ) && self
399 .ability_info()
400 .map(|a| a.ability_meta.capabilities)
401 .is_some_and(|c| {
402 c.contains(Capability::PARRIES)
403 && matches!(
404 self.stage_section(),
405 Some(StageSection::Buildup | StageSection::Action)
406 )
407 });
408 let from_state = match self {
409 CharacterState::BasicBlock(c) => c.is_parry(attack_source),
410 CharacterState::RiposteMelee(c) => {
411 melee
412 && matches!(
413 c.stage_section,
414 StageSection::Buildup | StageSection::Action
415 )
416 },
417 _ => false,
418 };
419 from_capability_melee || from_capability || from_state
420 }
421
422 pub fn is_block(&self, attack_source: AttackSource) -> bool {
423 match self {
424 CharacterState::BasicBlock(data) => {
425 data.static_data.blocked_attacks.applies(attack_source)
426 && matches!(
427 self.stage_section(),
428 Some(StageSection::Buildup | StageSection::Action)
429 )
430 },
431 _ => self
432 .ability_info()
433 .map(|ability| ability.ability_meta.capabilities)
434 .is_some_and(|capabilities| {
435 capabilities.contains(Capability::BLOCKS)
436 && matches!(
437 self.stage_section(),
438 Some(StageSection::Buildup | StageSection::Action)
439 )
440 && matches!(attack_source, AttackSource::Melee)
441 }),
442 }
443 }
444
445 pub fn block_angle(&self) -> f32 {
447 match self {
448 CharacterState::BasicBlock(c) => c.static_data.max_angle.to_radians(),
449 CharacterState::ComboMelee2(c) => {
450 let strike_data =
451 c.static_data.strikes[c.completed_strikes % c.static_data.strikes.len()];
452 strike_data.melee_constructor.angle.to_radians()
453 },
454 CharacterState::RiposteMelee(c) => c.static_data.melee_constructor.angle.to_radians(),
455 _ => 0.0,
458 }
459 }
460
461 pub fn is_dodge(&self) -> bool {
462 if let CharacterState::Roll(c) = self {
463 matches!(
464 c.stage_section,
465 StageSection::Buildup | StageSection::Movement
466 )
467 } else {
468 false
469 }
470 }
471
472 pub fn is_glide(&self) -> bool { matches!(self, CharacterState::Glide(_)) }
473
474 pub fn is_skate(&self) -> bool { matches!(self, CharacterState::Skate(_)) }
475
476 pub fn is_music(&self) -> bool { matches!(self, CharacterState::Music(_)) }
477
478 pub fn roll_attack_immunities(&self) -> Option<AttackFilters> {
479 if self.is_dodge()
480 && let CharacterState::Roll(c) = self
481 {
482 Some(c.static_data.attack_immunities)
483 } else {
484 None
485 }
486 }
487
488 pub fn is_stunned(&self) -> bool { matches!(self, CharacterState::Stunned(_)) }
489
490 pub fn is_forced_movement(&self) -> bool {
491 matches!(self, CharacterState::ComboMelee2(s) if s.stage_section == StageSection::Action)
492 || matches!(self, CharacterState::DashMelee(s) if s.stage_section == StageSection::Charge)
493 || matches!(self, CharacterState::LeapMelee(s) if s.stage_section == StageSection::Movement)
494 || matches!(self, CharacterState::Roll(s) if s.stage_section == StageSection::Movement)
495 }
496
497 pub fn is_melee_attack(&self) -> bool {
498 matches!(self.attack_kind(), Some(AttackSource::Melee))
499 }
500
501 pub fn is_beam_attack(&self) -> bool { matches!(self.attack_kind(), Some(AttackSource::Beam)) }
502
503 pub fn can_perform_mounted(&self) -> bool {
504 matches!(
505 self,
506 CharacterState::Idle(_)
507 | CharacterState::Sit
508 | CharacterState::Dance
509 | CharacterState::Talk(_)
510 | CharacterState::Stunned(_)
511 | CharacterState::BasicBlock(_)
512 | CharacterState::Equipping(_)
513 | CharacterState::Wielding(_)
514 | CharacterState::BasicMelee(_)
515 | CharacterState::BasicRanged(_)
516 | CharacterState::ComboMelee2(_)
517 | CharacterState::ChargedRanged(_)
518 | CharacterState::RepeaterRanged(_)
519 | CharacterState::BasicBeam(_)
520 | CharacterState::BasicAura(_)
521 | CharacterState::BasicSummon(_)
522 | CharacterState::SelfBuff(_)
523 | CharacterState::SpriteSummon(_)
524 | CharacterState::UseItem(_)
525 | CharacterState::Interact(_)
526 | CharacterState::Music(_)
527 | CharacterState::RiposteMelee(_)
528 | CharacterState::RapidMelee(_)
529 )
530 }
531
532 pub fn is_sitting(&self) -> bool {
533 use use_item::{Data, ItemUseKind, StaticData};
534 matches!(
535 self,
536 CharacterState::Sit
537 | CharacterState::UseItem(Data {
538 static_data: StaticData {
539 item_kind: ItemUseKind::Consumable(
540 ConsumableKind::ComplexFood | ConsumableKind::Food
541 ),
542 ..
543 },
544 ..
545 })
546 )
547 }
548
549 pub fn same_variant(&self, other: &Self) -> bool {
551 std::mem::discriminant(self) == std::mem::discriminant(other)
553 }
554
555 pub fn behavior(&self, j: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
556 match &self {
557 CharacterState::Idle(data) => data.behavior(j, output_events),
558 CharacterState::Talk(data) => data.behavior(j, output_events),
559 CharacterState::Climb(data) => data.behavior(j, output_events),
560 CharacterState::Wallrun(data) => data.behavior(j, output_events),
561 CharacterState::Glide(data) => data.behavior(j, output_events),
562 CharacterState::GlideWield(data) => data.behavior(j, output_events),
563 CharacterState::Stunned(data) => data.behavior(j, output_events),
564 CharacterState::Sit => sit::Data::behavior(&sit::Data, j, output_events),
565 CharacterState::Crawl => crawl::Data::behavior(&crawl::Data, j, output_events),
566 CharacterState::Dance => dance::Data::behavior(&dance::Data, j, output_events),
567 CharacterState::BasicBlock(data) => data.behavior(j, output_events),
568 CharacterState::Roll(data) => data.behavior(j, output_events),
569 CharacterState::Wielding(data) => data.behavior(j, output_events),
570 CharacterState::Equipping(data) => data.behavior(j, output_events),
571 CharacterState::ComboMelee2(data) => data.behavior(j, output_events),
572 CharacterState::BasicMelee(data) => data.behavior(j, output_events),
573 CharacterState::BasicRanged(data) => data.behavior(j, output_events),
574 CharacterState::Boost(data) => data.behavior(j, output_events),
575 CharacterState::DashMelee(data) => data.behavior(j, output_events),
576 CharacterState::LeapMelee(data) => data.behavior(j, output_events),
577 CharacterState::LeapShockwave(data) => data.behavior(j, output_events),
578 CharacterState::ChargedMelee(data) => data.behavior(j, output_events),
579 CharacterState::ChargedRanged(data) => data.behavior(j, output_events),
580 CharacterState::RepeaterRanged(data) => data.behavior(j, output_events),
581 CharacterState::Shockwave(data) => data.behavior(j, output_events),
582 CharacterState::BasicBeam(data) => data.behavior(j, output_events),
583 CharacterState::BasicAura(data) => data.behavior(j, output_events),
584 CharacterState::Blink(data) => data.behavior(j, output_events),
585 CharacterState::BasicSummon(data) => data.behavior(j, output_events),
586 CharacterState::SelfBuff(data) => data.behavior(j, output_events),
587 CharacterState::SpriteSummon(data) => data.behavior(j, output_events),
588 CharacterState::UseItem(data) => data.behavior(j, output_events),
589 CharacterState::Interact(data) => data.behavior(j, output_events),
590 CharacterState::Skate(data) => data.behavior(j, output_events),
591 CharacterState::Music(data) => data.behavior(j, output_events),
592 CharacterState::FinisherMelee(data) => data.behavior(j, output_events),
593 CharacterState::DiveMelee(data) => data.behavior(j, output_events),
594 CharacterState::RiposteMelee(data) => data.behavior(j, output_events),
595 CharacterState::RapidMelee(data) => data.behavior(j, output_events),
596 CharacterState::Transform(data) => data.behavior(j, output_events),
597 CharacterState::RegrowHead(data) => data.behavior(j, output_events),
598 CharacterState::StaticAura(data) => data.behavior(j, output_events),
599 }
600 }
601
602 pub fn handle_event(
603 &self,
604 j: &JoinData,
605 output_events: &mut OutputEvents,
606 action: ControlAction,
607 ) -> StateUpdate {
608 match &self {
609 CharacterState::Idle(data) => data.handle_event(j, output_events, action),
610 CharacterState::Talk(data) => data.handle_event(j, output_events, action),
611 CharacterState::Climb(data) => data.handle_event(j, output_events, action),
612 CharacterState::Wallrun(data) => data.handle_event(j, output_events, action),
613 CharacterState::Glide(data) => data.handle_event(j, output_events, action),
614 CharacterState::GlideWield(data) => data.handle_event(j, output_events, action),
615 CharacterState::Stunned(data) => data.handle_event(j, output_events, action),
616 CharacterState::Sit => {
617 states::sit::Data::handle_event(&sit::Data, j, output_events, action)
618 },
619 CharacterState::Crawl => {
620 states::crawl::Data::handle_event(&crawl::Data, j, output_events, action)
621 },
622 CharacterState::Dance => {
623 states::dance::Data::handle_event(&dance::Data, j, output_events, action)
624 },
625 CharacterState::BasicBlock(data) => data.handle_event(j, output_events, action),
626 CharacterState::Roll(data) => data.handle_event(j, output_events, action),
627 CharacterState::Wielding(data) => data.handle_event(j, output_events, action),
628 CharacterState::Equipping(data) => data.handle_event(j, output_events, action),
629 CharacterState::ComboMelee2(data) => data.handle_event(j, output_events, action),
630 CharacterState::BasicMelee(data) => data.handle_event(j, output_events, action),
631 CharacterState::BasicRanged(data) => data.handle_event(j, output_events, action),
632 CharacterState::Boost(data) => data.handle_event(j, output_events, action),
633 CharacterState::DashMelee(data) => data.handle_event(j, output_events, action),
634 CharacterState::LeapMelee(data) => data.handle_event(j, output_events, action),
635 CharacterState::LeapShockwave(data) => data.handle_event(j, output_events, action),
636 CharacterState::ChargedMelee(data) => data.handle_event(j, output_events, action),
637 CharacterState::ChargedRanged(data) => data.handle_event(j, output_events, action),
638 CharacterState::RepeaterRanged(data) => data.handle_event(j, output_events, action),
639 CharacterState::Shockwave(data) => data.handle_event(j, output_events, action),
640 CharacterState::BasicBeam(data) => data.handle_event(j, output_events, action),
641 CharacterState::BasicAura(data) => data.handle_event(j, output_events, action),
642 CharacterState::Blink(data) => data.handle_event(j, output_events, action),
643 CharacterState::BasicSummon(data) => data.handle_event(j, output_events, action),
644 CharacterState::SelfBuff(data) => data.handle_event(j, output_events, action),
645 CharacterState::SpriteSummon(data) => data.handle_event(j, output_events, action),
646 CharacterState::UseItem(data) => data.handle_event(j, output_events, action),
647 CharacterState::Interact(data) => data.handle_event(j, output_events, action),
648 CharacterState::Skate(data) => data.handle_event(j, output_events, action),
649 CharacterState::Music(data) => data.handle_event(j, output_events, action),
650 CharacterState::FinisherMelee(data) => data.handle_event(j, output_events, action),
651 CharacterState::DiveMelee(data) => data.handle_event(j, output_events, action),
652 CharacterState::RiposteMelee(data) => data.handle_event(j, output_events, action),
653 CharacterState::RapidMelee(data) => data.handle_event(j, output_events, action),
654 CharacterState::Transform(data) => data.handle_event(j, output_events, action),
655 CharacterState::RegrowHead(data) => data.handle_event(j, output_events, action),
656 CharacterState::StaticAura(data) => data.handle_event(j, output_events, action),
657 }
658 }
659
660 pub fn footwear(&self) -> Option<Friction> {
661 match &self {
662 CharacterState::Idle(data) => data.footwear,
663 CharacterState::Skate(data) => Some(data.footwear),
664 _ => None,
665 }
666 }
667
668 pub fn ability_info(&self) -> Option<AbilityInfo> {
669 match &self {
670 CharacterState::Idle(_) => None,
671 CharacterState::Talk(_) => None,
672 CharacterState::Climb(_) => None,
673 CharacterState::Wallrun(_) => None,
674 CharacterState::Skate(_) => None,
675 CharacterState::Glide(_) => None,
676 CharacterState::GlideWield(_) => None,
677 CharacterState::Stunned(_) => None,
678 CharacterState::Sit => None,
679 CharacterState::Crawl => None,
680 CharacterState::Dance => None,
681 CharacterState::BasicBlock(data) => Some(data.static_data.ability_info),
682 CharacterState::Roll(data) => Some(data.static_data.ability_info),
683 CharacterState::Wielding(_) => None,
684 CharacterState::Equipping(_) => None,
685 CharacterState::ComboMelee2(data) => Some(data.static_data.ability_info),
686 CharacterState::BasicMelee(data) => Some(data.static_data.ability_info),
687 CharacterState::BasicRanged(data) => Some(data.static_data.ability_info),
688 CharacterState::Boost(data) => Some(data.static_data.ability_info),
689 CharacterState::DashMelee(data) => Some(data.static_data.ability_info),
690 CharacterState::LeapMelee(data) => Some(data.static_data.ability_info),
691 CharacterState::LeapShockwave(data) => Some(data.static_data.ability_info),
692 CharacterState::ChargedMelee(data) => Some(data.static_data.ability_info),
693 CharacterState::ChargedRanged(data) => Some(data.static_data.ability_info),
694 CharacterState::RepeaterRanged(data) => Some(data.static_data.ability_info),
695 CharacterState::Shockwave(data) => Some(data.static_data.ability_info),
696 CharacterState::BasicBeam(data) => Some(data.static_data.ability_info),
697 CharacterState::BasicAura(data) => Some(data.static_data.ability_info),
698 CharacterState::Blink(data) => Some(data.static_data.ability_info),
699 CharacterState::BasicSummon(data) => Some(data.static_data.ability_info),
700 CharacterState::SelfBuff(data) => Some(data.static_data.ability_info),
701 CharacterState::SpriteSummon(data) => Some(data.static_data.ability_info),
702 CharacterState::UseItem(_) => None,
703 CharacterState::Interact(_) => None,
704 CharacterState::FinisherMelee(data) => Some(data.static_data.ability_info),
705 CharacterState::Music(data) => Some(data.static_data.ability_info),
706 CharacterState::DiveMelee(data) => Some(data.static_data.ability_info),
707 CharacterState::RiposteMelee(data) => Some(data.static_data.ability_info),
708 CharacterState::RapidMelee(data) => Some(data.static_data.ability_info),
709 CharacterState::Transform(data) => Some(data.static_data.ability_info),
710 CharacterState::RegrowHead(data) => Some(data.static_data.ability_info),
711 CharacterState::StaticAura(data) => Some(data.static_data.ability_info),
712 }
713 }
714
715 pub fn stage_section(&self) -> Option<StageSection> {
716 match &self {
717 CharacterState::Idle(_) => None,
718 CharacterState::Talk(_) => None,
719 CharacterState::Climb(_) => None,
720 CharacterState::Wallrun(_) => None,
721 CharacterState::Skate(_) => None,
722 CharacterState::Glide(_) => None,
723 CharacterState::GlideWield(_) => None,
724 CharacterState::Stunned(data) => Some(data.stage_section),
725 CharacterState::Sit => None,
726 CharacterState::Crawl => None,
727 CharacterState::Dance => None,
728 CharacterState::BasicBlock(data) => Some(data.stage_section),
729 CharacterState::Roll(data) => Some(data.stage_section),
730 CharacterState::Equipping(_) => Some(StageSection::Buildup),
731 CharacterState::Wielding(_) => None,
732 CharacterState::ComboMelee2(data) => Some(data.stage_section),
733 CharacterState::BasicMelee(data) => Some(data.stage_section),
734 CharacterState::BasicRanged(data) => Some(data.stage_section),
735 CharacterState::Boost(_) => None,
736 CharacterState::DashMelee(data) => Some(data.stage_section),
737 CharacterState::LeapMelee(data) => Some(data.stage_section),
738 CharacterState::LeapShockwave(data) => Some(data.stage_section),
739 CharacterState::ChargedMelee(data) => Some(data.stage_section),
740 CharacterState::ChargedRanged(data) => Some(data.stage_section),
741 CharacterState::RepeaterRanged(data) => Some(data.stage_section),
742 CharacterState::Shockwave(data) => Some(data.stage_section),
743 CharacterState::BasicBeam(data) => Some(data.stage_section),
744 CharacterState::BasicAura(data) => Some(data.stage_section),
745 CharacterState::Blink(data) => Some(data.stage_section),
746 CharacterState::BasicSummon(data) => Some(data.stage_section),
747 CharacterState::SelfBuff(data) => Some(data.stage_section),
748 CharacterState::SpriteSummon(data) => Some(data.stage_section),
749 CharacterState::UseItem(data) => Some(data.stage_section),
750 CharacterState::Interact(data) => Some(data.stage_section),
751 CharacterState::FinisherMelee(data) => Some(data.stage_section),
752 CharacterState::Music(data) => Some(data.stage_section),
753 CharacterState::DiveMelee(data) => Some(data.stage_section),
754 CharacterState::RiposteMelee(data) => Some(data.stage_section),
755 CharacterState::RapidMelee(data) => Some(data.stage_section),
756 CharacterState::Transform(data) => Some(data.stage_section),
757 CharacterState::RegrowHead(data) => Some(data.stage_section),
758 CharacterState::StaticAura(data) => Some(data.stage_section),
759 }
760 }
761
762 pub fn durations(&self) -> Option<DurationsInfo> {
763 match &self {
764 CharacterState::Idle(_) => None,
765 CharacterState::Talk(_) => None,
766 CharacterState::Climb(_) => None,
767 CharacterState::Wallrun(_) => None,
768 CharacterState::Skate(_) => None,
769 CharacterState::Glide(_) => None,
770 CharacterState::GlideWield(_) => None,
771 CharacterState::Stunned(data) => Some(DurationsInfo {
772 buildup: Some(data.static_data.buildup_duration),
773 recover: Some(data.static_data.recover_duration),
774 ..Default::default()
775 }),
776 CharacterState::Sit => None,
777 CharacterState::Crawl => None,
778 CharacterState::Dance => None,
779 CharacterState::BasicBlock(data) => Some(DurationsInfo {
780 buildup: Some(data.static_data.buildup_duration),
781 recover: Some(data.static_data.recover_duration),
782 ..Default::default()
783 }),
784 CharacterState::Roll(data) => Some(DurationsInfo {
785 buildup: Some(data.static_data.buildup_duration),
786 recover: Some(data.static_data.recover_duration),
787 movement: Some(data.static_data.movement_duration),
788 ..Default::default()
789 }),
790 CharacterState::Wielding(_) => None,
791 CharacterState::Equipping(data) => Some(DurationsInfo {
792 buildup: Some(data.static_data.buildup_duration),
793 ..Default::default()
794 }),
795 CharacterState::ComboMelee2(data) => {
796 let strike = data.strike_data();
797 Some(DurationsInfo {
798 buildup: Some(strike.buildup_duration),
799 action: Some(strike.swing_duration),
800 recover: Some(strike.recover_duration),
801 ..Default::default()
802 })
803 },
804 CharacterState::BasicMelee(data) => Some(DurationsInfo {
805 buildup: Some(data.static_data.buildup_duration),
806 action: Some(data.static_data.swing_duration),
807 recover: Some(data.static_data.recover_duration),
808 ..Default::default()
809 }),
810 CharacterState::BasicRanged(data) => Some(DurationsInfo {
811 buildup: Some(data.static_data.buildup_duration),
812 recover: Some(data.static_data.recover_duration),
813 ..Default::default()
814 }),
815 CharacterState::Boost(data) => Some(DurationsInfo {
816 movement: Some(data.static_data.movement_duration),
817 ..Default::default()
818 }),
819 CharacterState::DashMelee(data) => Some(DurationsInfo {
820 buildup: Some(data.static_data.buildup_duration),
821 action: Some(data.static_data.swing_duration),
822 recover: Some(data.static_data.recover_duration),
823 charge: Some(data.static_data.charge_duration),
824 ..Default::default()
825 }),
826 CharacterState::LeapMelee(data) => Some(DurationsInfo {
827 buildup: Some(data.static_data.buildup_duration),
828 action: Some(data.static_data.swing_duration),
829 recover: Some(data.static_data.recover_duration),
830 movement: Some(data.static_data.movement_duration),
831 ..Default::default()
832 }),
833 CharacterState::LeapShockwave(data) => Some(DurationsInfo {
834 buildup: Some(data.static_data.buildup_duration),
835 action: Some(data.static_data.swing_duration),
836 recover: Some(data.static_data.recover_duration),
837 movement: Some(data.static_data.movement_duration),
838 ..Default::default()
839 }),
840 CharacterState::ChargedMelee(data) => Some(DurationsInfo {
841 buildup: data.static_data.buildup_strike.map(|x| x.0),
842 action: Some(data.static_data.swing_duration),
843 recover: Some(data.static_data.recover_duration),
844 charge: Some(data.static_data.charge_duration),
845 ..Default::default()
846 }),
847 CharacterState::ChargedRanged(data) => Some(DurationsInfo {
848 buildup: Some(data.static_data.buildup_duration),
849 recover: Some(data.static_data.recover_duration),
850 charge: Some(data.static_data.charge_duration),
851 ..Default::default()
852 }),
853 CharacterState::RepeaterRanged(data) => Some(DurationsInfo {
854 buildup: Some(data.static_data.buildup_duration),
855 action: Some(data.static_data.shoot_duration),
856 recover: Some(data.static_data.recover_duration),
857 ..Default::default()
858 }),
859 CharacterState::Shockwave(data) => Some(DurationsInfo {
860 buildup: Some(data.static_data.buildup_duration),
861 action: Some(data.static_data.swing_duration),
862 recover: Some(data.static_data.recover_duration),
863 ..Default::default()
864 }),
865 CharacterState::BasicBeam(data) => Some(DurationsInfo {
866 buildup: Some(data.static_data.buildup_duration),
867 recover: Some(data.static_data.recover_duration),
868 ..Default::default()
869 }),
870 CharacterState::BasicAura(data) => Some(DurationsInfo {
871 buildup: Some(data.static_data.buildup_duration),
872 action: Some(data.static_data.cast_duration),
873 recover: Some(data.static_data.recover_duration),
874 ..Default::default()
875 }),
876 CharacterState::Blink(data) => Some(DurationsInfo {
877 buildup: Some(data.static_data.buildup_duration),
878 recover: Some(data.static_data.recover_duration),
879 ..Default::default()
880 }),
881 CharacterState::BasicSummon(data) => Some(DurationsInfo {
882 buildup: Some(data.static_data.buildup_duration),
883 action: Some(data.static_data.cast_duration),
884 recover: Some(data.static_data.recover_duration),
885 ..Default::default()
886 }),
887 CharacterState::SelfBuff(data) => Some(DurationsInfo {
888 buildup: Some(data.static_data.buildup_duration),
889 action: Some(data.static_data.cast_duration),
890 recover: Some(data.static_data.recover_duration),
891 ..Default::default()
892 }),
893 CharacterState::SpriteSummon(data) => Some(DurationsInfo {
894 buildup: Some(data.static_data.buildup_duration),
895 action: Some(data.static_data.cast_duration),
896 recover: Some(data.static_data.recover_duration),
897 ..Default::default()
898 }),
899 CharacterState::UseItem(data) => Some(DurationsInfo {
900 buildup: Some(data.static_data.buildup_duration),
901 action: Some(data.static_data.use_duration),
902 recover: Some(data.static_data.recover_duration),
903 ..Default::default()
904 }),
905 CharacterState::Interact(data) => Some(DurationsInfo {
906 buildup: Some(data.static_data.buildup_duration),
907 action: data.static_data.use_duration,
908 recover: Some(data.static_data.recover_duration),
909 ..Default::default()
910 }),
911 CharacterState::FinisherMelee(data) => Some(DurationsInfo {
912 buildup: Some(data.static_data.buildup_duration),
913 action: Some(data.static_data.swing_duration),
914 recover: Some(data.static_data.recover_duration),
915 ..Default::default()
916 }),
917 CharacterState::Music(data) => Some(DurationsInfo {
918 action: Some(data.static_data.play_duration),
919 ..Default::default()
920 }),
921 CharacterState::DiveMelee(data) => Some(DurationsInfo {
922 action: Some(data.static_data.swing_duration),
923 recover: Some(data.static_data.recover_duration),
924 movement: Some(data.static_data.movement_duration),
925 ..Default::default()
926 }),
927 CharacterState::RiposteMelee(data) => Some(DurationsInfo {
928 buildup: Some(data.static_data.buildup_duration),
929 action: Some(data.static_data.swing_duration),
930 recover: Some(if data.whiffed {
931 data.static_data.whiffed_recover_duration
932 } else {
933 data.static_data.recover_duration
934 }),
935 ..Default::default()
936 }),
937 CharacterState::RapidMelee(data) => Some(DurationsInfo {
938 buildup: Some(data.static_data.buildup_duration),
939 action: Some(data.static_data.swing_duration),
940 recover: Some(data.static_data.recover_duration),
941 ..Default::default()
942 }),
943 CharacterState::Transform(data) => Some(DurationsInfo {
944 buildup: Some(data.static_data.buildup_duration),
945 recover: Some(data.static_data.recover_duration),
946 ..Default::default()
947 }),
948 CharacterState::RegrowHead(data) => Some(DurationsInfo {
949 buildup: Some(data.static_data.buildup_duration),
950 recover: Some(data.static_data.recover_duration),
951 ..Default::default()
952 }),
953 CharacterState::StaticAura(data) => Some(DurationsInfo {
954 buildup: Some(data.static_data.buildup_duration),
955 action: Some(data.static_data.cast_duration),
956 recover: Some(data.static_data.recover_duration),
957 ..Default::default()
958 }),
959 }
960 }
961
962 pub fn timer(&self) -> Option<Duration> {
963 match &self {
964 CharacterState::Idle(_) => None,
965 CharacterState::Crawl => None,
966 CharacterState::Talk(_) => None,
967 CharacterState::Climb(_) => None,
968 CharacterState::Wallrun(_) => None,
969 CharacterState::Skate(_) => None,
970 CharacterState::Glide(data) => Some(data.timer),
971 CharacterState::GlideWield(_) => None,
972 CharacterState::Stunned(data) => Some(data.timer),
973 CharacterState::Sit => None,
974 CharacterState::Dance => None,
975 CharacterState::BasicBlock(data) => Some(data.timer),
976 CharacterState::Roll(data) => Some(data.timer),
977 CharacterState::Wielding(_) => None,
978 CharacterState::Equipping(data) => Some(data.timer),
979 CharacterState::ComboMelee2(data) => Some(data.timer),
980 CharacterState::BasicMelee(data) => Some(data.timer),
981 CharacterState::BasicRanged(data) => Some(data.timer),
982 CharacterState::Boost(data) => Some(data.timer),
983 CharacterState::DashMelee(data) => Some(data.timer),
984 CharacterState::LeapMelee(data) => Some(data.timer),
985 CharacterState::LeapShockwave(data) => Some(data.timer),
986 CharacterState::ChargedMelee(data) => Some(data.timer),
987 CharacterState::ChargedRanged(data) => Some(data.timer),
988 CharacterState::RepeaterRanged(data) => Some(data.timer),
989 CharacterState::Shockwave(data) => Some(data.timer),
990 CharacterState::BasicBeam(data) => Some(data.timer),
991 CharacterState::BasicAura(data) => Some(data.timer),
992 CharacterState::Blink(data) => Some(data.timer),
993 CharacterState::BasicSummon(data) => Some(data.timer),
994 CharacterState::SelfBuff(data) => Some(data.timer),
995 CharacterState::SpriteSummon(data) => Some(data.timer),
996 CharacterState::UseItem(data) => Some(data.timer),
997 CharacterState::Interact(data) => Some(data.timer),
998 CharacterState::FinisherMelee(data) => Some(data.timer),
999 CharacterState::Music(data) => Some(data.timer),
1000 CharacterState::DiveMelee(data) => Some(data.timer),
1001 CharacterState::RiposteMelee(data) => Some(data.timer),
1002 CharacterState::RapidMelee(data) => Some(data.timer),
1003 CharacterState::Transform(data) => Some(data.timer),
1004 CharacterState::RegrowHead(data) => Some(data.timer),
1005 CharacterState::StaticAura(data) => Some(data.timer),
1006 }
1007 }
1008
1009 pub fn attack_kind(&self) -> Option<AttackSource> {
1010 match self {
1011 CharacterState::Idle(_) => None,
1012 CharacterState::Crawl => None,
1013 CharacterState::Talk(_) => None,
1014 CharacterState::Climb(_) => None,
1015 CharacterState::Wallrun(_) => None,
1016 CharacterState::Skate(_) => None,
1017 CharacterState::Glide(_) => None,
1018 CharacterState::GlideWield(_) => None,
1019 CharacterState::Stunned(_) => None,
1020 CharacterState::Sit => None,
1021 CharacterState::Dance => None,
1022 CharacterState::BasicBlock(_) => None,
1023 CharacterState::Roll(_) => None,
1024 CharacterState::Wielding(_) => None,
1025 CharacterState::Equipping(_) => None,
1026 CharacterState::ComboMelee2(_) => Some(AttackSource::Melee),
1027 CharacterState::BasicMelee(_) => Some(AttackSource::Melee),
1028 CharacterState::BasicRanged(data) => {
1029 Some(if data.static_data.projectile.is_explosive() {
1030 AttackSource::Explosion
1031 } else {
1032 AttackSource::Projectile
1033 })
1034 },
1035 CharacterState::Boost(_) => None,
1036 CharacterState::DashMelee(_) => Some(AttackSource::Melee),
1037 CharacterState::LeapMelee(_) => Some(AttackSource::Melee),
1038 CharacterState::ChargedMelee(_) => Some(AttackSource::Melee),
1039 CharacterState::ChargedRanged(_) => Some(AttackSource::Projectile),
1041 CharacterState::RepeaterRanged(data) => {
1042 Some(if data.static_data.projectile.is_explosive() {
1043 AttackSource::Explosion
1044 } else {
1045 AttackSource::Projectile
1046 })
1047 },
1048 CharacterState::Shockwave(data) => {
1049 Some(data.static_data.dodgeable.shockwave_attack_source())
1050 },
1051 CharacterState::LeapShockwave(data) => {
1052 Some(data.static_data.dodgeable.shockwave_attack_source())
1053 },
1054 CharacterState::BasicBeam(_) => Some(AttackSource::Beam),
1055 CharacterState::BasicAura(_) => None,
1056 CharacterState::Blink(_) => None,
1057 CharacterState::BasicSummon(_) => None,
1058 CharacterState::SelfBuff(_) => None,
1059 CharacterState::SpriteSummon(_) => None,
1060 CharacterState::UseItem(_) => None,
1061 CharacterState::Interact(_) => None,
1062 CharacterState::FinisherMelee(_) => Some(AttackSource::Melee),
1063 CharacterState::Music(_) => None,
1064 CharacterState::DiveMelee(_) => Some(AttackSource::Melee),
1065 CharacterState::RiposteMelee(_) => Some(AttackSource::Melee),
1066 CharacterState::RapidMelee(_) => Some(AttackSource::Melee),
1067 CharacterState::Transform(_) => None,
1068 CharacterState::RegrowHead(_) => None,
1069 CharacterState::StaticAura(_) => None,
1070 }
1071 }
1072}
1073
1074#[derive(Default, Copy, Clone)]
1075pub struct DurationsInfo {
1076 pub buildup: Option<Duration>,
1077 pub action: Option<Duration>,
1078 pub recover: Option<Duration>,
1079 pub movement: Option<Duration>,
1080 pub charge: Option<Duration>,
1081}
1082
1083#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq)]
1084pub struct AttackFilters {
1085 pub melee: bool,
1086 pub projectiles: bool,
1087 pub beams: bool,
1088 pub ground_shockwaves: bool,
1089 pub air_shockwaves: bool,
1090 pub explosions: bool,
1091}
1092
1093impl AttackFilters {
1094 pub fn applies(&self, attack: AttackSource) -> bool {
1095 match attack {
1096 AttackSource::Melee => self.melee,
1097 AttackSource::Projectile => self.projectiles,
1098 AttackSource::Beam => self.beams,
1099 AttackSource::GroundShockwave => self.ground_shockwaves,
1100 AttackSource::AirShockwave => self.air_shockwaves,
1101 AttackSource::UndodgeableShockwave => false,
1102 AttackSource::Explosion => self.explosions,
1103 }
1104 }
1105}
1106
1107impl Default for CharacterState {
1108 fn default() -> Self {
1109 Self::Idle(idle::Data {
1110 is_sneaking: false,
1111 footwear: None,
1112 time_entered: Time(0.0),
1113 })
1114 }
1115}
1116
1117impl Component for CharacterState {
1118 type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
1119}
1120
1121#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
1128pub struct CharacterActivity {
1129 pub look_dir: Option<Dir>,
1132 pub steer_dir: f32,
1136 pub is_pet_staying: bool,
1139}
1140
1141impl Component for CharacterActivity {
1142 type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
1143}