mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Ability comps and sys
This commit is contained in:
parent
b67a4835f4
commit
2635c405fe
34
common/src/comp/ability.rs
Normal file
34
common/src/comp/ability.rs
Normal 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>;
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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::{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
64
common/src/sys/ability.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user