2019-12-03 06:30:08 +00:00
|
|
|
use super::movement::ROLL_DURATION;
|
2019-08-23 10:11:37 +00:00
|
|
|
use crate::{
|
|
|
|
comp::{
|
2019-12-03 06:30:08 +00:00
|
|
|
self, item, projectile, ActionState, ActionState::*, Body, CharacterState, ControlEvent,
|
2019-11-22 00:53:28 +00:00
|
|
|
Controller, ControllerInputs, Energy, EnergySource, HealthChange, HealthSource, ItemKind,
|
|
|
|
Mounting, MovementState, MovementState::*, PhysicsState, Projectile, Stats, Vel,
|
2019-08-23 10:11:37 +00:00
|
|
|
},
|
2019-12-03 06:30:08 +00:00
|
|
|
event::{Emitter, EventBus, LocalEvent, ServerEvent},
|
|
|
|
state::DeltaTime,
|
2019-11-24 20:12:03 +00:00
|
|
|
sync::{Uid, UidAllocator},
|
2019-06-09 14:20:20 +00:00
|
|
|
};
|
2019-09-09 19:11:40 +00:00
|
|
|
use specs::{
|
|
|
|
saveload::{Marker, MarkerAllocator},
|
2019-12-03 06:30:08 +00:00
|
|
|
Entities, Entity, Join, Read, ReadStorage, System, WriteStorage,
|
2019-09-09 19:11:40 +00:00
|
|
|
};
|
2019-08-23 10:11:37 +00:00
|
|
|
use std::time::Duration;
|
2019-08-29 17:39:34 +00:00
|
|
|
use vek::*;
|
2019-06-09 14:20:20 +00:00
|
|
|
|
2020-01-17 20:08:27 +00:00
|
|
|
const CHARGE_COST: i32 = 200;
|
2019-11-22 00:53:28 +00:00
|
|
|
|
2019-12-03 06:30:08 +00:00
|
|
|
/// # Controller System
|
|
|
|
/// #### Responsible for validating controller inputs and setting new Character States
|
|
|
|
/// ----
|
|
|
|
///
|
|
|
|
/// **Writes:**
|
|
|
|
/// `CharacterState`, `ControllerInputs`
|
|
|
|
///
|
|
|
|
/// **Reads:**
|
|
|
|
/// `Stats`, `Vel`, `PhysicsState`, `Uid`, `Mounting`
|
|
|
|
///
|
|
|
|
/// _TODO: Join ActionStates and MovementStates into one and have a handle() trait / fn?_
|
|
|
|
/// _TODO: Move weapon action to trait fn?_
|
2019-06-09 14:20:20 +00:00
|
|
|
pub struct Sys;
|
2019-12-03 06:30:08 +00:00
|
|
|
|
|
|
|
impl Sys {
|
|
|
|
/// Assumes `input.primary` has been pressed
|
|
|
|
/// handles primary actions. ie. equipping, mainhand weapon attacks.
|
|
|
|
///
|
|
|
|
/// Returns the `ActionState` that occurred
|
|
|
|
fn handle_primary(
|
|
|
|
inputs: &mut ControllerInputs,
|
|
|
|
character: &mut CharacterState,
|
|
|
|
stats: &Stats,
|
|
|
|
entity: Entity,
|
|
|
|
uid: &Uid,
|
|
|
|
server_emitter: &mut Emitter<'_, ServerEvent>,
|
|
|
|
local_emitter: &mut Emitter<'_, LocalEvent>,
|
|
|
|
) -> ActionState {
|
|
|
|
match stats.equipment.main.as_ref().map(|i| &i.kind) {
|
|
|
|
// Character is wielding something
|
|
|
|
Some(ItemKind::Tool { kind, power, .. }) => {
|
|
|
|
let attack_duration = kind.attack_duration();
|
|
|
|
let wield_duration = kind.wield_duration();
|
|
|
|
|
|
|
|
// Since primary input was pressed, set
|
|
|
|
// action to new Wield, in case of
|
|
|
|
// instant primary actions
|
|
|
|
if character.action == Idle {
|
|
|
|
character.action = Wield {
|
|
|
|
time_left: wield_duration,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
match kind {
|
|
|
|
item::Tool::Bow if character.action.is_action_finished() => {
|
|
|
|
// Immediately end the wield
|
|
|
|
server_emitter.emit(ServerEvent::Shoot {
|
|
|
|
entity,
|
|
|
|
dir: inputs.look_dir,
|
|
|
|
body: comp::Body::Object(comp::object::Body::Arrow),
|
|
|
|
light: None,
|
|
|
|
gravity: Some(comp::Gravity(0.3)),
|
|
|
|
projectile: Projectile {
|
|
|
|
owner: *uid,
|
|
|
|
hit_ground: vec![projectile::Effect::Stick],
|
|
|
|
hit_wall: vec![projectile::Effect::Stick],
|
|
|
|
hit_entity: vec![
|
|
|
|
projectile::Effect::Damage(HealthChange {
|
|
|
|
amount: -(*power as i32),
|
|
|
|
cause: HealthSource::Attack { by: *uid },
|
|
|
|
}),
|
|
|
|
projectile::Effect::Vanish,
|
|
|
|
],
|
2019-12-12 20:13:45 +00:00
|
|
|
time_left: Duration::from_secs(15),
|
2019-12-03 06:30:08 +00:00
|
|
|
},
|
|
|
|
});
|
2019-12-12 20:13:45 +00:00
|
|
|
Attack {
|
|
|
|
time_left: attack_duration,
|
|
|
|
applied: false, // We don't want to do a melee attack
|
|
|
|
}
|
|
|
|
//character.action
|
2019-12-03 06:30:08 +00:00
|
|
|
}
|
|
|
|
item::Tool::Debug(item::Debug::Boost) => {
|
|
|
|
local_emitter.emit(LocalEvent::Boost {
|
|
|
|
entity,
|
|
|
|
vel: inputs.look_dir * 7.0,
|
|
|
|
});
|
|
|
|
character.action
|
|
|
|
}
|
|
|
|
|
|
|
|
item::Tool::Debug(item::Debug::Possess)
|
|
|
|
if character.action.is_action_finished() =>
|
|
|
|
{
|
|
|
|
server_emitter.emit(ServerEvent::Shoot {
|
|
|
|
entity,
|
|
|
|
gravity: Some(comp::Gravity(0.1)),
|
|
|
|
dir: inputs.look_dir,
|
|
|
|
body: comp::Body::Object(comp::object::Body::ArrowSnake),
|
|
|
|
light: Some(comp::LightEmitter {
|
|
|
|
col: (0.0, 1.0, 0.3).into(),
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
projectile: Projectile {
|
|
|
|
owner: *uid,
|
|
|
|
hit_ground: vec![projectile::Effect::Stick],
|
|
|
|
hit_wall: vec![projectile::Effect::Stick],
|
|
|
|
hit_entity: vec![
|
|
|
|
projectile::Effect::Stick,
|
|
|
|
projectile::Effect::Possess,
|
|
|
|
],
|
|
|
|
time_left: Duration::from_secs(10),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
character.action
|
|
|
|
}
|
|
|
|
// All other weapons
|
|
|
|
_ if character.action.is_action_finished() => Attack {
|
|
|
|
time_left: attack_duration,
|
|
|
|
applied: false,
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
// Return the new Wield action
|
|
|
|
character.action
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Without a weapon
|
|
|
|
None => {
|
|
|
|
// Attack
|
|
|
|
if !character.action.is_attack() {
|
|
|
|
Attack {
|
|
|
|
time_left: Duration::from_millis(100),
|
|
|
|
applied: false,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
character.action
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => character.action,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Assumes `input.seconday` has been pressed
|
|
|
|
/// handles seconday actions. ie. blocking, althand weapons
|
|
|
|
///
|
|
|
|
/// Returns the `ActionState` that occurred
|
|
|
|
fn handle_secondary(
|
|
|
|
inputs: &mut ControllerInputs,
|
|
|
|
character: &mut CharacterState,
|
|
|
|
stats: &Stats,
|
|
|
|
entity: Entity,
|
|
|
|
uid: &Uid,
|
|
|
|
server_emitter: &mut Emitter<'_, ServerEvent>,
|
|
|
|
local_emitter: &mut Emitter<'_, LocalEvent>,
|
|
|
|
) -> ActionState {
|
|
|
|
match stats.equipment.main.as_ref().map(|i| &i.kind) {
|
|
|
|
// Character is wielding something
|
|
|
|
Some(ItemKind::Tool { kind, power, .. }) => {
|
|
|
|
let attack_duration = kind.attack_duration();
|
|
|
|
let wield_duration = kind.wield_duration();
|
|
|
|
|
|
|
|
// Since primary input was pressed, set
|
|
|
|
// action to new Wield, in case of
|
|
|
|
// instant primary actions
|
|
|
|
if character.action == Idle {
|
|
|
|
character.action = Wield {
|
|
|
|
time_left: wield_duration,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
match kind {
|
|
|
|
// Magical Bolt
|
|
|
|
item::Tool::Staff
|
|
|
|
if character.movement == Stand && character.action.is_action_finished() =>
|
|
|
|
{
|
|
|
|
server_emitter.emit(ServerEvent::Shoot {
|
|
|
|
entity,
|
|
|
|
dir: inputs.look_dir,
|
|
|
|
body: comp::Body::Object(comp::object::Body::BoltFire),
|
|
|
|
gravity: Some(comp::Gravity(0.0)),
|
|
|
|
light: Some(comp::LightEmitter {
|
|
|
|
col: (0.72, 0.11, 0.11).into(),
|
|
|
|
strength: 10.0,
|
|
|
|
offset: Vec3::new(0.0, -5.0, 2.0),
|
|
|
|
}),
|
|
|
|
projectile: Projectile {
|
|
|
|
owner: *uid,
|
|
|
|
hit_ground: vec![projectile::Effect::Vanish],
|
|
|
|
hit_wall: vec![projectile::Effect::Vanish],
|
|
|
|
hit_entity: vec![
|
|
|
|
projectile::Effect::Damage(HealthChange {
|
|
|
|
amount: -(*power as i32),
|
|
|
|
cause: HealthSource::Attack { by: *uid },
|
|
|
|
}),
|
|
|
|
projectile::Effect::Vanish,
|
|
|
|
],
|
|
|
|
time_left: Duration::from_secs(5),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
// TODO: Don't play melee animation
|
|
|
|
Attack {
|
|
|
|
time_left: attack_duration,
|
|
|
|
applied: true, // We don't want to do a melee attack
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Go upward
|
|
|
|
item::Tool::Debug(item::Debug::Boost) => {
|
|
|
|
local_emitter.emit(LocalEvent::Boost {
|
|
|
|
entity,
|
|
|
|
vel: Vec3::new(0.0, 0.0, 7.0),
|
|
|
|
});
|
|
|
|
|
|
|
|
character.action
|
|
|
|
}
|
|
|
|
|
|
|
|
// All other weapons block
|
|
|
|
_ if character.action.is_action_finished() => Block {
|
|
|
|
time_active: Duration::from_secs(0),
|
|
|
|
},
|
|
|
|
|
|
|
|
_ => character.action,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => character.action,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-09 14:20:20 +00:00
|
|
|
impl<'a> System<'a> for Sys {
|
|
|
|
type SystemData = (
|
|
|
|
Entities<'a>,
|
2019-12-03 06:30:08 +00:00
|
|
|
Read<'a, UidAllocator>,
|
2019-08-25 14:49:54 +00:00
|
|
|
Read<'a, EventBus<ServerEvent>>,
|
|
|
|
Read<'a, EventBus<LocalEvent>>,
|
2019-12-03 06:30:08 +00:00
|
|
|
Read<'a, DeltaTime>,
|
2019-07-21 16:50:13 +00:00
|
|
|
WriteStorage<'a, Controller>,
|
2019-12-03 06:30:08 +00:00
|
|
|
WriteStorage<'a, CharacterState>,
|
2019-06-09 14:20:20 +00:00
|
|
|
ReadStorage<'a, Stats>,
|
2019-11-22 00:53:28 +00:00
|
|
|
WriteStorage<'a, Energy>,
|
2019-08-04 08:21:29 +00:00
|
|
|
ReadStorage<'a, Body>,
|
2019-06-13 18:09:50 +00:00
|
|
|
ReadStorage<'a, Vel>,
|
2019-08-23 10:11:37 +00:00
|
|
|
ReadStorage<'a, PhysicsState>,
|
2019-10-11 04:30:34 +00:00
|
|
|
ReadStorage<'a, Uid>,
|
2019-12-03 06:30:08 +00:00
|
|
|
ReadStorage<'a, Mounting>,
|
2019-06-09 14:20:20 +00:00
|
|
|
);
|
|
|
|
fn run(
|
|
|
|
&mut self,
|
|
|
|
(
|
|
|
|
entities,
|
2019-12-03 06:30:08 +00:00
|
|
|
uid_allocator,
|
2019-08-25 14:49:54 +00:00
|
|
|
server_bus,
|
|
|
|
local_bus,
|
2019-12-03 06:30:08 +00:00
|
|
|
dt,
|
2019-07-21 16:50:13 +00:00
|
|
|
mut controllers,
|
2019-12-03 06:30:08 +00:00
|
|
|
mut character_states,
|
2019-06-09 14:20:20 +00:00
|
|
|
stats,
|
2019-11-22 00:53:28 +00:00
|
|
|
mut energies,
|
2019-08-04 08:21:29 +00:00
|
|
|
bodies,
|
2019-06-13 18:09:50 +00:00
|
|
|
velocities,
|
2019-08-23 10:11:37 +00:00
|
|
|
physics_states,
|
2019-10-17 20:59:36 +00:00
|
|
|
uids,
|
2019-12-03 06:30:08 +00:00
|
|
|
mountings,
|
2019-06-09 14:20:20 +00:00
|
|
|
): Self::SystemData,
|
|
|
|
) {
|
2019-08-25 14:49:54 +00:00
|
|
|
let mut server_emitter = server_bus.emitter();
|
|
|
|
let mut local_emitter = local_bus.emitter();
|
2020-01-19 19:19:44 +00:00
|
|
|
for (
|
|
|
|
entity,
|
|
|
|
uid,
|
|
|
|
controller,
|
|
|
|
mut character,
|
|
|
|
stats,
|
|
|
|
mut energy,
|
|
|
|
body,
|
|
|
|
vel,
|
|
|
|
physics,
|
|
|
|
mount,
|
|
|
|
) in (
|
2019-06-09 14:20:20 +00:00
|
|
|
&entities,
|
2019-10-17 20:59:36 +00:00
|
|
|
&uids,
|
2019-07-21 16:50:13 +00:00
|
|
|
&mut controllers,
|
2019-12-03 06:30:08 +00:00
|
|
|
&mut character_states,
|
2019-06-09 14:20:20 +00:00
|
|
|
&stats,
|
2020-01-19 19:19:44 +00:00
|
|
|
&mut energies.restrict_mut(),
|
2019-08-04 08:21:29 +00:00
|
|
|
&bodies,
|
2019-06-13 18:09:50 +00:00
|
|
|
&velocities,
|
2019-08-23 10:11:37 +00:00
|
|
|
&physics_states,
|
2019-12-03 06:30:08 +00:00
|
|
|
mountings.maybe(),
|
2019-06-09 14:20:20 +00:00
|
|
|
)
|
|
|
|
.join()
|
|
|
|
{
|
2019-12-03 06:30:08 +00:00
|
|
|
let inputs = &mut controller.inputs;
|
2019-10-15 04:06:14 +00:00
|
|
|
|
2019-12-03 06:30:08 +00:00
|
|
|
// ---------------------------------------
|
|
|
|
// Common actions for multiple states as closure fn's for convenience
|
|
|
|
// Returns a Wield action, or Idle if nothing to wield
|
|
|
|
let try_wield = |stats: &Stats| -> ActionState {
|
|
|
|
// Get weapon to wield
|
|
|
|
if let Some(ItemKind::Tool { kind, .. }) =
|
|
|
|
stats.equipment.main.as_ref().map(|i| &i.kind)
|
|
|
|
{
|
|
|
|
let wield_duration = kind.wield_duration();
|
|
|
|
Wield {
|
|
|
|
time_left: wield_duration,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Idle
|
2019-06-09 19:33:20 +00:00
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
};
|
2019-06-09 14:20:20 +00:00
|
|
|
|
2019-12-03 06:30:08 +00:00
|
|
|
let get_state_from_move_dir = |move_dir: &Vec2<f32>| -> MovementState {
|
|
|
|
if move_dir.magnitude_squared() > 0.0 {
|
|
|
|
Run
|
|
|
|
} else {
|
|
|
|
Stand
|
|
|
|
}
|
2019-08-23 10:11:37 +00:00
|
|
|
};
|
|
|
|
|
2019-12-03 06:30:08 +00:00
|
|
|
// End common actions
|
|
|
|
// ---------------------------------------
|
2019-06-16 15:40:47 +00:00
|
|
|
|
2019-12-03 06:30:08 +00:00
|
|
|
// 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_emitter.emit(ServerEvent::Respawn(entity));
|
|
|
|
}
|
|
|
|
// Or do nothing
|
|
|
|
continue;
|
2019-06-09 14:20:20 +00:00
|
|
|
}
|
2019-12-31 21:37:55 +00:00
|
|
|
|
|
|
|
// Process controller events
|
|
|
|
for event in controller.events.drain(..) {
|
|
|
|
match event {
|
|
|
|
ControlEvent::Mount(mountee_uid) => {
|
|
|
|
if let Some(mountee_entity) =
|
|
|
|
uid_allocator.retrieve_entity_internal(mountee_uid.id())
|
|
|
|
{
|
|
|
|
server_emitter.emit(ServerEvent::Mount(entity, mountee_entity));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ControlEvent::Unmount => server_emitter.emit(ServerEvent::Unmount(entity)),
|
|
|
|
ControlEvent::InventoryManip(manip) => {
|
|
|
|
server_emitter.emit(ServerEvent::InventoryManip(entity, manip))
|
2020-01-02 09:46:15 +00:00
|
|
|
} /*ControlEvent::Respawn => {
|
|
|
|
if state.is_dead {
|
|
|
|
server_emitter.emit(ServerEvent::Respawn(entity)),
|
|
|
|
}
|
|
|
|
}*/
|
2019-12-31 21:37:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-03 06:30:08 +00:00
|
|
|
// If mounted, character state is controlled by mount
|
|
|
|
if mount.is_some() {
|
2019-09-09 19:11:40 +00:00
|
|
|
character.movement = Sit;
|
2019-12-03 06:30:08 +00:00
|
|
|
continue;
|
2019-09-09 19:11:40 +00:00
|
|
|
}
|
|
|
|
|
2019-12-09 14:45:10 +00:00
|
|
|
inputs.update_look_dir();
|
|
|
|
inputs.update_move_dir();
|
2019-12-15 17:44:19 +00:00
|
|
|
|
2019-12-03 06:30:08 +00:00
|
|
|
match (character.action, character.movement) {
|
|
|
|
// Jumping, one frame state that calls jump server event
|
|
|
|
(_, Jump) => {
|
|
|
|
character.movement = Fall;
|
|
|
|
local_emitter.emit(LocalEvent::Jump(entity));
|
|
|
|
}
|
|
|
|
// Charging + Any Movement, prioritizes finishing charge
|
|
|
|
// over movement states
|
|
|
|
(Charge { time_left }, _) => {
|
|
|
|
inputs.update_move_dir();
|
|
|
|
if time_left == Duration::default() || vel.0.magnitude_squared() < 10.0 {
|
|
|
|
character.action = try_wield(stats);
|
|
|
|
} else {
|
|
|
|
character.action = Charge {
|
|
|
|
time_left: time_left
|
|
|
|
.checked_sub(Duration::from_secs_f32(dt.0))
|
|
|
|
.unwrap_or_default(),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if let Some(uid_b) = physics.touch_entity {
|
|
|
|
server_emitter.emit(ServerEvent::Damage {
|
|
|
|
uid: uid_b,
|
|
|
|
change: HealthChange {
|
|
|
|
amount: -20,
|
|
|
|
cause: HealthSource::Attack { by: *uid },
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
character.action = try_wield(stats);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Rolling + Any Movement, prioritizes finishing charge
|
|
|
|
// over movement states
|
2019-12-09 14:45:10 +00:00
|
|
|
(
|
|
|
|
Roll {
|
|
|
|
time_left,
|
|
|
|
was_wielding,
|
|
|
|
},
|
|
|
|
_,
|
|
|
|
) => {
|
2019-12-03 06:30:08 +00:00
|
|
|
if time_left == Duration::default() {
|
2019-12-09 14:45:10 +00:00
|
|
|
if was_wielding {
|
|
|
|
character.action = try_wield(stats);
|
|
|
|
} else {
|
|
|
|
character.action = Idle;
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
} else {
|
|
|
|
character.action = Roll {
|
|
|
|
time_left: time_left
|
|
|
|
.checked_sub(Duration::from_secs_f32(dt.0))
|
|
|
|
.unwrap_or_default(),
|
2019-12-09 14:45:10 +00:00
|
|
|
was_wielding,
|
2019-10-17 20:59:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
// Any Action + Falling
|
|
|
|
(action_state, Fall) => {
|
2019-12-15 17:44:19 +00:00
|
|
|
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
2019-12-31 21:37:55 +00:00
|
|
|
if inputs.glide.is_pressed() && can_glide(body) {
|
2019-12-09 14:45:10 +00:00
|
|
|
character.movement = Glide;
|
|
|
|
continue;
|
|
|
|
}
|
2019-12-15 17:44:19 +00:00
|
|
|
// Try to climb
|
|
|
|
if let (true, Some(_wall_dir)) = (
|
2019-12-31 21:37:55 +00:00
|
|
|
(inputs.climb.is_pressed() | inputs.climb_down.is_pressed())
|
|
|
|
&& can_climb(body),
|
2019-12-15 17:44:19 +00:00
|
|
|
physics.on_wall,
|
|
|
|
) {
|
|
|
|
character.movement = Climb;
|
|
|
|
continue;
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
// Reset to Falling while not standing on ground,
|
|
|
|
// otherwise keep the state given above
|
|
|
|
if !physics.on_ground {
|
|
|
|
if physics.in_fluid {
|
|
|
|
character.movement = Swim;
|
|
|
|
} else {
|
|
|
|
character.movement = Fall;
|
2019-10-17 20:59:36 +00:00
|
|
|
}
|
2019-12-09 14:45:10 +00:00
|
|
|
} else {
|
|
|
|
character.movement = Stand;
|
2019-12-03 06:30:08 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
match action_state {
|
|
|
|
// Unwield if buttons pressed
|
|
|
|
Wield { .. } | Attack { .. } => {
|
|
|
|
if inputs.toggle_wield.is_just_pressed() {
|
|
|
|
character.action = Idle;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Try to wield if any of buttons pressed
|
|
|
|
Idle => {
|
|
|
|
if inputs.primary.is_pressed() || inputs.secondary.is_pressed() {
|
|
|
|
character.action = try_wield(stats);
|
2019-12-15 17:44:19 +00:00
|
|
|
continue;
|
2019-09-16 15:58:40 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
// Cancel blocks
|
|
|
|
Block { .. } => {
|
|
|
|
character.action = try_wield(stats);
|
2019-12-15 17:44:19 +00:00
|
|
|
continue;
|
2019-12-03 06:30:08 +00:00
|
|
|
}
|
|
|
|
// Don't change action
|
|
|
|
Charge { .. } | Roll { .. } => {}
|
2019-09-16 15:58:40 +00:00
|
|
|
}
|
2019-12-15 17:44:19 +00:00
|
|
|
if inputs.primary.is_pressed() {
|
|
|
|
character.action = Self::handle_primary(
|
|
|
|
inputs,
|
|
|
|
character,
|
|
|
|
stats,
|
|
|
|
entity,
|
|
|
|
uid,
|
|
|
|
&mut server_emitter,
|
|
|
|
&mut local_emitter,
|
|
|
|
);
|
|
|
|
} else if inputs.secondary.is_pressed() {
|
|
|
|
character.action = Self::handle_secondary(
|
|
|
|
inputs,
|
|
|
|
character,
|
|
|
|
stats,
|
|
|
|
entity,
|
|
|
|
uid,
|
|
|
|
&mut server_emitter,
|
|
|
|
&mut local_emitter,
|
|
|
|
);
|
|
|
|
}
|
2019-09-16 15:58:40 +00:00
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
// Any Action + Swimming
|
|
|
|
(_action_state, Swim) => {
|
|
|
|
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
|
|
|
|
|
|
|
if !physics.on_ground && physics.in_fluid {
|
|
|
|
character.movement = Swim;
|
|
|
|
}
|
2019-11-29 15:20:35 +00:00
|
|
|
if inputs.primary.is_pressed() {
|
2019-12-03 06:30:08 +00:00
|
|
|
character.action = Self::handle_primary(
|
|
|
|
inputs,
|
|
|
|
character,
|
|
|
|
stats,
|
2019-08-28 20:47:52 +00:00
|
|
|
entity,
|
2019-12-03 06:30:08 +00:00
|
|
|
uid,
|
|
|
|
&mut server_emitter,
|
|
|
|
&mut local_emitter,
|
|
|
|
);
|
|
|
|
} else if inputs.secondary.is_pressed() {
|
|
|
|
character.action = Self::handle_secondary(
|
|
|
|
inputs,
|
|
|
|
character,
|
|
|
|
stats,
|
|
|
|
entity,
|
|
|
|
uid,
|
|
|
|
&mut server_emitter,
|
|
|
|
&mut local_emitter,
|
|
|
|
);
|
2019-08-29 17:39:34 +00:00
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
}
|
|
|
|
// Blocking, restricted look_dir compared to other states
|
|
|
|
(Block { .. }, Stand) | (Block { .. }, Run) => {
|
|
|
|
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
|
|
|
|
|
|
|
if !inputs.secondary.is_pressed() {
|
|
|
|
character.action = try_wield(stats);
|
|
|
|
} else {
|
|
|
|
character.action = Self::handle_secondary(
|
|
|
|
inputs,
|
|
|
|
character,
|
|
|
|
stats,
|
2019-08-29 17:39:34 +00:00
|
|
|
entity,
|
2019-12-03 06:30:08 +00:00
|
|
|
uid,
|
|
|
|
&mut server_emitter,
|
|
|
|
&mut local_emitter,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-12-15 17:44:19 +00:00
|
|
|
if !physics.on_ground {
|
|
|
|
if physics.in_fluid {
|
|
|
|
character.movement = Swim;
|
|
|
|
} else {
|
|
|
|
character.movement = Fall;
|
|
|
|
}
|
2019-08-28 20:47:52 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
// Standing and Running states, typical states :shrug:
|
|
|
|
(action_state, Run) | (action_state, Stand) => {
|
|
|
|
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
|
|
|
// Try to sit
|
|
|
|
if inputs.sit.is_pressed() && physics.on_ground && body.is_humanoid() {
|
|
|
|
character.movement = Sit;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to climb
|
|
|
|
if let (true, Some(_wall_dir)) = (
|
2019-12-31 21:37:55 +00:00
|
|
|
(inputs.climb.is_pressed() | inputs.climb_down.is_pressed())
|
|
|
|
&& can_climb(body),
|
2019-12-03 06:30:08 +00:00
|
|
|
physics.on_wall,
|
|
|
|
) {
|
|
|
|
character.movement = Climb;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to swim
|
2019-12-15 17:44:19 +00:00
|
|
|
if !physics.on_ground {
|
|
|
|
if physics.in_fluid {
|
|
|
|
character.movement = Swim;
|
|
|
|
} else {
|
|
|
|
character.movement = Fall;
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// While on ground ...
|
|
|
|
if physics.on_ground {
|
|
|
|
// Try to jump
|
2019-12-15 17:44:19 +00:00
|
|
|
if inputs.jump.is_pressed() {
|
2019-12-03 06:30:08 +00:00
|
|
|
character.movement = Jump;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to charge
|
|
|
|
if inputs.charge.is_pressed() && !inputs.charge.is_held_down() {
|
2019-11-22 00:53:28 +00:00
|
|
|
if energy
|
2020-01-19 19:19:44 +00:00
|
|
|
.get_mut_unchecked()
|
2019-11-22 00:53:28 +00:00
|
|
|
.try_change_by(-CHARGE_COST, EnergySource::CastSpell)
|
|
|
|
.is_ok()
|
|
|
|
{
|
|
|
|
character.action = Charge {
|
|
|
|
time_left: Duration::from_millis(250),
|
|
|
|
}
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to roll
|
|
|
|
if character.movement == Run
|
|
|
|
&& inputs.roll.is_pressed()
|
|
|
|
&& body.is_humanoid()
|
|
|
|
{
|
|
|
|
character.action = Roll {
|
|
|
|
time_left: ROLL_DURATION,
|
2019-12-09 14:45:10 +00:00
|
|
|
was_wielding: character.action.is_wield(),
|
2019-12-03 06:30:08 +00:00
|
|
|
};
|
|
|
|
continue;
|
2019-10-11 04:30:34 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
// While not on ground ...
|
|
|
|
else {
|
|
|
|
// Try to glide
|
2019-12-31 21:37:55 +00:00
|
|
|
if physics.on_wall == None && inputs.glide.is_pressed() && can_glide(&body)
|
2019-12-03 06:30:08 +00:00
|
|
|
{
|
|
|
|
character.movement = Glide;
|
|
|
|
continue;
|
|
|
|
}
|
2019-10-17 20:59:36 +00:00
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
|
|
|
|
// 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);
|
2019-12-09 14:45:10 +00:00
|
|
|
continue;
|
2019-10-22 20:58:27 +00:00
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
Charge { .. } | Roll { .. } | Block { .. } => {}
|
2019-10-22 20:58:27 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
if inputs.primary.is_pressed() {
|
|
|
|
character.action = Self::handle_primary(
|
|
|
|
inputs,
|
|
|
|
character,
|
|
|
|
stats,
|
|
|
|
entity,
|
|
|
|
uid,
|
|
|
|
&mut server_emitter,
|
|
|
|
&mut local_emitter,
|
|
|
|
);
|
|
|
|
} else if inputs.secondary.is_pressed() {
|
|
|
|
character.action = Self::handle_secondary(
|
|
|
|
inputs,
|
|
|
|
character,
|
|
|
|
stats,
|
|
|
|
entity,
|
|
|
|
uid,
|
|
|
|
&mut server_emitter,
|
|
|
|
&mut local_emitter,
|
|
|
|
);
|
2019-10-22 20:58:27 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
// Sitting
|
2019-12-09 14:45:10 +00:00
|
|
|
(_, Sit) => {
|
|
|
|
character.action = Idle;
|
2019-12-03 06:30:08 +00:00
|
|
|
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
|
|
|
|
|
|
|
// character.movement will be Stand after updating when
|
|
|
|
// no movement has occurred
|
|
|
|
if character.movement == Stand {
|
|
|
|
character.movement = Sit;
|
|
|
|
}
|
2019-12-15 17:44:19 +00:00
|
|
|
if inputs.jump.is_pressed() {
|
2019-12-03 06:30:08 +00:00
|
|
|
character.movement = Jump;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if !physics.on_ground {
|
|
|
|
character.movement = Fall;
|
2019-09-07 09:46:30 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
// Any Action + Gliding, shouldnt care about action,
|
|
|
|
// because should be Idle
|
|
|
|
(_, Glide) => {
|
|
|
|
character.action = Idle;
|
2019-08-24 16:38:59 +00:00
|
|
|
|
2019-12-03 06:30:08 +00:00
|
|
|
if !inputs.glide.is_pressed() {
|
|
|
|
character.movement = Fall;
|
2019-12-31 21:37:55 +00:00
|
|
|
} else if let (Some(_wall_dir), true) = (physics.on_wall, can_climb(body)) {
|
2019-12-15 17:44:19 +00:00
|
|
|
character.movement = Climb;
|
2019-12-03 06:30:08 +00:00
|
|
|
}
|
2019-09-09 19:11:40 +00:00
|
|
|
|
2019-12-03 06:30:08 +00:00
|
|
|
if physics.on_ground {
|
|
|
|
character.movement = Stand
|
|
|
|
}
|
2019-09-09 19:11:40 +00:00
|
|
|
}
|
2019-12-03 06:30:08 +00:00
|
|
|
// Any Action + Climbing, shouldnt care about action,
|
|
|
|
// because should be Idle
|
|
|
|
(_, Climb) => {
|
2019-12-09 14:45:10 +00:00
|
|
|
character.action = Idle;
|
2019-12-03 06:30:08 +00:00
|
|
|
if let None = physics.on_wall {
|
2019-12-09 14:45:10 +00:00
|
|
|
if inputs.jump.is_pressed() {
|
2019-12-03 06:30:08 +00:00
|
|
|
character.movement = Jump;
|
|
|
|
} else {
|
|
|
|
character.movement = Fall;
|
|
|
|
}
|
|
|
|
}
|
2019-12-09 14:45:10 +00:00
|
|
|
if physics.on_ground {
|
|
|
|
character.movement = Stand;
|
|
|
|
}
|
|
|
|
} // In case of adding new states
|
|
|
|
// (_, _) => {
|
|
|
|
// println!("UNKNOWN STATE");
|
|
|
|
// character.action = Idle;
|
|
|
|
// character.movement = Fall;
|
|
|
|
// }
|
2019-12-03 06:30:08 +00:00
|
|
|
};
|
2019-06-09 14:20:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-12-31 21:37:55 +00:00
|
|
|
|
|
|
|
fn can_glide(body: &Body) -> bool {
|
|
|
|
body.is_humanoid()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn can_climb(body: &Body) -> bool {
|
|
|
|
body.is_humanoid()
|
|
|
|
}
|