diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 05fadd2ab4..a6f9b41eef 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -6,6 +6,7 @@ pub enum AbilityState { BasicBlock, Roll, ChargeAttack, + TripleAttack, } impl Default for AbilityState { fn default() -> Self { Self::BasicAttack } @@ -27,7 +28,8 @@ impl Default for AbilityPool { fn default() -> Self { Self { primary: Some(AbilityState::BasicAttack), - secondary: Some(AbilityState::ChargeAttack), + // primary: Some(AbilityState::TripleAttack), + secondary: Some(AbilityState::BasicBlock), block: None, dodge: Some(AbilityState::Roll), } diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 4726226ec1..5486366afb 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -50,6 +50,20 @@ pub enum CharacterState { /// How long the state has until exiting remaining_duration: Duration, }, + /// A three-stage attack where play must click at appropriate times + /// to continue attack chain. + TripleAttack { + /// The tool this state will read to handle damage, etc. + tool: ToolData, + /// `int` denoting what stage (of 3) the attack is in. + stage: i8, + /// How long current stage has been active + stage_time_active: Duration, + /// Whether current stage has exhausted its attack + stage_exhausted: bool, + /// Whether player has clicked at the proper time to go to next stage + can_transition: bool, + }, } impl CharacterState { diff --git a/common/src/states/basic_block.rs b/common/src/states/basic_block.rs index 09f827d432..912707d954 100644 --- a/common/src/states/basic_block.rs +++ b/common/src/states/basic_block.rs @@ -1,8 +1,5 @@ use super::utils::*; -use crate::{ - comp::{CharacterState, ItemKind, StateUpdate}, - sys::character_behavior::JoinData, -}; +use crate::{comp::StateUpdate, sys::character_behavior::JoinData}; use std::collections::VecDeque; // const BLOCK_ACCEL: f32 = 30.0; diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index 5f48ad7367..530679e37a 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -8,5 +8,6 @@ pub mod glide; pub mod idle; pub mod roll; pub mod sit; +pub mod triple_attack; pub mod utils; pub mod wielding; diff --git a/common/src/states/triple_attack/mod.rs b/common/src/states/triple_attack/mod.rs new file mode 100644 index 0000000000..dfeaa5c260 --- /dev/null +++ b/common/src/states/triple_attack/mod.rs @@ -0,0 +1,92 @@ +use crate::{ + comp::{Attacking, CharacterState, ItemKind::Tool, StateUpdate}, + states::utils::*, + sys::character_behavior::JoinData, +}; +use std::{collections::VecDeque, time::Duration}; + +pub fn behavior(data: &JoinData) -> StateUpdate { + let mut update = StateUpdate { + pos: *data.pos, + vel: *data.vel, + ori: *data.ori, + energy: *data.energy, + character: *data.character, + local_events: VecDeque::new(), + server_events: VecDeque::new(), + }; + + if let CharacterState::TripleAttack { + tool, + stage, + stage_time_active, + stage_exhausted, + can_transition, + } = data.character + { + let mut new_can_transition = *can_transition; + let mut new_stage_exhausted = *stage_exhausted; + let new_stage_time_active = stage_time_active + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or(Duration::default()); + + match stage { + 1 => { + if new_stage_time_active > tool.attack_buildup_duration() { + if !*stage_exhausted { + // Try to deal damage + data.updater.insert(data.entity, Attacking { + weapon: Some(*tool), + }); + new_stage_exhausted = true; + } else { + // Make sure to remove Attacking component + data.updater.remove::(data.entity); + } + + // Check if player has timed click right + if data.inputs.primary.is_just_pressed() { + println!("Can transition"); + new_can_transition = true; + } + } + + if new_stage_time_active > tool.attack_duration() { + if new_can_transition { + update.character = CharacterState::TripleAttack { + tool: *tool, + stage: 2, + stage_time_active: Duration::default(), + stage_exhausted: false, + can_transition: false, + } + } else { + println!("Failed"); + attempt_wield(data, &mut update); + } + } else { + update.character = CharacterState::TripleAttack { + tool: *tool, + stage: 1, + stage_time_active: new_stage_time_active, + stage_exhausted: new_stage_exhausted, + can_transition: new_can_transition, + } + } + }, + 2 => { + println!("2"); + attempt_wield(data, &mut update); + }, + 3 => { + println!("3"); + attempt_wield(data, &mut update); + }, + _ => { + // Should never get here. + }, + } + } + + update +} diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 3611b6c3b8..4beffc9d6d 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -1,11 +1,12 @@ use crate::{ - comp::{AbilityState, CharacterState, EnergySource, ItemKind::Tool, StateUpdate}, + comp::{AbilityState, CharacterState, EnergySource, ItemKind::Tool, StateUpdate, ToolData}, event::LocalEvent, sys::{character_behavior::JoinData, phys::GRAVITY}, }; use std::time::Duration; use vek::vec::{Vec2, Vec3}; +pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; const BASE_HUMANOID_ACCEL: f32 = 100.0; const BASE_HUMANOID_SPEED: f32 = 150.0; const BASE_HUMANOID_AIR_ACCEL: f32 = 15.0; @@ -24,8 +25,6 @@ const BASE_HUMANOID_WATER_SPEED: f32 = 120.0; // const CLIMB_SPEED: f32 = 5.0; // const CLIMB_COST: i32 = 5; -pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0; - /// Handles updating `Components` to move player based on state of `JoinData` pub fn handle_move(data: &JoinData, update: &mut StateUpdate) { if data.physics.in_fluid { @@ -231,7 +230,7 @@ pub fn attempt_dodge_ability(data: &JoinData, update: &mut StateUpdate) { } } -// TODO: This might need a CharacterState::new(data, update) fn if +// TODO: Wight need a fn `CharacterState::new(data, update)` if // initialization gets too lengthy. /// Maps from `AbilityState`s to `CharacterStates`s. Also handles intializing @@ -239,13 +238,13 @@ pub fn attempt_dodge_ability(data: &JoinData, update: &mut StateUpdate) { pub fn ability_to_character_state(data: &JoinData, ability_state: AbilityState) -> CharacterState { match ability_state { AbilityState::BasicAttack { .. } => { - if let Some(Tool(tool)) = data.stats.equipment.main.as_ref().map(|i| i.kind) { + if let Some(tool) = get_tool_data(data) { CharacterState::BasicAttack { exhausted: false, remaining_duration: tool.attack_duration(), } } else { - CharacterState::Idle {} + *data.character } }, AbilityState::BasicBlock { .. } => CharacterState::BasicBlock {}, @@ -255,5 +254,29 @@ pub fn ability_to_character_state(data: &JoinData, ability_state: AbilityState) AbilityState::ChargeAttack { .. } => CharacterState::ChargeAttack { remaining_duration: Duration::from_millis(600), }, + AbilityState::TripleAttack { .. } => { + if let Some(tool) = get_tool_data(data) { + CharacterState::TripleAttack { + tool, + stage: 1, + stage_time_active: Duration::default(), + stage_exhausted: false, + can_transition: false, + } + } else { + *data.character + } + }, + + // Do not use default match + // _ => *data.character + } +} + +pub fn get_tool_data(data: &JoinData) -> Option { + if let Some(Tool(tool)) = data.stats.equipment.main.as_ref().map(|i| i.kind) { + Some(tool) + } else { + None } } diff --git a/common/src/sys/character_behavior.rs b/common/src/sys/character_behavior.rs index 89034ace4d..01cf463c62 100644 --- a/common/src/sys/character_behavior.rs +++ b/common/src/sys/character_behavior.rs @@ -171,7 +171,9 @@ impl<'a> System<'a> for Sys { CharacterState::BasicBlock { .. } => states::basic_block::behavior(&j), CharacterState::ChargeAttack { .. } => states::charge_attack::behavior(&j), CharacterState::Sit { .. } => states::sit::behavior(&j), + CharacterState::TripleAttack { .. } => states::triple_attack::behavior(&j), + // Do not use default match. // _ => StateUpdate { // character: *j.character, // pos: *j.pos,