veloren_common/comp/
character_state.rs

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
23/// Data returned from character behavior fn's to Character Behavior System.
24pub 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        throw: event::ThrowEvent,
44        teleport_to: event::TeleportToEvent,
45        shockwave: event::ShockwaveEvent,
46        explosion: event::ExplosionEvent,
47        buff: event::BuffEvent,
48        inventory_manip: event::InventoryManipEvent,
49        sprite_summon: event::CreateSpriteEvent,
50        beam_pillar_summon: event::SummonBeamPillarsEvent,
51        change_stance: event::ChangeStanceEvent,
52        create_npc: event::CreateNpcEvent,
53        create_object: event::CreateObjectEvent,
54        energy_change: event::EnergyChangeEvent,
55        knockback: event::KnockbackEvent,
56        sprite_light: event::ToggleSpriteLightEvent,
57        transform: event::TransformEvent,
58        regrow_head: event::RegrowHeadEvent,
59        create_aura_entity: event::CreateAuraEntityEvent,
60        help_downed: event::HelpDownedEvent,
61    }
62}
63
64pub struct OutputEvents<'a, 'b> {
65    local: &'a mut Vec<LocalEvent>,
66    server: &'a mut CharacterStateEventEmitters<'b>,
67}
68
69impl<'a, 'b: 'a> OutputEvents<'a, 'b> {
70    pub fn new(
71        local: &'a mut Vec<LocalEvent>,
72        server: &'a mut CharacterStateEventEmitters<'b>,
73    ) -> Self {
74        Self { local, server }
75    }
76
77    pub fn emit_local(&mut self, event: LocalEvent) { self.local.push(event); }
78
79    pub fn emit_server<E>(&mut self, event: E)
80    where
81        CharacterStateEventEmitters<'b>: EmitExt<E>,
82    {
83        self.server.emit(event);
84    }
85}
86
87impl From<&JoinData<'_>> for StateUpdate {
88    fn from(data: &JoinData) -> Self {
89        StateUpdate {
90            pos: *data.pos,
91            vel: *data.vel,
92            ori: *data.ori,
93            density: *data.density,
94            energy: *data.energy,
95            swap_equipped_weapons: false,
96            should_strafe: data.inputs.strafing,
97            character: data.character.clone(),
98            queued_inputs: BTreeMap::new(),
99            removed_inputs: Vec::new(),
100            character_activity: data.character_activity.clone(),
101        }
102    }
103}
104#[derive(Clone, Debug, Display, PartialEq, Serialize, Deserialize)]
105pub enum CharacterState {
106    Idle(idle::Data),
107    Crawl,
108    Climb(climb::Data),
109    Sit,
110    Dance,
111    Talk(talk::Data),
112    Glide(glide::Data),
113    GlideWield(glide_wield::Data),
114    /// A stunned state
115    Stunned(stunned::Data),
116    /// A basic blocking state
117    BasicBlock(basic_block::Data),
118    /// Player is busy equipping or unequipping weapons
119    Equipping(equipping::Data),
120    /// Player is holding a weapon and can perform other actions
121    Wielding(wielding::Data),
122    /// A dodge where player can roll
123    Roll(roll::Data),
124    /// A basic melee attack (e.g. sword)
125    BasicMelee(basic_melee::Data),
126    /// A basic ranged attack (e.g. bow)
127    BasicRanged(basic_ranged::Data),
128    /// A force will boost you into a direction for some duration
129    Boost(boost::Data),
130    /// Dash forward and then attack
131    DashMelee(dash_melee::Data),
132    /// A state where you progress through multiple melee attacks
133    ComboMelee2(combo_melee2::Data),
134    /// A leap followed by an explosion and a shockwave
135    LeapExplosionShockwave(leap_explosion_shockwave::Data),
136    /// A leap followed by a small aoe ground attack
137    LeapMelee(leap_melee::Data),
138    /// A leap followed by a shockwave
139    LeapShockwave(leap_shockwave::Data),
140    /// A charged ranged attack (e.g. bow)
141    ChargedRanged(charged_ranged::Data),
142    /// A charged melee attack
143    ChargedMelee(charged_melee::Data),
144    /// A repeating ranged attack
145    RepeaterRanged(repeater_ranged::Data),
146    /// An item throw
147    Throw(throw::Data),
148    /// A ground shockwave attack
149    Shockwave(shockwave::Data),
150    /// An explosion attack
151    Explosion(explosion::Data),
152    /// A continuous attack that affects all creatures in a cone originating
153    /// from the source
154    BasicBeam(basic_beam::Data),
155    /// Creates an aura that persists as long as you are actively casting
156    BasicAura(basic_aura::Data),
157    /// Creates an aura that is attached to a pseudo entity, so it doesn't move
158    /// with you Optionally allows for sprites to be created as well
159    StaticAura(static_aura::Data),
160    /// A short teleport that targets either a position or entity
161    Blink(blink::Data),
162    /// Summons creatures that fight for the caster
163    BasicSummon(basic_summon::Data),
164    /// Inserts a buff on the caster
165    SelfBuff(self_buff::Data),
166    /// Creates sprites around the caster
167    SpriteSummon(sprite_summon::Data),
168    /// Handles logic for using an item so it is not simply instant
169    UseItem(use_item::Data),
170    /// Handles logic for interacting with a sprite or an entity, e.g. using a
171    /// chest, picking a plant, helping a downed entity up
172    Interact(interact::Data),
173    /// Runs on the wall
174    Wallrun(wallrun::Data),
175    /// Ice skating or skiing
176    Skate(skate::Data),
177    /// Play music instrument
178    Music(music::Data),
179    /// Melee attack that scales off and consumes combo
180    FinisherMelee(finisher_melee::Data),
181    /// State entered when diving, melee attack triggered upon landing on the
182    /// ground
183    DiveMelee(dive_melee::Data),
184    /// Attack that attempts to parry, and if it parries moves to an attack
185    RiposteMelee(riposte_melee::Data),
186    /// A series of consecutive, identical attacks that only go through buildup
187    /// and recover once for the entire state
188    RapidMelee(rapid_melee::Data),
189    /// Transforms an entity into another
190    Transform(transform::Data),
191    /// Regrow a missing head
192    RegrowHead(regrow_head::Data),
193}
194
195impl CharacterState {
196    pub fn is_wield(&self) -> bool {
197        match self {
198            CharacterState::Wallrun(wallrun::Data { was_wielded })
199            | CharacterState::Climb(climb::Data { was_wielded, .. })
200            | CharacterState::Roll(roll::Data { was_wielded, .. })
201            | CharacterState::Stunned(stunned::Data { was_wielded, .. }) => *was_wielded,
202            CharacterState::Wielding(_)
203            | CharacterState::BasicMelee(_)
204            | CharacterState::BasicRanged(_)
205            | CharacterState::Throw(_)
206            | CharacterState::DashMelee(_)
207            | CharacterState::ComboMelee2(_)
208            | CharacterState::BasicBlock(_)
209            | CharacterState::LeapMelee(_)
210            | CharacterState::LeapShockwave(_)
211            | CharacterState::LeapExplosionShockwave(_)
212            | CharacterState::Explosion(_)
213            | CharacterState::ChargedMelee(_)
214            | CharacterState::ChargedRanged(_)
215            | CharacterState::RepeaterRanged(_)
216            | CharacterState::Shockwave(_)
217            | CharacterState::BasicBeam(_)
218            | CharacterState::BasicAura(_)
219            | CharacterState::SelfBuff(_)
220            | CharacterState::Blink(_)
221            | CharacterState::Music(_)
222            | CharacterState::BasicSummon(_)
223            | CharacterState::SpriteSummon(_)
224            | CharacterState::FinisherMelee(_)
225            | CharacterState::DiveMelee(_)
226            | CharacterState::RiposteMelee(_)
227            | CharacterState::RapidMelee(_)
228            | CharacterState::StaticAura(_) => true,
229            CharacterState::Idle(_)
230            | CharacterState::Crawl
231            | CharacterState::Sit
232            | CharacterState::Dance
233            | CharacterState::Talk(_)
234            | CharacterState::Glide(_)
235            | CharacterState::GlideWield(_)
236            | CharacterState::Equipping(_)
237            | CharacterState::Boost(_)
238            | CharacterState::UseItem(_)
239            | CharacterState::Interact(_)
240            | CharacterState::Skate(_)
241            | CharacterState::Transform(_)
242            | CharacterState::RegrowHead(_) => false,
243        }
244    }
245
246    pub fn is_ranged(&self) -> bool {
247        matches!(
248            self,
249            CharacterState::BasicRanged(_)
250                | CharacterState::Throw(_)
251                | CharacterState::ChargedRanged(_)
252                | CharacterState::RepeaterRanged(_)
253        )
254    }
255
256    /// If this state can manipulate loadout, interact with sprites etc.
257    pub fn can_interact(&self) -> bool {
258        match self {
259            CharacterState::Idle(_)
260            | CharacterState::Sit
261            | CharacterState::Dance
262            | CharacterState::Talk(_)
263            | CharacterState::Equipping(_)
264            | CharacterState::Wielding(_)
265            | CharacterState::GlideWield(_) => true,
266            CharacterState::Crawl
267            | CharacterState::Climb(_)
268            | CharacterState::Glide(_)
269            | CharacterState::Stunned(_)
270            | CharacterState::BasicBlock(_)
271            | CharacterState::Roll(_)
272            | CharacterState::BasicMelee(_)
273            | CharacterState::BasicRanged(_)
274            | CharacterState::Throw(_)
275            | CharacterState::Boost(_)
276            | CharacterState::DashMelee(_)
277            | CharacterState::ComboMelee2(_)
278            | CharacterState::LeapExplosionShockwave(_)
279            | CharacterState::LeapMelee(_)
280            | CharacterState::LeapShockwave(_)
281            | CharacterState::ChargedRanged(_)
282            | CharacterState::ChargedMelee(_)
283            | CharacterState::RepeaterRanged(_)
284            | CharacterState::Shockwave(_)
285            | CharacterState::Explosion(_)
286            | CharacterState::BasicBeam(_)
287            | CharacterState::BasicAura(_)
288            | CharacterState::StaticAura(_)
289            | CharacterState::Blink(_)
290            | CharacterState::BasicSummon(_)
291            | CharacterState::SelfBuff(_)
292            | CharacterState::SpriteSummon(_)
293            | CharacterState::UseItem(_)
294            | CharacterState::Interact(_)
295            | CharacterState::Wallrun(_)
296            | CharacterState::Skate(_)
297            | CharacterState::Music(_)
298            | CharacterState::FinisherMelee(_)
299            | CharacterState::DiveMelee(_)
300            | CharacterState::RiposteMelee(_)
301            | CharacterState::RapidMelee(_)
302            | CharacterState::Transform(_)
303            | CharacterState::RegrowHead(_) => false,
304        }
305    }
306
307    pub fn was_wielded(&self) -> bool {
308        match self {
309            CharacterState::Roll(data) => data.was_wielded,
310            CharacterState::Stunned(data) => data.was_wielded,
311            CharacterState::Interact(data) => data.static_data.was_wielded,
312            CharacterState::UseItem(data) => data.static_data.was_wielded,
313            CharacterState::Wallrun(data) => data.was_wielded,
314            CharacterState::Climb(data) => data.was_wielded,
315            CharacterState::Idle(_)
316            | CharacterState::Crawl
317            | CharacterState::Sit
318            | CharacterState::Dance
319            | CharacterState::Talk(_)
320            | CharacterState::Glide(_)
321            | CharacterState::GlideWield(_)
322            | CharacterState::BasicBlock(_)
323            | CharacterState::Equipping(_)
324            | CharacterState::Wielding(_)
325            | CharacterState::BasicMelee(_)
326            | CharacterState::BasicRanged(_)
327            | CharacterState::Throw(_)
328            | CharacterState::Boost(_)
329            | CharacterState::DashMelee(_)
330            | CharacterState::ComboMelee2(_)
331            | CharacterState::LeapMelee(_)
332            | CharacterState::LeapShockwave(_)
333            | CharacterState::LeapExplosionShockwave(_)
334            | CharacterState::Explosion(_)
335            | CharacterState::ChargedRanged(_)
336            | CharacterState::ChargedMelee(_)
337            | CharacterState::RepeaterRanged(_)
338            | CharacterState::Shockwave(_)
339            | CharacterState::BasicBeam(_)
340            | CharacterState::BasicAura(_)
341            | CharacterState::StaticAura(_)
342            | CharacterState::Blink(_)
343            | CharacterState::BasicSummon(_)
344            | CharacterState::SelfBuff(_)
345            | CharacterState::SpriteSummon(_)
346            | CharacterState::Skate(_)
347            | CharacterState::Music(_)
348            | CharacterState::FinisherMelee(_)
349            | CharacterState::DiveMelee(_)
350            | CharacterState::RiposteMelee(_)
351            | CharacterState::RapidMelee(_)
352            | CharacterState::Transform(_)
353            | CharacterState::RegrowHead(_) => false,
354        }
355    }
356
357    pub fn is_glide_wielded(&self) -> bool {
358        matches!(
359            self,
360            CharacterState::Glide { .. } | CharacterState::GlideWield { .. }
361        )
362    }
363
364    pub fn is_stealthy(&self) -> bool {
365        matches!(
366            self,
367            CharacterState::Idle(idle::Data {
368                is_sneaking: true,
369                footwear: _,
370                time_entered: _,
371            }) | CharacterState::Wielding(wielding::Data {
372                is_sneaking: true,
373                ..
374            }) | CharacterState::Roll(roll::Data {
375                is_sneaking: true,
376                ..
377            })
378        )
379    }
380
381    pub fn should_follow_look(&self) -> bool {
382        matches!(self, CharacterState::Boost(_)) || self.is_attack()
383    }
384
385    pub fn is_attack(&self) -> bool {
386        matches!(
387            self,
388            CharacterState::BasicMelee(_)
389                | CharacterState::BasicRanged(_)
390                | CharacterState::DashMelee(_)
391                | CharacterState::ComboMelee2(_)
392                | CharacterState::LeapExplosionShockwave(_)
393                | CharacterState::LeapMelee(_)
394                | CharacterState::LeapShockwave(_)
395                | CharacterState::ChargedMelee(_)
396                | CharacterState::ChargedRanged(_)
397                | CharacterState::RepeaterRanged(_)
398                | CharacterState::Throw(_)
399                | CharacterState::Shockwave(_)
400                | CharacterState::Explosion(_)
401                | CharacterState::BasicBeam(_)
402                | CharacterState::BasicAura(_)
403                | CharacterState::SelfBuff(_)
404                | CharacterState::Blink(_)
405                | CharacterState::BasicSummon(_)
406                | CharacterState::SpriteSummon(_)
407                | CharacterState::FinisherMelee(_)
408                | CharacterState::DiveMelee(_)
409                | CharacterState::RiposteMelee(_)
410                | CharacterState::RapidMelee(_)
411                | CharacterState::StaticAura(_)
412        )
413    }
414
415    pub fn is_aimed(&self) -> bool {
416        matches!(
417            self,
418            CharacterState::BasicMelee(_)
419                | CharacterState::BasicRanged(_)
420                | CharacterState::DashMelee(_)
421                | CharacterState::ComboMelee2(_)
422                | CharacterState::BasicBlock(_)
423                | CharacterState::LeapExplosionShockwave(_)
424                | CharacterState::LeapMelee(_)
425                | CharacterState::LeapShockwave(_)
426                | CharacterState::ChargedMelee(_)
427                | CharacterState::ChargedRanged(_)
428                | CharacterState::RepeaterRanged(_)
429                | CharacterState::Throw(_)
430                | CharacterState::Shockwave(_)
431                | CharacterState::BasicBeam(_)
432                | CharacterState::Stunned(_)
433                | CharacterState::Wielding(_)
434                | CharacterState::FinisherMelee(_)
435                | CharacterState::DiveMelee(_)
436                | CharacterState::RiposteMelee(_)
437                | CharacterState::RapidMelee(_)
438        )
439    }
440
441    pub fn is_using_hands(&self) -> bool {
442        matches!(
443            self,
444            CharacterState::Climb(_)
445                | CharacterState::Equipping(_)
446                | CharacterState::Dance
447                | CharacterState::Glide(_)
448                | CharacterState::GlideWield(_)
449                | CharacterState::Talk(_)
450                | CharacterState::Roll(_),
451        )
452    }
453
454    pub fn is_parry(&self, attack_source: AttackSource) -> bool {
455        let melee = matches!(attack_source, AttackSource::Melee);
456        let from_capability_melee = melee
457            && self
458                .ability_info()
459                .map(|a| a.ability_meta.capabilities)
460                .is_some_and(|c| {
461                    c.contains(Capability::PARRIES_MELEE)
462                        && matches!(
463                            self.stage_section(),
464                            Some(StageSection::Buildup | StageSection::Action)
465                        )
466                });
467        let from_capability = matches!(
468            attack_source,
469            AttackSource::Melee
470                | AttackSource::Projectile
471                | AttackSource::Beam
472                | AttackSource::AirShockwave
473                | AttackSource::Explosion
474        ) && self
475            .ability_info()
476            .map(|a| a.ability_meta.capabilities)
477            .is_some_and(|c| {
478                c.contains(Capability::PARRIES)
479                    && matches!(
480                        self.stage_section(),
481                        Some(StageSection::Buildup | StageSection::Action)
482                    )
483            });
484        let from_state = match self {
485            CharacterState::BasicBlock(c) => c.is_parry(attack_source),
486            CharacterState::RiposteMelee(c) => {
487                melee
488                    && matches!(
489                        c.stage_section,
490                        StageSection::Buildup | StageSection::Action
491                    )
492            },
493            _ => false,
494        };
495        from_capability_melee || from_capability || from_state
496    }
497
498    pub fn is_block(&self, attack_source: AttackSource) -> bool {
499        match self {
500            CharacterState::BasicBlock(data) => {
501                data.static_data.blocked_attacks.applies(attack_source)
502                    && matches!(
503                        self.stage_section(),
504                        Some(StageSection::Buildup | StageSection::Action)
505                    )
506            },
507            _ => self
508                .ability_info()
509                .map(|ability| ability.ability_meta.capabilities)
510                .is_some_and(|capabilities| {
511                    capabilities.contains(Capability::BLOCKS)
512                        && matches!(
513                            self.stage_section(),
514                            Some(StageSection::Buildup | StageSection::Action)
515                        )
516                        && matches!(attack_source, AttackSource::Melee)
517                }),
518        }
519    }
520
521    /// In radians
522    pub fn block_angle(&self) -> f32 {
523        match self {
524            CharacterState::BasicBlock(c) => c.static_data.max_angle.to_radians(),
525            CharacterState::ComboMelee2(c) => {
526                let strike_data =
527                    c.static_data.strikes[c.completed_strikes % c.static_data.strikes.len()];
528                strike_data.melee_constructor.angle.to_radians()
529            },
530            CharacterState::RiposteMelee(c) => c.static_data.melee_constructor.angle.to_radians(),
531            // TODO: Add more here as needed, maybe look into having character state return the
532            // melee constructor if it has one and using that?
533            _ => 0.0,
534        }
535    }
536
537    pub fn is_dodge(&self) -> bool {
538        if let CharacterState::Roll(c) = self {
539            matches!(
540                c.stage_section,
541                StageSection::Buildup | StageSection::Movement
542            )
543        } else {
544            false
545        }
546    }
547
548    pub fn is_glide(&self) -> bool { matches!(self, CharacterState::Glide(_)) }
549
550    pub fn is_skate(&self) -> bool { matches!(self, CharacterState::Skate(_)) }
551
552    pub fn is_music(&self) -> bool { matches!(self, CharacterState::Music(_)) }
553
554    pub fn roll_attack_immunities(&self) -> Option<AttackFilters> {
555        if self.is_dodge()
556            && let CharacterState::Roll(c) = self
557        {
558            Some(c.static_data.attack_immunities)
559        } else {
560            None
561        }
562    }
563
564    pub fn is_stunned(&self) -> bool { matches!(self, CharacterState::Stunned(_)) }
565
566    pub fn is_forced_movement(&self) -> bool {
567        matches!(self, CharacterState::ComboMelee2(s) if s.stage_section == StageSection::Action)
568            || matches!(self, CharacterState::DashMelee(s) if s.stage_section == StageSection::Charge)
569            || matches!(self, CharacterState::LeapMelee(s) if s.stage_section == StageSection::Movement)
570            || matches!(self, CharacterState::Roll(s) if s.stage_section == StageSection::Movement)
571    }
572
573    pub fn is_melee_attack(&self) -> bool { self.attack_kind().contains(&AttackSource::Melee) }
574
575    pub fn is_beam_attack(&self) -> bool { self.attack_kind().contains(&AttackSource::Beam) }
576
577    pub fn can_perform_mounted(&self) -> bool {
578        matches!(
579            self,
580            CharacterState::Idle(_)
581                | CharacterState::Sit
582                | CharacterState::Dance
583                | CharacterState::Talk(_)
584                | CharacterState::Stunned(_)
585                | CharacterState::BasicBlock(_)
586                | CharacterState::Equipping(_)
587                | CharacterState::Wielding(_)
588                | CharacterState::BasicMelee(_)
589                | CharacterState::BasicRanged(_)
590                | CharacterState::ComboMelee2(_)
591                | CharacterState::ChargedRanged(_)
592                | CharacterState::RepeaterRanged(_)
593                | CharacterState::Throw(_)
594                | CharacterState::BasicBeam(_)
595                | CharacterState::BasicAura(_)
596                | CharacterState::BasicSummon(_)
597                | CharacterState::SelfBuff(_)
598                | CharacterState::SpriteSummon(_)
599                | CharacterState::UseItem(_)
600                | CharacterState::Interact(_)
601                | CharacterState::Music(_)
602                | CharacterState::RiposteMelee(_)
603                | CharacterState::RapidMelee(_)
604        )
605    }
606
607    /// A subset of `can_perform_mounted` actions that allow the character
608    /// to change their orientation towards their look dir when mounted.
609    pub fn can_look_while_mounted(&self) -> bool {
610        !matches!(
611            self,
612            CharacterState::Idle(_)
613                | CharacterState::Sit
614                | CharacterState::Dance
615                | CharacterState::Talk(_)
616                | CharacterState::Stunned(_)
617                | CharacterState::Equipping(_)
618                | CharacterState::SelfBuff(_)
619                | CharacterState::BasicAura(_)
620        ) && self.can_perform_mounted()
621    }
622
623    pub fn is_sitting(&self) -> bool {
624        use use_item::{Data, ItemUseKind, StaticData};
625        matches!(
626            self,
627            CharacterState::Sit
628                | CharacterState::UseItem(Data {
629                    static_data: StaticData {
630                        item_kind: ItemUseKind::Consumable(
631                            ConsumableKind::ComplexFood | ConsumableKind::Food
632                        ),
633                        ..
634                    },
635                    ..
636                })
637        )
638    }
639
640    /// Compares for shallow equality (does not check internal struct equality)
641    pub fn same_variant(&self, other: &Self) -> bool {
642        // Check if state is the same without looking at the inner data
643        std::mem::discriminant(self) == std::mem::discriminant(other)
644    }
645
646    pub fn behavior(&self, j: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
647        match &self {
648            CharacterState::Idle(data) => data.behavior(j, output_events),
649            CharacterState::Talk(data) => data.behavior(j, output_events),
650            CharacterState::Climb(data) => data.behavior(j, output_events),
651            CharacterState::Wallrun(data) => data.behavior(j, output_events),
652            CharacterState::Glide(data) => data.behavior(j, output_events),
653            CharacterState::GlideWield(data) => data.behavior(j, output_events),
654            CharacterState::Stunned(data) => data.behavior(j, output_events),
655            CharacterState::Sit => sit::Data::behavior(&sit::Data, j, output_events),
656            CharacterState::Crawl => crawl::Data::behavior(&crawl::Data, j, output_events),
657            CharacterState::Dance => dance::Data::behavior(&dance::Data, j, output_events),
658            CharacterState::BasicBlock(data) => data.behavior(j, output_events),
659            CharacterState::Roll(data) => data.behavior(j, output_events),
660            CharacterState::Wielding(data) => data.behavior(j, output_events),
661            CharacterState::Equipping(data) => data.behavior(j, output_events),
662            CharacterState::ComboMelee2(data) => data.behavior(j, output_events),
663            CharacterState::BasicMelee(data) => data.behavior(j, output_events),
664            CharacterState::BasicRanged(data) => data.behavior(j, output_events),
665            CharacterState::Boost(data) => data.behavior(j, output_events),
666            CharacterState::DashMelee(data) => data.behavior(j, output_events),
667            CharacterState::LeapExplosionShockwave(data) => data.behavior(j, output_events),
668            CharacterState::LeapMelee(data) => data.behavior(j, output_events),
669            CharacterState::LeapShockwave(data) => data.behavior(j, output_events),
670            CharacterState::ChargedMelee(data) => data.behavior(j, output_events),
671            CharacterState::ChargedRanged(data) => data.behavior(j, output_events),
672            CharacterState::RepeaterRanged(data) => data.behavior(j, output_events),
673            CharacterState::Throw(data) => data.behavior(j, output_events),
674            CharacterState::Shockwave(data) => data.behavior(j, output_events),
675            CharacterState::Explosion(data) => data.behavior(j, output_events),
676            CharacterState::BasicBeam(data) => data.behavior(j, output_events),
677            CharacterState::BasicAura(data) => data.behavior(j, output_events),
678            CharacterState::Blink(data) => data.behavior(j, output_events),
679            CharacterState::BasicSummon(data) => data.behavior(j, output_events),
680            CharacterState::SelfBuff(data) => data.behavior(j, output_events),
681            CharacterState::SpriteSummon(data) => data.behavior(j, output_events),
682            CharacterState::UseItem(data) => data.behavior(j, output_events),
683            CharacterState::Interact(data) => data.behavior(j, output_events),
684            CharacterState::Skate(data) => data.behavior(j, output_events),
685            CharacterState::Music(data) => data.behavior(j, output_events),
686            CharacterState::FinisherMelee(data) => data.behavior(j, output_events),
687            CharacterState::DiveMelee(data) => data.behavior(j, output_events),
688            CharacterState::RiposteMelee(data) => data.behavior(j, output_events),
689            CharacterState::RapidMelee(data) => data.behavior(j, output_events),
690            CharacterState::Transform(data) => data.behavior(j, output_events),
691            CharacterState::RegrowHead(data) => data.behavior(j, output_events),
692            CharacterState::StaticAura(data) => data.behavior(j, output_events),
693        }
694    }
695
696    pub fn handle_event(
697        &self,
698        j: &JoinData,
699        output_events: &mut OutputEvents,
700        action: ControlAction,
701    ) -> StateUpdate {
702        match &self {
703            CharacterState::Idle(data) => data.handle_event(j, output_events, action),
704            CharacterState::Talk(data) => data.handle_event(j, output_events, action),
705            CharacterState::Climb(data) => data.handle_event(j, output_events, action),
706            CharacterState::Wallrun(data) => data.handle_event(j, output_events, action),
707            CharacterState::Glide(data) => data.handle_event(j, output_events, action),
708            CharacterState::GlideWield(data) => data.handle_event(j, output_events, action),
709            CharacterState::Stunned(data) => data.handle_event(j, output_events, action),
710            CharacterState::Sit => {
711                states::sit::Data::handle_event(&sit::Data, j, output_events, action)
712            },
713            CharacterState::Crawl => {
714                states::crawl::Data::handle_event(&crawl::Data, j, output_events, action)
715            },
716            CharacterState::Dance => {
717                states::dance::Data::handle_event(&dance::Data, j, output_events, action)
718            },
719            CharacterState::BasicBlock(data) => data.handle_event(j, output_events, action),
720            CharacterState::Roll(data) => data.handle_event(j, output_events, action),
721            CharacterState::Wielding(data) => data.handle_event(j, output_events, action),
722            CharacterState::Equipping(data) => data.handle_event(j, output_events, action),
723            CharacterState::ComboMelee2(data) => data.handle_event(j, output_events, action),
724            CharacterState::BasicMelee(data) => data.handle_event(j, output_events, action),
725            CharacterState::BasicRanged(data) => data.handle_event(j, output_events, action),
726            CharacterState::Boost(data) => data.handle_event(j, output_events, action),
727            CharacterState::DashMelee(data) => data.handle_event(j, output_events, action),
728            CharacterState::LeapExplosionShockwave(data) => {
729                data.handle_event(j, output_events, action)
730            },
731            CharacterState::LeapMelee(data) => data.handle_event(j, output_events, action),
732            CharacterState::LeapShockwave(data) => data.handle_event(j, output_events, action),
733            CharacterState::ChargedMelee(data) => data.handle_event(j, output_events, action),
734            CharacterState::ChargedRanged(data) => data.handle_event(j, output_events, action),
735            CharacterState::RepeaterRanged(data) => data.handle_event(j, output_events, action),
736            CharacterState::Throw(data) => data.handle_event(j, output_events, action),
737            CharacterState::Shockwave(data) => data.handle_event(j, output_events, action),
738            CharacterState::Explosion(data) => data.handle_event(j, output_events, action),
739            CharacterState::BasicBeam(data) => data.handle_event(j, output_events, action),
740            CharacterState::BasicAura(data) => data.handle_event(j, output_events, action),
741            CharacterState::Blink(data) => data.handle_event(j, output_events, action),
742            CharacterState::BasicSummon(data) => data.handle_event(j, output_events, action),
743            CharacterState::SelfBuff(data) => data.handle_event(j, output_events, action),
744            CharacterState::SpriteSummon(data) => data.handle_event(j, output_events, action),
745            CharacterState::UseItem(data) => data.handle_event(j, output_events, action),
746            CharacterState::Interact(data) => data.handle_event(j, output_events, action),
747            CharacterState::Skate(data) => data.handle_event(j, output_events, action),
748            CharacterState::Music(data) => data.handle_event(j, output_events, action),
749            CharacterState::FinisherMelee(data) => data.handle_event(j, output_events, action),
750            CharacterState::DiveMelee(data) => data.handle_event(j, output_events, action),
751            CharacterState::RiposteMelee(data) => data.handle_event(j, output_events, action),
752            CharacterState::RapidMelee(data) => data.handle_event(j, output_events, action),
753            CharacterState::Transform(data) => data.handle_event(j, output_events, action),
754            CharacterState::RegrowHead(data) => data.handle_event(j, output_events, action),
755            CharacterState::StaticAura(data) => data.handle_event(j, output_events, action),
756        }
757    }
758
759    pub fn footwear(&self) -> Option<Friction> {
760        match &self {
761            CharacterState::Idle(data) => data.footwear,
762            CharacterState::Skate(data) => Some(data.footwear),
763            _ => None,
764        }
765    }
766
767    pub fn ability_info(&self) -> Option<AbilityInfo> {
768        match &self {
769            CharacterState::Idle(_) => None,
770            CharacterState::Talk(_) => None,
771            CharacterState::Climb(_) => None,
772            CharacterState::Wallrun(_) => None,
773            CharacterState::Skate(_) => None,
774            CharacterState::Glide(_) => None,
775            CharacterState::GlideWield(_) => None,
776            CharacterState::Stunned(_) => None,
777            CharacterState::Sit => None,
778            CharacterState::Crawl => None,
779            CharacterState::Dance => None,
780            CharacterState::BasicBlock(data) => Some(data.static_data.ability_info),
781            CharacterState::Roll(data) => Some(data.static_data.ability_info),
782            CharacterState::Wielding(_) => None,
783            CharacterState::Equipping(_) => None,
784            CharacterState::ComboMelee2(data) => Some(data.static_data.ability_info),
785            CharacterState::BasicMelee(data) => Some(data.static_data.ability_info),
786            CharacterState::BasicRanged(data) => Some(data.static_data.ability_info),
787            CharacterState::Boost(data) => Some(data.static_data.ability_info),
788            CharacterState::DashMelee(data) => Some(data.static_data.ability_info),
789            CharacterState::LeapExplosionShockwave(data) => Some(data.static_data.ability_info),
790            CharacterState::LeapMelee(data) => Some(data.static_data.ability_info),
791            CharacterState::LeapShockwave(data) => Some(data.static_data.ability_info),
792            CharacterState::ChargedMelee(data) => Some(data.static_data.ability_info),
793            CharacterState::ChargedRanged(data) => Some(data.static_data.ability_info),
794            CharacterState::RepeaterRanged(data) => Some(data.static_data.ability_info),
795            CharacterState::Throw(data) => Some(data.static_data.ability_info),
796            CharacterState::Shockwave(data) => Some(data.static_data.ability_info),
797            CharacterState::Explosion(data) => Some(data.static_data.ability_info),
798            CharacterState::BasicBeam(data) => Some(data.static_data.ability_info),
799            CharacterState::BasicAura(data) => Some(data.static_data.ability_info),
800            CharacterState::Blink(data) => Some(data.static_data.ability_info),
801            CharacterState::BasicSummon(data) => Some(data.static_data.ability_info),
802            CharacterState::SelfBuff(data) => Some(data.static_data.ability_info),
803            CharacterState::SpriteSummon(data) => Some(data.static_data.ability_info),
804            CharacterState::UseItem(_) => None,
805            CharacterState::Interact(_) => None,
806            CharacterState::FinisherMelee(data) => Some(data.static_data.ability_info),
807            CharacterState::Music(data) => Some(data.static_data.ability_info),
808            CharacterState::DiveMelee(data) => Some(data.static_data.ability_info),
809            CharacterState::RiposteMelee(data) => Some(data.static_data.ability_info),
810            CharacterState::RapidMelee(data) => Some(data.static_data.ability_info),
811            CharacterState::Transform(data) => Some(data.static_data.ability_info),
812            CharacterState::RegrowHead(data) => Some(data.static_data.ability_info),
813            CharacterState::StaticAura(data) => Some(data.static_data.ability_info),
814        }
815    }
816
817    pub fn stage_section(&self) -> Option<StageSection> {
818        match &self {
819            CharacterState::Idle(_) => None,
820            CharacterState::Talk(_) => None,
821            CharacterState::Climb(_) => None,
822            CharacterState::Wallrun(_) => None,
823            CharacterState::Skate(_) => None,
824            CharacterState::Glide(_) => None,
825            CharacterState::GlideWield(_) => None,
826            CharacterState::Stunned(data) => Some(data.stage_section),
827            CharacterState::Sit => None,
828            CharacterState::Crawl => None,
829            CharacterState::Dance => None,
830            CharacterState::BasicBlock(data) => Some(data.stage_section),
831            CharacterState::Roll(data) => Some(data.stage_section),
832            CharacterState::Equipping(_) => Some(StageSection::Buildup),
833            CharacterState::Wielding(_) => None,
834            CharacterState::ComboMelee2(data) => Some(data.stage_section),
835            CharacterState::BasicMelee(data) => Some(data.stage_section),
836            CharacterState::BasicRanged(data) => Some(data.stage_section),
837            CharacterState::Boost(_) => None,
838            CharacterState::DashMelee(data) => Some(data.stage_section),
839            CharacterState::LeapExplosionShockwave(data) => Some(data.stage_section),
840            CharacterState::LeapMelee(data) => Some(data.stage_section),
841            CharacterState::LeapShockwave(data) => Some(data.stage_section),
842            CharacterState::ChargedMelee(data) => Some(data.stage_section),
843            CharacterState::ChargedRanged(data) => Some(data.stage_section),
844            CharacterState::RepeaterRanged(data) => Some(data.stage_section),
845            CharacterState::Throw(data) => Some(data.stage_section),
846            CharacterState::Shockwave(data) => Some(data.stage_section),
847            CharacterState::Explosion(data) => Some(data.stage_section),
848            CharacterState::BasicBeam(data) => Some(data.stage_section),
849            CharacterState::BasicAura(data) => Some(data.stage_section),
850            CharacterState::Blink(data) => Some(data.stage_section),
851            CharacterState::BasicSummon(data) => Some(data.stage_section),
852            CharacterState::SelfBuff(data) => Some(data.stage_section),
853            CharacterState::SpriteSummon(data) => Some(data.stage_section),
854            CharacterState::UseItem(data) => Some(data.stage_section),
855            CharacterState::Interact(data) => Some(data.stage_section),
856            CharacterState::FinisherMelee(data) => Some(data.stage_section),
857            CharacterState::Music(data) => Some(data.stage_section),
858            CharacterState::DiveMelee(data) => Some(data.stage_section),
859            CharacterState::RiposteMelee(data) => Some(data.stage_section),
860            CharacterState::RapidMelee(data) => Some(data.stage_section),
861            CharacterState::Transform(data) => Some(data.stage_section),
862            CharacterState::RegrowHead(data) => Some(data.stage_section),
863            CharacterState::StaticAura(data) => Some(data.stage_section),
864        }
865    }
866
867    pub fn durations(&self) -> Option<DurationsInfo> {
868        match &self {
869            CharacterState::Idle(_) => None,
870            CharacterState::Talk(_) => None,
871            CharacterState::Climb(_) => None,
872            CharacterState::Wallrun(_) => None,
873            CharacterState::Skate(_) => None,
874            CharacterState::Glide(_) => None,
875            CharacterState::GlideWield(_) => None,
876            CharacterState::Stunned(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::Sit => None,
882            CharacterState::Crawl => None,
883            CharacterState::Dance => None,
884            CharacterState::BasicBlock(data) => Some(DurationsInfo {
885                buildup: Some(data.static_data.buildup_duration),
886                recover: Some(data.static_data.recover_duration),
887                ..Default::default()
888            }),
889            CharacterState::Roll(data) => Some(DurationsInfo {
890                buildup: Some(data.static_data.buildup_duration),
891                recover: Some(data.static_data.recover_duration),
892                movement: Some(data.static_data.movement_duration),
893                ..Default::default()
894            }),
895            CharacterState::Wielding(_) => None,
896            CharacterState::Equipping(data) => Some(DurationsInfo {
897                buildup: Some(data.static_data.buildup_duration),
898                ..Default::default()
899            }),
900            CharacterState::ComboMelee2(data) => {
901                let strike = data.strike_data();
902                Some(DurationsInfo {
903                    buildup: Some(strike.buildup_duration),
904                    action: Some(strike.swing_duration),
905                    recover: Some(strike.recover_duration),
906                    ..Default::default()
907                })
908            },
909            CharacterState::BasicMelee(data) => Some(DurationsInfo {
910                buildup: Some(data.static_data.buildup_duration),
911                action: Some(data.static_data.swing_duration),
912                recover: Some(data.static_data.recover_duration),
913                ..Default::default()
914            }),
915            CharacterState::BasicRanged(data) => Some(DurationsInfo {
916                buildup: Some(data.static_data.buildup_duration),
917                recover: Some(data.static_data.recover_duration),
918                ..Default::default()
919            }),
920            CharacterState::Boost(data) => Some(DurationsInfo {
921                movement: Some(data.static_data.movement_duration),
922                ..Default::default()
923            }),
924            CharacterState::DashMelee(data) => Some(DurationsInfo {
925                buildup: Some(data.static_data.buildup_duration),
926                action: Some(data.static_data.swing_duration),
927                recover: Some(data.static_data.recover_duration),
928                charge: Some(data.static_data.charge_duration),
929                ..Default::default()
930            }),
931            CharacterState::LeapExplosionShockwave(data) => Some(DurationsInfo {
932                buildup: Some(data.static_data.buildup_duration),
933                action: Some(data.static_data.swing_duration),
934                recover: Some(data.static_data.recover_duration),
935                movement: Some(data.static_data.movement_duration),
936                ..Default::default()
937            }),
938            CharacterState::LeapMelee(data) => Some(DurationsInfo {
939                buildup: Some(data.static_data.buildup_duration),
940                action: Some(data.static_data.swing_duration),
941                recover: Some(data.static_data.recover_duration),
942                movement: Some(data.static_data.movement_duration),
943                ..Default::default()
944            }),
945            CharacterState::LeapShockwave(data) => Some(DurationsInfo {
946                buildup: Some(data.static_data.buildup_duration),
947                action: Some(data.static_data.swing_duration),
948                recover: Some(data.static_data.recover_duration),
949                movement: Some(data.static_data.movement_duration),
950                ..Default::default()
951            }),
952            CharacterState::ChargedMelee(data) => Some(DurationsInfo {
953                buildup: data.static_data.buildup_strike.map(|x| x.0),
954                action: Some(data.static_data.swing_duration),
955                recover: Some(data.static_data.recover_duration),
956                charge: Some(data.static_data.charge_duration),
957                ..Default::default()
958            }),
959            CharacterState::ChargedRanged(data) => Some(DurationsInfo {
960                buildup: Some(data.static_data.buildup_duration),
961                recover: Some(data.static_data.recover_duration),
962                charge: Some(data.static_data.charge_duration),
963                ..Default::default()
964            }),
965            CharacterState::RepeaterRanged(data) => Some(DurationsInfo {
966                buildup: Some(data.static_data.buildup_duration),
967                action: Some(data.static_data.shoot_duration),
968                recover: Some(data.static_data.recover_duration),
969                ..Default::default()
970            }),
971            CharacterState::Throw(data) => Some(DurationsInfo {
972                buildup: Some(data.static_data.buildup_duration),
973                charge: Some(data.static_data.charge_duration),
974                recover: Some(data.static_data.recover_duration),
975                ..Default::default()
976            }),
977            CharacterState::Shockwave(data) => Some(DurationsInfo {
978                buildup: Some(data.static_data.buildup_duration),
979                action: Some(data.static_data.swing_duration),
980                recover: Some(data.static_data.recover_duration),
981                ..Default::default()
982            }),
983            CharacterState::Explosion(data) => Some(DurationsInfo {
984                buildup: Some(data.static_data.buildup_duration),
985                action: Some(data.static_data.action_duration),
986                recover: Some(data.static_data.recover_duration),
987                ..Default::default()
988            }),
989            CharacterState::BasicBeam(data) => Some(DurationsInfo {
990                buildup: Some(data.static_data.buildup_duration),
991                recover: Some(data.static_data.recover_duration),
992                ..Default::default()
993            }),
994            CharacterState::BasicAura(data) => Some(DurationsInfo {
995                buildup: Some(data.static_data.buildup_duration),
996                action: Some(data.static_data.cast_duration),
997                recover: Some(data.static_data.recover_duration),
998                ..Default::default()
999            }),
1000            CharacterState::Blink(data) => Some(DurationsInfo {
1001                buildup: Some(data.static_data.buildup_duration),
1002                recover: Some(data.static_data.recover_duration),
1003                ..Default::default()
1004            }),
1005            CharacterState::BasicSummon(data) => Some(DurationsInfo {
1006                buildup: Some(data.static_data.buildup_duration),
1007                action: Some(data.static_data.cast_duration),
1008                recover: Some(data.static_data.recover_duration),
1009                ..Default::default()
1010            }),
1011            CharacterState::SelfBuff(data) => Some(DurationsInfo {
1012                buildup: Some(data.static_data.buildup_duration),
1013                action: Some(data.static_data.cast_duration),
1014                recover: Some(data.static_data.recover_duration),
1015                ..Default::default()
1016            }),
1017            CharacterState::SpriteSummon(data) => Some(DurationsInfo {
1018                buildup: Some(data.static_data.buildup_duration),
1019                action: Some(data.static_data.cast_duration),
1020                recover: Some(data.static_data.recover_duration),
1021                ..Default::default()
1022            }),
1023            CharacterState::UseItem(data) => Some(DurationsInfo {
1024                buildup: Some(data.static_data.buildup_duration),
1025                action: Some(data.static_data.use_duration),
1026                recover: Some(data.static_data.recover_duration),
1027                ..Default::default()
1028            }),
1029            CharacterState::Interact(data) => Some(DurationsInfo {
1030                buildup: Some(data.static_data.buildup_duration),
1031                action: data.static_data.use_duration,
1032                recover: Some(data.static_data.recover_duration),
1033                ..Default::default()
1034            }),
1035            CharacterState::FinisherMelee(data) => Some(DurationsInfo {
1036                buildup: Some(data.static_data.buildup_duration),
1037                action: Some(data.static_data.swing_duration),
1038                recover: Some(data.static_data.recover_duration),
1039                ..Default::default()
1040            }),
1041            CharacterState::Music(data) => Some(DurationsInfo {
1042                action: Some(data.static_data.play_duration),
1043                ..Default::default()
1044            }),
1045            CharacterState::DiveMelee(data) => Some(DurationsInfo {
1046                buildup: data.static_data.buildup_duration,
1047                movement: Some(data.static_data.movement_duration),
1048                action: Some(data.static_data.swing_duration),
1049                recover: Some(data.static_data.recover_duration),
1050                ..Default::default()
1051            }),
1052            CharacterState::RiposteMelee(data) => Some(DurationsInfo {
1053                buildup: Some(data.static_data.buildup_duration),
1054                action: Some(data.static_data.swing_duration),
1055                recover: Some(if data.whiffed {
1056                    data.static_data.whiffed_recover_duration
1057                } else {
1058                    data.static_data.recover_duration
1059                }),
1060                ..Default::default()
1061            }),
1062            CharacterState::RapidMelee(data) => Some(DurationsInfo {
1063                buildup: Some(data.static_data.buildup_duration),
1064                action: Some(data.static_data.swing_duration),
1065                recover: Some(data.static_data.recover_duration),
1066                ..Default::default()
1067            }),
1068            CharacterState::Transform(data) => Some(DurationsInfo {
1069                buildup: Some(data.static_data.buildup_duration),
1070                recover: Some(data.static_data.recover_duration),
1071                ..Default::default()
1072            }),
1073            CharacterState::RegrowHead(data) => Some(DurationsInfo {
1074                buildup: Some(data.static_data.buildup_duration),
1075                recover: Some(data.static_data.recover_duration),
1076                ..Default::default()
1077            }),
1078            CharacterState::StaticAura(data) => Some(DurationsInfo {
1079                buildup: Some(data.static_data.buildup_duration),
1080                action: Some(data.static_data.cast_duration),
1081                recover: Some(data.static_data.recover_duration),
1082                ..Default::default()
1083            }),
1084        }
1085    }
1086
1087    pub fn timer(&self) -> Option<Duration> {
1088        match &self {
1089            CharacterState::Idle(_) => None,
1090            CharacterState::Crawl => None,
1091            CharacterState::Talk(_) => None,
1092            CharacterState::Climb(_) => None,
1093            CharacterState::Wallrun(_) => None,
1094            CharacterState::Skate(_) => None,
1095            CharacterState::Glide(data) => Some(data.timer),
1096            CharacterState::GlideWield(_) => None,
1097            CharacterState::Stunned(data) => Some(data.timer),
1098            CharacterState::Sit => None,
1099            CharacterState::Dance => None,
1100            CharacterState::BasicBlock(data) => Some(data.timer),
1101            CharacterState::Roll(data) => Some(data.timer),
1102            CharacterState::Wielding(_) => None,
1103            CharacterState::Equipping(data) => Some(data.timer),
1104            CharacterState::ComboMelee2(data) => Some(data.timer),
1105            CharacterState::BasicMelee(data) => Some(data.timer),
1106            CharacterState::BasicRanged(data) => Some(data.timer),
1107            CharacterState::Boost(data) => Some(data.timer),
1108            CharacterState::DashMelee(data) => Some(data.timer),
1109            CharacterState::LeapExplosionShockwave(data) => Some(data.timer),
1110            CharacterState::LeapMelee(data) => Some(data.timer),
1111            CharacterState::LeapShockwave(data) => Some(data.timer),
1112            CharacterState::ChargedMelee(data) => Some(data.timer),
1113            CharacterState::ChargedRanged(data) => Some(data.timer),
1114            CharacterState::RepeaterRanged(data) => Some(data.timer),
1115            CharacterState::Throw(data) => Some(data.timer),
1116            CharacterState::Shockwave(data) => Some(data.timer),
1117            CharacterState::Explosion(data) => Some(data.timer),
1118            CharacterState::BasicBeam(data) => Some(data.timer),
1119            CharacterState::BasicAura(data) => Some(data.timer),
1120            CharacterState::Blink(data) => Some(data.timer),
1121            CharacterState::BasicSummon(data) => Some(data.timer),
1122            CharacterState::SelfBuff(data) => Some(data.timer),
1123            CharacterState::SpriteSummon(data) => Some(data.timer),
1124            CharacterState::UseItem(data) => Some(data.timer),
1125            CharacterState::Interact(data) => Some(data.timer),
1126            CharacterState::FinisherMelee(data) => Some(data.timer),
1127            CharacterState::Music(data) => Some(data.timer),
1128            CharacterState::DiveMelee(data) => Some(data.timer),
1129            CharacterState::RiposteMelee(data) => Some(data.timer),
1130            CharacterState::RapidMelee(data) => Some(data.timer),
1131            CharacterState::Transform(data) => Some(data.timer),
1132            CharacterState::RegrowHead(data) => Some(data.timer),
1133            CharacterState::StaticAura(data) => Some(data.timer),
1134        }
1135    }
1136
1137    pub fn attack_kind(&self) -> &[AttackSource] {
1138        match self {
1139            CharacterState::Idle(_) => &[],
1140            CharacterState::Crawl => &[],
1141            CharacterState::Talk(_) => &[],
1142            CharacterState::Climb(_) => &[],
1143            CharacterState::Wallrun(_) => &[],
1144            CharacterState::Skate(_) => &[],
1145            CharacterState::Glide(_) => &[],
1146            CharacterState::GlideWield(_) => &[],
1147            CharacterState::Stunned(_) => &[],
1148            CharacterState::Sit => &[],
1149            CharacterState::Dance => &[],
1150            CharacterState::BasicBlock(_) => &[],
1151            CharacterState::Roll(_) => &[],
1152            CharacterState::Wielding(_) => &[],
1153            CharacterState::Equipping(_) => &[],
1154            CharacterState::ComboMelee2(_) => &[AttackSource::Melee],
1155            CharacterState::BasicMelee(_) => &[AttackSource::Melee],
1156            CharacterState::BasicRanged(data) => {
1157                if data.static_data.projectile.is_explosive() {
1158                    &[AttackSource::Explosion]
1159                } else {
1160                    &[AttackSource::Projectile]
1161                }
1162            },
1163            CharacterState::Boost(_) => &[],
1164            CharacterState::DashMelee(_) => &[AttackSource::Melee],
1165            CharacterState::LeapMelee(_) => &[AttackSource::Melee],
1166            CharacterState::ChargedMelee(_) => &[AttackSource::Melee],
1167            // TODO: When charged ranged not only arrow make this check projectile type
1168            CharacterState::ChargedRanged(_) => &[AttackSource::Projectile],
1169            CharacterState::RepeaterRanged(data) => {
1170                if data.static_data.projectile.is_explosive() {
1171                    &[AttackSource::Explosion]
1172                } else {
1173                    &[AttackSource::Projectile]
1174                }
1175            },
1176            CharacterState::LeapExplosionShockwave(data) => data
1177                .static_data
1178                .shockwave_dodgeable
1179                .explosion_shockwave_attack_source_slice(),
1180            CharacterState::Throw(_) => &[AttackSource::Projectile],
1181            CharacterState::Shockwave(data) => {
1182                data.static_data.dodgeable.shockwave_attack_source_slice()
1183            },
1184            CharacterState::LeapShockwave(data) => {
1185                data.static_data.dodgeable.shockwave_attack_source_slice()
1186            },
1187            CharacterState::Explosion(_) => &[AttackSource::Explosion],
1188            CharacterState::BasicBeam(_) => &[AttackSource::Beam],
1189            CharacterState::BasicAura(_) => &[],
1190            CharacterState::Blink(_) => &[],
1191            CharacterState::BasicSummon(_) => &[],
1192            CharacterState::SelfBuff(_) => &[],
1193            CharacterState::SpriteSummon(_) => &[],
1194            CharacterState::UseItem(_) => &[],
1195            CharacterState::Interact(_) => &[],
1196            CharacterState::FinisherMelee(_) => &[AttackSource::Melee],
1197            CharacterState::Music(_) => &[],
1198            CharacterState::DiveMelee(_) => &[AttackSource::Melee],
1199            CharacterState::RiposteMelee(_) => &[AttackSource::Melee],
1200            CharacterState::RapidMelee(_) => &[AttackSource::Melee],
1201            CharacterState::Transform(_) => &[],
1202            CharacterState::RegrowHead(_) => &[],
1203            CharacterState::StaticAura(_) => &[],
1204        }
1205    }
1206}
1207
1208#[derive(Default, Copy, Clone)]
1209pub struct DurationsInfo {
1210    pub buildup: Option<Duration>,
1211    pub action: Option<Duration>,
1212    pub recover: Option<Duration>,
1213    pub movement: Option<Duration>,
1214    pub charge: Option<Duration>,
1215}
1216
1217#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq)]
1218pub struct AttackFilters {
1219    pub melee: bool,
1220    pub projectiles: bool,
1221    pub beams: bool,
1222    pub ground_shockwaves: bool,
1223    pub air_shockwaves: bool,
1224    pub explosions: bool,
1225}
1226
1227impl AttackFilters {
1228    pub fn applies(&self, attack: AttackSource) -> bool {
1229        match attack {
1230            AttackSource::Melee => self.melee,
1231            AttackSource::Projectile => self.projectiles,
1232            AttackSource::Beam => self.beams,
1233            AttackSource::GroundShockwave => self.ground_shockwaves,
1234            AttackSource::AirShockwave => self.air_shockwaves,
1235            AttackSource::UndodgeableShockwave => false,
1236            AttackSource::Explosion => self.explosions,
1237        }
1238    }
1239}
1240
1241impl Default for CharacterState {
1242    fn default() -> Self {
1243        Self::Idle(idle::Data {
1244            is_sneaking: false,
1245            footwear: None,
1246            time_entered: Time(0.0),
1247        })
1248    }
1249}
1250
1251impl Component for CharacterState {
1252    type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
1253}
1254
1255/// Contains information about the visual activity of a character.
1256///
1257/// For now this only includes the direction they're looking in, but later it
1258/// might include markers indicating that they're available for
1259/// trade/interaction, more details about their stance or appearance, facial
1260/// expression, etc.
1261#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
1262pub struct CharacterActivity {
1263    /// `None` means that the look direction should be derived from the
1264    /// orientation
1265    pub look_dir: Option<Dir>,
1266    /// If the character is using a Helm, this is the y direction the
1267    /// character steering. If the character is not steering this is
1268    /// a stale value.
1269    pub steer_dir: f32,
1270    /// If true, the owner has set this pet to stay at a fixed location and
1271    /// to not engage in combat
1272    pub is_pet_staying: bool,
1273}
1274
1275impl Component for CharacterActivity {
1276    type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
1277}