diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 882eb65ac3..5cfad8ccbc 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -47,6 +47,7 @@ pub enum CharacterAbility { }, TripleStrike { base_damage: u32, + needs_timing: bool, }, } @@ -55,7 +56,7 @@ impl CharacterAbility { /// applicable. pub fn requirements_paid(&self, data: &JoinData, update: &mut StateUpdate) -> bool { match self { - CharacterAbility::TripleStrike { .. } => { + CharacterAbility::TimedCombo { .. } | CharacterAbility::TripleStrike { .. } => { data.physics.on_ground && data.body.is_humanoid() && data.inputs.look_dir.xy().magnitude_squared() > 0.01 @@ -179,16 +180,20 @@ impl From<&CharacterAbility> for CharacterState { stage_time_active: Duration::default(), base_damage: *base_damage, }), - CharacterAbility::TripleStrike { base_damage } => { - CharacterState::TripleStrike(triple_strike::Data { - base_damage: *base_damage, - stage: triple_strike::Stage::First, - stage_exhausted: false, - stage_time_active: Duration::default(), - should_transition: true, - initialized: false, - }) - }, + CharacterAbility::TripleStrike { + base_damage, + needs_timing, + } => CharacterState::TripleStrike(triple_strike::Data { + base_damage: *base_damage, + stage: triple_strike::Stage::First, + stage_exhausted: false, + stage_time_active: Duration::default(), + needs_timing: *needs_timing, + // If `needs_timing`, prevent tansitioning by default, + // unless pressed at the right time. + should_transition: !*needs_timing, + initialized: false, + }), } } } diff --git a/common/src/comp/inventory/item.rs b/common/src/comp/inventory/item.rs index 27ced5f27b..2750358d1e 100644 --- a/common/src/comp/inventory/item.rs +++ b/common/src/comp/inventory/item.rs @@ -70,11 +70,17 @@ impl ToolData { use ToolKind::*; match self.kind { - Sword(_) => vec![TripleStrike { base_damage: 5 }, DashMelee { - buildup_duration: Duration::from_millis(500), - recover_duration: Duration::from_millis(500), - base_damage: 10, - }], + Sword(_) => vec![ + TripleStrike { + base_damage: 5, + needs_timing: false, + }, + DashMelee { + buildup_duration: Duration::from_millis(500), + recover_duration: Duration::from_millis(500), + base_damage: 10, + }, + ], Axe(_) => vec![BasicMelee { energy_cost: 0, buildup_duration: Duration::from_millis(700), @@ -83,14 +89,20 @@ impl ToolData { range: 3.5, max_angle: 30.0, }], - Hammer(_) => vec![BasicMelee { - energy_cost: 0, - buildup_duration: Duration::from_millis(700), - recover_duration: Duration::from_millis(300), - base_healthchange: -10, - range: 3.5, - max_angle: 60.0, - }], + Hammer(_) => vec![ + BasicMelee { + energy_cost: 0, + buildup_duration: Duration::from_millis(700), + recover_duration: Duration::from_millis(300), + base_healthchange: -10, + range: 3.5, + max_angle: 60.0, + }, + TripleStrike { + base_damage: 7, + needs_timing: true, + }, + ], Bow(_) => vec![BasicRanged { energy_cost: 0, holdable: true, diff --git a/common/src/states/triple_strike.rs b/common/src/states/triple_strike.rs index f557ce3163..5dcc0de307 100644 --- a/common/src/states/triple_strike.rs +++ b/common/src/states/triple_strike.rs @@ -8,7 +8,6 @@ use vek::vec::Vec3; // In millis const STAGE_DURATION: u64 = 700; - const INITIAL_ACCEL: f32 = 90.0; const BASE_SPEED: f32 = 25.0; @@ -39,6 +38,10 @@ pub struct Data { pub should_transition: bool, /// Whether state has performed intialization logic pub initialized: bool, + /// Whether player must time button pressed properly to continue combo + pub needs_timing: bool, + /* /// Set to prevent transitioning, true by default when `needs_timing` + * pub prevent_transition: bool, */ } impl CharacterBehavior for Data { @@ -50,9 +53,6 @@ impl CharacterBehavior for Data { .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or(Duration::default()); - // If player stops holding input, don't go to the next stage - let should_transition = data.inputs.primary.is_pressed() && self.should_transition; - if !self.initialized { update.vel.0 = Vec3::zero(); if let Some(dir) = data.inputs.look_dir.try_normalized() { @@ -61,6 +61,24 @@ impl CharacterBehavior for Data { } let initialized = true; + // If player stops holding input, don't go to the next stage + let mut should_transition = self.should_transition; + + // Check inputs based on whether `needs_timing` + if self.needs_timing { + // Player must press at right time + if data.inputs.primary.is_pressed() + && stage_time_active > Duration::from_millis(STAGE_DURATION * 0.7) + { + should_transition = true; + } + } else { + // Prevent transitioning if player ever stops holding input + if !data.inputs.primary.is_pressed() { + should_transition = false; + } + } + // Handle hit applied if let Some(attack) = data.attacking { if attack.applied && attack.hit_count > 0 { @@ -125,6 +143,7 @@ impl CharacterBehavior for Data { stage_exhausted: true, should_transition, initialized, + needs_timing: self.needs_timing, }) } else if stage_time_active > Duration::from_millis(STAGE_DURATION) { let next_stage = match self.stage { @@ -142,6 +161,7 @@ impl CharacterBehavior for Data { stage_exhausted: false, should_transition, initialized, + needs_timing: self.needs_timing, }) } else { // Make sure attack component is removed @@ -157,6 +177,7 @@ impl CharacterBehavior for Data { stage_exhausted: self.stage_exhausted, should_transition, initialized, + needs_timing: self.needs_timing, }) };