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