Re add combat

This commit is contained in:
Adam Whitehurst 2020-02-11 07:42:17 -08:00
parent 0bc07a0835
commit d383abf950
9 changed files with 146 additions and 175 deletions

View File

@ -1,13 +1,13 @@
use crate::{
comp::{AbilityPool, Body, ControllerInputs, Ori, PhysicsState, Pos, Stats, Vel},
comp::{AbilityPool, Body, ControllerInputs, Ori, PhysicsState, Pos, Stats, ToolData, Vel},
event::{LocalEvent, ServerEvent},
state::DeltaTime,
states::*,
sync::Uid,
};
use serde::{Deserialize, Serialize};
use specs::{Component, Entity, FlaggedStorage, HashMapStorage, LazyUpdate};
use std::collections::VecDeque;
use specs::{Component, Entity, FlaggedStorage, HashMapStorage, LazyUpdate, VecStorage};
use std::{collections::VecDeque, time::Duration};
pub struct EcsStateData<'a> {
pub entity: &'a Entity,
@ -127,3 +127,30 @@ impl Default for CharacterState {
impl Component for CharacterState {
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct Attacking {
pub weapon: ToolData,
pub time_active: Duration,
}
impl Attacking {
pub fn remaining_duration(&self) -> Duration {
self.weapon
.attack_duration()
.checked_sub(self.time_active)
.unwrap_or_default()
}
pub fn tick_time_active(&mut self, dt: Duration) {
self.time_active = self.time_active.checked_add(dt).unwrap_or_default();
}
pub fn can_apply_damage(&self) -> bool {
(self.time_active > self.weapon.attack_buildup_duration())
}
}
impl Component for Attacking {
type Storage = VecStorage<Self>;
}

View File

@ -23,7 +23,7 @@ pub use body::{
biped_large, bird_medium, bird_small, critter, dragon, fish_medium, fish_small, humanoid,
object, quadruped_medium, quadruped_small, AllBodies, Body, BodyData,
};
pub use character_state::{CharacterState, EcsStateData, StateUpdate};
pub use character_state::{Attacking, CharacterState, EcsStateData, StateUpdate};
pub use controller::{
ControlEvent, Controller, ControllerInputs, Input, InputState, InventoryManip, MountState,
Mounting,

View File

@ -23,6 +23,7 @@ sum_type! {
Sticky(comp::Sticky),
AbilityAction(comp::AbilityAction),
AbilityPool(comp::AbilityPool),
Attacking(comp::Attacking),
}
}
// Automatically derive From<T> for EcsCompPhantom
@ -45,6 +46,7 @@ sum_type! {
Sticky(PhantomData<comp::Sticky>),
AbilityAction(PhantomData<comp::AbilityAction>),
AbilityPool(PhantomData<comp::AbilityPool>),
Attacking(PhantomData<comp::Attacking>),
}
}
impl sync::CompPacket for EcsCompPacket {
@ -67,6 +69,7 @@ impl sync::CompPacket for EcsCompPacket {
EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::AbilityAction(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::AbilityPool(comp) => sync::handle_insert(comp, entity, world),
EcsCompPacket::Attacking(comp) => sync::handle_insert(comp, entity, world),
}
}
@ -87,6 +90,7 @@ impl sync::CompPacket for EcsCompPacket {
EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::AbilityAction(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::AbilityPool(comp) => sync::handle_modify(comp, entity, world),
EcsCompPacket::Attacking(comp) => sync::handle_modify(comp, entity, world),
}
}
@ -109,10 +113,11 @@ impl sync::CompPacket for EcsCompPacket {
EcsCompPhantom::Sticky(_) => sync::handle_remove::<comp::Sticky>(entity, world),
EcsCompPhantom::AbilityAction(_) => {
sync::handle_remove::<comp::AbilityAction>(entity, world)
}
},
EcsCompPhantom::AbilityPool(_) => {
sync::handle_remove::<comp::AbilityPool>(entity, world)
}
},
EcsCompPhantom::Attacking(_) => sync::handle_remove::<comp::Attacking>(entity, world),
}
}
}

View File

