mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Re add combat
This commit is contained in:
parent
0bc07a0835
commit
d383abf950
@ -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>;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>();
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user