Finish state struct data refactor

This commit is contained in:
AdamWhitehurst 2020-03-14 15:17:27 -06:00
parent ee706fa32a
commit 7dfe00b674
16 changed files with 417 additions and 359 deletions

View File

@ -39,23 +39,25 @@ impl From<CharacterAbility> 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,

View File

@ -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 {

View File

@ -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::<Attacking>(data.entity);
} else {

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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::<f32>::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::<f32>::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
}
}

View File

@ -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
}

View File

@ -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::<f32>::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::<f32>::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::<f32>::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::<f32>::from(update.vel.0).magnitude() * 0.075)
.min(1.0)
.max(0.2);
}
// Otherwise keep gliding
update
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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::<Attacking>(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::<Attacking>(data.entity);
}

View File

@ -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
}
}

View File

@ -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 {};
};

View File

@ -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
}
}

View File

@ -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 {