This commit is contained in:
Adam Whitehurst 2020-01-07 07:49:08 -08:00
parent dc33f6ad6a
commit 8648641362
24 changed files with 252 additions and 456 deletions

View File

@ -30,5 +30,5 @@ pub struct AbilityPool {
}
impl Component for AbilityPool {
type Storage = HashMapStorage<Self>;
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
}

View File

@ -1,6 +1,6 @@
use crate::comp::{
ActionState::Attack, AttackKind::BasicAttack, EcsStateData, ItemKind::Tool, MoveState,
StateHandler, StateUpdate, ToolData,
ActionState::Attack, AttackKind::BasicAttack, EcsStateData, ItemKind::Tool, StateHandler,
StateUpdate, ToolData,
};
use crate::util::state_utils::*;
use std::time::Duration;

View File

@ -1,9 +1,11 @@
use super::{BLOCK_ACCEL, BLOCK_SPEED};
use crate::comp::{EcsStateData, StateHandler, StateUpdate};
use crate::util::state_utils::*;
use std::time::Duration;
use vek::Vec2;
const BLOCK_ACCEL: f32 = 30.0;
const BLOCK_SPEED: f32 = 75.0;
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct BasicBlockState {
/// How long the blocking state has been active
@ -11,7 +13,7 @@ pub struct BasicBlockState {
}
impl StateHandler for BasicBlockState {
fn new(ecs_data: &EcsStateData) -> Self {
fn new(_ecs_data: &EcsStateData) -> Self {
Self {
active_duration: Duration::default(),
}

View File

@ -1,13 +1,13 @@
use crate::comp::{
ActionState::Attack, AttackKind::Charge, EcsStateData, HealthChange, HealthSource,
ItemKind::Tool, MoveState::Run, RunState, StateHandler, StateUpdate, ToolData,
ItemKind::Tool, MoveState::Run, StateHandler, StateUpdate, ToolData,
};
use crate::event::ServerEvent;
use crate::util::state_utils::*;
use std::time::Duration;
use vek::Vec3;
use super::CHARGE_SPEED;
const CHARGE_SPEED: f32 = 20.0;
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct ChargeAttackState {
@ -37,7 +37,7 @@ impl StateHandler for ChargeAttackState {
};
// Prevent move state handling, handled here
update.character.move_state = Run(Some(RunState));
update.character.move_state = Run(None);
// Move player
update.vel.0 = Vec3::new(0.0, 0.0, update.vel.0.z)

View File

@ -1,17 +1,16 @@
use super::{
ActionState::*, EcsStateData, FallState, IdleState, JumpState, MoveState::*, StandState,
StateHandler, StateUpdate,
};
use super::{CLIMB_SPEED, HUMANOID_CLIMB_ACCEL, HUMANOID_SPEED};
use super::{ActionState::*, EcsStateData, MoveState::*, StateHandler, StateUpdate};
use crate::sys::phys::GRAVITY;
use vek::vec::{Vec2, Vec3};
use vek::Lerp;
const HUMANOID_CLIMB_ACCEL: f32 = 5.0;
const CLIMB_SPEED: f32 = 5.0;
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct ClimbState;
impl StateHandler for ClimbState {
fn new(ecs_data: &EcsStateData) -> Self {
fn new(_ecs_data: &EcsStateData) -> Self {
Self {}
}
@ -23,12 +22,33 @@ impl StateHandler for ClimbState {
character: *ecs_data.character,
};
update.character.action_state = Idle(Some(IdleState));
update.character.action_state = Idle(None);
// If no wall is in front of character ...
if let None = ecs_data.physics.on_wall {
if ecs_data.inputs.jump.is_pressed() {
// They've climbed atop something, give them a boost
update.character.move_state = Jump(None);
return update;
} else {
// Just fall off
update.character.move_state = Fall(None);
return update;
}
}
// Remove climb state on ground, otherwise character will get stuck
if ecs_data.physics.on_ground {
update.character.move_state = Stand(None);
return update;
}
// Move player
update.vel.0 += Vec2::broadcast(ecs_data.dt.0)
* ecs_data.inputs.move_dir
* if update.vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) {
* if update.vel.0.magnitude_squared() < CLIMB_SPEED.powf(2.0) {
HUMANOID_CLIMB_ACCEL
} else {
0.0
@ -78,27 +98,6 @@ impl StateHandler for ClimbState {
}
}
// If no wall is infront of character ...
if let None = ecs_data.physics.on_wall {
if ecs_data.inputs.jump.is_pressed() {
// They've climbed atop something, give them a boost
update.character.move_state = Jump(Some(JumpState));
return update;
} else {
// Just fall off
update.character.move_state = Fall(Some(FallState));
return update;
}
}
// Remove climb state on ground, otherwise character will get stuck
if ecs_data.physics.on_ground {
update.character.move_state = Stand(Some(StandState));
return update;
}
return update;
}
}

View File

@ -1,14 +1,16 @@
use super::{HUMANOID_AIR_ACCEL, HUMANOID_AIR_SPEED};
use crate::comp::{ClimbState, EcsStateData, GlideState, MoveState::*, StateHandler, StateUpdate};
use crate::comp::{EcsStateData, MoveState::*, StateHandler, StateUpdate};
use crate::util::state_utils::*;
use vek::{Vec2, Vec3};
const HUMANOID_AIR_ACCEL: f32 = 10.0;
const HUMANOID_AIR_SPEED: f32 = 100.0;
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct FallState;
impl StateHandler for FallState {
fn new(ecs_data: &EcsStateData) -> Self {
fn new(_ecs_data: &EcsStateData) -> Self {
Self {}
}
@ -48,13 +50,13 @@ impl StateHandler for FallState {
// Check to start climbing
if can_climb(ecs_data.physics, ecs_data.inputs, ecs_data.body) {
update.character.move_state = Climb(Some(ClimbState));
update.character.move_state = Climb(None);
return update;
}
// Check gliding
if ecs_data.inputs.glide.is_pressed() {
update.character.move_state = Glide(Some(GlideState));
update.character.move_state = Glide(None);
return update;
}

View File

@ -1,15 +1,16 @@
use super::{GLIDE_ACCEL, GLIDE_ANTIGRAV, GLIDE_SPEED};
use crate::comp::{
ActionState::*, ClimbState, EcsStateData, FallState, IdleState, MoveState::*, StandState,
StateHandler, StateUpdate,
};
use crate::comp::{ActionState::*, EcsStateData, MoveState::*, StateHandler, StateUpdate};
use vek::{Vec2, Vec3};
// Gravity is 9.81 * 4, so this makes gravity equal to .15
const GLIDE_ANTIGRAV: f32 = crate::sys::phys::GRAVITY * 0.96;
const GLIDE_ACCEL: f32 = 15.0;
const GLIDE_SPEED: f32 = 45.0;
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct GlideState;
impl StateHandler for GlideState {
fn new(ecs_data: &EcsStateData) -> Self {
fn new(_ecs_data: &EcsStateData) -> Self {
Self {}
}
@ -22,8 +23,26 @@ impl StateHandler for GlideState {
};
// Defaults for this state
update.character.action_state = Idle(Some(IdleState));
update.character.move_state = Glide(Some(GlideState));
update.character.action_state = Idle(None);
update.character.move_state = Glide(None);
// If glide button isn't held, start falling
if !ecs_data.inputs.glide.is_pressed() {
update.character.move_state = Fall(None);
return update;
}
// If there is a wall in front of character go to climb
if let Some(_wall_dir) = ecs_data.physics.on_wall {
update.character.move_state = Climb(None);
return update;
}
// If on ground go to stand
if ecs_data.physics.on_ground {
update.character.move_state = Stand(None);
return update;
}
// Move player according to movement direction vector
update.vel.0 += Vec2::broadcast(ecs_data.dt.0)
@ -56,24 +75,6 @@ impl StateHandler for GlideState {
.max(0.2);
}
// If glide button isn't held, start falling
if !ecs_data.inputs.glide.is_pressed() {
update.character.move_state = Fall(Some(FallState));
return update;
}
// If there is a wall in front of character go to climb
if let Some(_wall_dir) = ecs_data.physics.on_wall {
update.character.move_state = Climb(Some(ClimbState));
return update;
}
// If on ground go to stand
if ecs_data.physics.on_ground {
update.character.move_state = Stand(Some(StandState));
return update;
}
// Otherwise keep gliding
return update;
}

View File

@ -1,14 +1,10 @@
use super::TEMP_EQUIP_DELAY;
use crate::comp::{
ActionState::Wield, EcsStateData, ItemKind::Tool, StateHandler, StateUpdate, WieldState,
};
use std::time::Duration;
use crate::comp::{ActionState::Wield, EcsStateData, ItemKind::Tool, StateHandler, StateUpdate};
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct IdleState;
impl StateHandler for IdleState {
fn new(ecs_data: &EcsStateData) -> Self {
fn new(_ecs_data: &EcsStateData) -> Self {
Self {}
}
@ -26,10 +22,8 @@ impl StateHandler for IdleState {
|| (ecs_data.inputs.toggle_wield.is_just_pressed()
&& update.character.action_state.is_equip_finished())
{
if let Some(Tool(data)) = ecs_data.stats.equipment.main.as_ref().map(|i| &i.kind) {
update.character.action_state = Wield(Some(WieldState {
equip_delay: data.equip_time(),
}));
if let Some(Tool(_)) = ecs_data.stats.equipment.main.as_ref().map(|i| &i.kind) {
update.character.action_state = Wield(None);
}
// else unarmed stuff?

View File

@ -1,11 +1,11 @@
use super::{EcsStateData, FallState, MoveState::*, StateHandler, StateUpdate};
use super::{EcsStateData, MoveState::*, StateHandler, StateUpdate};
use crate::event::LocalEvent;
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct JumpState;
impl StateHandler for JumpState {
fn new(ecs_data: &EcsStateData) -> Self {
fn new(_ecs_data: &EcsStateData) -> Self {
Self {}
}
@ -23,7 +23,7 @@ impl StateHandler for JumpState {
.emit(LocalEvent::Jump(*ecs_data.entity));
// Immediately go to falling state after jump impulse
update.character.move_state = Fall(Some(FallState));
update.character.move_state = Fall(None);
return update;
}
}

View File

@ -30,47 +30,106 @@ 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::{
ActionState, ActionState::*, AttackKind::*, BlockKind::*, DodgeKind::*, EcsStateData,
MoveState, MoveState::*, StateUpdate,
};
/// #### A trait for implementing state `handle()`ing logic.
/// _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:_
///
/// 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.
/// ## A type for implementing State Handling Behavior.
///
/// Called by state machines' update functions to allow current states to handle updating
/// their parent machine's current state.
///
/// Structures must implement a `handle()` fn to handle update behavior, and a `new()` for
/// instantiating new instances of a state. `handle()` function recieves `EcsStateData`, a struct
/// of readonly ECS Component data, and returns a `StateUpdate` tuple, with new components that will
/// overwrite an entitie's old components.
///
/// ## Example Implementation:
/// ```
/// use crate::comp::{
/// ClimbState, EcsStateData, GlideState, JumpState, MoveState::*, SitState, StateHandler,
/// StateUpdate,
/// };
/// use crate::util::state_utils::*
/// #[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
/// pub struct RunState {
/// active_duration: Duration,
/// }
///
/// impl StateHandler for RunState {
/// fn new(ecs_data: &EcsStateData) -> Self {
/// Self {
/// active_duration: Duration::default(),
/// }
/// }
///
/// fn handle(&self, ecs_data: &EcsStateData) -> StateUpdate {
/// let mut update = StateUpdate {
/// character: *ecs_data.character,
/// pos: *ecs_data.pos,
/// vel: *ecs_data.vel,
/// ori: *ecs_data.ori,
/// };
///
/// // Move player according to move_dir
/// update.vel.0 += Vec2::broadcast(ecs_data.dt.0)
/// * ecs_data.inputs.move_dir
/// * if update.vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) {
/// HUMANOID_ACCEL
/// } else {
/// 0.0
/// };
///
/// // Set direction based on move direction when on the ground
/// let ori_dir = if update.character.action_state.is_attacking()
/// || update.character.action_state.is_blocking()
/// {
/// Vec2::from(ecs_data.inputs.look_dir).normalized()
/// } else {
/// 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(), 9.0 * ecs_data.dt.0);
/// }
///
/// // Try to sit
/// if can_sit(ecs_data.physics, ecs_data.inputs, ecs_data.body) {
/// update.character.move_state = Sit(Some(SitState));
/// return update;
/// }
///
/// // Try to climb
/// if can_climb(ecs_data.physics, ecs_data.inputs, ecs_data.body) {
/// update.character.move_state = Climb(Some(ClimbState));
/// return update;
/// }
///
/// // Try to jump
/// if can_jump(ecs_data.physics, ecs_data.inputs) {
/// update.character.move_state = Jump(Some(JumpState));
/// return update;
/// }
///
/// // Try to glide
/// if can_glide(ecs_data.physics, ecs_data.inputs, ecs_data.body) {
/// update.character.move_state = Glide(Some(GlideState));
/// return update;
/// }
///
/// // Update based on groundedness
/// update.character.move_state =
/// determine_move_from_grounded_state(ecs_data.physics, ecs_data.inputs);
///
/// return update;
/// }
/// }
/// ```
pub trait StateHandler: Default {
fn handle(&self, ecs_data: &EcsStateData) -> StateUpdate;
fn new(ecs_data: &EcsStateData) -> Self;

View File

@ -1,4 +1,3 @@
use super::ROLL_SPEED;
use crate::comp::{
ActionState::*, DodgeKind::*, EcsStateData, ItemKind::Tool, StateHandler, StateUpdate, ToolData,
};
@ -6,6 +5,8 @@ use crate::util::state_utils::*;
use std::time::Duration;
use vek::Vec3;
const ROLL_SPEED: f32 = 17.0;
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct RollState {
/// How long the state has until exitting

View File

@ -1,16 +1,15 @@
use super::{HUMANOID_ACCEL, HUMANOID_SPEED};
use crate::comp::{
ClimbState, EcsStateData, GlideState, JumpState, MoveState::*, SitState, StateHandler,
StateUpdate,
};
use crate::comp::{EcsStateData, MoveState::*, StateHandler, StateUpdate};
use crate::util::state_utils::*;
use vek::vec::{Vec2, Vec3};
const HUMANOID_ACCEL: f32 = 50.0;
const HUMANOID_SPEED: f32 = 120.0;
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct RunState;
impl StateHandler for RunState {
fn new(ecs_data: &EcsStateData) -> Self {
fn new(_ecs_data: &EcsStateData) -> Self {
Self {}
}
@ -50,25 +49,25 @@ impl StateHandler for RunState {
// Try to sit
if can_sit(ecs_data.physics, ecs_data.inputs, ecs_data.body) {
update.character.move_state = Sit(Some(SitState));
update.character.move_state = Sit(None);
return update;
}
// Try to climb
if can_climb(ecs_data.physics, ecs_data.inputs, ecs_data.body) {
update.character.move_state = Climb(Some(ClimbState));
update.character.move_state = Climb(None);
return update;
}
// Try to jump
if can_jump(ecs_data.physics, ecs_data.inputs) {
update.character.move_state = Jump(Some(JumpState));
update.character.move_state = Jump(None);
return update;
}
// Try to glide
if can_glide(ecs_data.physics, ecs_data.inputs, ecs_data.body) {
update.character.move_state = Glide(Some(GlideState));
update.character.move_state = Glide(None);
return update;
}

View File

@ -1,14 +1,11 @@
use crate::comp::{
ActionState::*, EcsStateData, IdleState, JumpState, MoveState::*, RunState, StandState,
StateHandler, StateUpdate,
};
use crate::comp::{ActionState::*, EcsStateData, MoveState::*, StateHandler, StateUpdate};
use crate::util::state_utils::*;
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct SitState;
impl StateHandler for SitState {
fn new(ecs_data: &EcsStateData) -> Self {
fn new(_ecs_data: &EcsStateData) -> Self {
Self {}
}
@ -21,8 +18,8 @@ impl StateHandler for SitState {
};
// Prevent action state handling
update.character.action_state = Idle(Some(IdleState));
update.character.move_state = Sit(Some(SitState));
update.character.action_state = Idle(None);
update.character.move_state = Sit(None);
// Try to Fall
// ... maybe the ground disappears,
@ -34,19 +31,19 @@ impl StateHandler for SitState {
}
// Try to jump
if ecs_data.inputs.jump.is_pressed() {
update.character.move_state = Jump(Some(JumpState));
update.character.move_state = Jump(None);
return update;
}
// Try to Run
if ecs_data.inputs.move_dir.magnitude_squared() > 0.0 {
update.character.move_state = Run(Some(RunState));
update.character.move_state = Run(None);
return update;
}
// Try to Stand
if ecs_data.inputs.sit.is_just_pressed() {
update.character.move_state = Stand(Some(StandState));
update.character.move_state = Stand(None);
return update;
}

View File

@ -1,14 +1,11 @@
use crate::comp::{
ClimbState, EcsStateData, GlideState, JumpState, MoveState::*, SitState, StateHandler,
StateUpdate,
};
use crate::comp::{EcsStateData, MoveState::*, StateHandler, StateUpdate};
use crate::util::state_utils::*;
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct StandState;
impl StateHandler for StandState {
fn new(ecs_data: &EcsStateData) -> Self {
fn new(_ecs_data: &EcsStateData) -> Self {
Self {}
}
@ -22,25 +19,25 @@ impl StateHandler for StandState {
// Try to sit
if can_sit(ecs_data.physics, ecs_data.inputs, ecs_data.body) {
update.character.move_state = Sit(Some(SitState));
update.character.move_state = Sit(None);
return update;
}
// Try to climb
if can_climb(ecs_data.physics, ecs_data.inputs, ecs_data.body) {
update.character.move_state = Climb(Some(ClimbState));
update.character.move_state = Climb(None);
return update;
}
// Try to jump
if can_jump(ecs_data.physics, ecs_data.inputs) {
update.character.move_state = Jump(Some(JumpState));
update.character.move_state = Jump(None);
return update;
}
// Check gliding
if can_glide(ecs_data.physics, ecs_data.inputs, ecs_data.body) {
update.character.move_state = Glide(Some(GlideState));
update.character.move_state = Glide(None);
return update;
}

View File

@ -1,13 +1,15 @@
use super::{HUMANOID_WATER_ACCEL, HUMANOID_WATER_SPEED};
use crate::comp::{EcsStateData, MoveState::*, RunState, StandState, StateHandler, StateUpdate};
use crate::comp::{EcsStateData, MoveState::*, StateHandler, StateUpdate};
use crate::sys::phys::GRAVITY;
use vek::{Vec2, Vec3};
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct SwimState;
const HUMANOID_WATER_ACCEL: f32 = 70.0;
const HUMANOID_WATER_SPEED: f32 = 120.0;
impl StateHandler for SwimState {
fn new(ecs_data: &EcsStateData) -> Self {
fn new(_ecs_data: &EcsStateData) -> Self {
Self {}
}
@ -55,16 +57,16 @@ impl StateHandler for SwimState {
// Not on ground
if !ecs_data.physics.on_ground {
update.character.move_state = Swim(Some(SwimState));
update.character.move_state = Swim(None);
return update;
}
// On ground
else {
// Return to running or standing based on move inputs
update.character.move_state = if ecs_data.inputs.move_dir.magnitude_squared() > 0.0 {
Run(Some(RunState))
Run(None)
} else {
Stand(Some(StandState))
Stand(None)
};
return update;

View File

@ -1,8 +1,6 @@
use crate::comp::{
AbilityAction, AbilityActionKind::*, ActionState::*, EcsStateData, IdleState, ItemKind::Tool,
StateHandler, StateUpdate, ToolData,
ActionState::*, EcsStateData, IdleState, ItemKind::Tool, StateHandler, StateUpdate, ToolData,
};
use crate::util::state_utils::*;
use std::time::Duration;
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]

View File

@ -36,6 +36,8 @@ sphynx::sum_type! {
OverrideAction(comp::OverrideAction),
OverrideMove(comp::OverrideMove),
OverrideState(comp::OverrideState),
AbilityAction(comp::AbilityAction),
AbilityPool(comp::AbilityPool),
}
}
// Automatically derive From<T> for EcsCompPhantom
@ -62,6 +64,8 @@ sphynx::sum_type! {
OverrideAction(PhantomData<comp::OverrideAction>),
OverrideMove(PhantomData<comp::OverrideMove>),
OverrideState(PhantomData<comp::OverrideState>),
AbilityAction(PhantomData<comp::AbilityAction>),
AbilityPool(PhantomData<comp::AbilityPool>),
}
}
impl sphynx::CompPacket for EcsCompPacket {

View File

@ -123,6 +123,8 @@ impl State {
// TODO: Split up registering into server and client (e.g. move EventBus<ServerEvent> to the server)
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsCompPacket, EcsResPacket>) {
// Register server -> all clients synced components.
ecs.register_synced::<comp::AbilityPool>();
ecs.register_synced::<comp::AbilityAction>();
ecs.register_synced::<comp::Body>();
ecs.register_synced::<comp::Player>();
ecs.register_synced::<comp::Stats>();

View File

@ -25,13 +25,13 @@ impl<'a> System<'a> for Sys {
&mut self,
(
entities,
updater,
_updater,
mut character_state_storage,
ability_action_storage,
ability_pool_storage,
): Self::SystemData,
) {
for (entity, mut character, ability_action, ability_pool) in (
for (_entity, mut _character, _ability_action, _ability_pool) in (
&entities,
&mut character_state_storage,
&ability_action_storage,

View File

@ -1,8 +1,7 @@
use crate::{
comp::{
states::*, Body, CharacterState, Controller, EcsStateData, Mounting, MoveState::*, Ori,
OverrideAction, OverrideMove, OverrideState, PhysicsState, Pos, SitState, StateHandler,
Stats, Vel,
Body, CharacterState, Controller, EcsStateData, Mounting, MoveState::*, Ori,
OverrideAction, OverrideMove, OverrideState, PhysicsState, Pos, Stats, Vel,
},
event::{EventBus, LocalEvent, ServerEvent},
state::DeltaTime,
@ -11,18 +10,11 @@ use crate::{
use specs::{Entities, Join, LazyUpdate, Read, ReadStorage, System, WriteStorage};
use sphynx::{Uid, UidAllocator};
/// # Character State System
/// #### Updates tuples of ( `CharacterState`, `Pos`, `Vel`, and `Ori` ) in parallel.
/// _Each update for a single character involves first passing an `EcsStateData` struct of ECS components
/// to the character's `MoveState`, then the character's `ActionState`. State update logic is
/// is encapsulated in state's `handle()` fn, impl'd by the `StateHandle` trait. `handle()` fn's
/// return a `StateUpdate` tuple containing new ( `CharacterState`, `Pos`, `Vel`, and `Ori` ) components.
/// Since `handle()` accepts readonly components, component updates are contained within this system and ECS
/// behavior constraints are satisfied._
/// ## Character State System
/// #### Calls updates to `CharacterState`s. Acts on tuples of ( `CharacterState`, `Pos`, `Vel`, and `Ori` ).
///
/// _This mimics the typical OOP style state machine pattern, but remains performant
/// under ECS since trait fn's are syntactic sugar for static fn's that accept their implementor's
/// object type as its first parameter. See `StateHandle` for more information._
/// _System forms `EcsStateData` tuples and passes those to `ActionState` `update()` fn,
/// then does the same for `MoveState` `update`_
pub struct Sys;
impl<'a> System<'a> for Sys {
@ -102,14 +94,14 @@ impl<'a> System<'a> for Sys {
// If mounted, character state is controlled by mount
// TODO: Make mounting a state
if let Some(Mounting(_)) = mountings.get(entity) {
character.move_state = Sit(Some(SitState));
character.move_state = Sit(None);
return;
}
// Determine new action if character can act
if let (None, false) = (
action_overrides.get(entity),
character.action_state.overrides_move_state(),
character.move_state.overrides_action_state(),
) {
let state_update = character.action_state.update(&EcsStateData {
entity: &entity,
@ -137,7 +129,7 @@ impl<'a> System<'a> for Sys {
// Determine new move state if character can move
if let (None, false) = (
move_overrides.get(entity),
character.move_state.overrides_action_state(),
character.action_state.overrides_move_state(),
) {
let state_update = character.move_state.update(&EcsStateData {
entity: &entity,

View File

@ -1,236 +0,0 @@
use super::phys::GRAVITY;
use crate::{
comp::{
CharacterState, Controller, Mounting, MoveState::*, Ori, PhysicsState, Pos, RunState,
StandState, Stats, Vel,
},
event::{EventBus, ServerEvent},
state::DeltaTime,
terrain::TerrainGrid,
};
use specs::prelude::*;
use sphynx::Uid;
use std::time::Duration;
use vek::*;
pub const ROLL_DURATION: Duration = Duration::from_millis(600);
const HUMANOID_ACCEL: f32 = 50.0;
const HUMANOID_SPEED: f32 = 120.0;
const HUMANOID_AIR_ACCEL: f32 = 10.0;
const HUMANOID_AIR_SPEED: f32 = 100.0;
const HUMANOID_WATER_ACCEL: f32 = 70.0;
const HUMANOID_WATER_SPEED: f32 = 120.0;
const HUMANOID_CLIMB_ACCEL: f32 = 5.0;
const ROLL_SPEED: f32 = 17.0;
const CHARGE_SPEED: f32 = 20.0;
const GLIDE_ACCEL: f32 = 15.0;
const GLIDE_SPEED: f32 = 45.0;
const BLOCK_ACCEL: f32 = 30.0;
const BLOCK_SPEED: f32 = 75.0;
// Gravity is 9.81 * 4, so this makes gravity equal to .15
const GLIDE_ANTIGRAV: f32 = GRAVITY * 0.96;
const CLIMB_SPEED: f32 = 5.0;
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
/// # Movement System
/// #### Applies forces, calculates new positions and velocities,7
/// #### based on Controller(Inputs) and CharacterState.
/// ----
///
/// **Writes:**
/// Pos, Vel, Ori
///
/// **Reads:**
/// Uid, Stats, Controller, PhysicsState, CharacterState, Mounting
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
ReadExpect<'a, TerrainGrid>,
Read<'a, EventBus<ServerEvent>>,
Read<'a, DeltaTime>,
WriteStorage<'a, Pos>,
WriteStorage<'a, Vel>,
WriteStorage<'a, Ori>,
ReadStorage<'a, Uid>,
ReadStorage<'a, Stats>,
ReadStorage<'a, Controller>,
ReadStorage<'a, PhysicsState>,
ReadStorage<'a, CharacterState>,
ReadStorage<'a, Mounting>,
);
fn run(
&mut self,
(
entities,
_terrain,
_server_bus,
dt,
mut positions,
mut velocities,
mut orientations,
uids,
stats,
controllers,
physics_states,
character_states,
mountings,
): Self::SystemData,
) {
// Apply movement inputs
for (
_entity,
mut _pos,
mut vel,
mut ori,
_uid,
stats,
controller,
physics,
character,
mount,
) in (
&entities,
&mut positions,
&mut velocities,
&mut orientations,
&uids,
&stats,
&controllers,
&physics_states,
&character_states,
mountings.maybe(),
)
.join()
{
// if character.movement == Run(RunState) || character.movement == Stand(StandState) {
// continue;
// }
// if stats.is_dead {
// continue;
// }
// if mount.is_some() {
// continue;
// }
// let inputs = &controller.inputs;
// if character.action.is_roll() {
// vel.0 = Vec3::new(0.0, 0.0, vel.0.z)
// + (vel.0 * Vec3::new(1.0, 1.0, 0.0)
// + 1.5 * inputs.move_dir.try_normalized().unwrap_or_default())
// .try_normalized()
// .unwrap_or_default()
// * ROLL_SPEED;
// } else if character.action.is_charge() {
// vel.0 = Vec3::new(0.0, 0.0, vel.0.z)
// + (vel.0 * Vec3::new(1.0, 1.0, 0.0)
// + 1.5 * inputs.move_dir.try_normalized().unwrap_or_default())
// .try_normalized()
// .unwrap_or_default()
// * CHARGE_SPEED;
// } else if character.action.is_block() {
// vel.0 += Vec2::broadcast(dt.0)
// * inputs.move_dir
// * match physics.on_ground {
// true if vel.0.magnitude_squared() < BLOCK_SPEED.powf(2.0) => BLOCK_ACCEL,
// _ => 0.0,
// }
// } else {
// // Move player according to move_dir
// vel.0 += Vec2::broadcast(dt.0)
// * inputs.move_dir
// * match (physics.on_ground, &character.movement) {
// (true, Run(_)) if vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) => {
// HUMANOID_ACCEL
// }
// (false, Climb) if vel.0.magnitude_squared() < HUMANOID_SPEED.powf(2.0) => {
// HUMANOID_CLIMB_ACCEL
// }
// (false, Glide) if vel.0.magnitude_squared() < GLIDE_SPEED.powf(2.0) => {
// GLIDE_ACCEL
// }
// (false, Fall) | (false, Jump)
// if vel.0.magnitude_squared() < HUMANOID_AIR_SPEED.powf(2.0) =>
// {
// HUMANOID_AIR_ACCEL
// }
// (false, Swim)
// if vel.0.magnitude_squared() < HUMANOID_WATER_SPEED.powf(2.0) =>
// {
// HUMANOID_WATER_ACCEL
// }
// _ => 0.0,
// };
// }
// // Set direction based on move direction when on the ground
// let ori_dir = if
// //character.action.is_wield() ||
// character.action.is_attack() || character.action.is_block() {
// Vec2::from(inputs.look_dir).normalized()
// } else if let (Climb, Some(wall_dir)) = (character.movement, physics.on_wall) {
// if Vec2::<f32>::from(wall_dir).magnitude_squared() > 0.001 {
// Vec2::from(wall_dir).normalized()
// } else {
// Vec2::from(vel.0)
// }
// } else {
// Vec2::from(vel.0)
// };
// if ori_dir.magnitude_squared() > 0.0001
// && (ori.0.normalized() - Vec3::from(ori_dir).normalized()).magnitude_squared()
// > 0.001
// {
// ori.0 = vek::ops::Slerp::slerp(
// ori.0,
// ori_dir.into(),
// if physics.on_ground { 9.0 } else { 2.0 } * dt.0,
// );
// }
// // Glide
// if character.movement == Glide
// && Vec2::<f32>::from(vel.0).magnitude_squared() < GLIDE_SPEED.powf(2.0)
// && vel.0.z < 0.0
// {
// let lift = GLIDE_ANTIGRAV + vel.0.z.abs().powf(2.0) * 0.15;
// vel.0.z += dt.0
// * lift
// * (Vec2::<f32>::from(vel.0).magnitude() * 0.075)
// .min(1.0)
// .max(0.2);
// }
// // Climb
// if let (true, Some(_wall_dir)) = (
// (inputs.climb.is_pressed() | inputs.climb_down.is_pressed())
// && vel.0.z <= CLIMB_SPEED,
// physics.on_wall,
// ) {
// if inputs.climb_down.is_pressed() && !inputs.climb.is_pressed() {
// vel.0 -= dt.0 * vel.0.map(|e| e.abs().powf(1.5) * e.signum() * 6.0);
// } else if inputs.climb.is_pressed() && !inputs.climb_down.is_pressed() {
// vel.0.z = (vel.0.z + dt.0 * GRAVITY * 1.25).min(CLIMB_SPEED);
// } else {
// vel.0.z = vel.0.z + dt.0 * GRAVITY * 1.5;
// vel.0 = Lerp::lerp(
// vel.0,
// Vec3::zero(),
// 30.0 * dt.0 / (1.0 - vel.0.z.min(0.0) * 5.0),
// );
// }
// }
// if character.movement == Swim && inputs.jump.is_pressed() {
// vel.0.z = (vel.0.z + dt.0 * GRAVITY * 1.25).min(HUMANOID_WATER_SPEED);
// }
}
}
}

View File

@ -1,8 +1,7 @@
use crate::comp::TEMP_EQUIP_DELAY;
use crate::comp::{
ActionState, ActionState::*, AttackKind::*, BasicAttackState, BasicBlockState, BlockKind::*,
Body, ControllerInputs, FallState, IdleState, ItemKind::Tool, MoveState, MoveState::*,
PhysicsState, RunState, StandState, Stats, SwimState, ToolData, WieldState,
PhysicsState, RunState, StandState, Stats, SwimState, WieldState,
};
use std::time::Duration;
@ -23,7 +22,7 @@ pub fn determine_primary_ability(stats: &Stats) -> ActionState {
/// and returns the corresponding `ActionState`
/// ... or Idle if nothing it possible?_
pub fn determine_secondary_ability(stats: &Stats) -> ActionState {
if let Some(Tool(data)) = stats.equipment.main.as_ref().map(|i| &i.kind) {
if let Some(Tool(_data)) = stats.equipment.main.as_ref().map(|i| &i.kind) {
Block(BasicBlock(Some(BasicBlockState {
active_duration: Duration::default(),
})))
@ -32,7 +31,7 @@ pub fn determine_secondary_ability(stats: &Stats) -> ActionState {
}
}
/// __Returns a `MoveState` based on `in_fluid` condition__
/// _Returns a `MoveState` based on `in_fluid` condition_
pub fn determine_fall_or_swim(physics: &PhysicsState) -> MoveState {
// Check if in fluid to go to swimming or back to falling
if physics.in_fluid {
@ -41,7 +40,7 @@ pub fn determine_fall_or_swim(physics: &PhysicsState) -> MoveState {
Fall(Some(FallState))
}
}
/// __Returns a `MoveState` based on `move_dir` magnitude__
/// _Returns a `MoveState` based on `move_dir` magnitude_
pub fn determine_stand_or_run(inputs: &ControllerInputs) -> MoveState {
// Return to running or standing based on move inputs
if inputs.move_dir.magnitude_squared() > 0.0 {
@ -51,7 +50,7 @@ pub fn determine_stand_or_run(inputs: &ControllerInputs) -> MoveState {
}
}
/// __Returns a `MoveState` based on `on_ground` state.__
/// _Returns a `MoveState` based on `on_ground` state._
///
/// _`FallState`, or `SwimState` if not `on_ground`,
/// `StandState` or `RunState` if is `on_ground`_
@ -69,11 +68,11 @@ pub fn determine_move_from_grounded_state(
}
}
/// __Returns an ActionState based on whether character has a weapon equipped.__
/// _Returns an ActionState based on whether character has a weapon equipped._
pub fn attempt_wield(stats: &Stats) -> ActionState {
if let Some(Tool { .. }) = stats.equipment.main.as_ref().map(|i| &i.kind) {
if let Some(Tool(data)) = stats.equipment.main.as_ref().map(|i| &i.kind) {
Wield(Some(WieldState {
equip_delay: Duration::from_millis(TEMP_EQUIP_DELAY),
equip_delay: data.equip_time(),
}))
} else {
Idle(Some(IdleState))
@ -82,7 +81,7 @@ pub fn attempt_wield(stats: &Stats) -> ActionState {
pub fn can_climb(physics: &PhysicsState, inputs: &ControllerInputs, body: &Body) -> bool {
if let (true, Some(_wall_dir)) = (
inputs.climb.is_pressed() | inputs.climb_down.is_pressed() && body.is_humanoid(),
(inputs.climb.is_pressed() | inputs.climb_down.is_pressed()) && body.is_humanoid(),
physics.on_wall,
) {
true

View File

@ -277,10 +277,8 @@ mod tests {
let result = SfxEventMapper::map_character_event(
&CharacterState {
move_state: MoveState::Stand(StandState),
action_state: ActionState::Idle(IdleState),
action_disabled_this_tick: false,
move_disabled_this_tick: false,
move_state: MoveState::Stand(None),
action_state: ActionState::Idle(None),
},
SfxEvent::Idle,
&stats,
@ -295,10 +293,8 @@ mod tests {
let result = SfxEventMapper::map_character_event(
&CharacterState {
move_state: MoveState::Run(RunState),
action_state: ActionState::Idle(IdleState),
action_disabled_this_tick: false,
move_disabled_this_tick: false,
move_state: MoveState::Run(None),
action_state: ActionState::Idle(None),
},
SfxEvent::Idle,
&stats,
@ -313,10 +309,8 @@ mod tests {
let result = SfxEventMapper::map_character_event(
&CharacterState {
action_state: ActionState::Dodge(Roll(RollState::default())),
move_state: MoveState::Run(RunState),
action_disabled_this_tick: false,
move_disabled_this_tick: true,
action_state: ActionState::Dodge(Roll(None)),
move_state: MoveState::Run(None),
},
SfxEvent::Run,
&stats,
@ -331,10 +325,8 @@ mod tests {
let result = SfxEventMapper::map_character_event(
&CharacterState {
move_state: MoveState::Fall(FallState),
action_state: ActionState::Idle(IdleState),
action_disabled_this_tick: false,
move_disabled_this_tick: false,
move_state: MoveState::Fall((None)),
action_state: ActionState::Idle((None)),
},
SfxEvent::Idle,
&stats,
@ -349,10 +341,8 @@ mod tests {
let result = SfxEventMapper::map_character_event(
&CharacterState {
move_state: MoveState::Glide(GlideState),
action_state: ActionState::Idle(IdleState),
action_disabled_this_tick: true,
move_disabled_this_tick: false,
move_state: MoveState::Glide(None),
action_state: ActionState::Idle(None),
},
SfxEvent::Jump,
&stats,
@ -367,10 +357,8 @@ mod tests {
let result = SfxEventMapper::map_character_event(
&CharacterState {
move_state: MoveState::Glide(GlideState),
action_state: ActionState::Idle(IdleState),
action_disabled_this_tick: true,
move_disabled_this_tick: false,
move_state: MoveState::Glide(None),
action_state: ActionState::Idle(None),
},
SfxEvent::Glide,
&stats,
@ -385,10 +373,8 @@ mod tests {
let result = SfxEventMapper::map_character_event(
&CharacterState {
move_state: MoveState::Fall(FallState),
action_state: ActionState::Idle(IdleState),
move_disabled_this_tick: false,
action_disabled_this_tick: false,
move_state: MoveState::Fall(None),
action_state: ActionState::Idle(None),
},
SfxEvent::Glide,
&stats,
@ -408,10 +394,8 @@ mod tests {
let result = SfxEventMapper::map_character_event(
&CharacterState {
move_state: MoveState::Stand(StandState),
action_state: ActionState::Attack(BasicAttack(BasicAttackState::default())),
move_disabled_this_tick: false,
action_disabled_this_tick: false,
move_state: MoveState::Stand(None),
action_state: ActionState::Attack(BasicAttack(None)),
},
SfxEvent::Idle,
&stats,

View File

@ -1,9 +1,9 @@
use super::{
img_ids::Imgs, BarNumbers, Fonts, ShortcutNumbers, XpBar, CRITICAL_HP_COLOR,
/*FOCUS_COLOR, RAGE_COLOR,*/ HP_COLOR, LOW_HP_COLOR, MANA_COLOR, TEXT_COLOR, XP_COLOR,
img_ids::Imgs, BarNumbers, Fonts, ShortcutNumbers, XpBar, CRITICAL_HP_COLOR, HP_COLOR,
LOW_HP_COLOR, MANA_COLOR, TEXT_COLOR, XP_COLOR,
};
use crate::GlobalState;
use common::comp::{item::Debug, item::ToolData, item::ToolKind, Equipment, ItemKind, Stats};
use common::comp::{item::Debug, item::ToolData, item::ToolKind, ItemKind, Stats};
use conrod_core::{
color,
widget::{self, Button, Image, Rectangle, Text},