2019-12-20 13:30:37 +00:00
|
|
|
use crate::comp::{Body, CharacterState, Controller, ControllerInputs, PhysicsState};
|
2019-08-23 10:11:37 +00:00
|
|
|
use specs::{Component, FlaggedStorage, HashMapStorage};
|
2019-12-20 13:30:37 +00:00
|
|
|
use specs::{Entities, Join, LazyUpdate, Read, ReadStorage, System};
|
|
|
|
use sphynx::{Uid, UidAllocator};
|
2019-09-04 23:03:49 +00:00
|
|
|
//use specs_idvs::IDVStorage;
|
2019-12-20 13:30:37 +00:00
|
|
|
use self::{ActionState::*, MovementState::*};
|
2019-08-23 10:11:37 +00:00
|
|
|
use std::time::Duration;
|
2019-12-20 13:30:37 +00:00
|
|
|
pub trait State {
|
|
|
|
fn handle(
|
|
|
|
&self,
|
|
|
|
character: &CharacterState,
|
|
|
|
inputs: &ControllerInputs,
|
|
|
|
body: &Body,
|
|
|
|
physics: &PhysicsState,
|
|
|
|
) -> CharacterState;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
|
|
|
pub struct RunData;
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
|
|
|
pub struct StandData;
|
|
|
|
|
|
|
|
impl State for StandData {
|
|
|
|
fn handle(
|
|
|
|
&self,
|
|
|
|
character: &CharacterState,
|
|
|
|
inputs: &ControllerInputs,
|
|
|
|
body: &Body,
|
|
|
|
physics: &PhysicsState,
|
|
|
|
) -> CharacterState {
|
|
|
|
let mut new_move: MovementState = if inputs.move_dir.magnitude_squared() > 0.0 {
|
|
|
|
MovementState::Run(RunData)
|
|
|
|
} else {
|
|
|
|
MovementState::Stand(StandData)
|
|
|
|
};
|
|
|
|
|
|
|
|
// Try to sit
|
|
|
|
if inputs.sit.is_pressed() && physics.on_ground && body.is_humanoid() {
|
|
|
|
return CharacterState {
|
|
|
|
movement: Sit,
|
|
|
|
action: Idle,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to climb
|
|
|
|
if let (true, Some(_wall_dir)) = (
|
|
|
|
inputs.climb.is_pressed() | inputs.climb_down.is_pressed() && body.is_humanoid(),
|
|
|
|
physics.on_wall,
|
|
|
|
) {
|
|
|
|
return CharacterState {
|
|
|
|
movement: Climb,
|
|
|
|
action: Idle,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to swim
|
|
|
|
if !physics.on_ground && physics.in_fluid {
|
|
|
|
return CharacterState {
|
|
|
|
action: character.action,
|
|
|
|
movement: Swim,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// While on ground ...
|
|
|
|
if physics.on_ground {
|
|
|
|
// Try to jump
|
|
|
|
if inputs.jump.is_pressed() && !inputs.jump.is_held_down() {
|
|
|
|
return CharacterState {
|
|
|
|
action: character.action,
|
|
|
|
movement: Jump,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to charge
|
|
|
|
if inputs.charge.is_pressed() && !inputs.charge.is_held_down() {
|
|
|
|
return CharacterState {
|
|
|
|
action: Charge {
|
|
|
|
time_left: Duration::from_millis(250),
|
|
|
|
},
|
|
|
|
movement: Run(RunData),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to roll
|
|
|
|
if inputs.roll.is_pressed() && body.is_humanoid() {
|
|
|
|
return CharacterState {
|
|
|
|
action: Roll {
|
|
|
|
time_left: Duration::from_millis(600),
|
|
|
|
was_wielding: character.action.is_wield(),
|
|
|
|
},
|
|
|
|
movement: Run(RunData),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// While not on ground ...
|
|
|
|
else {
|
|
|
|
// Try to glide
|
|
|
|
if physics.on_wall == None
|
|
|
|
&& inputs.glide.is_pressed()
|
|
|
|
&& !inputs.glide.is_held_down()
|
|
|
|
&& body.is_humanoid()
|
|
|
|
{
|
|
|
|
character.movement = Glide;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
character.movement = Fall;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Tool Actions
|
|
|
|
if inputs.toggle_wield.is_just_pressed() {
|
|
|
|
match action_state {
|
|
|
|
Wield { .. } | Attack { .. } => {
|
|
|
|
// Prevent instantaneous reequipping by checking
|
|
|
|
// for done wielding
|
|
|
|
if character.action.is_action_finished() {
|
|
|
|
character.action = Idle;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Idle => {
|
|
|
|
character.action = try_wield(stats);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
Charge { .. } | Roll { .. } | Block { .. } => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if inputs.primary.is_pressed() {
|
|
|
|
// TODO: PrimaryStart
|
|
|
|
} else if inputs.secondary.is_pressed() {
|
|
|
|
// TODO: SecondaryStart
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-08-23 10:11:37 +00:00
|
|
|
|
2019-08-18 09:01:57 +00:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
2019-08-23 10:11:37 +00:00
|
|
|
pub enum MovementState {
|
2019-12-20 13:30:37 +00:00
|
|
|
Stand(Stand),
|
2019-09-09 19:11:40 +00:00
|
|
|
Sit,
|
2019-12-20 13:30:37 +00:00
|
|
|
Run(Run),
|
2019-08-23 10:11:37 +00:00
|
|
|
Jump,
|
2019-12-03 06:30:08 +00:00
|
|
|
Fall,
|
2019-08-23 10:11:37 +00:00
|
|
|
Glide,
|
2019-09-09 19:11:40 +00:00
|
|
|
Swim,
|
|
|
|
Climb,
|
2019-08-23 10:11:37 +00:00
|
|
|
}
|
|
|
|
|
2019-08-18 09:01:57 +00:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
2019-08-23 10:11:37 +00:00
|
|
|
pub enum ActionState {
|
|
|
|
Idle,
|
2019-12-09 14:45:10 +00:00
|
|
|
Wield {
|
|
|
|
time_left: Duration,
|
|
|
|
},
|
|
|
|
Attack {
|
|
|
|
time_left: Duration,
|
|
|
|
applied: bool,
|
|
|
|
},
|
|
|
|
Block {
|
|
|
|
time_active: Duration,
|
|
|
|
},
|
|
|
|
Roll {
|
|
|
|
time_left: Duration,
|
|
|
|
// Whether character was wielding before they started roll
|
|
|
|
was_wielding: bool,
|
|
|
|
},
|
|
|
|
Charge {
|
|
|
|
time_left: Duration,
|
|
|
|
},
|
2019-12-20 13:30:37 +00:00
|
|
|
// Handle(CharacterAction),
|
2019-08-23 10:11:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ActionState {
|
|
|
|
pub fn is_wield(&self) -> bool {
|
|
|
|
if let Self::Wield { .. } = self {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-03 06:30:08 +00:00
|
|
|
pub fn is_action_finished(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
Self::Wield { time_left }
|
|
|
|
| Self::Attack { time_left, .. }
|
2019-12-09 14:45:10 +00:00
|
|
|
| Self::Roll { time_left, .. }
|
2019-12-03 06:30:08 +00:00
|
|
|
| Self::Charge { time_left } => *time_left == Duration::default(),
|
|
|
|
Self::Idle | Self::Block { .. } => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-23 10:11:37 +00:00
|
|
|
pub fn is_attack(&self) -> bool {
|
|
|
|
if let Self::Attack { .. } = self {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2019-08-24 16:38:59 +00:00
|
|
|
|
|
|
|
pub fn is_block(&self) -> bool {
|
|
|
|
if let Self::Block { .. } = self {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
|
|
|
|
pub fn is_roll(&self) -> bool {
|
|
|
|
if let Self::Roll { .. } = self {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn is_charge(&self) -> bool {
|
|
|
|
if let Self::Charge { .. } = self {
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2019-08-23 10:11:37 +00:00
|
|
|
}
|
|
|
|
|
2019-08-18 09:01:57 +00:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
2019-08-23 10:11:37 +00:00
|
|
|
pub struct CharacterState {
|
|
|
|
pub movement: MovementState,
|
|
|
|
pub action: ActionState,
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:40:22 +00:00
|
|
|
impl CharacterState {
|
|
|
|
pub fn is_same_movement(&self, other: &Self) -> bool {
|
|
|
|
// Check if enum item is the same without looking at the inner data
|
|
|
|
std::mem::discriminant(&self.movement) == std::mem::discriminant(&other.movement)
|
|
|
|
}
|
|
|
|
pub fn is_same_action(&self, other: &Self) -> bool {
|
|
|
|
// Check if enum item is the same without looking at the inner data
|
|
|
|
std::mem::discriminant(&self.action) == std::mem::discriminant(&other.action)
|
|
|
|
}
|
|
|
|
pub fn is_same_state(&self, other: &Self) -> bool {
|
|
|
|
self.is_same_movement(other) && self.is_same_action(other)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-23 10:11:37 +00:00
|
|
|
impl Default for CharacterState {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
movement: MovementState::Jump,
|
|
|
|
action: ActionState::Idle,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Component for CharacterState {
|
|
|
|
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
|
|
|
|
}
|