veloren/common/src/comp/states/mod.rs

196 lines
7.5 KiB
Rust
Raw Normal View History

2019-12-26 14:43:59 +00:00
// Module declarations
mod basic_attack;
mod basic_block;
mod charge_attack;
mod climb;
mod fall;
mod glide;
2019-12-28 16:10:39 +00:00
mod idle;
2019-12-26 14:43:59 +00:00
mod jump;
mod roll;
mod run;
mod sit;
mod stand;
mod swim;
mod wield;
// Reexports
pub use basic_attack::*;
pub use basic_block::*;
pub use charge_attack::*;
pub use climb::*;
pub use fall::*;
pub use glide::*;
2019-12-28 16:10:39 +00:00
pub use idle::*;
2019-12-26 14:43:59 +00:00
pub use jump::*;
pub use roll::*;
pub use run::*;
pub use sit::*;
pub use stand::*;
pub use swim::*;
pub use wield::*;
// TODO: Attach these to racial components and/or ecs resources
pub const HUMANOID_ACCEL: f32 = 50.0;
pub const HUMANOID_SPEED: f32 = 120.0;
pub const HUMANOID_AIR_ACCEL: f32 = 10.0;
pub const HUMANOID_AIR_SPEED: f32 = 100.0;
pub const HUMANOID_WATER_ACCEL: f32 = 70.0;
pub const HUMANOID_WATER_SPEED: f32 = 120.0;
pub const HUMANOID_CLIMB_ACCEL: f32 = 5.0;
pub const ROLL_SPEED: f32 = 17.0;
pub const CHARGE_SPEED: f32 = 20.0;
pub const GLIDE_ACCEL: f32 = 15.0;
pub const GLIDE_SPEED: f32 = 45.0;
pub const BLOCK_ACCEL: f32 = 30.0;
pub const BLOCK_SPEED: f32 = 75.0;
pub const TEMP_EQUIP_DELAY: u64 = 100;
// Gravity is 9.81 * 4, so this makes gravity equal to .15
pub const GLIDE_ANTIGRAV: f32 = crate::sys::phys::GRAVITY * 0.96;
pub const CLIMB_SPEED: f32 = 5.0;
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
use super::{
2019-12-28 16:10:39 +00:00
ActionState, ActionState::*, AttackKind::*, BlockKind::*, DodgeKind::*, EcsStateData,
MoveState, MoveState::*, StateUpdate,
2019-12-26 14:43:59 +00:00
};
2020-01-05 18:19:09 +00:00
2019-12-29 16:36:59 +00:00
/// #### A trait for implementing state `handle()`ing logic.
2019-12-31 13:19:23 +00:00
/// _Mimics the typical OOP style state machine pattern where states implement their own behavior,
/// exit conditions, and return new states to the state machine upon exit.
/// This is still performant and consistent with ECS data-behavior-separation constraint
/// since trait fn's are syntactic sugar for static fn's that accept their implementor's
/// object type as its first parameter. This allows for several benefits over implementing
/// each state's behavior within the `CharacterState` update `System` itself:_
2019-12-29 16:36:59 +00:00
///
/// 1. Less cognitive overhead: State's handling logic is next to the its data, and component (inside the state's .rs file).
/// 2. Separation of concerns (between states): all logic within a state's `handle()` is relevant only to that state.
/// States can be added/editted without concerns of affecting other state's logic.
/// 3. Clearly defined API and pattern: All states accept the same `EcsStateData` struct, which can be added to as necessary,
/// without the need for updating every state's implementation. All states return the same `StateUpdate` component.
/// `CharacterState` update `System` passes `EcsStateData` to `ActionState`/`MoveState` `handle()` which matches the character's
/// current state to its `handle()` fn, hiding the implementation details, since the System is only concerned with
/// how the update flow occurs and is in charge of updating the ECS components.
2020-01-05 18:19:09 +00:00
pub trait StateHandler: Default {
2019-12-28 16:10:39 +00:00
fn handle(&self, ecs_data: &EcsStateData) -> StateUpdate;
2020-01-05 23:17:22 +00:00
fn new(ecs_data: &EcsStateData) -> Self;
2019-12-26 14:43:59 +00:00
}
2020-01-05 23:17:22 +00:00
// fn's relating to individual `ActionState`s
// or passing data from system to handlers
2020-01-05 18:19:09 +00:00
impl ActionState {
2020-01-05 23:17:22 +00:00
/// Passes data to variant or subvariant handlers
/// States contain `Option<StateHandler Implementor>`s, and will be
/// `None` if state data has not been initialized. So we have to
/// check and intialize new state data if so.
2020-01-05 18:19:09 +00:00
pub fn update(&self, ecs_data: &EcsStateData) -> StateUpdate {
2019-12-26 14:43:59 +00:00
match self {
Attack(kind) => match kind {
2020-01-05 23:17:22 +00:00
BasicAttack(opt_state) => opt_state
// If data hasn't been initialized, initialize a new one
.unwrap_or_else(|| BasicAttackState::new(ecs_data))
// Call handler
.handle(ecs_data),
Charge(opt_state) => opt_state
.unwrap_or_else(|| ChargeAttackState::new(ecs_data))
.handle(ecs_data),
2019-12-26 14:43:59 +00:00
},
Block(kind) => match kind {
2020-01-05 23:17:22 +00:00
BasicBlock(opt_state) => opt_state
.unwrap_or_else(|| BasicBlockState::new(ecs_data))
.handle(ecs_data),
2019-12-26 14:43:59 +00:00
},
Dodge(kind) => match kind {
2020-01-05 23:17:22 +00:00
Roll(opt_state) => opt_state
.unwrap_or_else(|| RollState::new(ecs_data))
.handle(ecs_data),
2019-12-26 14:43:59 +00:00
},
2020-01-05 23:17:22 +00:00
Wield(opt_state) => opt_state
.unwrap_or_else(|| WieldState::new(ecs_data))
.handle(ecs_data),
Idle(opt_state) => opt_state
.unwrap_or_else(|| IdleState::new(ecs_data))
.handle(ecs_data),
2019-12-26 14:43:59 +00:00
// All states should be explicitly handled
// Do not use default match: _ => {},
}
}
2020-01-01 17:16:29 +00:00
/// Returns whether a given `ActionState` overrides `MoveState` `handle()`ing
pub fn overrides_move_state(&self) -> bool {
match self {
Attack(kind) => match kind {
2020-01-05 18:19:09 +00:00
BasicAttack(_) => false,
Charge(_) => true,
2020-01-01 17:16:29 +00:00
},
Block(kind) => match kind {
2020-01-05 18:19:09 +00:00
BasicBlock(_) => true,
2020-01-01 17:16:29 +00:00
},
Dodge(kind) => match kind {
2020-01-05 18:19:09 +00:00
Roll(_) => true,
2020-01-01 17:16:29 +00:00
},
2020-01-05 18:19:09 +00:00
Wield(_) => false,
Idle(_) => false,
2020-01-01 17:16:29 +00:00
// All states should be explicitly handled
// Do not use default match: _ => {},
}
}
}
2020-01-05 23:17:22 +00:00
// fn's that relate to individual `MoveState`s
// or passing data from system to handlers
2020-01-01 17:16:29 +00:00
impl MoveState {
2020-01-05 23:17:22 +00:00
/// Passes data to variant or subvariant handlers
/// States contain `Option<StateHandler Implementor>`s, and will be
/// `None` if state data has not been initialized. So we have to
/// check and intialize new state data if so.
2020-01-01 17:16:29 +00:00
pub fn overrides_action_state(&self) -> bool {
match self {
2020-01-05 18:19:09 +00:00
Stand(_) => false,
Run(_) => false,
Jump(_) => false,
Climb(_) => true,
Glide(_) => true,
Swim(_) => false,
Fall(_) => false,
Sit(_) => true,
2020-01-01 17:16:29 +00:00
// All states should be explicitly handled
// Do not use default match: _ => {},
}
}
2019-12-26 14:43:59 +00:00
/// Passes handle to variant handlers
2020-01-05 18:19:09 +00:00
pub fn update(&self, ecs_data: &EcsStateData) -> StateUpdate {
2019-12-26 14:43:59 +00:00
match self {
2020-01-05 23:17:22 +00:00
Stand(opt_state) => opt_state
// If data hasn't been initialized, initialize a new one
.unwrap_or_else(|| StandState::new(ecs_data))
// Call handler
.handle(ecs_data),
Run(opt_state) => opt_state
.unwrap_or_else(|| RunState::new(ecs_data))
.handle(ecs_data),
Jump(opt_state) => opt_state
.unwrap_or_else(|| JumpState::new(ecs_data))
.handle(ecs_data),
Climb(opt_state) => opt_state
.unwrap_or_else(|| ClimbState::new(ecs_data))
.handle(ecs_data),
Glide(opt_state) => opt_state
.unwrap_or_else(|| GlideState::new(ecs_data))
.handle(ecs_data),
Swim(opt_state) => opt_state
.unwrap_or_else(|| SwimState::new(ecs_data))
.handle(ecs_data),
Fall(opt_state) => opt_state
.unwrap_or_else(|| FallState::new(ecs_data))
.handle(ecs_data),
Sit(opt_state) => opt_state
.unwrap_or_else(|| SitState::new(ecs_data))
.handle(ecs_data),
2019-12-26 14:43:59 +00:00
// All states should be explicitly handled
// Do not use default match: _ => {},
}
}
}