@ -122,6 +122,7 @@ impl State {
ecs.register::<comp::Mass>();
ecs.register::<comp::Sticky>();
ecs.register::<comp::Gravity>();
ecs.register::<comp::Attacking>();
// Register components send from clients -> server
ecs.register::<comp::Controller>();

View File

@ -1,13 +1,15 @@
use crate::comp::{CharacterState, EcsStateData, ItemKind::Tool, StateUpdate, ToolData};
use crate::states::StateHandler;
use std::collections::VecDeque;
use std::time::Duration;
use crate::{
comp::{Attacking, CharacterState, EcsStateData, ItemKind::Tool, StateUpdate, ToolData},
states::{utils, StateHandler},
};
use std::{collections::VecDeque, time::Duration};
#[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct State {
/// How long the state has until exitting
pub remaining_duration: Duration,
///Whether damage can be applied
pub can_apply_damage: bool,
}
impl StateHandler for State {
@ -20,6 +22,7 @@ impl StateHandler for State {
};
Self {
remaining_duration: tool_data.attack_duration(),
can_apply_damage: false,
}
}
@ -33,18 +36,26 @@ impl StateHandler for State {
server_events: VecDeque::new(),
};
// Tick down remaining_duration
update.character = CharacterState::BasicAttack(Some(State {
remaining_duration: self
.remaining_duration
.checked_sub(Duration::from_secs_f32(ecs_data.dt.0))
.unwrap_or_default(),
}));
// // Tick down
// update.character = CharacterState::BasicAttack(Some(State {
// remaining_duration: self
// .remaining_duration
// .checked_sub(Duration::from_secs_f32(ecs_data.dt.0))
// .unwrap_or_default(),
// can_apply_damage: if let Some(Tool(data)) =
// ecs_data.stats.equipment.main.as_ref().map(|i| i.kind)
// {
// (self.remaining_duration < data.attack_recover_duration())
// } else {
// false
// },
// }));
// Check if attack duration has expired
if self.remaining_duration == Duration::default() {
update.character = CharacterState::Wielded(None);
}
// // Check if attack duration has expired
// if self.remaining_duration == Duration::default() {
// update.character = CharacterState::Wielded(None);
// ecs_data.updater.remove::<Attacking>(*ecs_data.entity);
// }
update
}

View File

