mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Documentation comments
This commit is contained in:
parent
ca44497258
commit
7a4cdfb7a4
@ -79,6 +79,8 @@ impl ActionState {
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current `equip_delay` if in `WieldState`, otherwise `Duration::default()`
|
||||
pub fn get_delay(&self) -> Duration {
|
||||
match self {
|
||||
Wield(WieldState { equip_delay }) => *equip_delay,
|
||||
@ -123,6 +125,9 @@ impl ActionState {
|
||||
}
|
||||
}
|
||||
|
||||
/// __A concurrent state machine that allows for spearate `ActionState`s and `MoveState`s.__
|
||||
///
|
||||
/// _Each state can optionally override the other through `*_disabled` flag_
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
||||
pub struct CharacterState {
|
||||
/// __How the character is currently moving, e.g. Running, Standing, Falling.__
|
||||
@ -196,21 +201,3 @@ pub struct OverrideMove;
|
||||
impl Component for OverrideMove {
|
||||
type Storage = FlaggedStorage<Self, NullStorage<Self>>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
||||
pub enum StartAction {
|
||||
Primary,
|
||||
Secondary,
|
||||
Tertiary,
|
||||
Four,
|
||||
Five,
|
||||
}
|
||||
impl Default for StartAction {
|
||||
fn default() -> Self {
|
||||
Self::Primary
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for StartAction {
|
||||
type Storage = FlaggedStorage<Self, NullStorage<Self>>;
|
||||
}
|
||||
|
@ -17,9 +17,10 @@ impl StateHandle for IdleState {
|
||||
};
|
||||
|
||||
// Try to wield
|
||||
if ecs_data.inputs.toggle_wield.is_pressed()
|
||||
|| ecs_data.inputs.primary.is_pressed()
|
||||
if ecs_data.inputs.primary.is_pressed()
|
||||
|| ecs_data.inputs.secondary.is_pressed()
|
||||
|| (ecs_data.inputs.toggle_wield.is_just_pressed()
|
||||
&& update.character.action_state.is_equip_finished())
|
||||
{
|
||||
if let Some(Tool { .. }) = ecs_data.stats.equipment.main.as_ref().map(|i| &i.kind) {
|
||||
update.character.action_state = Wield(WieldState {
|
||||
|
@ -50,16 +50,31 @@ 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;
|
||||
|
||||
// Public interface, wires character states to their handlers.
|
||||
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, but remains 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.
|
||||
pub trait StateHandle {
|
||||
fn handle(&self, ecs_data: &EcsStateData) -> StateUpdate;
|
||||
}
|
||||
|
||||
// Public interface that passes EcsStateData to `StateHandle`s `handle()` fn
|
||||
impl StateHandle for ActionState {
|
||||
/// Passes handle to variant or subvariant handlers
|
||||
fn handle(&self, ecs_data: &EcsStateData) -> StateUpdate {
|
||||
@ -82,6 +97,7 @@ impl StateHandle for ActionState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Public interface that passes EcsStateData to `StateHandle`s `handle()` fn
|
||||
impl StateHandle for MoveState {
|
||||
/// Passes handle to variant handlers
|
||||
fn handle(&self, ecs_data: &EcsStateData) -> StateUpdate {
|
||||
|
@ -20,7 +20,7 @@ impl StateHandle for WieldState {
|
||||
// Only act once equip_delay has expired
|
||||
if self.equip_delay == Duration::default() {
|
||||
// Toggle Weapons
|
||||
if ecs_data.inputs.toggle_wield.is_pressed()
|
||||
if ecs_data.inputs.toggle_wield.is_just_pressed()
|
||||
&& ecs_data.character.action_state.is_equip_finished()
|
||||
{
|
||||
update.character.action_state = Idle(IdleState);
|
||||
|
@ -8,11 +8,22 @@ use crate::{
|
||||
state::DeltaTime,
|
||||
};
|
||||
|
||||
use specs::{Entities, Join, LazyUpdate, Read, ReadStorage, System, WriteStorage};
|
||||
use rayon::prelude::*;
|
||||
use specs::{Entities, LazyUpdate, ParJoin, Read, ReadStorage, System, WriteStorage};
|
||||
use sphynx::{Uid, UidAllocator};
|
||||
|
||||
/// # Character StateHandle System
|
||||
/// #### Updates then detemrines next Character States based on ControllerInputs
|
||||
/// # 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._
|
||||
///
|
||||
/// _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._
|
||||
pub struct Sys;
|
||||
|
||||
impl<'a> System<'a> for Sys {
|
||||
@ -61,22 +72,11 @@ impl<'a> System<'a> for Sys {
|
||||
action_overrides,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
for (
|
||||
entity,
|
||||
uid,
|
||||
mut character,
|
||||
pos,
|
||||
vel,
|
||||
ori,
|
||||
controller,
|
||||
stats,
|
||||
body,
|
||||
physics,
|
||||
maybe_mount,
|
||||
maybe_move_override,
|
||||
maybe_action_override,
|
||||
(),
|
||||
) in (
|
||||
// Parallel joining behaves similarly to normal `join()`ing
|
||||
// with the difference that iteration can potentially be
|
||||
// executed in parallel by a thread pool.
|
||||
// https://specs.amethyst.rs/docs/tutorials/09_parallel_join.html
|
||||
(
|
||||
&entities,
|
||||
&uids,
|
||||
&mut character_states,
|
||||
@ -92,77 +92,94 @@ impl<'a> System<'a> for Sys {
|
||||
action_overrides.maybe(),
|
||||
!&state_overrides,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let inputs = &controller.inputs;
|
||||
|
||||
// Being dead overrides all other states
|
||||
if stats.is_dead {
|
||||
// Only options: click respawn
|
||||
// prevent instant-respawns (i.e. player was holding attack)
|
||||
// by disallowing while input is held down
|
||||
if inputs.respawn.is_pressed() && !inputs.respawn.is_held_down() {
|
||||
server_bus.emitter().emit(ServerEvent::Respawn(entity));
|
||||
}
|
||||
// Or do nothing
|
||||
continue;
|
||||
}
|
||||
// If mounted, character state is controlled by mount
|
||||
// TODO: Make mounting a state
|
||||
if maybe_mount.is_some() {
|
||||
character.move_state = Sit(SitState);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine new move state if can move
|
||||
if !maybe_move_override.is_some() && !character.move_disabled {
|
||||
let state_update = character.move_state.handle(&EcsStateData {
|
||||
entity: &entity,
|
||||
.par_join()
|
||||
.for_each(
|
||||
|(
|
||||
entity,
|
||||
uid,
|
||||
character,
|
||||
mut character,
|
||||
pos,
|
||||
vel,
|
||||
ori,
|
||||
dt: &dt,
|
||||
inputs,
|
||||
controller,
|
||||
stats,
|
||||
body,
|
||||
physics,
|
||||
updater: &updater,
|
||||
server_bus: &server_bus,
|
||||
local_bus: &local_bus,
|
||||
});
|
||||
maybe_mount,
|
||||
maybe_move_override,
|
||||
maybe_action_override,
|
||||
(),
|
||||
)| {
|
||||
let inputs = &controller.inputs;
|
||||
|
||||
*character = state_update.character;
|
||||
*pos = state_update.pos;
|
||||
*vel = state_update.vel;
|
||||
*ori = state_update.ori;
|
||||
}
|
||||
// Being dead overrides all other states
|
||||
if stats.is_dead {
|
||||
// Only options: click respawn
|
||||
// prevent instant-respawns (i.e. player was holding attack)
|
||||
// by disallowing while input is held down
|
||||
if inputs.respawn.is_pressed() && !inputs.respawn.is_held_down() {
|
||||
server_bus.emitter().emit(ServerEvent::Respawn(entity));
|
||||
}
|
||||
// Or do nothing
|
||||
continue;
|
||||
}
|
||||
// If mounted, character state is controlled by mount
|
||||
// TODO: Make mounting a state
|
||||
if maybe_mount.is_some() {
|
||||
character.move_state = Sit(SitState);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine new action if can_act
|
||||
if !maybe_action_override.is_some() && !character.action_disabled {
|
||||
let state_update = character.action_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,
|
||||
});
|
||||
// Determine new move state if character can move
|
||||
if !maybe_move_override.is_some() && !character.move_disabled {
|
||||
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;
|
||||
}
|
||||
}
|
||||
*character = state_update.character;
|
||||
*pos = state_update.pos;
|
||||
*vel = state_update.vel;
|
||||
*ori = state_update.ori;
|
||||
}
|
||||
|
||||
// Determine new action if character can act
|
||||
if !maybe_action_override.is_some() && !character.action_disabled {
|
||||
let state_update = character.action_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;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user