Ability comps and sys

This commit is contained in:
Adam Whitehurst 2020-01-01 09:16:29 -08:00
parent b67a4835f4
commit 2635c405fe
14 changed files with 184 additions and 74 deletions

View File

@ -0,0 +1,34 @@
use crate::comp;
use specs::{Component, FlaggedStorage, HashMapStorage, VecStorage};
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub enum AbilityActionKind {
Primary,
Secondary,
Dodge,
Block,
// UpdatePool?
}
impl Default for AbilityActionKind {
fn default() -> Self {
Self::Primary
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct AbilityAction(pub AbilityActionKind);
impl Component for AbilityAction {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
pub struct AbilityPool {
pub primary: Option<comp::AttackKind>,
pub secondary: Option<comp::AttackKind>,
pub block: Option<comp::BlockKind>,
pub dodge: Option<comp::DodgeKind>,
}
impl Component for AbilityPool {
type Storage = HashMapStorage<Self>;
}

View File

@ -141,14 +141,6 @@ pub struct CharacterState {
/// _Primarily `handle()`s how character interacts with world, and upper body animations.
/// Can be overidden by `MoveState`s using `action_disabled` flag. Example: `GlideState`_
pub action_state: ActionState,
/// Used by `move_state` to disable `action_state` `handle()` calls.
/// Resets after every tick. States that use it should set it every tick.
pub action_disabled_this_tick: bool,
/// Used by `action_state` to disable `move_state` `handle()` calls.
/// Resets after every tick. States that use it should set it every tick.
pub move_disabled_this_tick: bool,
}
impl CharacterState {
@ -176,8 +168,6 @@ impl Default for CharacterState {
Self {
move_state: MoveState::Fall(FallState),
action_state: ActionState::Idle(IdleState),
action_disabled_this_tick: false,
move_disabled_this_tick: false,
}
}
}

View File

@ -1,3 +1,4 @@
mod ability;
mod admin;
mod agent;
mod body;
@ -15,6 +16,7 @@ mod stats;
mod visual;
// Reexports
pub use ability::{AbilityAction, AbilityActionKind, AbilityPool};
pub use admin::Admin;
pub use agent::Agent;
pub use body::{

View File

@ -20,7 +20,6 @@ impl StateHandle for BasicBlockState {
};
// TODO: Apply simple move speed debuff instead
update.character.move_disabled_this_tick = true;
// Update movement
update.vel.0 += Vec2::broadcast(ecs_data.dt.0)
@ -32,7 +31,6 @@ impl StateHandle for BasicBlockState {
if !ecs_data.inputs.secondary.is_pressed() {
update.character.action_state = attempt_wield(ecs_data.stats);
update.character.move_disabled_this_tick = false;
return update;
}

View File

@ -25,8 +25,6 @@ impl StateHandle for ChargeAttackState {
};
// Prevent move state handling, handled here
// ecs_data.updater.insert(*ecs_data.entity, OverrideMove);
update.character.action_disabled_this_tick = true;
update.character.move_state = Run(RunState);
// Move player
@ -55,7 +53,6 @@ impl StateHandle for ChargeAttackState {
// Go back to wielding or idling
update.character.action_state = attempt_wield(ecs_data.stats);
update.character.move_disabled_this_tick= false;
return update;
}
@ -63,7 +60,6 @@ impl StateHandle for ChargeAttackState {
if self.remaining_duration == Duration::default() || update.vel.0.magnitude_squared() < 10.0
{
update.character.action_state = attempt_wield(ecs_data.stats);
update.character.move_disabled_this_tick= false;
return update;
}

View File

@ -19,9 +19,7 @@ impl StateHandle for ClimbState {
character: *ecs_data.character,
};
// Disable actions in this state
update.character.action_state = Idle(IdleState);
update.character.action_disabled_this_tick = true;
// Move player
update.vel.0 += Vec2::broadcast(ecs_data.dt.0)
@ -81,13 +79,11 @@ impl StateHandle for ClimbState {
if ecs_data.inputs.jump.is_pressed() {
// They've climbed atop something, give them a boost
update.character.move_state = Jump(JumpState);
update.character.action_disabled_this_tick = false;
return update;
} else {
// Just fall off
update.character.move_state = Fall(FallState);
update.character.action_disabled_this_tick = false;
return update;
}
@ -96,7 +92,6 @@ impl StateHandle for ClimbState {
// Remove climb state on ground, otherwise character will get stuck
if ecs_data.physics.on_ground {
update.character.move_state = Stand(StandState);
update.character.action_disabled_this_tick = false;
return update;
}

View File

@ -17,9 +17,6 @@ impl StateHandle for GlideState {
character: *ecs_data.character,
};
// Prevent action in this state, set here
update.character.action_disabled_this_tick = true;
// Defaults for this state
update.character.action_state = Idle(IdleState);
update.character.move_state = Glide(GlideState);

View File

@ -54,7 +54,6 @@ 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.
@ -98,6 +97,48 @@ impl StateHandle for ActionState {
}
}
// Other fn's that relate to individual `ActionState`s
impl ActionState {
/// Returns whether a given `ActionState` overrides `MoveState` `handle()`ing
pub fn overrides_move_state(&self) -> bool {
match self {
Attack(kind) => match kind {
BasicAttack(state) => false,
Charge(state) => true,
},
Block(kind) => match kind {
BasicBlock(state) => true,
},
Dodge(kind) => match kind {
Roll(state) => true,
},
Wield(state) => false,
Idle(state) => false,
// All states should be explicitly handled
// Do not use default match: _ => {},
}
}
}
// Other fn's that relate to individual `MoveState`s
impl MoveState {
/// Returns whether a given `ActionState` overrides `MoveState` `handle()`ing
pub fn overrides_action_state(&self) -> bool {
match self {
Stand(state) => false,
Run(state) => false,
Jump(state) => false,
Climb(state) => true,
Glide(state) => true,
Swim(state) => false,
Fall(state) => false,
Sit(state) => true,
// All states should be explicitly handled
// Do not use default match: _ => {},
}
}
}
/// Public interface that passes EcsStateData to `StateHandle`s `handle()` fn
impl StateHandle for MoveState {
/// Passes handle to variant handlers

View File

@ -19,9 +19,6 @@ impl StateHandle for RollState {
ori: *ecs_data.ori,
};
// Prevent move state handling, handled here
update.character.move_disabled_this_tick= true;
// 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)
@ -39,7 +36,6 @@ impl StateHandle for RollState {
if self.remaining_duration == Duration::default() {
// If so, go back to wielding or idling
update.character.action_state = attempt_wield(ecs_data.stats);
update.character.move_disabled_this_tick= false;
return update;
}

View File

@ -17,7 +17,6 @@ impl StateHandle for SitState {
};
// Prevent action state handling
update.character.action_disabled_this_tick = true;
update.character.action_state = Idle(IdleState);
update.character.move_state = Sit(SitState);
@ -27,27 +26,23 @@ impl StateHandle for SitState {
// Can't hurt to be safe :shrug:
if !ecs_data.physics.on_ground {
update.character.move_state = determine_fall_or_swim(ecs_data.physics);
update.character.move_disabled_this_tick = false;
return update;
}
// Try to jump
if ecs_data.inputs.jump.is_pressed() {
update.character.move_state = Jump(JumpState);
update.character.action_disabled_this_tick = false;
return update;
}
// Try to Run
if ecs_data.inputs.move_dir.magnitude_squared() > 0.0 {
update.character.move_state = Run(RunState);
update.character.action_disabled_this_tick = false;
return update;
}
// Try to Stand
if ecs_data.inputs.sit.is_just_pressed() {
update.character.move_state = Stand(StandState);
update.character.action_disabled_this_tick = false;
return update;
}

View File

@ -1,4 +1,7 @@
use crate::comp::{ActionState::*, EcsStateData, IdleState, StateHandle, StateUpdate};
use crate::comp::{
AbilityAction, AbilityActionKind::*, ActionState::*, EcsStateData, IdleState, StateHandle,
StateUpdate,
};
use crate::util::movement_utils::*;
use std::time::Duration;
@ -30,9 +33,13 @@ impl StateHandle for WieldState {
// Try weapon actions
if ecs_data.inputs.primary.is_pressed() {
update.character.action_state = determine_primary_ability(ecs_data.stats);
ecs_data
.updater
.insert(*ecs_data.entity, AbilityAction(Primary));
} else if ecs_data.inputs.secondary.is_pressed() {
// TODO: SecondaryStart
ecs_data
.updater
.insert(*ecs_data.entity, AbilityAction(Secondary));
}
} else {
// Equip delay hasn't expired yet

64
common/src/sys/ability.rs Normal file
View File

@ -0,0 +1,64 @@
use crate::comp::{
AbilityAction, AbilityActionKind, AbilityPool, ActionState::*, AttackKind, CharacterState,
};
use specs::{Entities, Join, LazyUpdate, Read, ReadStorage, System, WriteStorage};
/// # Ability System
/// #### Updates tuples of ( `CharacterState`, `AbilityAction`, and `AbilityPool`s )
/// _Each update determines what type of ability is being started, and looks into the AbilityPool for which
/// Ability that should be used. System then updates `CharacterState` to that Ability._
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
Read<'a, LazyUpdate>,
WriteStorage<'a, CharacterState>,
ReadStorage<'a, AbilityAction>,
ReadStorage<'a, AbilityPool>,
);
fn run(
&mut self,
(
entities,
updater,
mut character_state_storage,
ability_action_storage,
ability_pool_storage,
): Self::SystemData,
) {
for (entity, mut character, ability_action, ability_pool) in (
&entities,
&mut character_state_storage,
&ability_action_storage,
&ability_pool_storage,
)
.join()
{
match ability_action.0 {
AbilityActionKind::Primary => {
if let Some(attack_kind) = ability_pool.primary {
character.action_state = Attack(attack_kind);
}
}
AbilityActionKind::Secondary => {
if let Some(attack_kind) = ability_pool.secondary {
character.action_state = Attack(attack_kind);
}
}
AbilityActionKind::Block => {
if let Some(block_kind) = ability_pool.block {
character.action_state = Block(block_kind);
}
}
AbilityActionKind::Dodge => {
if let Some(dodge_kind) = ability_pool.dodge {
character.action_state = Dodge(dodge_kind);
}
}
_ => {}
}
}
}
}

View File

@ -1,6 +1,6 @@
use crate::{
comp::{
Body, CharacterState, Controller, EcsStateData, Mounting, MoveState::*, Ori,
states::*, Body, CharacterState, Controller, EcsStateData, Mounting, MoveState::*, Ori,
OverrideAction, OverrideMove, OverrideState, PhysicsState, Pos, SitState, StateHandle,
Stats, Vel,
},
@ -106,42 +106,10 @@ impl<'a> System<'a> for Sys {
return;
}
// Determine new move state if character can move
if let (None, false) = (
move_overrides.get(entity),
character.move_disabled_this_tick,
) {
let state_update = character.move_state.handle(&EcsStateData {
entity: &entity,
uid,
character,
pos,
vel,
ori,
dt: &dt,
inputs,
stats,
body,
physics,
updater: &updater,
server_bus: &server_bus,
local_bus: &local_bus,
});
*character = state_update.character;
*pos = state_update.pos;
*vel = state_update.vel;
*ori = state_update.ori;
}
// Reset disabled every tick. Should be
// set every tick by states that use it.
character.move_disabled_this_tick = false;
// Determine new action if character can act
if let (None, false) = (
action_overrides.get(entity),
character.action_disabled_this_tick,
character.action_state.overrides_move_state(),
) {
let state_update = character.action_state.handle(&EcsStateData {
entity: &entity,
@ -166,9 +134,33 @@ impl<'a> System<'a> for Sys {
*ori = state_update.ori;
}
// Reset disabled every tick. Should
// be set every tick by states that use it.
character.action_disabled_this_tick = false;
// Determine new move state if character can move
if let (None, false) = (
move_overrides.get(entity),
character.move_state.overrides_action_state(),
) {
let state_update = character.move_state.handle(&EcsStateData {
entity: &entity,
uid,
character,
pos,
vel,
ori,
dt: &dt,
inputs,
stats,
body,
physics,
updater: &updater,
server_bus: &server_bus,
local_bus: &local_bus,
});
*character = state_update.character;
*pos = state_update.pos;
*vel = state_update.vel;
*ori = state_update.ori;
}
}
}
}

View File

@ -1,3 +1,4 @@
mod ability;
pub mod agent;
pub mod character_state;
mod cleanup;
@ -11,6 +12,7 @@ mod stats;
use specs::DispatcherBuilder;
// System names
const ABILITY_SYS: &str = "ability_sys";
const AGENT_SYS: &str = "agent_sys";
const CHARACTER_STATE_SYS: &str = "character_state_sys";
const CONTROLLER_SYS: &str = "controller_sys";
@ -24,6 +26,7 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[AGENT_SYS]);
dispatch_builder.add(character_state::Sys, CHARACTER_STATE_SYS, &[CONTROLLER_SYS]);
dispatch_builder.add(ability::Sys, ABILITY_SYS, &[CHARACTER_STATE_SYS]);
dispatch_builder.add(combat::Sys, COMBAT_SYS, &[CONTROLLER_SYS]);
dispatch_builder.add(stats::Sys, STATS_SYS, &[COMBAT_SYS]);
dispatch_builder.add(