@ -13,49 +13,14 @@ use crate::comp::{EcsStateData, StateUpdate};
/// ## A type for implementing State Handling Behavior.
///
/// Called by state machines' update functions to allow current states to handle updating
/// their parent machine's current state.
/// Called by state machines' update functions to allow current states to handle
/// updating their parent machine's current state.
///
/// Structures must implement a `handle()` fn to handle update behavior, and a `new()` for
/// instantiating new instances of a state. `handle()` function recieves `EcsStateData`, a struct
/// of readonly ECS Component data, and returns a `StateUpdate` tuple, with new components that will
/// overwrite an entitie's old components.
///
/// ## Example Implementation:
/// ```
/// use crate::states::utils;
///
/// #[derive(Clone, Copy, Default, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
/// pub struct RunState {
/// active_duration: Duration,
/// }
///
/// impl StateHandler for RunState {
/// fn new(ecs_data: &EcsStateData) -> Self {
/// Self {
/// active_duration: Duration::default(),
/// }
/// }
///
/// fn handle(&self, ecs_data: &EcsStateData) -> StateUpdate {
/// let mut update = StateUpdate {
/// character: *ecs_data.character,
/// pos: *ecs_data.pos,
/// vel: *ecs_data.vel,
/// ori: *ecs_data.ori,
/// };
///
/// // -- snip --
/// // Updates; checks for gliding, climbing, etc.
///
/// // Update based on groundedness
/// update.character.move_state =
/// utils::determine_move_from_grounded_state(ecs_data.physics, ecs_data.inputs);
///
/// update
/// }
/// }
/// ```
/// Structures must implement a `handle()` fn to handle update behavior, and a
/// `new()` for instantiating new instances of a state. `handle()` function
/// recieves `EcsStateData`, a struct of readonly ECS Component data, and
/// returns a `StateUpdate` tuple, with new components that will overwrite an
/// entitie's old components.
pub trait StateHandler: Default {
fn handle(&self, ecs_data: &EcsStateData) -> StateUpdate;
fn new(ecs_data: &EcsStateData) -> Self;

View File

@ -1,7 +1,8 @@
use crate::{
comp::{CharacterState, EcsStateData, ItemKind::Tool, StateUpdate},
comp::{Attacking, CharacterState, EcsStateData, ItemKind::Tool, StateUpdate},
event::LocalEvent,
};
use std::time::Duration;
use vek::vec::{Vec2, Vec3};
pub fn handle_move_dir(ecs_data: &EcsStateData, update: &mut StateUpdate) {
@ -96,9 +97,13 @@ pub fn handle_jump(ecs_data: &EcsStateData, update: &mut StateUpdate) {
pub fn handle_primary(ecs_data: &EcsStateData, update: &mut StateUpdate) {
if let Some(state) = ecs_data.ability_pool.primary {
if let CharacterState::Wielded(_) = update.character {
if ecs_data.inputs.primary.is_pressed() {
update.character = state;
if ecs_data.inputs.primary.is_pressed() {
update.character = state;
if let Some(Tool(data)) = ecs_data.stats.equipment.main.as_ref().map(|i| i.kind) {
ecs_data.updater.insert(*ecs_data.entity, Attacking {
weapon: data,
time_active: Duration::default(),
});
}
}
}

View File

@ -1,7 +1,7 @@
use crate::{
comp::{
ActionState::*, Body, CharacterState, Controller, HealthChange, HealthSource, Item,
ItemKind, Ori, Pos, Scale, Stats,
Attacking, Body, CharacterState, Controller, HealthChange, HealthSource, Item, ItemKind,
Ori, Pos, Scale, Stats,
},
event::{EventBus, LocalEvent, ServerEvent},
state::DeltaTime,
@ -33,6 +33,7 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, Controller>,
ReadStorage<'a, Body>,
ReadStorage<'a, Stats>,
WriteStorage<'a, Attacking>,
WriteStorage<'a, CharacterState>,
);
@ -50,14 +51,12 @@ impl<'a> System<'a> for Sys {
controllers,
bodies,
stats,
mut attacking_storage,
mut character_states,
): Self::SystemData,
) {
// let mut server_emitter = server_bus.emitter();
// let mut _local_emitter = local_bus.emitter();
// Attacks
for (entity, uid, pos, ori, scale_maybe, _, attacker_stats) in (
for (entity, uid, pos, ori, scale_maybe, _, attacker_stats, attack) in (
&entities,
&uids,
&positions,
@ -65,115 +64,70 @@ impl<'a> System<'a> for Sys {
scales.maybe(),
&controllers,
&stats,
&mut attacking_storage,
)
.join()
{
let recover_duration = if let Some(Item {
kind: ItemKind::Tool { kind, .. },
..
}) = attacker_stats.equipment.main
{
kind.attack_recover_duration()
} else {
Duration::from_millis(250)
};
attack.tick_time_active(Duration::from_secs_f32(dt.0));
// let (deal_damage, should_end) = if let Some(Attack { time_left, applied }) =
// &mut character_states.get_mut(entity).map(|c| &mut c.action)
// {
// *time_left = time_left
// .checked_sub(Duration::from_secs_f32(dt.0))
// .unwrap_or_default();
// if !*applied && recover_duration > *time_left {
// *applied = true;
// (true, false)
// } else if *time_left == Duration::default() {
// (false, true)
// } else {
// (false, false)
// }
// } else {
// (false, false)
// };
if attack.can_apply_damage() {
// Go through all other entities
for (b, uid_b, pos_b, ori_b, scale_b_maybe, character_b, stats_b, body_b) in (
&entities,
&uids,
&positions,
&orientations,
scales.maybe(),
&character_states,
&stats,
&bodies,
)
.join()
{
// 2D versions
let pos2 = Vec2::from(pos.0);
let pos_b2: Vec2<f32> = Vec2::from(pos_b.0);
let ori2 = Vec2::from(ori.0);
// if deal_damage {
// if let Some(Attack { .. }) = &character_states.get(entity).map(|c| c.action) {
// // Go through all other entities
// for (b, uid_b, pos_b, ori_b, scale_b_maybe, character_b, stats_b, body_b) in (
// &entities,
// &uids,
// &positions,
// &orientations,
// scales.maybe(),
// &character_states,
// &stats,
// &bodies,
// )
// .join()
// {
// // 2D versions
// let pos2 = Vec2::from(pos.0);
// let pos_b2: Vec2<f32> = Vec2::from(pos_b.0);
// let ori2 = Vec2::from(ori.0);
// Scales
let scale = scale_maybe.map_or(1.0, |s| s.0);
let scale_b = scale_b_maybe.map_or(1.0, |s| s.0);
let rad_b = body_b.radius() * scale_b;
// // Scales
// let scale = scale_maybe.map_or(1.0, |s| s.0);
// let scale_b = scale_b_maybe.map_or(1.0, |s| s.0);
// let rad_b = body_b.radius() * scale_b;
// Check if it is a hit
if entity != b
&& !stats_b.is_dead
// Spherical wedge shaped attack field
&& pos.0.distance_squared(pos_b.0) < (rad_b + scale * ATTACK_RANGE).powi(2)
&& ori2.angle_between(pos_b2 - pos2) < ATTACK_ANGLE.to_radians() / 2.0 + (rad_b / pos2.distance(pos_b2)).atan()
{
// Weapon gives base damage
let mut dmg = attack.weapon.base_damage as i32;
// // Check if it is a hit
// if entity != b
// && !stats_b.is_dead
// // Spherical wedge shaped attack field
// && pos.0.distance_squared(pos_b.0) < (rad_b + scale * ATTACK_RANGE).powi(2)
// && ori2.angle_between(pos_b2 - pos2) < ATTACK_ANGLE.to_radians() / 2.0 + (rad_b / pos2.distance(pos_b2)).atan()
// {
// // Weapon gives base damage
// let mut dmg = if let Some(ItemKind::Tool { power, .. }) =
// attacker_stats.equipment.main.as_ref().map(|i| &i.kind)
// {
// *power as i32
// } else {
// 1
// };
// Block
if character_b.is_block()
&& ori_b.0.angle_between(pos.0 - pos_b.0)
< BLOCK_ANGLE.to_radians() / 2.0
{
dmg = (dmg as f32 * (1.0 - BLOCK_EFFICIENCY)) as i32
}
// // Block
// if character_b.action.is_block()
// && ori_b.0.angle_between(pos.0 - pos_b.0)
// < BLOCK_ANGLE.to_radians() / 2.0
// {
// dmg = (dmg as f32 * (1.0 - BLOCK_EFFICIENCY)) as i32
// }
server_bus.emitter().emit(ServerEvent::Damage {
uid: *uid_b,
change: HealthChange {
amount: -dmg,
cause: HealthSource::Attack { by: *uid },
},
});
}
}
}
// server_emitter.emit(ServerEvent::Damage {
// uid: *uid_b,
// change: HealthChange {
// amount: -dmg,
// cause: HealthSource::Attack { by: *uid },
// },
// });
// }
// }
// }
// }
// if should_end {
// if let Some(character) = &mut character_states.get_mut(entity) {
// character.action = Wield {
// time_left: Duration::default(),
// };
// }
// }
// if let Some(Wield { time_left }) =
// &mut character_states.get_mut(entity).map(|c| &mut c.action)
// {
// if *time_left != Duration::default() {
// *time_left = time_left
// .checked_sub(Duration::from_secs_f32(dt.0))
// .unwrap_or_default();
// }
// }
// }
if attack.remaining_duration() == Duration::default() {
if let Some(character) = character_states.get_mut(entity) {
*character = CharacterState::Wielded(None);
}
}
}
}
}

View File

@ -1,5 +1,6 @@
pub mod agent;
pub mod character_state;
mod combat;
pub mod controller;
mod mount;
pub mod phys;
@ -11,6 +12,7 @@ use specs::DispatcherBuilder;
// System names
pub const CHARACTER_STATE_SYS: &str = "character_state_sys";
pub const COMBAT_SYS: &str = "combat_sys";
pub const AGENT_SYS: &str = "agent_sys";
pub const CONTROLLER_SYS: &str = "controller_sys";
pub const MOUNT_SYS: &str = "mount_sys";
@ -26,4 +28,5 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch_builder.add(stats::Sys, STATS_SYS, &[]);
dispatch_builder.add(phys::Sys, PHYS_SYS, &[CONTROLLER_SYS, MOUNT_SYS, STATS_SYS]);
dispatch_builder.add(projectile::Sys, PROJECTILE_SYS, &[PHYS_SYS]);
dispatch_builder.add(combat::Sys, COMBAT_SYS, &[PROJECTILE_SYS]);
}