veloren_common/comp/
character_state.rs

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