diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 8c56e7af5a..f9b6903e87 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -39,23 +39,25 @@ impl From for CharacterState { CharacterAbility::BasicAttack { buildup_duration, recover_duration, - } => CharacterState::BasicAttack(basic_attack::State { + } => CharacterState::BasicAttack(basic_attack::Data { exhausted: false, buildup_duration, recover_duration, }), - CharacterAbility::BasicBlock { .. } => CharacterState::BasicBlock {}, - CharacterAbility::Roll { .. } => CharacterState::Roll { - remaining_duration: Duration::from_millis(600), - }, - CharacterAbility::ChargeAttack { .. } => CharacterState::ChargeAttack { + CharacterAbility::BasicBlock { .. } => CharacterState::BasicBlock, + CharacterAbility::Roll { .. } => CharacterState::Roll(roll::Data { remaining_duration: Duration::from_millis(600), + }), + CharacterAbility::ChargeAttack { .. } => { + CharacterState::ChargeAttack(charge_attack::Data { + remaining_duration: Duration::from_millis(600), + }) }, CharacterAbility::TimedCombo { tool, buildup_duration, recover_duration, - } => CharacterState::TimedCombo(timed_combo::State { + } => CharacterState::TimedCombo(timed_combo::Data { tool, buildup_duration, recover_duration, diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index fa2460a15b..3c92f65890 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -5,7 +5,7 @@ use crate::{ }; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage, HashMapStorage, VecStorage}; -use std::{collections::VecDeque, time::Duration}; +use std::collections::VecDeque; /// Data returned from character behavior fn's to Character Behavior System. pub struct StateUpdate { @@ -22,54 +22,35 @@ pub struct StateUpdate { pub enum CharacterState { Idle, Climb, - Sit {}, - Equipping { - /// The weapon being equipped - tool: ToolData, - /// Time left before next state - time_left: Duration, - }, - Wielding { - /// The weapon being wielded - tool: ToolData, - }, + Sit, Glide, /// A basic blocking state BasicBlock, - ChargeAttack { - /// How long the state has until exiting - remaining_duration: Duration, - }, - Roll { - /// How long the state has until exiting - remaining_duration: Duration, - }, + /// Player is busy equipping or unequipping weapons + Equipping(equipping::Data), + /// Player is holding a weapon and can perform other actions + Wielding(wielding::Data), + /// Player rushes forward and slams an enemy with their weapon + ChargeAttack(charge_attack::Data), + /// A dodge where player can roll + Roll(roll::Data), /// A basic attacking state - BasicAttack(basic_attack::State), + BasicAttack(basic_attack::Data), /// A three-stage attack where play must click at appropriate times /// to continue attack chain. - TimedCombo(timed_combo::State), + TimedCombo(timed_combo::Data), /// A three-stage attack where each attack pushes player forward /// and successive attacks increase in damage, while player holds button. - TripleStrike { - /// 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, - }, + TripleStrike(triple_strike::Data), } impl CharacterState { pub fn is_wield(&self) -> bool { match self { - CharacterState::Wielding { .. } + CharacterState::Wielding(_) | CharacterState::BasicAttack(_) | CharacterState::TimedCombo(_) - | CharacterState::BasicBlock { .. } => true, + | CharacterState::BasicBlock => true, _ => false, } } @@ -78,21 +59,21 @@ impl CharacterState { match self { CharacterState::BasicAttack(_) | CharacterState::TimedCombo(_) - | CharacterState::ChargeAttack { .. } => true, + | CharacterState::ChargeAttack(_) => true, _ => false, } } pub fn is_block(&self) -> bool { match self { - CharacterState::BasicBlock { .. } => true, + CharacterState::BasicBlock => true, _ => false, } } pub fn is_dodge(&self) -> bool { match self { - CharacterState::Roll { .. } => true, + CharacterState::Roll(_) => true, _ => false, } } @@ -105,7 +86,7 @@ impl CharacterState { } impl Default for CharacterState { - fn default() -> Self { Self::Idle {} } + fn default() -> Self { Self::Idle } } impl Component for CharacterState { diff --git a/common/src/states/basic_attack.rs b/common/src/states/basic_attack.rs index 4bf8f67108..f8d2ba5230 100644 --- a/common/src/states/basic_attack.rs +++ b/common/src/states/basic_attack.rs @@ -1,12 +1,12 @@ use crate::{ - comp::{Attacking, CharacterState, EnergySource, ItemKind::Tool, StateUpdate}, - states::utils::*, + comp::{Attacking, CharacterState, EnergySource, StateUpdate}, + states::{utils::*, wielding}, sys::character_behavior::*, }; use std::{collections::VecDeque, time::Duration}; #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] -pub struct State { +pub struct Data { /// How long until state should deal damage pub buildup_duration: Duration, /// How long the state has until exiting @@ -15,7 +15,7 @@ pub struct State { pub exhausted: bool, } -impl CharacterBehavior for State { +impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate { pos: *data.pos, @@ -32,7 +32,7 @@ impl CharacterBehavior for State { // Build up window if self.buildup_duration != Duration::default() { // Start to swing - update.character = CharacterState::BasicAttack(State { + update.character = CharacterState::BasicAttack(Data { buildup_duration: self .buildup_duration .checked_sub(Duration::from_secs_f32(data.dt.0)) @@ -52,7 +52,7 @@ impl CharacterBehavior for State { }); } - update.character = CharacterState::BasicAttack(State { + update.character = CharacterState::BasicAttack(Data { buildup_duration: self.buildup_duration, recover_duration: self.recover_duration, exhausted: true, @@ -60,7 +60,7 @@ impl CharacterBehavior for State { } // Swing recovery window else if self.recover_duration != Duration::default() { - update.character = CharacterState::BasicAttack(State { + update.character = CharacterState::BasicAttack(Data { buildup_duration: self.buildup_duration, recover_duration: self .recover_duration @@ -72,7 +72,7 @@ impl CharacterBehavior for State { // Done else { if let Some(tool) = unwrap_tool_data(data) { - update.character = CharacterState::Wielding { tool }; + update.character = CharacterState::Wielding(wielding::Data { tool }); // Make sure attack component is removed data.updater.remove::(data.entity); } else { diff --git a/common/src/states/basic_block.rs b/common/src/states/basic_block.rs index 912707d954..efb78ad323 100644 --- a/common/src/states/basic_block.rs +++ b/common/src/states/basic_block.rs @@ -1,25 +1,33 @@ use super::utils::*; -use crate::{comp::StateUpdate, sys::character_behavior::JoinData}; +use crate::{ + comp::StateUpdate, + sys::character_behavior::{CharacterBehavior, JoinData}, +}; use std::collections::VecDeque; // const BLOCK_ACCEL: f32 = 30.0; // const BLOCK_SPEED: f32 = 75.0; -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(), - }; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +pub struct Data; - handle_move(&data, &mut update); +impl CharacterBehavior for Data { + fn behavior(&self, 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 !data.physics.on_ground || !data.inputs.secondary.is_pressed() { - attempt_wield(data, &mut update); + handle_move(&data, &mut update); + + if !data.physics.on_ground || !data.inputs.secondary.is_pressed() { + attempt_wield(data, &mut update); + } + update } - update } diff --git a/common/src/states/charge_attack.rs b/common/src/states/charge_attack.rs index 7958885a4e..af479613b1 100644 --- a/common/src/states/charge_attack.rs +++ b/common/src/states/charge_attack.rs @@ -2,25 +2,30 @@ use super::utils::*; use crate::{ comp::{CharacterState::*, HealthChange, HealthSource, StateUpdate}, event::ServerEvent, - sys::character_behavior::JoinData, + sys::character_behavior::{CharacterBehavior, JoinData}, }; use std::{collections::VecDeque, time::Duration}; use vek::Vec3; const CHARGE_SPEED: f32 = 20.0; -pub fn behavior(data: &JoinData) -> StateUpdate { - let mut update = StateUpdate { - pos: *data.pos, - vel: *data.vel, - ori: *data.ori, - character: *data.character, - energy: *data.energy, - local_events: VecDeque::new(), - server_events: VecDeque::new(), - }; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +pub struct Data { + /// How long the state has until exiting + pub remaining_duration: Duration, +} - if let ChargeAttack { remaining_duration } = data.character { +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate { + pos: *data.pos, + vel: *data.vel, + ori: *data.ori, + character: *data.character, + energy: *data.energy, + local_events: VecDeque::new(), + server_events: VecDeque::new(), + }; // Move player update.vel.0 = Vec3::new(0.0, 0.0, update.vel.0.z) + (update.vel.0 * Vec3::new(1.0, 1.0, 0.0) @@ -46,18 +51,20 @@ pub fn behavior(data: &JoinData) -> StateUpdate { } // Check if charge timed out or can't keep moving forward - if *remaining_duration == Duration::default() || update.vel.0.magnitude_squared() < 10.0 { + if self.remaining_duration == Duration::default() || update.vel.0.magnitude_squared() < 10.0 + { attempt_wield(data, &mut update); return update; } // Tick remaining-duration and keep charging - update.character = ChargeAttack { - remaining_duration: remaining_duration + update.character = ChargeAttack(Data { + remaining_duration: self + .remaining_duration .checked_sub(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - }; - } + }); - update + update + } } diff --git a/common/src/states/climb.rs b/common/src/states/climb.rs index 6d5f09b1ea..0bff46fdad 100644 --- a/common/src/states/climb.rs +++ b/common/src/states/climb.rs @@ -1,7 +1,10 @@ use crate::{ comp::{CharacterState, EnergySource, StateUpdate}, event::LocalEvent, - sys::{character_behavior::JoinData, phys::GRAVITY}, + sys::{ + character_behavior::{CharacterBehavior, JoinData}, + phys::GRAVITY, + }, }; use std::collections::VecDeque; use vek::{ @@ -12,84 +15,90 @@ use vek::{ const HUMANOID_CLIMB_ACCEL: f32 = 5.0; const CLIMB_SPEED: f32 = 5.0; -pub fn behavior(data: &JoinData) -> StateUpdate { - let mut update = StateUpdate { - pos: *data.pos, - vel: *data.vel, - ori: *data.ori, - character: *data.character, - energy: *data.energy, - local_events: VecDeque::new(), - server_events: VecDeque::new(), - }; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +pub struct Data; - if let Err(_) = update.energy.try_change_by(-5, EnergySource::Climb) { - update.character = CharacterState::Idle {}; - } - - // If no wall is in front of character ... - if data.physics.on_wall.is_none() || data.physics.on_ground { - if data.inputs.jump.is_pressed() { - // They've climbed atop something, give them a boost - update - .local_events - .push_front(LocalEvent::Jump(data.entity)); - } - update.character = CharacterState::Idle {}; - return update; - } - - // Move player - update.vel.0 += Vec2::broadcast(data.dt.0) - * data.inputs.move_dir - * if update.vel.0.magnitude_squared() < CLIMB_SPEED.powf(2.0) { - HUMANOID_CLIMB_ACCEL - } else { - 0.0 +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate { + pos: *data.pos, + vel: *data.vel, + ori: *data.ori, + character: *data.character, + energy: *data.energy, + local_events: VecDeque::new(), + server_events: VecDeque::new(), }; - // Set orientation direction based on wall direction - let ori_dir = if let Some(wall_dir) = data.physics.on_wall { - if Vec2::::from(wall_dir).magnitude_squared() > 0.001 { - Vec2::from(wall_dir).normalized() + if let Err(_) = update.energy.try_change_by(-5, EnergySource::Climb) { + update.character = CharacterState::Idle {}; + } + + // If no wall is in front of character ... + if data.physics.on_wall.is_none() || data.physics.on_ground { + if data.inputs.jump.is_pressed() { + // They've climbed atop something, give them a boost + update + .local_events + .push_front(LocalEvent::Jump(data.entity)); + } + update.character = CharacterState::Idle {}; + return update; + } + + // Move player + update.vel.0 += Vec2::broadcast(data.dt.0) + * data.inputs.move_dir + * if update.vel.0.magnitude_squared() < CLIMB_SPEED.powf(2.0) { + HUMANOID_CLIMB_ACCEL + } else { + 0.0 + }; + + // Set orientation direction based on wall direction + let ori_dir = if let Some(wall_dir) = data.physics.on_wall { + if Vec2::::from(wall_dir).magnitude_squared() > 0.001 { + Vec2::from(wall_dir).normalized() + } else { + Vec2::from(update.vel.0) + } } else { Vec2::from(update.vel.0) - } - } else { - Vec2::from(update.vel.0) - }; + }; - // Smooth orientation - if ori_dir.magnitude_squared() > 0.0001 - && (update.ori.0.normalized() - Vec3::from(ori_dir).normalized()).magnitude_squared() - > 0.001 - { - update.ori.0 = vek::ops::Slerp::slerp( - update.ori.0, - ori_dir.into(), - if data.physics.on_ground { 9.0 } else { 2.0 } * data.dt.0, - ); - } - - // Apply Vertical Climbing Movement - if let (true, Some(_wall_dir)) = ( - (data.inputs.climb.is_pressed() | data.inputs.climb_down.is_pressed()) - && update.vel.0.z <= CLIMB_SPEED, - data.physics.on_wall, - ) { - if data.inputs.climb_down.is_pressed() && !data.inputs.climb.is_pressed() { - update.vel.0 -= data.dt.0 * update.vel.0.map(|e| e.abs().powf(1.5) * e.signum() * 6.0); - } else if data.inputs.climb.is_pressed() && !data.inputs.climb_down.is_pressed() { - update.vel.0.z = (update.vel.0.z + data.dt.0 * GRAVITY * 1.25).min(CLIMB_SPEED); - } else { - update.vel.0.z = update.vel.0.z + data.dt.0 * GRAVITY * 1.5; - update.vel.0 = Lerp::lerp( - update.vel.0, - Vec3::zero(), - 30.0 * data.dt.0 / (1.0 - update.vel.0.z.min(0.0) * 5.0), + // Smooth orientation + if ori_dir.magnitude_squared() > 0.0001 + && (update.ori.0.normalized() - Vec3::from(ori_dir).normalized()).magnitude_squared() + > 0.001 + { + update.ori.0 = vek::ops::Slerp::slerp( + update.ori.0, + ori_dir.into(), + if data.physics.on_ground { 9.0 } else { 2.0 } * data.dt.0, ); } - } - update + // Apply Vertical Climbing Movement + if let (true, Some(_wall_dir)) = ( + (data.inputs.climb.is_pressed() | data.inputs.climb_down.is_pressed()) + && update.vel.0.z <= CLIMB_SPEED, + data.physics.on_wall, + ) { + if data.inputs.climb_down.is_pressed() && !data.inputs.climb.is_pressed() { + update.vel.0 -= + data.dt.0 * update.vel.0.map(|e| e.abs().powf(1.5) * e.signum() * 6.0); + } else if data.inputs.climb.is_pressed() && !data.inputs.climb_down.is_pressed() { + update.vel.0.z = (update.vel.0.z + data.dt.0 * GRAVITY * 1.25).min(CLIMB_SPEED); + } else { + update.vel.0.z = update.vel.0.z + data.dt.0 * GRAVITY * 1.5; + update.vel.0 = Lerp::lerp( + update.vel.0, + Vec3::zero(), + 30.0 * data.dt.0 / (1.0 - update.vel.0.z.min(0.0) * 5.0), + ); + } + } + + update + } } diff --git a/common/src/states/equipping.rs b/common/src/states/equipping.rs index 27ae4f1122..d79be81b34 100644 --- a/common/src/states/equipping.rs +++ b/common/src/states/equipping.rs @@ -1,38 +1,49 @@ use super::utils::*; use crate::{ - comp::{CharacterState, StateUpdate}, - sys::character_behavior::JoinData, + comp::{CharacterState, StateUpdate, ToolData}, + states::wielding, + sys::character_behavior::{CharacterBehavior, JoinData}, }; use std::{collections::VecDeque, time::Duration}; -pub fn behavior(data: &JoinData) -> StateUpdate { - let mut update = StateUpdate { - character: *data.character, - pos: *data.pos, - vel: *data.vel, - ori: *data.ori, - energy: *data.energy, - local_events: VecDeque::new(), - server_events: VecDeque::new(), - }; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +pub struct Data { + /// The weapon being equipped + pub tool: ToolData, + /// Time left before next state + pub time_left: Duration, +} - handle_move(&data, &mut update); - handle_jump(&data, &mut update); +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate { + character: *data.character, + pos: *data.pos, + vel: *data.vel, + ori: *data.ori, + energy: *data.energy, + local_events: VecDeque::new(), + server_events: VecDeque::new(), + }; - if let CharacterState::Equipping { tool, time_left } = data.character { - if *time_left == Duration::default() { + handle_move(&data, &mut update); + handle_jump(&data, &mut update); + + if self.time_left == Duration::default() { // Wield delay has expired - update.character = CharacterState::Wielding { tool: *tool }; + update.character = CharacterState::Wielding(wielding::Data { tool: self.tool }); } else { // Wield delay hasn't expired yet // Update wield delay - update.character = CharacterState::Equipping { - time_left: time_left + update.character = CharacterState::Equipping(Data { + time_left: self + .time_left .checked_sub(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - tool: *tool, - }; + tool: self.tool, + }); } + + update } - update } diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 239f250f72..78851f006a 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -1,6 +1,6 @@ use crate::{ comp::{CharacterState, StateUpdate}, - sys::character_behavior::JoinData, + sys::character_behavior::{CharacterBehavior, JoinData}, }; use std::collections::VecDeque; use vek::{Vec2, Vec3}; @@ -10,57 +10,62 @@ const GLIDE_ANTIGRAV: f32 = crate::sys::phys::GRAVITY * 0.96; const GLIDE_ACCEL: f32 = 15.0; const GLIDE_SPEED: f32 = 45.0; -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(), - }; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +pub struct Data; - // If glide button isn't held or player is on ground, end glide - if !data.inputs.glide.is_pressed() || data.physics.on_ground { - update.character = CharacterState::Idle {}; - } - - // If there is a wall in front of character go to climb - if let Some(_) = data.physics.on_wall { - update.character = CharacterState::Climb {}; - } - - // Move player according to movement direction vector - update.vel.0 += Vec2::broadcast(data.dt.0) - * data.inputs.move_dir - * if data.vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) { - GLIDE_ACCEL - } else { - 0.0 +impl CharacterBehavior for Data { + fn behavior(&self, 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(), }; - // Determine orientation vector from movement direction vector - let ori_dir = Vec2::from(update.vel.0); - if ori_dir.magnitude_squared() > 0.0001 - && (update.ori.0.normalized() - Vec3::from(ori_dir).normalized()).magnitude_squared() - > 0.001 - { - update.ori.0 = vek::ops::Slerp::slerp(update.ori.0, ori_dir.into(), 2.0 * data.dt.0); - } + // If glide button isn't held or player is on ground, end glide + if !data.inputs.glide.is_pressed() || data.physics.on_ground { + update.character = CharacterState::Idle {}; + } - // Apply Glide antigrav lift - if Vec2::::from(update.vel.0).magnitude_squared() < GLIDE_SPEED.powf(2.0) - && update.vel.0.z < 0.0 - { - let lift = GLIDE_ANTIGRAV + update.vel.0.z.abs().powf(2.0) * 0.15; - update.vel.0.z += data.dt.0 - * lift - * (Vec2::::from(update.vel.0).magnitude() * 0.075) - .min(1.0) - .max(0.2); - } + // If there is a wall in front of character go to climb + if let Some(_) = data.physics.on_wall { + update.character = CharacterState::Climb {}; + } - // Otherwise keep gliding - update + // Move player according to movement direction vector + update.vel.0 += Vec2::broadcast(data.dt.0) + * data.inputs.move_dir + * if data.vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) { + GLIDE_ACCEL + } else { + 0.0 + }; + + // Determine orientation vector from movement direction vector + let ori_dir = Vec2::from(update.vel.0); + if ori_dir.magnitude_squared() > 0.0001 + && (update.ori.0.normalized() - Vec3::from(ori_dir).normalized()).magnitude_squared() + > 0.001 + { + update.ori.0 = vek::ops::Slerp::slerp(update.ori.0, ori_dir.into(), 2.0 * data.dt.0); + } + + // Apply Glide antigrav lift + if Vec2::::from(update.vel.0).magnitude_squared() < GLIDE_SPEED.powf(2.0) + && update.vel.0.z < 0.0 + { + let lift = GLIDE_ANTIGRAV + update.vel.0.z.abs().powf(2.0) * 0.15; + update.vel.0.z += data.dt.0 + * lift + * (Vec2::::from(update.vel.0).magnitude() * 0.075) + .min(1.0) + .max(0.2); + } + + // Otherwise keep gliding + update + } } diff --git a/common/src/states/idle.rs b/common/src/states/idle.rs index df13129a2a..282d465b54 100644 --- a/common/src/states/idle.rs +++ b/common/src/states/idle.rs @@ -1,24 +1,31 @@ use super::utils::*; -use crate::{comp::StateUpdate, sys::character_behavior::JoinData}; +use crate::{ + comp::StateUpdate, + sys::character_behavior::{CharacterBehavior, JoinData}, +}; use std::collections::VecDeque; -pub fn behavior(data: &JoinData) -> StateUpdate { - let mut update = StateUpdate { - character: *data.character, - pos: *data.pos, - vel: *data.vel, - ori: *data.ori, - energy: *data.energy, - local_events: VecDeque::new(), - server_events: VecDeque::new(), - }; - handle_move(data, &mut update); - handle_jump(data, &mut update); - handle_wield(data, &mut update); - handle_sit(data, &mut update); - handle_climb(data, &mut update); - handle_glide(data, &mut update); - handle_dodge_input(data, &mut update); +pub struct Data; - update +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate { + character: *data.character, + pos: *data.pos, + vel: *data.vel, + ori: *data.ori, + energy: *data.energy, + local_events: VecDeque::new(), + server_events: VecDeque::new(), + }; + handle_move(data, &mut update); + handle_jump(data, &mut update); + handle_wield(data, &mut update); + handle_sit(data, &mut update); + handle_climb(data, &mut update); + handle_glide(data, &mut update); + handle_dodge_input(data, &mut update); + + update + } } diff --git a/common/src/states/roll.rs b/common/src/states/roll.rs index 9f176817f3..8d57e56546 100644 --- a/common/src/states/roll.rs +++ b/common/src/states/roll.rs @@ -1,24 +1,29 @@ use crate::{ comp::{CharacterState, StateUpdate}, - sys::character_behavior::JoinData, + sys::character_behavior::{CharacterBehavior, JoinData}, }; use std::{collections::VecDeque, time::Duration}; use vek::Vec3; const ROLL_SPEED: f32 = 17.0; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +pub struct Data { + /// How long the state has until exiting + pub remaining_duration: Duration, +} -pub fn behavior(data: &JoinData) -> StateUpdate { - let mut update = StateUpdate { - character: *data.character, - pos: *data.pos, - vel: *data.vel, - ori: *data.ori, - energy: *data.energy, - local_events: VecDeque::new(), - server_events: VecDeque::new(), - }; +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate { + character: *data.character, + pos: *data.pos, + vel: *data.vel, + ori: *data.ori, + energy: *data.energy, + local_events: VecDeque::new(), + server_events: VecDeque::new(), + }; - if let CharacterState::Roll { remaining_duration } = data.character { // Update velocity update.vel.0 = Vec3::new(0.0, 0.0, update.vel.0.z) + (update.vel.0 * Vec3::new(1.0, 1.0, 0.0) @@ -37,19 +42,20 @@ pub fn behavior(data: &JoinData) -> StateUpdate { vek::ops::Slerp::slerp(update.ori.0, update.vel.0.into(), 9.0 * data.dt.0); } - if *remaining_duration == Duration::default() { + if self.remaining_duration == Duration::default() { // Roll duration has expired update.vel.0 *= 0.3; update.character = CharacterState::Idle {}; } else { // Otherwise, tick down remaining_duration - update.character = CharacterState::Roll { - remaining_duration: remaining_duration + update.character = CharacterState::Roll(Data { + remaining_duration: self + .remaining_duration .checked_sub(Duration::from_secs_f32(data.dt.0)) .unwrap_or_default(), - }; + }); } - } - update + update + } } diff --git a/common/src/states/sit.rs b/common/src/states/sit.rs index 89c312d3c2..407e176c01 100644 --- a/common/src/states/sit.rs +++ b/common/src/states/sit.rs @@ -1,30 +1,35 @@ use super::utils::*; use crate::{ comp::{CharacterState, StateUpdate}, - sys::character_behavior::JoinData, + sys::character_behavior::{CharacterBehavior, JoinData}, }; use std::collections::VecDeque; -pub fn behavior(data: &JoinData) -> StateUpdate { - let mut update = StateUpdate { - character: *data.character, - pos: *data.pos, - vel: *data.vel, - ori: *data.ori, - energy: *data.energy, - local_events: VecDeque::new(), - server_events: VecDeque::new(), - }; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +pub struct Data; - handle_wield(data, &mut update); +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate { + character: *data.character, + pos: *data.pos, + vel: *data.vel, + ori: *data.ori, + energy: *data.energy, + local_events: VecDeque::new(), + server_events: VecDeque::new(), + }; - // Try to Fall/Stand up/Move - if !data.physics.on_ground - || data.inputs.sit.is_just_pressed() - || data.inputs.move_dir.magnitude_squared() > 0.0 - { - update.character = CharacterState::Idle {}; + handle_wield(data, &mut update); + + // Try to Fall/Stand up/Move + if !data.physics.on_ground + || data.inputs.sit.is_just_pressed() + || data.inputs.move_dir.magnitude_squared() > 0.0 + { + update.character = CharacterState::Idle; + } + + update } - - update } diff --git a/common/src/states/timed_combo.rs b/common/src/states/timed_combo.rs index e55e6c0dd3..6c8ad42e6c 100644 --- a/common/src/states/timed_combo.rs +++ b/common/src/states/timed_combo.rs @@ -1,11 +1,11 @@ use crate::{ comp::{Attacking, CharacterState, EnergySource, StateUpdate, ToolData}, - states::utils::*, + states::wielding, sys::character_behavior::{CharacterBehavior, JoinData}, }; use std::{collections::VecDeque, time::Duration}; #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] -pub struct State { +pub struct Data { /// Denotes what stage (of 3) the attack is in pub stage: i8, /// Whether current stage has exhausted its attack @@ -20,7 +20,7 @@ pub struct State { pub tool: ToolData, } -impl CharacterBehavior for State { +impl CharacterBehavior for Data { fn behavior(&self, data: &JoinData) -> StateUpdate { let mut update = StateUpdate { pos: *data.pos, @@ -44,11 +44,11 @@ impl CharacterBehavior for State { if data.inputs.primary.is_just_pressed() { println!("Failed"); // They failed, go back to `Wielding` - update.character = CharacterState::Wielding { tool: self.tool }; + update.character = CharacterState::Wielding(wielding::Data { tool: self.tool }); } // Keep updating else { - update.character = CharacterState::TimedCombo(State { + update.character = CharacterState::TimedCombo(Data { tool: self.tool, stage: self.stage, buildup_duration: self.buildup_duration, @@ -67,7 +67,7 @@ impl CharacterBehavior for State { hit_count: 0, }); - update.character = CharacterState::TimedCombo(State { + update.character = CharacterState::TimedCombo(Data { tool: self.tool, stage: self.stage, buildup_duration: self.buildup_duration, @@ -86,7 +86,7 @@ impl CharacterBehavior for State { // Try to transition to next stage if data.inputs.primary.is_just_pressed() { println!("Transition"); - update.character = CharacterState::TimedCombo(State { + update.character = CharacterState::TimedCombo(Data { tool: self.tool, stage: self.stage + 1, buildup_duration: self.buildup_duration, @@ -99,7 +99,7 @@ impl CharacterBehavior for State { else { // Update state println!("Missed"); - update.character = CharacterState::TimedCombo(State { + update.character = CharacterState::TimedCombo(Data { tool: self.tool, stage: self.stage, buildup_duration: self.buildup_duration, @@ -112,7 +112,7 @@ impl CharacterBehavior for State { // Stage expired but missed transition to next stage else { // Back to `Wielding` - update.character = CharacterState::Wielding { tool: self.tool }; + update.character = CharacterState::Wielding(wielding::Data { tool: self.tool }); // Make sure attack component is removed data.updater.remove::(data.entity); } @@ -121,7 +121,7 @@ impl CharacterBehavior for State { else { println!("Success!"); // Back to `Wielding` - update.character = CharacterState::Wielding { tool: self.tool }; + update.character = CharacterState::Wielding(wielding::Data { tool: self.tool }); // Make sure attack component is removed data.updater.remove::(data.entity); } diff --git a/common/src/states/triple_strike.rs b/common/src/states/triple_strike.rs index 60f5d9e63b..fd10f3ccb6 100644 --- a/common/src/states/triple_strike.rs +++ b/common/src/states/triple_strike.rs @@ -1,7 +1,7 @@ use crate::{ - comp::{Attacking, CharacterState, ItemKind::Tool, StateUpdate}, + comp::{StateUpdate, ToolData}, states::utils::*, - sys::character_behavior::JoinData, + sys::character_behavior::{CharacterBehavior, JoinData}, }; use std::{collections::VecDeque, time::Duration}; @@ -14,26 +14,33 @@ const STAGE_DURATION: u64 = 600; /// each one pushes the player forward as the character steps into the swings. /// The player can let go of the left mouse button at any time /// and stop their attacks by interrupting the attack animation. -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(), - }; +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +pub struct Data { + /// The tool this state will read to handle damage, etc. + pub tool: ToolData, + /// `int` denoting what stage (of 3) the attack is in. + pub stage: i8, + /// How long current stage has been active + pub stage_time_active: Duration, + /// Whether current stage has exhausted its attack + stage_exhausted: bool, +} - if let CharacterState::TripleStrike { - tool, - stage, - stage_time_active, - stage_exhausted, - } = data.character - { - let mut new_stage_exhausted = *stage_exhausted; - let new_stage_time_active = stage_time_active +impl CharacterBehavior for Data { + fn behavior(&self, 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(), + }; + + let new_stage_exhausted = self.stage_exhausted; + let new_stage_time_active = self + .stage_time_active .checked_add(Duration::from_secs_f32(data.dt.0)) .unwrap_or(Duration::default()); @@ -43,7 +50,7 @@ pub fn behavior(data: &JoinData) -> StateUpdate { return update; } - while *stage < 3 { + if self.stage < 3 { if new_stage_time_active < Duration::from_millis(STAGE_DURATION / 3) { // Move player forward while in first third of each stage handle_move(data, &mut update); @@ -54,7 +61,7 @@ pub fn behavior(data: &JoinData) -> StateUpdate { // TODO: deal damage } } - } - update + update + } } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index d8238700ce..a0f83674a3 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -1,5 +1,5 @@ use crate::{ - comp::{CharacterAbility, CharacterState, EnergySource, ItemKind::Tool, StateUpdate, ToolData}, + comp::{CharacterState, EnergySource, ItemKind::Tool, StateUpdate, ToolData}, event::LocalEvent, states::*, sys::{character_behavior::JoinData, phys::GRAVITY}, @@ -120,10 +120,10 @@ pub fn handle_wield(data: &JoinData, update: &mut StateUpdate) { /// If a tool is equipped, goes into Equipping state, otherwise goes to Idle pub fn attempt_wield(data: &JoinData, update: &mut StateUpdate) { if let Some(Tool(tool)) = data.stats.equipment.main.as_ref().map(|i| i.kind) { - update.character = CharacterState::Equipping { + update.character = CharacterState::Equipping(equipping::Data { tool, time_left: tool.equip_time(), - }; + }); } else { update.character = CharacterState::Idle {}; }; diff --git a/common/src/states/wielding.rs b/common/src/states/wielding.rs index e9b30a77d0..27e60d9be7 100644 --- a/common/src/states/wielding.rs +++ b/common/src/states/wielding.rs @@ -1,27 +1,37 @@ use super::utils::*; -use crate::{comp::StateUpdate, sys::character_behavior::JoinData}; +use crate::{ + comp::{StateUpdate, ToolData}, + sys::character_behavior::{CharacterBehavior, JoinData}, +}; use std::collections::VecDeque; - -pub fn behavior(data: &JoinData) -> StateUpdate { - let mut update = StateUpdate { - character: *data.character, - pos: *data.pos, - vel: *data.vel, - ori: *data.ori, - energy: *data.energy, - local_events: VecDeque::new(), - server_events: VecDeque::new(), - }; - - handle_move(&data, &mut update); - handle_jump(&data, &mut update); - handle_sit(&data, &mut update); - handle_climb(&data, &mut update); - handle_glide(&data, &mut update); - handle_unwield(&data, &mut update); - handle_primary_input(&data, &mut update); - handle_secondary_input(&data, &mut update); - handle_dodge_input(&data, &mut update); - - update +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +pub struct Data { + /// The weapon being wielded + pub tool: ToolData, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate { + character: *data.character, + pos: *data.pos, + vel: *data.vel, + ori: *data.ori, + energy: *data.energy, + local_events: VecDeque::new(), + server_events: VecDeque::new(), + }; + + handle_move(&data, &mut update); + handle_jump(&data, &mut update); + handle_sit(&data, &mut update); + handle_climb(&data, &mut update); + handle_glide(&data, &mut update); + handle_unwield(&data, &mut update); + handle_primary_input(&data, &mut update); + handle_secondary_input(&data, &mut update); + handle_dodge_input(&data, &mut update); + + update + } } diff --git a/common/src/sys/character_behavior.rs b/common/src/sys/character_behavior.rs index 126e72224c..e35bfbbb10 100644 --- a/common/src/sys/character_behavior.rs +++ b/common/src/sys/character_behavior.rs @@ -172,18 +172,18 @@ impl<'a> System<'a> for Sys { } let mut state_update = match j.character { - CharacterState::Idle { .. } => states::idle::behavior(&j), - CharacterState::Climb { .. } => states::climb::behavior(&j), - CharacterState::Glide { .. } => states::glide::behavior(&j), - CharacterState::Roll { .. } => states::roll::behavior(&j), - CharacterState::Wielding { .. } => states::wielding::behavior(&j), - CharacterState::Equipping { .. } => states::equipping::behavior(&j), - CharacterState::BasicBlock { .. } => states::basic_block::behavior(&j), - CharacterState::ChargeAttack { .. } => states::charge_attack::behavior(&j), - CharacterState::Sit { .. } => states::sit::behavior(&j), - CharacterState::TripleStrike { .. } => states::triple_strike::behavior(&j), - CharacterState::BasicAttack (state) => state.behavior(&j), - CharacterState::TimedCombo(state) => state.behavior(&j), + CharacterState::Idle => states::idle::Data::behavior(&states::idle::Data, &j), + CharacterState::Climb => states::climb::Data::behavior(&states::climb::Data, &j), + CharacterState::Glide => states::glide::Data::behavior(&states::glide::Data, &j), + CharacterState::Sit => states::sit::Data::behavior(&states::sit::Data, &j), + CharacterState::BasicBlock => states::basic_block::Data::behavior(&states::basic_block::Data, &j), + CharacterState::Roll (data) => data.behavior(&j), + CharacterState::Wielding (data) => data.behavior(&j), + CharacterState::Equipping (data) => data.behavior(&j), + CharacterState::ChargeAttack (data) => data.behavior(&j), + CharacterState::TripleStrike (data) => data.behavior(&j), + CharacterState::BasicAttack (data) => data.behavior(&j), + CharacterState::TimedCombo(data) => data.behavior(&j), // Do not use default match. // _ => StateUpdate {