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    /// If this state can manipulate loadout, interact with sprites etc.
247    pub fn can_interact(&self) -> bool {
248        match self {
249            CharacterState::Idle(_)
250            | CharacterState::Sit
251            | CharacterState::Dance
252            | CharacterState::Talk(_)
253            | CharacterState::Equipping(_)
254            | CharacterState::Wielding(_)
255            | CharacterState::GlideWield(_) => true,
256            CharacterState::Crawl
257            | CharacterState::Climb(_)
258            | CharacterState::Glide(_)
259            | CharacterState::Stunned(_)
260            | CharacterState::BasicBlock(_)
261            | CharacterState::Roll(_)
262            | CharacterState::BasicMelee(_)
263            | CharacterState::BasicRanged(_)
264            | CharacterState::Throw(_)
265            | CharacterState::Boost(_)
266            | CharacterState::DashMelee(_)
267            | CharacterState::ComboMelee2(_)
268            | CharacterState::LeapExplosionShockwave(_)
269            | CharacterState::LeapMelee(_)
270            | CharacterState::LeapShockwave(_)
271            | CharacterState::ChargedRanged(_)
272            | CharacterState::ChargedMelee(_)
273            | CharacterState::RepeaterRanged(_)
274            | CharacterState::Shockwave(_)
275            | CharacterState::Explosion(_)
276            | CharacterState::BasicBeam(_)
277            | CharacterState::BasicAura(_)
278            | CharacterState::StaticAura(_)
279            | CharacterState::Blink(_)
280            | CharacterState::BasicSummon(_)
281            | CharacterState::SelfBuff(_)
282            | CharacterState::SpriteSummon(_)
283            | CharacterState::UseItem(_)
284            | CharacterState::Interact(_)
285            | CharacterState::Wallrun(_)
286            | CharacterState::Skate(_)
287            | CharacterState::Music(_)
288            | CharacterState::FinisherMelee(_)
289            | CharacterState::DiveMelee(_)
290            | CharacterState::RiposteMelee(_)
291            | CharacterState::RapidMelee(_)
292            | CharacterState::Transform(_)
293            | CharacterState::RegrowHead(_) => false,
294        }
295    }
296
297    pub fn was_wielded(&self) -> bool {
298        match self {
299            CharacterState::Roll(data) => data.was_wielded,
300            CharacterState::Stunned(data) => data.was_wielded,
301            CharacterState::Interact(data) => data.static_data.was_wielded,
302            CharacterState::UseItem(data) => data.static_data.was_wielded,
303            CharacterState::Wallrun(data) => data.was_wielded,
304            CharacterState::Climb(data) => data.was_wielded,
305            CharacterState::Idle(_)
306            | CharacterState::Crawl
307            | CharacterState::Sit
308            | CharacterState::Dance
309            | CharacterState::Talk(_)
310            | CharacterState::Glide(_)
311            | CharacterState::GlideWield(_)
312            | CharacterState::BasicBlock(_)
313            | CharacterState::Equipping(_)
314            | CharacterState::Wielding(_)
315            | CharacterState::BasicMelee(_)
316            | CharacterState::BasicRanged(_)
317            | CharacterState::Throw(_)
318            | CharacterState::Boost(_)
319            | CharacterState::DashMelee(_)
320            | CharacterState::ComboMelee2(_)
321            | CharacterState::LeapMelee(_)
322            | CharacterState::LeapShockwave(_)
323            | CharacterState::LeapExplosionShockwave(_)
324            | CharacterState::Explosion(_)
325            | CharacterState::ChargedRanged(_)
326            | CharacterState::ChargedMelee(_)
327            | CharacterState::RepeaterRanged(_)
328            | CharacterState::Shockwave(_)
329            | CharacterState::BasicBeam(_)
330            | CharacterState::BasicAura(_)
331            | CharacterState::StaticAura(_)
332            | CharacterState::Blink(_)
333            | CharacterState::BasicSummon(_)
334            | CharacterState::SelfBuff(_)
335            | CharacterState::SpriteSummon(_)
336            | CharacterState::Skate(_)
337            | CharacterState::Music(_)
338            | CharacterState::FinisherMelee(_)
339            | CharacterState::DiveMelee(_)
340            | CharacterState::RiposteMelee(_)
341            | CharacterState::RapidMelee(_)
342            | CharacterState::Transform(_)
343            | CharacterState::RegrowHead(_) => false,
344        }
345    }
346
347    pub fn is_glide_wielded(&self) -> bool {
348        matches!(
349            self,
350            CharacterState::Glide { .. } | CharacterState::GlideWield { .. }
351        )
352    }
353
354    pub fn is_stealthy(&self) -> bool {
355        matches!(
356            self,
357            CharacterState::Idle(idle::Data {
358                is_sneaking: true,
359                footwear: _,
360                time_entered: _,
361            }) | CharacterState::Wielding(wielding::Data {
362                is_sneaking: true,
363                ..
364            }) | CharacterState::Roll(roll::Data {
365                is_sneaking: true,
366                ..
367            })
368        )
369    }
370
371    pub fn should_follow_look(&self) -> bool {
372        matches!(self, CharacterState::Boost(_)) || self.is_attack()
373    }
374
375    pub fn is_attack(&self) -> bool {
376        matches!(
377            self,
378            CharacterState::BasicMelee(_)
379                | CharacterState::BasicRanged(_)
380                | CharacterState::DashMelee(_)
381                | CharacterState::ComboMelee2(_)
382                | CharacterState::LeapExplosionShockwave(_)
383                | CharacterState::LeapMelee(_)
384                | CharacterState::LeapShockwave(_)
385                | CharacterState::ChargedMelee(_)
386                | CharacterState::ChargedRanged(_)
387                | CharacterState::RepeaterRanged(_)
388                | CharacterState::Throw(_)
389                | CharacterState::Shockwave(_)
390                | CharacterState::Explosion(_)
391                | CharacterState::BasicBeam(_)
392                | CharacterState::BasicAura(_)
393                | CharacterState::SelfBuff(_)
394                | CharacterState::Blink(_)
395                | CharacterState::BasicSummon(_)
396                | CharacterState::SpriteSummon(_)
397                | CharacterState::FinisherMelee(_)
398                | CharacterState::DiveMelee(_)
399                | CharacterState::RiposteMelee(_)
400                | CharacterState::RapidMelee(_)
401                | CharacterState::StaticAura(_)
402        )
403    }
404
405    pub fn is_aimed(&self) -> bool {
406        matches!(
407            self,
408            CharacterState::BasicMelee(_)
409                | CharacterState::BasicRanged(_)
410                | CharacterState::DashMelee(_)
411                | CharacterState::ComboMelee2(_)
412                | CharacterState::BasicBlock(_)
413                | CharacterState::LeapExplosionShockwave(_)
414                | CharacterState::LeapMelee(_)
415                | CharacterState::LeapShockwave(_)
416                | CharacterState::ChargedMelee(_)
417                | CharacterState::ChargedRanged(_)
418                | CharacterState::RepeaterRanged(_)
419                | CharacterState::Throw(_)
420                | CharacterState::Shockwave(_)
421                | CharacterState::BasicBeam(_)
422                | CharacterState::Stunned(_)
423                | CharacterState::Wielding(_)
424                | CharacterState::Talk(_)
425                | CharacterState::FinisherMelee(_)
426                | CharacterState::DiveMelee(_)
427                | CharacterState::RiposteMelee(_)
428                | CharacterState::RapidMelee(_)
429        )
430    }
431
432    pub fn is_using_hands(&self) -> bool {
433        matches!(
434            self,
435            CharacterState::Climb(_)
436                | CharacterState::Equipping(_)
437                | CharacterState::Dance
438                | CharacterState::Glide(_)
439                | CharacterState::GlideWield(_)
440                | CharacterState::Talk(_)
441                | CharacterState::Roll(_),
442        )
443    }
444
445    pub fn is_parry(&self, attack_source: AttackSource) -> bool {
446        let melee = matches!(attack_source, AttackSource::Melee);
447        let from_capability_melee = melee
448            && self
449                .ability_info()
450                .map(|a| a.ability_meta.capabilities)
451                .is_some_and(|c| {
452                    c.contains(Capability::PARRIES_MELEE)
453                        && matches!(
454                            self.stage_section(),
455                            Some(StageSection::Buildup | StageSection::Action)
456                        )
457                });
458        let from_capability = matches!(
459            attack_source,
460            AttackSource::Melee
461                | AttackSource::Projectile
462                | AttackSource::Beam
463                | AttackSource::AirShockwave
464                | AttackSource::Explosion
465        ) && self
466            .ability_info()
467            .map(|a| a.ability_meta.capabilities)
468            .is_some_and(|c| {
469                c.contains(Capability::PARRIES)
470                    && matches!(
471                        self.stage_section(),
472                        Some(StageSection::Buildup | StageSection::Action)
473                    )
474            });
475        let from_state = match self {
476            CharacterState::BasicBlock(c) => c.is_parry(attack_source),
477            CharacterState::RiposteMelee(c) => {
478                melee
479                    && matches!(
480                        c.stage_section,
481                        StageSection::Buildup | StageSection::Action
482                    )
483            },
484            _ => false,
485        };
486        from_capability_melee || from_capability || from_state
487    }
488
489    pub fn is_block(&self, attack_source: AttackSource) -> bool {
490        match self {
491            CharacterState::BasicBlock(data) => {
492                data.static_data.blocked_attacks.applies(attack_source)
493                    && matches!(
494                        self.stage_section(),
495                        Some(StageSection::Buildup | StageSection::Action)
496                    )
497            },
498            _ => self
499                .ability_info()
500                .map(|ability| ability.ability_meta.capabilities)
501                .is_some_and(|capabilities| {
502                    capabilities.contains(Capability::BLOCKS)
503                        && matches!(
504                            self.stage_section(),
505                            Some(StageSection::Buildup | StageSection::Action)
506                        )
507                        && matches!(attack_source, AttackSource::Melee)
508                }),
509        }
510    }
511
512    /// In radians
513    pub fn block_angle(&self) -> f32 {
514        match self {
515            CharacterState::BasicBlock(c) => c.static_data.max_angle.to_radians(),
516            CharacterState::ComboMelee2(c) => {
517                let strike_data =
518                    c.static_data.strikes[c.completed_strikes % c.static_data.strikes.len()];
519                strike_data.melee_constructor.angle.to_radians()
520            },
521            CharacterState::RiposteMelee(c) => c.static_data.melee_constructor.angle.to_radians(),
522            // TODO: Add more here as needed, maybe look into having character state return the
523            // melee constructor if it has one and using that?
524            _ => 0.0,
525        }
526    }
527
528    pub fn is_dodge(&self) -> bool {
529        if let CharacterState::Roll(c) = self {
530            matches!(
531                c.stage_section,
532                StageSection::Buildup | StageSection::Movement
533            )
534        } else {
535            false
536        }
537    }
538
539    pub fn is_glide(&self) -> bool { matches!(self, CharacterState::Glide(_)) }
540
541    pub fn is_skate(&self) -> bool { matches!(self, CharacterState::Skate(_)) }
542
543    pub fn is_music(&self) -> bool { matches!(self, CharacterState::Music(_)) }
544
545    pub fn roll_attack_immunities(&self) -> Option<AttackFilters> {
546        if self.is_dodge()
547            && let CharacterState::Roll(c) = self
548        {
549            Some(c.static_data.attack_immunities)
550        } else {
551            None
552        }
553    }
554
555    pub fn is_stunned(&self) -> bool { matches!(self, CharacterState::Stunned(_)) }
556
557    pub fn is_forced_movement(&self) -> bool {
558        matches!(self, CharacterState::ComboMelee2(s) if s.stage_section == StageSection::Action)
559            || matches!(self, CharacterState::DashMelee(s) if s.stage_section == StageSection::Charge)
560            || matches!(self, CharacterState::LeapMelee(s) if s.stage_section == StageSection::Movement)
561            || matches!(self, CharacterState::Roll(s) if s.stage_section == StageSection::Movement)
562    }
563
564    pub fn is_melee_attack(&self) -> bool { self.attack_kind().contains(&AttackSource::Melee) }
565
566    pub fn is_beam_attack(&self) -> bool { self.attack_kind().contains(&AttackSource::Beam) }
567
568    pub fn can_perform_mounted(&self) -> bool {
569        matches!(
570            self,
571            CharacterState::Idle(_)
572                | CharacterState::Sit
573                | CharacterState::Dance
574                | CharacterState::Talk(_)
575                | CharacterState::Stunned(_)
576                | CharacterState::BasicBlock(_)
577                | CharacterState::Equipping(_)
578                | CharacterState::Wielding(_)
579                | CharacterState::BasicMelee(_)
580                | CharacterState::BasicRanged(_)
581                | CharacterState::ComboMelee2(_)
582                | CharacterState::ChargedRanged(_)
583                | CharacterState::RepeaterRanged(_)
584                | CharacterState::Throw(_)
585                | CharacterState::BasicBeam(_)
586                | CharacterState::BasicAura(_)
587                | CharacterState::BasicSummon(_)
588                | CharacterState::SelfBuff(_)
589                | CharacterState::SpriteSummon(_)
590                | CharacterState::UseItem(_)
591                | CharacterState::Interact(_)
592                | CharacterState::Music(_)
593                | CharacterState::RiposteMelee(_)
594                | CharacterState::RapidMelee(_)
595        )
596    }
597
598    pub fn is_sitting(&self) -> bool {
599        use use_item::{Data, ItemUseKind, StaticData};
600        matches!(
601            self,
602            CharacterState::Sit
603                | CharacterState::UseItem(Data {
604                    static_data: StaticData {
605                        item_kind: ItemUseKind::Consumable(
606                            ConsumableKind::ComplexFood | ConsumableKind::Food
607                        ),
608                        ..
609                    },
610                    ..
611                })
612        )
613    }
614
615    /// Compares for shallow equality (does not check internal struct equality)
616    pub fn same_variant(&self, other: &Self) -> bool {
617        // Check if state is the same without looking at the inner data
618        std::mem::discriminant(self) == std::mem::discriminant(other)
619    }
620
621    pub fn behavior(&self, j: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
622        match &self {
623            CharacterState::Idle(data) => data.behavior(j, output_events),
624            CharacterState::Talk(data) => data.behavior(j, output_events),
625            CharacterState::Climb(data) => data.behavior(j, output_events),
626            CharacterState::Wallrun(data) => data.behavior(j, output_events),
627            CharacterState::Glide(data) => data.behavior(j, output_events),
628            CharacterState::GlideWield(data) => data.behavior(j, output_events),
629            CharacterState::Stunned(data) => data.behavior(j, output_events),
630            CharacterState::Sit => sit::Data::behavior(&sit::Data, j, output_events),
631            CharacterState::Crawl => crawl::Data::behavior(&crawl::Data, j, output_events),
632            CharacterState::Dance => dance::Data::behavior(&dance::Data, j, output_events),
633            CharacterState::BasicBlock(data) => data.behavior(j, output_events),
634            CharacterState::Roll(data) => data.behavior(j, output_events),
635            CharacterState::Wielding(data) => data.behavior(j, output_events),
636            CharacterState::Equipping(data) => data.behavior(j, output_events),
637            CharacterState::ComboMelee2(data) => data.behavior(j, output_events),
638            CharacterState::BasicMelee(data) => data.behavior(j, output_events),
639            CharacterState::BasicRanged(data) => data.behavior(j, output_events),
640            CharacterState::Boost(data) => data.behavior(j, output_events),
641            CharacterState::DashMelee(data) => data.behavior(j, output_events),
642            CharacterState::LeapExplosionShockwave(data) => data.behavior(j, output_events),
643            CharacterState::LeapMelee(data) => data.behavior(j, output_events),
644            CharacterState::LeapShockwave(data) => data.behavior(j, output_events),
645            CharacterState::ChargedMelee(data) => data.behavior(j, output_events),
646            CharacterState::ChargedRanged(data) => data.behavior(j, output_events),
647            CharacterState::RepeaterRanged(data) => data.behavior(j, output_events),
648            CharacterState::Throw(data) => data.behavior(j, output_events),
649            CharacterState::Shockwave(data) => data.behavior(j, output_events),
650            CharacterState::Explosion(data) => data.behavior(j, output_events),
651            CharacterState::BasicBeam(data) => data.behavior(j, output_events),
652            CharacterState::BasicAura(data) => data.behavior(j, output_events),
653            CharacterState::Blink(data) => data.behavior(j, output_events),
654            CharacterState::BasicSummon(data) => data.behavior(j, output_events),
655            CharacterState::SelfBuff(data) => data.behavior(j, output_events),
656            CharacterState::SpriteSummon(data) => data.behavior(j, output_events),
657            CharacterState::UseItem(data) => data.behavior(j, output_events),
658            CharacterState::Interact(data) => data.behavior(j, output_events),
659            CharacterState::Skate(data) => data.behavior(j, output_events),
660            CharacterState::Music(data) => data.behavior(j, output_events),
661            CharacterState::FinisherMelee(data) => data.behavior(j, output_events),
662            CharacterState::DiveMelee(data) => data.behavior(j, output_events),
663            CharacterState::RiposteMelee(data) => data.behavior(j, output_events),
664            CharacterState::RapidMelee(data) => data.behavior(j, output_events),
665            CharacterState::Transform(data) => data.behavior(j, output_events),
666            CharacterState::RegrowHead(data) => data.behavior(j, output_events),
667            CharacterState::StaticAura(data) => data.behavior(j, output_events),
668        }
669    }
670
671    pub fn handle_event(
672        &self,
673        j: &JoinData,
674        output_events: &mut OutputEvents,
675        action: ControlAction,
676    ) -> StateUpdate {
677        match &self {
678            CharacterState::Idle(data) => data.handle_event(j, output_events, action),
679            CharacterState::Talk(data) => data.handle_event(j, output_events, action),
680            CharacterState::Climb(data) => data.handle_event(j, output_events, action),
681            CharacterState::Wallrun(data) => data.handle_event(j, output_events, action),
682            CharacterState::Glide(data) => data.handle_event(j, output_events, action),
683            CharacterState::GlideWield(data) => data.handle_event(j, output_events, action),
684            CharacterState::Stunned(data) => data.handle_event(j, output_events, action),
685            CharacterState::Sit => {
686                states::sit::Data::handle_event(&sit::Data, j, output_events, action)
687            },
688            CharacterState::Crawl => {
689                states::crawl::Data::handle_event(&crawl::Data, j, output_events, action)
690            },
691            CharacterState::Dance => {
692                states::dance::Data::handle_event(&dance::Data, j, output_events, action)
693            },
694            CharacterState::BasicBlock(data) => data.handle_event(j, output_events, action),
695            CharacterState::Roll(data) => data.handle_event(j, output_events, action),
696            CharacterState::Wielding(data) => data.handle_event(j, output_events, action),
697            CharacterState::Equipping(data) => data.handle_event(j, output_events, action),
698            CharacterState::ComboMelee2(data) => data.handle_event(j, output_events, action),
699            CharacterState::BasicMelee(data) => data.handle_event(j, output_events, action),
700            CharacterState::BasicRanged(data) => data.handle_event(j, output_events, action),
701            CharacterState::Boost(data) => data.handle_event(j, output_events, action),
702            CharacterState::DashMelee(data) => data.handle_event(j, output_events, action),
703            CharacterState::LeapExplosionShockwave(data) => {
704                data.handle_event(j, output_events, action)
705            },
706            CharacterState::LeapMelee(data) => data.handle_event(j, output_events, action),
707            CharacterState::LeapShockwave(data) => data.handle_event(j, output_events, action),
708            CharacterState::ChargedMelee(data) => data.handle_event(j, output_events, action),
709            CharacterState::ChargedRanged(data) => data.handle_event(j, output_events, action),
710            CharacterState::RepeaterRanged(data) => data.handle_event(j, output_events, action),
711            CharacterState::Throw(data) => data.handle_event(j, output_events, action),
712            CharacterState::Shockwave(data) => data.handle_event(j, output_events, action),
713            CharacterState::Explosion(data) => data.handle_event(j, output_events, action),
714            CharacterState::BasicBeam(data) => data.handle_event(j, output_events, action),
715            CharacterState::BasicAura(data) => data.handle_event(j, output_events, action),
716            CharacterState::Blink(data) => data.handle_event(j, output_events, action),
717            CharacterState::BasicSummon(data) => data.handle_event(j, output_events, action),
718            CharacterState::SelfBuff(data) => data.handle_event(j, output_events, action),
719            CharacterState::SpriteSummon(data) => data.handle_event(j, output_events, action),
720            CharacterState::UseItem(data) => data.handle_event(j, output_events, action),
721            CharacterState::Interact(data) => data.handle_event(j, output_events, action),
722            CharacterState::Skate(data) => data.handle_event(j, output_events, action),
723            CharacterState::Music(data) => data.handle_event(j, output_events, action),
724            CharacterState::FinisherMelee(data) => data.handle_event(j, output_events, action),
725            CharacterState::DiveMelee(data) => data.handle_event(j, output_events, action),
726            CharacterState::RiposteMelee(data) => data.handle_event(j, output_events, action),
727            CharacterState::RapidMelee(data) => data.handle_event(j, output_events, action),
728            CharacterState::Transform(data) => data.handle_event(j, output_events, action),
729            CharacterState::RegrowHead(data) => data.handle_event(j, output_events, action),
730            CharacterState::StaticAura(data) => data.handle_event(j, output_events, action),
731        }
732    }
733
734    pub fn footwear(&self) -> Option<Friction> {
735        match &self {
736            CharacterState::Idle(data) => data.footwear,
737            CharacterState::Skate(data) => Some(data.footwear),
738            _ => None,
739        }
740    }
741
742    pub fn ability_info(&self) -> Option<AbilityInfo> {
743        match &self {
744            CharacterState::Idle(_) => None,
745            CharacterState::Talk(_) => None,
746            CharacterState::Climb(_) => None,
747            CharacterState::Wallrun(_) => None,
748            CharacterState::Skate(_) => None,
749            CharacterState::Glide(_) => None,
750            CharacterState::GlideWield(_) => None,
751            CharacterState::Stunned(_) => None,
752            CharacterState::Sit => None,
753            CharacterState::Crawl => None,
754            CharacterState::Dance => None,
755            CharacterState::BasicBlock(data) => Some(data.static_data.ability_info),
756            CharacterState::Roll(data) => Some(data.static_data.ability_info),
757            CharacterState::Wielding(_) => None,
758            CharacterState::Equipping(_) => None,
759            CharacterState::ComboMelee2(data) => Some(data.static_data.ability_info),
760            CharacterState::BasicMelee(data) => Some(data.static_data.ability_info),
761            CharacterState::BasicRanged(data) => Some(data.static_data.ability_info),
762            CharacterState::Boost(data) => Some(data.static_data.ability_info),
763            CharacterState::DashMelee(data) => Some(data.static_data.ability_info),
764            CharacterState::LeapExplosionShockwave(data) => Some(data.static_data.ability_info),
765            CharacterState::LeapMelee(data) => Some(data.static_data.ability_info),
766            CharacterState::LeapShockwave(data) => Some(data.static_data.ability_info),
767            CharacterState::ChargedMelee(data) => Some(data.static_data.ability_info),
768            CharacterState::ChargedRanged(data) => Some(data.static_data.ability_info),
769            CharacterState::RepeaterRanged(data) => Some(data.static_data.ability_info),
770            CharacterState::Throw(data) => Some(data.static_data.ability_info),
771            CharacterState::Shockwave(data) => Some(data.static_data.ability_info),
772            CharacterState::Explosion(data) => Some(data.static_data.ability_info),
773            CharacterState::BasicBeam(data) => Some(data.static_data.ability_info),
774            CharacterState::BasicAura(data) => Some(data.static_data.ability_info),
775            CharacterState::Blink(data) => Some(data.static_data.ability_info),
776            CharacterState::BasicSummon(data) => Some(data.static_data.ability_info),
777            CharacterState::SelfBuff(data) => Some(data.static_data.ability_info),
778            CharacterState::SpriteSummon(data) => Some(data.static_data.ability_info),
779            CharacterState::UseItem(_) => None,
780            CharacterState::Interact(_) => None,
781            CharacterState::FinisherMelee(data) => Some(data.static_data.ability_info),
782            CharacterState::Music(data) => Some(data.static_data.ability_info),
783            CharacterState::DiveMelee(data) => Some(data.static_data.ability_info),
784            CharacterState::RiposteMelee(data) => Some(data.static_data.ability_info),
785            CharacterState::RapidMelee(data) => Some(data.static_data.ability_info),
786            CharacterState::Transform(data) => Some(data.static_data.ability_info),
787            CharacterState::RegrowHead(data) => Some(data.static_data.ability_info),
788            CharacterState::StaticAura(data) => Some(data.static_data.ability_info),
789        }
790    }
791
792    pub fn stage_section(&self) -> Option<StageSection> {
793        match &self {
794            CharacterState::Idle(_) => None,
795            CharacterState::Talk(_) => None,
796            CharacterState::Climb(_) => None,
797            CharacterState::Wallrun(_) => None,
798            CharacterState::Skate(_) => None,
799            CharacterState::Glide(_) => None,
800            CharacterState::GlideWield(_) => None,
801            CharacterState::Stunned(data) => Some(data.stage_section),
802            CharacterState::Sit => None,
803            CharacterState::Crawl => None,
804            CharacterState::Dance => None,
805            CharacterState::BasicBlock(data) => Some(data.stage_section),
806            CharacterState::Roll(data) => Some(data.stage_section),
807            CharacterState::Equipping(_) => Some(StageSection::Buildup),
808            CharacterState::Wielding(_) => None,
809            CharacterState::ComboMelee2(data) => Some(data.stage_section),
810            CharacterState::BasicMelee(data) => Some(data.stage_section),
811            CharacterState::BasicRanged(data) => Some(data.stage_section),
812            CharacterState::Boost(_) => None,
813            CharacterState::DashMelee(data) => Some(data.stage_section),
814            CharacterState::LeapExplosionShockwave(data) => Some(data.stage_section),
815            CharacterState::LeapMelee(data) => Some(data.stage_section),
816            CharacterState::LeapShockwave(data) => Some(data.stage_section),
817            CharacterState::ChargedMelee(data) => Some(data.stage_section),
818            CharacterState::ChargedRanged(data) => Some(data.stage_section),
819            CharacterState::RepeaterRanged(data) => Some(data.stage_section),
820            CharacterState::Throw(data) => Some(data.stage_section),
821            CharacterState::Shockwave(data) => Some(data.stage_section),
822            CharacterState::Explosion(data) => Some(data.stage_section),
823            CharacterState::BasicBeam(data) => Some(data.stage_section),
824            CharacterState::BasicAura(data) => Some(data.stage_section),
825            CharacterState::Blink(data) => Some(data.stage_section),
826            CharacterState::BasicSummon(data) => Some(data.stage_section),
827            CharacterState::SelfBuff(data) => Some(data.stage_section),
828            CharacterState::SpriteSummon(data) => Some(data.stage_section),
829            CharacterState::UseItem(data) => Some(data.stage_section),
830            CharacterState::Interact(data) => Some(data.stage_section),
831            CharacterState::FinisherMelee(data) => Some(data.stage_section),
832            CharacterState::Music(data) => Some(data.stage_section),
833            CharacterState::DiveMelee(data) => Some(data.stage_section),
834            CharacterState::RiposteMelee(data) => Some(data.stage_section),
835            CharacterState::RapidMelee(data) => Some(data.stage_section),
836            CharacterState::Transform(data) => Some(data.stage_section),
837            CharacterState::RegrowHead(data) => Some(data.stage_section),
838            CharacterState::StaticAura(data) => Some(data.stage_section),
839        }
840    }
841
842    pub fn durations(&self) -> Option<DurationsInfo> {
843        match &self {
844            CharacterState::Idle(_) => None,
845            CharacterState::Talk(_) => None,
846            CharacterState::Climb(_) => None,
847            CharacterState::Wallrun(_) => None,
848            CharacterState::Skate(_) => None,
849            CharacterState::Glide(_) => None,
850            CharacterState::GlideWield(_) => None,
851            CharacterState::Stunned(data) => Some(DurationsInfo {
852                buildup: Some(data.static_data.buildup_duration),
853                recover: Some(data.static_data.recover_duration),
854                ..Default::default()
855            }),
856            CharacterState::Sit => None,
857            CharacterState::Crawl => None,
858            CharacterState::Dance => None,
859            CharacterState::BasicBlock(data) => Some(DurationsInfo {
860                buildup: Some(data.static_data.buildup_duration),
861                recover: Some(data.static_data.recover_duration),
862                ..Default::default()
863            }),
864            CharacterState::Roll(data) => Some(DurationsInfo {
865                buildup: Some(data.static_data.buildup_duration),
866                recover: Some(data.static_data.recover_duration),
867                movement: Some(data.static_data.movement_duration),
868                ..Default::default()
869            }),
870            CharacterState::Wielding(_) => None,
871            CharacterState::Equipping(data) => Some(DurationsInfo {
872                buildup: Some(data.static_data.buildup_duration),
873                ..Default::default()
874            }),
875            CharacterState::ComboMelee2(data) => {
876                let strike = data.strike_data();
877                Some(DurationsInfo {
878                    buildup: Some(strike.buildup_duration),
879                    action: Some(strike.swing_duration),
880                    recover: Some(strike.recover_duration),
881                    ..Default::default()
882                })
883            },
884            CharacterState::BasicMelee(data) => Some(DurationsInfo {
885                buildup: Some(data.static_data.buildup_duration),
886                action: Some(data.static_data.swing_duration),
887                recover: Some(data.static_data.recover_duration),
888                ..Default::default()
889            }),
890            CharacterState::BasicRanged(data) => Some(DurationsInfo {
891                buildup: Some(data.static_data.buildup_duration),
892                recover: Some(data.static_data.recover_duration),
893                ..Default::default()
894            }),
895            CharacterState::Boost(data) => Some(DurationsInfo {
896                movement: Some(data.static_data.movement_duration),
897                ..Default::default()
898            }),
899            CharacterState::DashMelee(data) => Some(DurationsInfo {
900                buildup: Some(data.static_data.buildup_duration),
901                action: Some(data.static_data.swing_duration),
902                recover: Some(data.static_data.recover_duration),
903                charge: Some(data.static_data.charge_duration),
904                ..Default::default()
905            }),
906            CharacterState::LeapExplosionShockwave(data) => Some(DurationsInfo {
907                buildup: Some(data.static_data.buildup_duration),
908                action: Some(data.static_data.swing_duration),
909                recover: Some(data.static_data.recover_duration),
910                movement: Some(data.static_data.movement_duration),
911                ..Default::default()
912            }),
913            CharacterState::LeapMelee(data) => Some(DurationsInfo {
914                buildup: Some(data.static_data.buildup_duration),
915                action: Some(data.static_data.swing_duration),
916                recover: Some(data.static_data.recover_duration),
917                movement: Some(data.static_data.movement_duration),
918                ..Default::default()
919            }),
920            CharacterState::LeapShockwave(data) => Some(DurationsInfo {
921                buildup: Some(data.static_data.buildup_duration),
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::ChargedMelee(data) => Some(DurationsInfo {
928                buildup: data.static_data.buildup_strike.map(|x| x.0),
929                action: Some(data.static_data.swing_duration),
930                recover: Some(data.static_data.recover_duration),
931                charge: Some(data.static_data.charge_duration),
932                ..Default::default()
933            }),
934            CharacterState::ChargedRanged(data) => Some(DurationsInfo {
935                buildup: Some(data.static_data.buildup_duration),
936                recover: Some(data.static_data.recover_duration),
937                charge: Some(data.static_data.charge_duration),
938                ..Default::default()
939            }),
940            CharacterState::RepeaterRanged(data) => Some(DurationsInfo {
941                buildup: Some(data.static_data.buildup_duration),
942                action: Some(data.static_data.shoot_duration),
943                recover: Some(data.static_data.recover_duration),
944                ..Default::default()
945            }),
946            CharacterState::Throw(data) => Some(DurationsInfo {
947                buildup: Some(data.static_data.buildup_duration),
948                charge: Some(data.static_data.charge_duration),
949                recover: Some(data.static_data.recover_duration),
950                ..Default::default()
951            }),
952            CharacterState::Shockwave(data) => Some(DurationsInfo {
953                buildup: Some(data.static_data.buildup_duration),
954                action: Some(data.static_data.swing_duration),
955                recover: Some(data.static_data.recover_duration),
956                ..Default::default()
957            }),
958            CharacterState::Explosion(data) => Some(DurationsInfo {
959                buildup: Some(data.static_data.buildup_duration),
960                action: Some(data.static_data.action_duration),
961                recover: Some(data.static_data.recover_duration),
962                ..Default::default()
963            }),
964            CharacterState::BasicBeam(data) => Some(DurationsInfo {
965                buildup: Some(data.static_data.buildup_duration),
966                recover: Some(data.static_data.recover_duration),
967                ..Default::default()
968            }),
969            CharacterState::BasicAura(data) => Some(DurationsInfo {
970                buildup: Some(data.static_data.buildup_duration),
971                action: Some(data.static_data.cast_duration),
972                recover: Some(data.static_data.recover_duration),
973                ..Default::default()
974            }),
975            CharacterState::Blink(data) => Some(DurationsInfo {
976                buildup: Some(data.static_data.buildup_duration),
977                recover: Some(data.static_data.recover_duration),
978                ..Default::default()
979            }),
980            CharacterState::BasicSummon(data) => Some(DurationsInfo {
981                buildup: Some(data.static_data.buildup_duration),
982                action: Some(data.static_data.cast_duration),
983                recover: Some(data.static_data.recover_duration),
984                ..Default::default()
985            }),
986            CharacterState::SelfBuff(data) => Some(DurationsInfo {
987                buildup: Some(data.static_data.buildup_duration),
988                action: Some(data.static_data.cast_duration),
989                recover: Some(data.static_data.recover_duration),
990                ..Default::default()
991            }),
992            CharacterState::SpriteSummon(data) => Some(DurationsInfo {
993                buildup: Some(data.static_data.buildup_duration),
994                action: Some(data.static_data.cast_duration),
995                recover: Some(data.static_data.recover_duration),
996                ..Default::default()
997            }),
998            CharacterState::UseItem(data) => Some(DurationsInfo {
999                buildup: Some(data.static_data.buildup_duration),
1000                action: Some(data.static_data.use_duration),
1001                recover: Some(data.static_data.recover_duration),
1002                ..Default::default()
1003            }),
1004            CharacterState::Interact(data) => Some(DurationsInfo {
1005                buildup: Some(data.static_data.buildup_duration),
1006                action: data.static_data.use_duration,
1007                recover: Some(data.static_data.recover_duration),
1008                ..Default::default()
1009            }),
1010            CharacterState::FinisherMelee(data) => Some(DurationsInfo {
1011                buildup: Some(data.static_data.buildup_duration),
1012                action: Some(data.static_data.swing_duration),
1013                recover: Some(data.static_data.recover_duration),
1014                ..Default::default()
1015            }),
1016            CharacterState::Music(data) => Some(DurationsInfo {
1017                action: Some(data.static_data.play_duration),
1018                ..Default::default()
1019            }),
1020            CharacterState::DiveMelee(data) => Some(DurationsInfo {
1021                buildup: data.static_data.buildup_duration,
1022                movement: Some(data.static_data.movement_duration),
1023                action: Some(data.static_data.swing_duration),
1024                recover: Some(data.static_data.recover_duration),
1025                ..Default::default()
1026            }),
1027            CharacterState::RiposteMelee(data) => Some(DurationsInfo {
1028                buildup: Some(data.static_data.buildup_duration),
1029                action: Some(data.static_data.swing_duration),
1030                recover: Some(if data.whiffed {
1031                    data.static_data.whiffed_recover_duration
1032                } else {
1033                    data.static_data.recover_duration
1034                }),
1035                ..Default::default()
1036            }),
1037            CharacterState::RapidMelee(data) => Some(DurationsInfo {
1038                buildup: Some(data.static_data.buildup_duration),
1039                action: Some(data.static_data.swing_duration),
1040                recover: Some(data.static_data.recover_duration),
1041                ..Default::default()
1042            }),
1043            CharacterState::Transform(data) => Some(DurationsInfo {
1044                buildup: Some(data.static_data.buildup_duration),
1045                recover: Some(data.static_data.recover_duration),
1046                ..Default::default()
1047            }),
1048            CharacterState::RegrowHead(data) => Some(DurationsInfo {
1049                buildup: Some(data.static_data.buildup_duration),
1050                recover: Some(data.static_data.recover_duration),
1051                ..Default::default()
1052            }),
1053            CharacterState::StaticAura(data) => Some(DurationsInfo {
1054                buildup: Some(data.static_data.buildup_duration),
1055                action: Some(data.static_data.cast_duration),
1056                recover: Some(data.static_data.recover_duration),
1057                ..Default::default()
1058            }),
1059        }
1060    }
1061
1062    pub fn timer(&self) -> Option<Duration> {
1063        match &self {
1064            CharacterState::Idle(_) => None,
1065            CharacterState::Crawl => None,
1066            CharacterState::Talk(_) => None,
1067            CharacterState::Climb(_) => None,
1068            CharacterState::Wallrun(_) => None,
1069            CharacterState::Skate(_) => None,
1070            CharacterState::Glide(data) => Some(data.timer),
1071            CharacterState::GlideWield(_) => None,
1072            CharacterState::Stunned(data) => Some(data.timer),
1073            CharacterState::Sit => None,
1074            CharacterState::Dance => None,
1075            CharacterState::BasicBlock(data) => Some(data.timer),
1076            CharacterState::Roll(data) => Some(data.timer),
1077            CharacterState::Wielding(_) => None,
1078            CharacterState::Equipping(data) => Some(data.timer),
1079            CharacterState::ComboMelee2(data) => Some(data.timer),
1080            CharacterState::BasicMelee(data) => Some(data.timer),
1081            CharacterState::BasicRanged(data) => Some(data.timer),
1082            CharacterState::Boost(data) => Some(data.timer),
1083            CharacterState::DashMelee(data) => Some(data.timer),
1084            CharacterState::LeapExplosionShockwave(data) => Some(data.timer),
1085            CharacterState::LeapMelee(data) => Some(data.timer),
1086            CharacterState::LeapShockwave(data) => Some(data.timer),
1087            CharacterState::ChargedMelee(data) => Some(data.timer),
1088            CharacterState::ChargedRanged(data) => Some(data.timer),
1089            CharacterState::RepeaterRanged(data) => Some(data.timer),
1090            CharacterState::Throw(data) => Some(data.timer),
1091            CharacterState::Shockwave(data) => Some(data.timer),
1092            CharacterState::Explosion(data) => Some(data.timer),
1093            CharacterState::BasicBeam(data) => Some(data.timer),
1094            CharacterState::BasicAura(data) => Some(data.timer),
1095            CharacterState::Blink(data) => Some(data.timer),
1096            CharacterState::BasicSummon(data) => Some(data.timer),
1097            CharacterState::SelfBuff(data) => Some(data.timer),
1098            CharacterState::SpriteSummon(data) => Some(data.timer),
1099            CharacterState::UseItem(data) => Some(data.timer),
1100            CharacterState::Interact(data) => Some(data.timer),
1101            CharacterState::FinisherMelee(data) => Some(data.timer),
1102            CharacterState::Music(data) => Some(data.timer),
1103            CharacterState::DiveMelee(data) => Some(data.timer),
1104            CharacterState::RiposteMelee(data) => Some(data.timer),
1105            CharacterState::RapidMelee(data) => Some(data.timer),
1106            CharacterState::Transform(data) => Some(data.timer),
1107            CharacterState::RegrowHead(data) => Some(data.timer),
1108            CharacterState::StaticAura(data) => Some(data.timer),
1109        }
1110    }
1111
1112    pub fn attack_kind(&self) -> &[AttackSource] {
1113        match self {
1114            CharacterState::Idle(_) => &[],
1115            CharacterState::Crawl => &[],
1116            CharacterState::Talk(_) => &[],
1117            CharacterState::Climb(_) => &[],
1118            CharacterState::Wallrun(_) => &[],
1119            CharacterState::Skate(_) => &[],
1120            CharacterState::Glide(_) => &[],
1121            CharacterState::GlideWield(_) => &[],
1122            CharacterState::Stunned(_) => &[],
1123            CharacterState::Sit => &[],
1124            CharacterState::Dance => &[],
1125            CharacterState::BasicBlock(_) => &[],
1126            CharacterState::Roll(_) => &[],
1127            CharacterState::Wielding(_) => &[],
1128            CharacterState::Equipping(_) => &[],
1129            CharacterState::ComboMelee2(_) => &[AttackSource::Melee],
1130            CharacterState::BasicMelee(_) => &[AttackSource::Melee],
1131            CharacterState::BasicRanged(data) => {
1132                if data.static_data.projectile.is_explosive() {
1133                    &[AttackSource::Explosion]
1134                } else {
1135                    &[AttackSource::Projectile]
1136                }
1137            },
1138            CharacterState::Boost(_) => &[],
1139            CharacterState::DashMelee(_) => &[AttackSource::Melee],
1140            CharacterState::LeapMelee(_) => &[AttackSource::Melee],
1141            CharacterState::ChargedMelee(_) => &[AttackSource::Melee],
1142            // TODO: When charged ranged not only arrow make this check projectile type
1143            CharacterState::ChargedRanged(_) => &[AttackSource::Projectile],
1144            CharacterState::RepeaterRanged(data) => {
1145                if data.static_data.projectile.is_explosive() {
1146                    &[AttackSource::Explosion]
1147                } else {
1148                    &[AttackSource::Projectile]
1149                }
1150            },
1151            CharacterState::LeapExplosionShockwave(data) => data
1152                .static_data
1153                .shockwave_dodgeable
1154                .explosion_shockwave_attack_source_slice(),
1155            CharacterState::Throw(_) => &[AttackSource::Projectile],
1156            CharacterState::Shockwave(data) => {
1157                data.static_data.dodgeable.shockwave_attack_source_slice()
1158            },
1159            CharacterState::LeapShockwave(data) => {
1160                data.static_data.dodgeable.shockwave_attack_source_slice()
1161            },
1162            CharacterState::Explosion(_) => &[AttackSource::Explosion],
1163            CharacterState::BasicBeam(_) => &[AttackSource::Beam],
1164            CharacterState::BasicAura(_) => &[],
1165            CharacterState::Blink(_) => &[],
1166            CharacterState::BasicSummon(_) => &[],
1167            CharacterState::SelfBuff(_) => &[],
1168            CharacterState::SpriteSummon(_) => &[],
1169            CharacterState::UseItem(_) => &[],
1170            CharacterState::Interact(_) => &[],
1171            CharacterState::FinisherMelee(_) => &[AttackSource::Melee],
1172            CharacterState::Music(_) => &[],
1173            CharacterState::DiveMelee(_) => &[AttackSource::Melee],
1174            CharacterState::RiposteMelee(_) => &[AttackSource::Melee],
1175            CharacterState::RapidMelee(_) => &[AttackSource::Melee],
1176            CharacterState::Transform(_) => &[],
1177            CharacterState::RegrowHead(_) => &[],
1178            CharacterState::StaticAura(_) => &[],
1179        }
1180    }
1181}
1182
1183#[derive(Default, Copy, Clone)]
1184pub struct DurationsInfo {
1185    pub buildup: Option<Duration>,
1186    pub action: Option<Duration>,
1187    pub recover: Option<Duration>,
1188    pub movement: Option<Duration>,
1189    pub charge: Option<Duration>,
1190}
1191
1192#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq)]
1193pub struct AttackFilters {
1194    pub melee: bool,
1195    pub projectiles: bool,
1196    pub beams: bool,
1197    pub ground_shockwaves: bool,
1198    pub air_shockwaves: bool,
1199    pub explosions: bool,
1200}
1201
1202impl AttackFilters {
1203    pub fn applies(&self, attack: AttackSource) -> bool {
1204        match attack {
1205            AttackSource::Melee => self.melee,
1206            AttackSource::Projectile => self.projectiles,
1207            AttackSource::Beam => self.beams,
1208            AttackSource::GroundShockwave => self.ground_shockwaves,
1209            AttackSource::AirShockwave => self.air_shockwaves,
1210            AttackSource::UndodgeableShockwave => false,
1211            AttackSource::Explosion => self.explosions,
1212        }
1213    }
1214}
1215
1216impl Default for CharacterState {
1217    fn default() -> Self {
1218        Self::Idle(idle::Data {
1219            is_sneaking: false,
1220            footwear: None,
1221            time_entered: Time(0.0),
1222        })
1223    }
1224}
1225
1226impl Component for CharacterState {
1227    type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
1228}
1229
1230/// Contains information about the visual activity of a character.
1231///
1232/// For now this only includes the direction they're looking in, but later it
1233/// might include markers indicating that they're available for
1234/// trade/interaction, more details about their stance or appearance, facial
1235/// expression, etc.
1236#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
1237pub struct CharacterActivity {
1238    /// `None` means that the look direction should be derived from the
1239    /// orientation
1240    pub look_dir: Option<Dir>,
1241    /// If the character is using a Helm, this is the y direction the
1242    /// character steering. If the character is not steering this is
1243    /// a stale value.
1244    pub steer_dir: f32,
1245    /// If true, the owner has set this pet to stay at a fixed location and
1246    /// to not engage in combat
1247    pub is_pet_staying: bool,
1248}
1249
1250impl Component for CharacterActivity {
1251    type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
1252}