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