mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
feat: weapon-type dependent wield and attack durations
also some controller.rs cleanup
This commit is contained in:
@ -1,388 +1,641 @@
|
||||
use super::{
|
||||
combat::{ATTACK_DURATION, WIELD_DURATION},
|
||||
movement::ROLL_DURATION,
|
||||
};
|
||||
use super::movement::ROLL_DURATION;
|
||||
use crate::{
|
||||
comp::{
|
||||
self, item, projectile, ActionState::*, Body, CharacterState, ControlEvent, Controller,
|
||||
HealthChange, HealthSource, ItemKind, MovementState::*, PhysicsState, Projectile, Stats,
|
||||
Vel,
|
||||
self, item, projectile, ActionState, ActionState::*, Body, CharacterState, ControlEvent,
|
||||
Controller, ControllerInputs, HealthChange, HealthSource, ItemKind, Mounting,
|
||||
MovementState, MovementState::*, PhysicsState, Projectile, Stats, Vel,
|
||||
},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
event::{Emitter, EventBus, LocalEvent, ServerEvent},
|
||||
state::DeltaTime,
|
||||
};
|
||||
use specs::{
|
||||
saveload::{Marker, MarkerAllocator},
|
||||
Entities, Join, Read, ReadStorage, System, WriteStorage,
|
||||
Entities, Entity, Join, Read, ReadStorage, System, WriteStorage,
|
||||
};
|
||||
use sphynx::{Uid, UidAllocator};
|
||||
use std::time::Duration;
|
||||
use vek::*;
|
||||
|
||||
/// This system is responsible for validating controller inputs
|
||||
/// # 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?_
|
||||
pub struct Sys;
|
||||
|
||||
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,
|
||||
],
|
||||
time_left: Duration::from_secs(30),
|
||||
},
|
||||
});
|
||||
character.action
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Read<'a, UidAllocator>,
|
||||
Entities<'a>,
|
||||
Read<'a, UidAllocator>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Read<'a, EventBus<LocalEvent>>,
|
||||
Read<'a, DeltaTime>,
|
||||
WriteStorage<'a, Controller>,
|
||||
WriteStorage<'a, CharacterState>,
|
||||
ReadStorage<'a, Stats>,
|
||||
ReadStorage<'a, Body>,
|
||||
ReadStorage<'a, Vel>,
|
||||
ReadStorage<'a, PhysicsState>,
|
||||
ReadStorage<'a, Uid>,
|
||||
WriteStorage<'a, CharacterState>,
|
||||
ReadStorage<'a, Mounting>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(
|
||||
uid_allocator,
|
||||
entities,
|
||||
uid_allocator,
|
||||
server_bus,
|
||||
local_bus,
|
||||
dt,
|
||||
mut controllers,
|
||||
mut character_states,
|
||||
stats,
|
||||
bodies,
|
||||
velocities,
|
||||
physics_states,
|
||||
uids,
|
||||
mut character_states,
|
||||
mountings,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
let mut local_emitter = local_bus.emitter();
|
||||
|
||||
for (entity, uid, controller, stats, body, vel, physics, mut character) in (
|
||||
for (entity, uid, controller, mut character, stats, body, vel, physics, mount) in (
|
||||
&entities,
|
||||
&uids,
|
||||
&mut controllers,
|
||||
&mut character_states,
|
||||
&stats,
|
||||
&bodies,
|
||||
&velocities,
|
||||
&physics_states,
|
||||
&mut character_states,
|
||||
mountings.maybe(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let mut inputs = &mut controller.inputs;
|
||||
let inputs = &mut controller.inputs;
|
||||
|
||||
// ---------------------------------------
|
||||
// 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
|
||||
}
|
||||
};
|
||||
|
||||
let get_state_from_move_dir = |move_dir: &Vec2<f32>| -> MovementState {
|
||||
if move_dir.magnitude_squared() > 0.0 {
|
||||
Run
|
||||
} else {
|
||||
Stand
|
||||
}
|
||||
};
|
||||
|
||||
// End common actions
|
||||
// ---------------------------------------
|
||||
|
||||
// Being dead overrides all other states
|
||||
if stats.is_dead {
|
||||
// Respawn
|
||||
if inputs.respawn.is_pressed() {
|
||||
// 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;
|
||||
}
|
||||
// If mounted, character state is controlled by mount
|
||||
if mount.is_some() {
|
||||
character.movement = Sit;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Move
|
||||
inputs.move_dir = if inputs.move_dir.magnitude_squared() > 1.0 {
|
||||
inputs.move_dir.normalized()
|
||||
} else {
|
||||
inputs.move_dir
|
||||
match (character.action, character.movement) {
|
||||
// Jumping, one frame state that calls jump server event
|
||||
(_, Jump) => {
|
||||
inputs.update_look_dir();
|
||||
inputs.update_move_dir();
|
||||
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
||||
|
||||
character.movement = Fall;
|
||||
local_emitter.emit(LocalEvent::Jump(entity));
|
||||
}
|
||||
// Charging + Any Movement, prioritizes finishing charge
|
||||
// over movement states
|
||||
(Charge { time_left }, _) => {
|
||||
println!("{:?}", character);
|
||||
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
|
||||
(Roll { time_left }, _) => {
|
||||
if time_left == Duration::default() {
|
||||
character.action = try_wield(stats);
|
||||
} else {
|
||||
character.action = Roll {
|
||||
time_left: time_left
|
||||
.checked_sub(Duration::from_secs_f32(dt.0))
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
// Any Action + Falling
|
||||
(action_state, Fall) => {
|
||||
inputs.update_move_dir();
|
||||
inputs.update_look_dir();
|
||||
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
if inputs.glide.is_pressed() && !inputs.glide.is_held_down() {
|
||||
character.movement = Glide;
|
||||
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);
|
||||
}
|
||||
}
|
||||
// Cancel blocks
|
||||
Block { .. } => {
|
||||
character.action = try_wield(stats);
|
||||
}
|
||||
// Don't change action
|
||||
Charge { .. } | Roll { .. } => {}
|
||||
}
|
||||
}
|
||||
// Any Action + Swimming
|
||||
(_action_state, Swim) => {
|
||||
inputs.update_move_dir();
|
||||
inputs.update_look_dir();
|
||||
character.movement = get_state_from_move_dir(&inputs.move_dir);
|
||||
|
||||
if !physics.on_ground && physics.in_fluid {
|
||||
character.movement = Swim;
|
||||
}
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Blocking, restricted look_dir compared to other states
|
||||
(Block { .. }, Stand) | (Block { .. }, Run) => {
|
||||
inputs.update_move_dir();
|
||||
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,
|
||||
entity,
|
||||
uid,
|
||||
&mut server_emitter,
|
||||
&mut local_emitter,
|
||||
);
|
||||
}
|
||||
|
||||
if !physics.on_ground && physics.in_fluid {
|
||||
character.movement = Swim;
|
||||
}
|
||||
}
|
||||
// Standing and Running states, typical states :shrug:
|
||||
(action_state, Run) | (action_state, Stand) => {
|
||||
inputs.update_move_dir();
|
||||
inputs.update_look_dir();
|
||||
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)) = (
|
||||
inputs.climb.is_pressed() | inputs.climb_down.is_pressed()
|
||||
&& body.is_humanoid(),
|
||||
physics.on_wall,
|
||||
) {
|
||||
character.movement = Climb;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to swim
|
||||
if !physics.on_ground && physics.in_fluid {
|
||||
character.movement = Swim;
|
||||
continue;
|
||||
}
|
||||
|
||||
// While on ground ...
|
||||
if physics.on_ground {
|
||||
// Try to jump
|
||||
if inputs.jump.is_pressed() && !inputs.jump.is_held_down() {
|
||||
character.movement = Jump;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to charge
|
||||
if inputs.charge.is_pressed() && !inputs.charge.is_held_down() {
|
||||
character.action = Charge {
|
||||
time_left: Duration::from_millis(250),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to roll
|
||||
if character.movement == Run
|
||||
&& inputs.roll.is_pressed()
|
||||
&& body.is_humanoid()
|
||||
{
|
||||
character.action = Roll {
|
||||
time_left: ROLL_DURATION,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// While not on ground ...
|
||||
else {
|
||||
// Try to glide
|
||||
if physics.on_wall == None
|
||||
&& inputs.glide.is_pressed()
|
||||
&& !inputs.glide.is_held_down()
|
||||
&& body.is_humanoid()
|
||||
{
|
||||
character.movement = Glide;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
Charge { .. } | Roll { .. } | Block { .. } => {}
|
||||
}
|
||||
}
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
// Sitting
|
||||
(Idle, Sit) => {
|
||||
inputs.update_move_dir();
|
||||
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;
|
||||
}
|
||||
if inputs.jump.is_pressed() && !inputs.jump.is_held_down() {
|
||||
character.movement = Jump;
|
||||
continue;
|
||||
}
|
||||
if !physics.on_ground {
|
||||
character.movement = Fall;
|
||||
}
|
||||
}
|
||||
// Any Action + Gliding, shouldnt care about action,
|
||||
// because should be Idle
|
||||
(_, Glide) => {
|
||||
character.action = Idle;
|
||||
|
||||
inputs.update_look_dir();
|
||||
inputs.update_move_dir();
|
||||
|
||||
if !inputs.glide.is_pressed() {
|
||||
character.movement = Fall;
|
||||
} else if let Some(_wall_dir) = physics.on_wall {
|
||||
character.movement = Fall;
|
||||
}
|
||||
|
||||
if physics.on_ground {
|
||||
character.movement = Stand
|
||||
}
|
||||
}
|
||||
// Any Action + Climbing, shouldnt care about action,
|
||||
// because should be Idle
|
||||
(_, Climb) => {
|
||||
if let None = physics.on_wall {
|
||||
if physics.on_ground {
|
||||
character.movement = Stand;
|
||||
} else if inputs.jump.is_pressed() && !inputs.jump.is_held_down() {
|
||||
character.movement = Jump;
|
||||
} else {
|
||||
character.movement = Fall;
|
||||
}
|
||||
}
|
||||
}
|
||||
// In case of adding new states
|
||||
(_, _) => {
|
||||
println!("UNKNOWN STATE");
|
||||
character.action = Idle;
|
||||
character.movement = Fall;
|
||||
}
|
||||
};
|
||||
|
||||
if character.movement == Stand && inputs.move_dir.magnitude_squared() > 0.0 {
|
||||
character.movement = Run;
|
||||
} else if character.movement == Run && inputs.move_dir.magnitude_squared() == 0.0 {
|
||||
character.movement = Stand;
|
||||
}
|
||||
|
||||
// Look
|
||||
inputs.look_dir = inputs
|
||||
.look_dir
|
||||
.try_normalized()
|
||||
.unwrap_or(inputs.move_dir.into());
|
||||
|
||||
// Glide
|
||||
// TODO: Check for glide ability/item
|
||||
if inputs.glide.is_pressed()
|
||||
&& !physics.on_ground
|
||||
&& (character.action == Idle || character.action.is_wield())
|
||||
&& character.movement == Jump
|
||||
&& body.is_humanoid()
|
||||
{
|
||||
character.movement = Glide;
|
||||
} else if !inputs.glide.is_pressed() && character.movement == Glide {
|
||||
// character.movement = Jump;
|
||||
}
|
||||
|
||||
// Sit
|
||||
if inputs.sit.is_pressed()
|
||||
&& physics.on_ground
|
||||
&& character.action == Idle
|
||||
&& character.movement != Sit
|
||||
&& body.is_humanoid()
|
||||
{
|
||||
character.movement = Sit;
|
||||
} else if character.movement == Sit
|
||||
&& (inputs.move_dir.magnitude_squared() > 0.0 || !physics.on_ground)
|
||||
{
|
||||
character.movement = Run;
|
||||
}
|
||||
|
||||
// Wield
|
||||
if inputs.primary.is_pressed()
|
||||
&& character.action == Idle
|
||||
&& (character.movement == Stand || character.movement == Run)
|
||||
{
|
||||
character.action = Wield {
|
||||
time_left: WIELD_DURATION,
|
||||
};
|
||||
}
|
||||
|
||||
match stats.equipment.main.as_ref().map(|i| &i.kind) {
|
||||
Some(ItemKind::Tool {
|
||||
kind: item::Tool::Bow,
|
||||
power,
|
||||
..
|
||||
}) => {
|
||||
if inputs.primary.is_pressed()
|
||||
&& (character.movement == Stand
|
||||
|| character.movement == Run
|
||||
|| character.movement == Jump)
|
||||
{
|
||||
if let Wield { time_left } = character.action {
|
||||
if time_left == Duration::default() {
|
||||
// Immediately end the wield
|
||||
character.action = Idle;
|
||||
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,
|
||||
],
|
||||
time_left: Duration::from_secs(30),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(ItemKind::Tool {
|
||||
kind: item::Tool::Staff,
|
||||
power,
|
||||
..
|
||||
}) => {
|
||||
// Melee Attack
|
||||
if inputs.primary.is_pressed()
|
||||
&& (character.movement == Stand
|
||||
|| character.movement == Run
|
||||
|| character.movement == Jump)
|
||||
{
|
||||
if let Wield { time_left } = character.action {
|
||||
if time_left == Duration::default() {
|
||||
character.action = Attack {
|
||||
time_left: ATTACK_DURATION,
|
||||
applied: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
// Magical Bolt
|
||||
if inputs.secondary.is_pressed()
|
||||
&& (
|
||||
character.movement == Stand
|
||||
//|| character.movement == Run
|
||||
//|| character.movement == Jump
|
||||
)
|
||||
{
|
||||
if let Wield { time_left } = character.action {
|
||||
if time_left == Duration::default() {
|
||||
character.action = Attack {
|
||||
time_left: ATTACK_DURATION,
|
||||
applied: false,
|
||||
};
|
||||
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),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(ItemKind::Tool {
|
||||
kind: item::Tool::Debug(item::Debug::Boost),
|
||||
..
|
||||
}) => {
|
||||
if inputs.primary.is_pressed() {
|
||||
local_emitter.emit(LocalEvent::Boost {
|
||||
entity,
|
||||
vel: inputs.look_dir * 7.0,
|
||||
});
|
||||
}
|
||||
if inputs.secondary.is_pressed() {
|
||||
// Go upward
|
||||
local_emitter.emit(LocalEvent::Boost {
|
||||
entity,
|
||||
vel: Vec3::new(0.0, 0.0, 7.0),
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(ItemKind::Tool {
|
||||
kind: item::Tool::Debug(item::Debug::Possess),
|
||||
..
|
||||
}) => {
|
||||
if inputs.primary.is_pressed()
|
||||
&& (character.movement == Stand
|
||||
|| character.movement == Run
|
||||
|| character.movement == Jump)
|
||||
{
|
||||
if let Wield { time_left } = character.action {
|
||||
if time_left == Duration::default() {
|
||||
// Immediately end the wield
|
||||
character.action = Idle;
|
||||
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),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Block
|
||||
if inputs.secondary.is_pressed()
|
||||
&& (character.movement == Stand || character.movement == Run)
|
||||
&& character.action.is_wield()
|
||||
{
|
||||
character.action = Block {
|
||||
time_left: Duration::from_secs(5),
|
||||
};
|
||||
} else if !inputs.secondary.is_pressed() && character.action.is_block() {
|
||||
character.action = Wield {
|
||||
time_left: Duration::default(),
|
||||
};
|
||||
}
|
||||
}
|
||||
// All other tools
|
||||
Some(ItemKind::Tool { .. }) => {
|
||||
// Attack
|
||||
if inputs.primary.is_pressed()
|
||||
&& (character.movement == Stand
|
||||
|| character.movement == Run
|
||||
|| character.movement == Jump)
|
||||
{
|
||||
if let Wield { time_left } = character.action {
|
||||
if time_left == Duration::default() {
|
||||
character.action = Attack {
|
||||
time_left: ATTACK_DURATION,
|
||||
applied: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Block
|
||||
if inputs.secondary.is_pressed()
|
||||
&& (character.movement == Stand || character.movement == Run)
|
||||
&& character.action.is_wield()
|
||||
{
|
||||
character.action = Block {
|
||||
time_left: Duration::from_secs(5),
|
||||
};
|
||||
} else if !inputs.secondary.is_pressed() && character.action.is_block() {
|
||||
character.action = Wield {
|
||||
time_left: Duration::default(),
|
||||
};
|
||||
}
|
||||
}
|
||||
None => {
|
||||
// Attack
|
||||
if inputs.primary.is_pressed()
|
||||
&& (character.movement == Stand
|
||||
|| character.movement == Run
|
||||
|| character.movement == Jump)
|
||||
&& !character.action.is_attack()
|
||||
{
|
||||
character.action = Attack {
|
||||
time_left: ATTACK_DURATION,
|
||||
applied: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Roll
|
||||
if inputs.roll.is_pressed()
|
||||
&& (character.action == Idle || character.action.is_wield())
|
||||
&& character.movement == Run
|
||||
&& physics.on_ground
|
||||
{
|
||||
character.movement = Roll {
|
||||
time_left: ROLL_DURATION,
|
||||
};
|
||||
}
|
||||
|
||||
// Jump
|
||||
if (inputs.jump.is_pressed() && !inputs.jump.is_held_down())
|
||||
&& physics.on_ground
|
||||
&& vel.0.z <= 0.0
|
||||
&& !character.movement.is_roll()
|
||||
|| (inputs.jump.is_pressed() && character.movement == Swim)
|
||||
{
|
||||
local_emitter.emit(LocalEvent::Jump(entity));
|
||||
}
|
||||
|
||||
// Wall leap
|
||||
if inputs.wall_leap.is_pressed() {
|
||||
if let (Some(_wall_dir), Climb) = (physics.on_wall, character.movement) {
|
||||
//local_emitter.emit(LocalEvent::WallLeap { entity, wall_dir });
|
||||
}
|
||||
}
|
||||
|
||||
if let (true, Wield { .. }) = (inputs.toggle_wield.is_pressed(), character.action) {
|
||||
character.action = Idle;
|
||||
}
|
||||
|
||||
// Process controller events
|
||||
// Process other controller events
|
||||
for event in controller.events.drain(..) {
|
||||
match event {
|
||||
ControlEvent::Mount(mountee_uid) => {
|
||||
|
Reference in New Issue
Block a user