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: _ => {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|