mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'sam/ecs-sys-struct' into 'master'
Sam/ecs sys struct See merge request veloren/veloren!1813
This commit is contained in:
commit
6081ebbe15
@ -4,12 +4,12 @@ ItemDef(
|
||||
kind: Tool((
|
||||
kind: Axe,
|
||||
hands: One,
|
||||
stats: (
|
||||
stats: Direct((
|
||||
equip_time_millis: 400,
|
||||
power: 1.5,
|
||||
poise_strength: 1.0,
|
||||
speed: 1.3,
|
||||
),
|
||||
)),
|
||||
)),
|
||||
quality: High,
|
||||
tags: [],
|
||||
|
@ -4,7 +4,7 @@ ItemDef(
|
||||
kind: Tool((
|
||||
kind: Sceptre,
|
||||
hands: Two,
|
||||
stats: ((
|
||||
stats: Direct((
|
||||
equip_time_millis: 400,
|
||||
power: 4.0,
|
||||
poise_strength: 1.5,
|
||||
|
@ -77,42 +77,42 @@ type RestrictedMut<'a, C> = PairedStorage<
|
||||
SequentialRestriction,
|
||||
>;
|
||||
|
||||
pub type JoinTuple<'a> = (
|
||||
Entity,
|
||||
&'a Uid,
|
||||
RestrictedMut<'a, CharacterState>,
|
||||
&'a mut Pos,
|
||||
&'a mut Vel,
|
||||
&'a mut Ori,
|
||||
RestrictedMut<'a, Energy>,
|
||||
RestrictedMut<'a, Inventory>,
|
||||
&'a mut Controller,
|
||||
&'a Health,
|
||||
&'a Body,
|
||||
&'a PhysicsState,
|
||||
Option<&'a Melee>,
|
||||
Option<&'a Beam>,
|
||||
&'a Stats,
|
||||
);
|
||||
pub struct JoinStruct<'a> {
|
||||
pub entity: Entity,
|
||||
pub uid: &'a Uid,
|
||||
pub char_state: RestrictedMut<'a, CharacterState>,
|
||||
pub pos: &'a mut Pos,
|
||||
pub vel: &'a mut Vel,
|
||||
pub ori: &'a mut Ori,
|
||||
pub energy: RestrictedMut<'a, Energy>,
|
||||
pub inventory: RestrictedMut<'a, Inventory>,
|
||||
pub controller: &'a mut Controller,
|
||||
pub health: &'a Health,
|
||||
pub body: &'a Body,
|
||||
pub physics: &'a PhysicsState,
|
||||
pub melee_attack: Option<&'a Melee>,
|
||||
pub beam: Option<&'a Beam>,
|
||||
pub stat: &'a Stats,
|
||||
}
|
||||
|
||||
impl<'a> JoinData<'a> {
|
||||
pub fn new(j: &'a JoinTuple<'a>, updater: &'a LazyUpdate, dt: &'a DeltaTime) -> Self {
|
||||
pub fn new(j: &'a JoinStruct<'a>, updater: &'a LazyUpdate, dt: &'a DeltaTime) -> Self {
|
||||
Self {
|
||||
entity: j.0,
|
||||
uid: j.1,
|
||||
character: j.2.get_unchecked(),
|
||||
pos: j.3,
|
||||
vel: j.4,
|
||||
ori: j.5,
|
||||
energy: j.6.get_unchecked(),
|
||||
inventory: j.7.get_unchecked(),
|
||||
controller: j.8,
|
||||
inputs: &j.8.inputs,
|
||||
health: j.9,
|
||||
body: j.10,
|
||||
physics: j.11,
|
||||
melee_attack: j.12,
|
||||
stats: j.14,
|
||||
entity: j.entity,
|
||||
uid: j.uid,
|
||||
character: j.char_state.get_unchecked(),
|
||||
pos: j.pos,
|
||||
vel: j.vel,
|
||||
ori: j.ori,
|
||||
energy: j.energy.get_unchecked(),
|
||||
inventory: j.inventory.get_unchecked(),
|
||||
controller: j.controller,
|
||||
inputs: &j.controller.inputs,
|
||||
health: j.health,
|
||||
body: j.body,
|
||||
physics: j.physics,
|
||||
melee_attack: j.melee_attack,
|
||||
stats: j.stat,
|
||||
updater,
|
||||
dt,
|
||||
}
|
||||
|
@ -9,53 +9,46 @@ use common::{
|
||||
resources::DeltaTime,
|
||||
uid::UidAllocator,
|
||||
};
|
||||
use specs::{saveload::MarkerAllocator, Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||
use specs::{
|
||||
saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, System,
|
||||
SystemData, World, WriteStorage,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
uid_allocator: Read<'a, UidAllocator>,
|
||||
positions: ReadStorage<'a, Pos>,
|
||||
char_states: ReadStorage<'a, CharacterState>,
|
||||
healths: ReadStorage<'a, Health>,
|
||||
groups: ReadStorage<'a, Group>,
|
||||
buffs: ReadStorage<'a, Buffs>,
|
||||
}
|
||||
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, DeltaTime>,
|
||||
ReadStorage<'a, Pos>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
ReadStorage<'a, CharacterState>,
|
||||
WriteStorage<'a, Auras>,
|
||||
WriteStorage<'a, Buffs>,
|
||||
ReadStorage<'a, Health>,
|
||||
ReadStorage<'a, Group>,
|
||||
Read<'a, UidAllocator>,
|
||||
);
|
||||
type SystemData = (ReadData<'a>, WriteStorage<'a, Auras>);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(
|
||||
entities,
|
||||
dt,
|
||||
positions,
|
||||
server_bus,
|
||||
character_states,
|
||||
mut auras,
|
||||
mut buffs,
|
||||
health,
|
||||
groups,
|
||||
uid_allocator,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
fn run(&mut self, (read_data, mut auras): Self::SystemData) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let dt = read_data.dt.0;
|
||||
|
||||
auras.set_event_emission(false);
|
||||
|
||||
// Iterate through all entities with an aura
|
||||
for (entity, pos, mut auras_comp) in (&entities, &positions, &mut auras).join() {
|
||||
for (entity, pos, mut auras_comp) in
|
||||
(&read_data.entities, &read_data.positions, &mut auras).join()
|
||||
{
|
||||
let mut expired_auras = Vec::<AuraKey>::new();
|
||||
// Iterate through the auras attached to this entity
|
||||
for (key, aura) in auras_comp.auras.iter_mut() {
|
||||
// Tick the aura and subtract dt from it
|
||||
if let Some(remaining_time) = &mut aura.duration {
|
||||
if let Some(new_duration) =
|
||||
remaining_time.checked_sub(Duration::from_secs_f32(dt.0))
|
||||
remaining_time.checked_sub(Duration::from_secs_f32(dt))
|
||||
{
|
||||
*remaining_time = new_duration;
|
||||
} else {
|
||||
@ -63,29 +56,23 @@ impl<'a> System<'a> for Sys {
|
||||
expired_auras.push(key);
|
||||
}
|
||||
}
|
||||
for (
|
||||
target_entity,
|
||||
target_pos,
|
||||
target_character_state_maybe,
|
||||
target_buffs,
|
||||
health,
|
||||
) in (
|
||||
&entities,
|
||||
&positions,
|
||||
character_states.maybe(),
|
||||
&mut buffs,
|
||||
&health,
|
||||
for (target, target_pos, target_buffs, health) in (
|
||||
&read_data.entities,
|
||||
&read_data.positions,
|
||||
&read_data.buffs,
|
||||
&read_data.healths,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
// Ensure entity is within the aura radius
|
||||
if target_pos.0.distance_squared(pos.0) < aura.radius.powi(2) {
|
||||
if let AuraTarget::GroupOf(uid) = aura.target {
|
||||
let same_group = uid_allocator
|
||||
let same_group = read_data
|
||||
.uid_allocator
|
||||
.retrieve_entity_internal(uid.into())
|
||||
.and_then(|e| groups.get(e))
|
||||
.and_then(|e| read_data.groups.get(e))
|
||||
.map_or(false, |owner_group| {
|
||||
Some(owner_group) == groups.get(target_entity)
|
||||
Some(owner_group) == read_data.groups.get(target)
|
||||
});
|
||||
|
||||
if !same_group {
|
||||
@ -112,7 +99,7 @@ impl<'a> System<'a> for Sys {
|
||||
let apply_buff = match kind {
|
||||
BuffKind::CampfireHeal => {
|
||||
matches!(
|
||||
target_character_state_maybe,
|
||||
read_data.char_states.get(target),
|
||||
Some(CharacterState::Sit)
|
||||
) && health.current() < health.maximum()
|
||||
},
|
||||
@ -122,7 +109,7 @@ impl<'a> System<'a> for Sys {
|
||||
if apply_buff {
|
||||
use buff::*;
|
||||
server_emitter.emit(ServerEvent::Buff {
|
||||
entity: target_entity,
|
||||
entity: target,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
kind,
|
||||
data,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use common::{
|
||||
combat::AttackerInfo,
|
||||
comp::{
|
||||
group, Beam, BeamSegment, Body, Energy, Health, HealthSource, Inventory, Last, Ori, Pos,
|
||||
Beam, BeamSegment, Body, Energy, Group, Health, HealthSource, Inventory, Last, Ori, Pos,
|
||||
Scale,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
@ -9,64 +9,55 @@ use common::{
|
||||
uid::{Uid, UidAllocator},
|
||||
GroupTarget,
|
||||
};
|
||||
use specs::{saveload::MarkerAllocator, Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||
use specs::{
|
||||
saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, System,
|
||||
SystemData, World, WriteStorage,
|
||||
};
|
||||
use std::time::Duration;
|
||||
use vek::*;
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
time: Read<'a, Time>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
uid_allocator: Read<'a, UidAllocator>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
positions: ReadStorage<'a, Pos>,
|
||||
last_positions: ReadStorage<'a, Last<Pos>>,
|
||||
orientations: ReadStorage<'a, Ori>,
|
||||
scales: ReadStorage<'a, Scale>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
healths: ReadStorage<'a, Health>,
|
||||
inventories: ReadStorage<'a, Inventory>,
|
||||
groups: ReadStorage<'a, Group>,
|
||||
energies: ReadStorage<'a, Energy>,
|
||||
}
|
||||
|
||||
/// This system is responsible for handling beams that heal or do damage
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Read<'a, Time>,
|
||||
Read<'a, DeltaTime>,
|
||||
Read<'a, UidAllocator>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Last<Pos>>,
|
||||
ReadStorage<'a, Ori>,
|
||||
ReadStorage<'a, Scale>,
|
||||
ReadStorage<'a, Body>,
|
||||
ReadStorage<'a, Health>,
|
||||
ReadStorage<'a, Inventory>,
|
||||
ReadStorage<'a, group::Group>,
|
||||
ReadStorage<'a, Energy>,
|
||||
ReadData<'a>,
|
||||
WriteStorage<'a, BeamSegment>,
|
||||
WriteStorage<'a, Beam>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(
|
||||
entities,
|
||||
server_bus,
|
||||
time,
|
||||
dt,
|
||||
uid_allocator,
|
||||
uids,
|
||||
positions,
|
||||
last_positions,
|
||||
orientations,
|
||||
scales,
|
||||
bodies,
|
||||
healths,
|
||||
inventories,
|
||||
groups,
|
||||
energies,
|
||||
mut beam_segments,
|
||||
mut beams,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
fn run(&mut self, (read_data, mut beam_segments, mut beams): Self::SystemData) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
|
||||
let time = time.0;
|
||||
let dt = dt.0;
|
||||
let time = read_data.time.0;
|
||||
let dt = read_data.dt.0;
|
||||
|
||||
// Beams
|
||||
for (entity, pos, ori, beam_segment) in
|
||||
(&entities, &positions, &orientations, &beam_segments).join()
|
||||
for (entity, pos, ori, beam_segment) in (
|
||||
&read_data.entities,
|
||||
&read_data.positions,
|
||||
&read_data.orientations,
|
||||
&beam_segments,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let creation_time = match beam_segment.creation {
|
||||
Some(time) => time,
|
||||
@ -100,11 +91,11 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let beam_owner = beam_segment
|
||||
.owner
|
||||
.and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into()));
|
||||
.and_then(|uid| read_data.uid_allocator.retrieve_entity_internal(uid.into()));
|
||||
|
||||
// Group to ignore collisions with
|
||||
// Might make this more nuanced if beams are used for non damage effects
|
||||
let group = beam_owner.and_then(|e| groups.get(e));
|
||||
let group = beam_owner.and_then(|e| read_data.groups.get(e));
|
||||
|
||||
let hit_entities = if let Some(beam) = beam_owner.and_then(|e| beams.get_mut(e)) {
|
||||
&mut beam.hit_entities
|
||||
@ -113,25 +104,12 @@ impl<'a> System<'a> for Sys {
|
||||
};
|
||||
|
||||
// Go through all other effectable entities
|
||||
for (
|
||||
b,
|
||||
uid_b,
|
||||
pos_b,
|
||||
last_pos_b_maybe,
|
||||
scale_b_maybe,
|
||||
health_b,
|
||||
body_b,
|
||||
inventory_b_maybe,
|
||||
) in (
|
||||
&entities,
|
||||
&uids,
|
||||
&positions,
|
||||
// TODO: make sure that these are maintained on the client and remove `.maybe()`
|
||||
last_positions.maybe(),
|
||||
scales.maybe(),
|
||||
&healths,
|
||||
&bodies,
|
||||
inventories.maybe(),
|
||||
for (target, uid_b, pos_b, health_b, body_b) in (
|
||||
&read_data.entities,
|
||||
&read_data.uids,
|
||||
&read_data.positions,
|
||||
&read_data.healths,
|
||||
&read_data.bodies,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -141,12 +119,13 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Scales
|
||||
let scale_b = scale_b_maybe.map_or(1.0, |s| s.0);
|
||||
let scale_b = read_data.scales.get(target).map_or(1.0, |s| s.0);
|
||||
let last_pos_b_maybe = read_data.last_positions.get(target);
|
||||
let rad_b = body_b.radius() * scale_b;
|
||||
let height_b = body_b.height() * scale_b;
|
||||
|
||||
// Check if it is a hit
|
||||
let hit = entity != b
|
||||
let hit = entity != target
|
||||
&& !health_b.is_dead
|
||||
// Collision shapes
|
||||
&& (sphere_wedge_cylinder_collision(pos.0, frame_start_dist, frame_end_dist, *ori.look_dir(), beam_segment.angle, pos_b.0, rad_b, height_b)
|
||||
@ -155,7 +134,7 @@ impl<'a> System<'a> for Sys {
|
||||
if hit {
|
||||
// See if entities are in the same group
|
||||
let same_group = group
|
||||
.map(|group_a| Some(group_a) == groups.get(b))
|
||||
.map(|group_a| Some(group_a) == read_data.groups.get(target))
|
||||
.unwrap_or(Some(*uid_b) == beam_segment.owner);
|
||||
|
||||
let target_group = if same_group {
|
||||
@ -175,14 +154,14 @@ impl<'a> System<'a> for Sys {
|
||||
.map(|(entity, uid)| AttackerInfo {
|
||||
entity,
|
||||
uid,
|
||||
energy: energies.get(entity),
|
||||
energy: read_data.energies.get(entity),
|
||||
});
|
||||
|
||||
beam_segment.properties.attack.apply_attack(
|
||||
target_group,
|
||||
attacker_info,
|
||||
b,
|
||||
inventory_b_maybe,
|
||||
target,
|
||||
read_data.inventories.get(target),
|
||||
ori.look_dir(),
|
||||
false,
|
||||
1.0,
|
||||
|
@ -7,33 +7,37 @@ use common::{
|
||||
resources::DeltaTime,
|
||||
Damage, DamageSource,
|
||||
};
|
||||
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||
use specs::{
|
||||
shred::ResourceId, Entities, Join, Read, ReadStorage, System, SystemData, World, WriteStorage,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
inventories: ReadStorage<'a, Inventory>,
|
||||
}
|
||||
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, DeltaTime>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
ReadStorage<'a, Inventory>,
|
||||
ReadData<'a>,
|
||||
WriteStorage<'a, Health>,
|
||||
WriteStorage<'a, Energy>,
|
||||
WriteStorage<'a, Buffs>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(entities, dt, server_bus, inventories, mut healths, mut energies, mut buffs): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
fn run(&mut self, (read_data, mut healths, mut energies, mut buffs): Self::SystemData) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let dt = read_data.dt.0;
|
||||
// Set to false to avoid spamming server
|
||||
buffs.set_event_emission(false);
|
||||
healths.set_event_emission(false);
|
||||
energies.set_event_emission(false);
|
||||
for (entity, mut buff_comp, mut health, mut energy) in
|
||||
(&entities, &mut buffs, &mut healths, &mut energies).join()
|
||||
(&read_data.entities, &mut buffs, &mut healths, &mut energies).join()
|
||||
{
|
||||
let (buff_comp_kinds, buff_comp_buffs) = buff_comp.parts();
|
||||
let mut expired_buffs = Vec::<BuffId>::new();
|
||||
@ -45,19 +49,19 @@ impl<'a> System<'a> for Sys {
|
||||
if let Some((Some(buff), id)) =
|
||||
ids.get(0).map(|id| (buff_comp_buffs.get_mut(id), id))
|
||||
{
|
||||
tick_buff(*id, buff, dt.0, |id| expired_buffs.push(id));
|
||||
tick_buff(*id, buff, dt, |id| expired_buffs.push(id));
|
||||
}
|
||||
} else {
|
||||
for (id, buff) in buff_comp_buffs
|
||||
.iter_mut()
|
||||
.filter(|(i, _)| ids.iter().any(|id| id == *i))
|
||||
{
|
||||
tick_buff(*id, buff, dt.0, |id| expired_buffs.push(id));
|
||||
tick_buff(*id, buff, dt, |id| expired_buffs.push(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(inventory) = inventories.get(entity) {
|
||||
if let Some(inventory) = read_data.inventories.get(entity) {
|
||||
let damage_reduction = Damage::compute_damage_reduction(inventory);
|
||||
if (damage_reduction - 1.0).abs() < f32::EPSILON {
|
||||
for (id, buff) in buff_comp.buffs.iter() {
|
||||
@ -94,7 +98,7 @@ impl<'a> System<'a> for Sys {
|
||||
accumulated,
|
||||
kind,
|
||||
} => {
|
||||
*accumulated += *rate * dt.0;
|
||||
*accumulated += *rate * dt;
|
||||
// Apply damage only once a second (with a minimum of 1 damage), or
|
||||
// when a buff is removed
|
||||
if accumulated.abs() > rate.abs().max(10.0)
|
||||
|
@ -1,4 +1,7 @@
|
||||
use specs::{Entities, Join, LazyUpdate, Read, ReadExpect, ReadStorage, System, WriteStorage};
|
||||
use specs::{
|
||||
shred::ResourceId, Entities, Join, LazyUpdate, Read, ReadExpect, ReadStorage, System,
|
||||
SystemData, World, WriteStorage,
|
||||
};
|
||||
|
||||
use common::{
|
||||
comp::{
|
||||
@ -12,26 +15,26 @@ use common::{
|
||||
span,
|
||||
states::{
|
||||
self,
|
||||
behavior::{CharacterBehavior, JoinData, JoinTuple},
|
||||
behavior::{CharacterBehavior, JoinData, JoinStruct},
|
||||
},
|
||||
uid::{Uid, UidAllocator},
|
||||
uid::Uid,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
fn incorporate_update(tuple: &mut JoinTuple, state_update: StateUpdate) {
|
||||
fn incorporate_update(join: &mut JoinStruct, state_update: StateUpdate) {
|
||||
// TODO: if checking equality is expensive use optional field in StateUpdate
|
||||
if tuple.2.get_unchecked() != &state_update.character {
|
||||
*tuple.2.get_mut_unchecked() = state_update.character
|
||||
if join.char_state.get_unchecked() != &state_update.character {
|
||||
*join.char_state.get_mut_unchecked() = state_update.character
|
||||
};
|
||||
*tuple.3 = state_update.pos;
|
||||
*tuple.4 = state_update.vel;
|
||||
*tuple.5 = state_update.ori;
|
||||
*join.pos = state_update.pos;
|
||||
*join.vel = state_update.vel;
|
||||
*join.ori = state_update.ori;
|
||||
// Note: might be changed every tick by timer anyway
|
||||
if tuple.6.get_unchecked() != &state_update.energy {
|
||||
*tuple.6.get_mut_unchecked() = state_update.energy
|
||||
if join.energy.get_unchecked() != &state_update.energy {
|
||||
*join.energy.get_mut_unchecked() = state_update.energy
|
||||
};
|
||||
if state_update.swap_equipped_weapons {
|
||||
let mut inventory = tuple.7.get_mut_unchecked();
|
||||
let mut inventory = join.inventory.get_mut_unchecked();
|
||||
let inventory = &mut *inventory;
|
||||
inventory
|
||||
.swap(
|
||||
@ -43,6 +46,24 @@ fn incorporate_update(tuple: &mut JoinTuple, state_update: StateUpdate) {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
local_bus: Read<'a, EventBus<LocalEvent>>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
lazy_update: Read<'a, LazyUpdate>,
|
||||
metrics: ReadExpect<'a, SysMetrics>,
|
||||
healths: ReadStorage<'a, Health>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
physics_states: ReadStorage<'a, PhysicsState>,
|
||||
melee_attacks: ReadStorage<'a, Melee>,
|
||||
beams: ReadStorage<'a, Beam>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
mountings: ReadStorage<'a, Mounting>,
|
||||
stats: ReadStorage<'a, Stats>,
|
||||
}
|
||||
|
||||
/// ## Character Behavior System
|
||||
/// Passes `JoinData` to `CharacterState`'s `behavior` handler fn's. Receives a
|
||||
/// `StateUpdate` in return and performs updates to ECS Components from that.
|
||||
@ -51,13 +72,7 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, UidAllocator>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Read<'a, EventBus<LocalEvent>>,
|
||||
Read<'a, DeltaTime>,
|
||||
Read<'a, LazyUpdate>,
|
||||
ReadExpect<'a, SysMetrics>,
|
||||
ReadData<'a>,
|
||||
WriteStorage<'a, CharacterState>,
|
||||
WriteStorage<'a, Pos>,
|
||||
WriteStorage<'a, Vel>,
|
||||
@ -65,28 +80,14 @@ impl<'a> System<'a> for Sys {
|
||||
WriteStorage<'a, Energy>,
|
||||
WriteStorage<'a, Inventory>,
|
||||
WriteStorage<'a, Controller>,
|
||||
ReadStorage<'a, Health>,
|
||||
WriteStorage<'a, Poise>,
|
||||
ReadStorage<'a, Body>,
|
||||
ReadStorage<'a, PhysicsState>,
|
||||
ReadStorage<'a, Melee>,
|
||||
ReadStorage<'a, Beam>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, Mounting>,
|
||||
ReadStorage<'a, Stats>,
|
||||
);
|
||||
|
||||
#[allow(clippy::while_let_on_iterator)] // TODO: Pending review in #587
|
||||
fn run(
|
||||
&mut self,
|
||||
(
|
||||
entities,
|
||||
_uid_allocator,
|
||||
server_bus,
|
||||
local_bus,
|
||||
dt,
|
||||
updater,
|
||||
sys_metrics,
|
||||
read_data,
|
||||
mut character_states,
|
||||
mut positions,
|
||||
mut velocities,
|
||||
@ -94,25 +95,31 @@ impl<'a> System<'a> for Sys {
|
||||
mut energies,
|
||||
mut inventories,
|
||||
mut controllers,
|
||||
healths,
|
||||
mut poises,
|
||||
bodies,
|
||||
physics_states,
|
||||
attacking_storage,
|
||||
beam_storage,
|
||||
uids,
|
||||
mountings,
|
||||
stats,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let start_time = std::time::Instant::now();
|
||||
span!(_guard, "run", "character_behavior::Sys::run");
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
let mut local_emitter = local_bus.emitter();
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
let mut local_emitter = read_data.local_bus.emitter();
|
||||
|
||||
for mut tuple in (
|
||||
&entities,
|
||||
&uids,
|
||||
for (
|
||||
entity,
|
||||
uid,
|
||||
mut char_state,
|
||||
mut pos,
|
||||
mut vel,
|
||||
mut ori,
|
||||
energy,
|
||||
inventory,
|
||||
mut controller,
|
||||
health,
|
||||
body,
|
||||
physics,
|
||||
stat,
|
||||
) in (
|
||||
&read_data.entities,
|
||||
&read_data.uids,
|
||||
&mut character_states.restrict_mut(),
|
||||
&mut positions,
|
||||
&mut velocities,
|
||||
@ -120,39 +127,37 @@ impl<'a> System<'a> for Sys {
|
||||
&mut energies.restrict_mut(),
|
||||
&mut inventories.restrict_mut(),
|
||||
&mut controllers,
|
||||
&healths,
|
||||
&bodies,
|
||||
&physics_states,
|
||||
attacking_storage.maybe(),
|
||||
beam_storage.maybe(),
|
||||
&stats,
|
||||
&read_data.healths,
|
||||
&read_data.bodies,
|
||||
&read_data.physics_states,
|
||||
&read_data.stats,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
// Being dead overrides all other states
|
||||
if tuple.9.is_dead {
|
||||
if health.is_dead {
|
||||
// Do nothing
|
||||
continue;
|
||||
}
|
||||
// If mounted, character state is controlled by mount
|
||||
// TODO: Make mounting a state
|
||||
if let Some(Mounting(_)) = mountings.get(tuple.0) {
|
||||
if let Some(Mounting(_)) = read_data.mountings.get(entity) {
|
||||
let sit_state = CharacterState::Sit {};
|
||||
if tuple.2.get_unchecked() != &sit_state {
|
||||
*tuple.2.get_mut_unchecked() = sit_state;
|
||||
if char_state.get_unchecked() != &sit_state {
|
||||
*char_state.get_mut_unchecked() = sit_state;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Enter stunned state if poise damage is enough
|
||||
if let Some(mut poise) = poises.get_mut(tuple.0) {
|
||||
let was_wielded = tuple.2.get_unchecked().is_wield();
|
||||
if let Some(mut poise) = poises.get_mut(entity) {
|
||||
let was_wielded = char_state.get_unchecked().is_wield();
|
||||
let poise_state = poise.poise_state();
|
||||
match poise_state {
|
||||
PoiseState::Normal => {},
|
||||
PoiseState::Interrupted => {
|
||||
poise.reset();
|
||||
*tuple.2.get_mut_unchecked() =
|
||||
*char_state.get_mut_unchecked() =
|
||||
CharacterState::Stunned(common::states::stunned::Data {
|
||||
static_data: common::states::stunned::StaticData {
|
||||
buildup_duration: Duration::from_millis(150),
|
||||
@ -167,7 +172,7 @@ impl<'a> System<'a> for Sys {
|
||||
},
|
||||
PoiseState::Stunned => {
|
||||
poise.reset();
|
||||
*tuple.2.get_mut_unchecked() =
|
||||
*char_state.get_mut_unchecked() =
|
||||
CharacterState::Stunned(common::states::stunned::Data {
|
||||
static_data: common::states::stunned::StaticData {
|
||||
buildup_duration: Duration::from_millis(500),
|
||||
@ -180,13 +185,13 @@ impl<'a> System<'a> for Sys {
|
||||
was_wielded,
|
||||
});
|
||||
server_emitter.emit(ServerEvent::Knockback {
|
||||
entity: tuple.0,
|
||||
entity,
|
||||
impulse: 5.0 * poise.knockback(),
|
||||
});
|
||||
},
|
||||
PoiseState::Dazed => {
|
||||
poise.reset();
|
||||
*tuple.2.get_mut_unchecked() =
|
||||
*char_state.get_mut_unchecked() =
|
||||
CharacterState::Stunned(common::states::stunned::Data {
|
||||
static_data: common::states::stunned::StaticData {
|
||||
buildup_duration: Duration::from_millis(800),
|
||||
@ -199,13 +204,13 @@ impl<'a> System<'a> for Sys {
|
||||
was_wielded,
|
||||
});
|
||||
server_emitter.emit(ServerEvent::Knockback {
|
||||
entity: tuple.0,
|
||||
entity,
|
||||
impulse: 10.0 * poise.knockback(),
|
||||
});
|
||||
},
|
||||
PoiseState::KnockedDown => {
|
||||
poise.reset();
|
||||
*tuple.2.get_mut_unchecked() =
|
||||
*char_state.get_mut_unchecked() =
|
||||
CharacterState::Stunned(common::states::stunned::Data {
|
||||
static_data: common::states::stunned::StaticData {
|
||||
buildup_duration: Duration::from_millis(1000),
|
||||
@ -218,7 +223,7 @@ impl<'a> System<'a> for Sys {
|
||||
was_wielded,
|
||||
});
|
||||
server_emitter.emit(ServerEvent::Knockback {
|
||||
entity: tuple.0,
|
||||
entity,
|
||||
impulse: 10.0 * poise.knockback(),
|
||||
});
|
||||
},
|
||||
@ -226,9 +231,28 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Controller actions
|
||||
let actions = std::mem::replace(&mut tuple.8.actions, Vec::new());
|
||||
let actions = std::mem::replace(&mut controller.actions, Vec::new());
|
||||
|
||||
let mut join_struct = JoinStruct {
|
||||
entity,
|
||||
uid: &uid,
|
||||
char_state,
|
||||
pos: &mut pos,
|
||||
vel: &mut vel,
|
||||
ori: &mut ori,
|
||||
energy,
|
||||
inventory,
|
||||
controller: &mut controller,
|
||||
health: &health,
|
||||
body: &body,
|
||||
physics: &physics,
|
||||
melee_attack: read_data.melee_attacks.get(entity),
|
||||
beam: read_data.beams.get(entity),
|
||||
stat: &stat,
|
||||
};
|
||||
|
||||
for action in actions {
|
||||
let j = JoinData::new(&tuple, &updater, &dt);
|
||||
let j = JoinData::new(&join_struct, &read_data.lazy_update, &read_data.dt);
|
||||
let mut state_update = match j.character {
|
||||
CharacterState::Idle => states::idle::Data.handle_event(&j, action),
|
||||
CharacterState::Talk => states::talk::Data.handle_event(&j, action),
|
||||
@ -268,10 +292,10 @@ impl<'a> System<'a> for Sys {
|
||||
};
|
||||
local_emitter.append(&mut state_update.local_events);
|
||||
server_emitter.append(&mut state_update.server_events);
|
||||
incorporate_update(&mut tuple, state_update);
|
||||
incorporate_update(&mut join_struct, state_update);
|
||||
}
|
||||
|
||||
let j = JoinData::new(&tuple, &updater, &dt);
|
||||
let j = JoinData::new(&join_struct, &read_data.lazy_update, &read_data.dt);
|
||||
|
||||
let mut state_update = match j.character {
|
||||
CharacterState::Idle => states::idle::Data.behavior(&j),
|
||||
@ -303,9 +327,9 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
local_emitter.append(&mut state_update.local_events);
|
||||
server_emitter.append(&mut state_update.server_events);
|
||||
incorporate_update(&mut tuple, state_update);
|
||||
incorporate_update(&mut join_struct, state_update);
|
||||
}
|
||||
sys_metrics.character_behavior_ns.store(
|
||||
read_data.metrics.character_behavior_ns.store(
|
||||
start_time.elapsed().as_nanos() as u64,
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
|
@ -1,53 +1,36 @@
|
||||
use common::{
|
||||
comp::{BuffChange, ControlEvent, Controller},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
event::{EventBus, ServerEvent},
|
||||
metrics::SysMetrics,
|
||||
resources::DeltaTime,
|
||||
span,
|
||||
uid::{Uid, UidAllocator},
|
||||
uid::UidAllocator,
|
||||
};
|
||||
use specs::{
|
||||
saveload::{Marker, MarkerAllocator},
|
||||
Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage,
|
||||
shred::ResourceId,
|
||||
Entities, Join, Read, ReadExpect, System, SystemData, World, WriteStorage,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
// const CHARGE_COST: i32 = 200;
|
||||
// const ROLL_COST: i32 = 30;
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
uid_allocator: Read<'a, UidAllocator>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
metrics: ReadExpect<'a, SysMetrics>,
|
||||
}
|
||||
|
||||
pub struct Sys;
|
||||
|
||||
impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, UidAllocator>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Read<'a, EventBus<LocalEvent>>,
|
||||
Read<'a, DeltaTime>,
|
||||
ReadExpect<'a, SysMetrics>,
|
||||
WriteStorage<'a, Controller>,
|
||||
ReadStorage<'a, Uid>,
|
||||
);
|
||||
type SystemData = (ReadData<'a>, WriteStorage<'a, Controller>);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(
|
||||
entities,
|
||||
uid_allocator,
|
||||
server_bus,
|
||||
_local_bus,
|
||||
_dt,
|
||||
sys_metrics,
|
||||
mut controllers,
|
||||
uids,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
fn run(&mut self, (read_data, mut controllers): Self::SystemData) {
|
||||
let start_time = std::time::Instant::now();
|
||||
span!(_guard, "run", "controller::Sys::run");
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
|
||||
for (entity, _uid, controller) in (&entities, &uids, &mut controllers).join() {
|
||||
for (entity, controller) in (&read_data.entities, &mut controllers).join() {
|
||||
let mut inputs = &mut controller.inputs;
|
||||
|
||||
// Note(imbris): I avoided incrementing the duration with inputs.tick() because
|
||||
@ -72,8 +55,9 @@ impl<'a> System<'a> for Sys {
|
||||
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())
|
||||
if let Some(mountee_entity) = read_data
|
||||
.uid_allocator
|
||||
.retrieve_entity_internal(mountee_uid.id())
|
||||
{
|
||||
server_emitter.emit(ServerEvent::Mount(entity, mountee_entity));
|
||||
}
|
||||
@ -92,8 +76,9 @@ impl<'a> System<'a> for Sys {
|
||||
server_emitter.emit(ServerEvent::DisableLantern(entity))
|
||||
},
|
||||
ControlEvent::Interact(npc_uid) => {
|
||||
if let Some(npc_entity) =
|
||||
uid_allocator.retrieve_entity_internal(npc_uid.id())
|
||||
if let Some(npc_entity) = read_data
|
||||
.uid_allocator
|
||||
.retrieve_entity_internal(npc_uid.id())
|
||||
{
|
||||
server_emitter.emit(ServerEvent::NpcInteract(entity, npc_entity));
|
||||
}
|
||||
@ -118,7 +103,7 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
}
|
||||
sys_metrics.controller_ns.store(
|
||||
read_data.metrics.controller_ns.store(
|
||||
start_time.elapsed().as_nanos() as u64,
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
|
@ -1,97 +1,69 @@
|
||||
use common::{
|
||||
combat::AttackerInfo,
|
||||
comp::{group, Body, CharacterState, Energy, Health, Inventory, Melee, Ori, Pos, Scale},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
comp::{Body, CharacterState, Energy, Group, Health, Inventory, Melee, Ori, Pos, Scale},
|
||||
event::{EventBus, ServerEvent},
|
||||
metrics::SysMetrics,
|
||||
span,
|
||||
uid::Uid,
|
||||
util::Dir,
|
||||
GroupTarget,
|
||||
};
|
||||
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
|
||||
use specs::{
|
||||
shred::ResourceId, Entities, Join, Read, ReadExpect, ReadStorage, System, SystemData, World,
|
||||
WriteStorage,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
positions: ReadStorage<'a, Pos>,
|
||||
orientations: ReadStorage<'a, Ori>,
|
||||
scales: ReadStorage<'a, Scale>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
healths: ReadStorage<'a, Health>,
|
||||
energies: ReadStorage<'a, Energy>,
|
||||
inventories: ReadStorage<'a, Inventory>,
|
||||
groups: ReadStorage<'a, Group>,
|
||||
char_states: ReadStorage<'a, CharacterState>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
metrics: ReadExpect<'a, SysMetrics>,
|
||||
}
|
||||
|
||||
/// This system is responsible for handling accepted inputs like moving or
|
||||
/// attacking
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Read<'a, EventBus<LocalEvent>>,
|
||||
ReadExpect<'a, SysMetrics>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Ori>,
|
||||
ReadStorage<'a, Scale>,
|
||||
ReadStorage<'a, Body>,
|
||||
ReadStorage<'a, Health>,
|
||||
ReadStorage<'a, Energy>,
|
||||
ReadStorage<'a, Inventory>,
|
||||
ReadStorage<'a, group::Group>,
|
||||
WriteStorage<'a, Melee>,
|
||||
ReadStorage<'a, CharacterState>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(
|
||||
entities,
|
||||
server_bus,
|
||||
local_bus,
|
||||
sys_metrics,
|
||||
uids,
|
||||
positions,
|
||||
orientations,
|
||||
scales,
|
||||
bodies,
|
||||
healths,
|
||||
energies,
|
||||
inventories,
|
||||
groups,
|
||||
mut attacking_storage,
|
||||
char_states,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (ReadData<'a>, WriteStorage<'a, Melee>);
|
||||
|
||||
fn run(&mut self, (read_data, mut melee_attacks): Self::SystemData) {
|
||||
let start_time = std::time::Instant::now();
|
||||
span!(_guard, "run", "melee::Sys::run");
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
let _local_emitter = local_bus.emitter();
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
// Attacks
|
||||
for (entity, uid, pos, ori, scale_maybe, attack, body) in (
|
||||
&entities,
|
||||
&uids,
|
||||
&positions,
|
||||
&orientations,
|
||||
scales.maybe(),
|
||||
&mut attacking_storage,
|
||||
&bodies,
|
||||
for (attacker, uid, pos, ori, melee_attack, body) in (
|
||||
&read_data.entities,
|
||||
&read_data.uids,
|
||||
&read_data.positions,
|
||||
&read_data.orientations,
|
||||
&mut melee_attacks,
|
||||
&read_data.bodies,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
if attack.applied {
|
||||
if melee_attack.applied {
|
||||
continue;
|
||||
}
|
||||
attack.applied = true;
|
||||
melee_attack.applied = true;
|
||||
|
||||
// Go through all other entities
|
||||
for (
|
||||
b,
|
||||
pos_b,
|
||||
scale_b_maybe,
|
||||
health_b,
|
||||
body_b,
|
||||
char_state_b_maybe,
|
||||
inventory_b_maybe,
|
||||
) in (
|
||||
&entities,
|
||||
&positions,
|
||||
scales.maybe(),
|
||||
&healths,
|
||||
&bodies,
|
||||
char_states.maybe(),
|
||||
inventories.maybe(),
|
||||
for (target, pos_b, health_b, body_b) in (
|
||||
&read_data.entities,
|
||||
&read_data.positions,
|
||||
&read_data.healths,
|
||||
&read_data.bodies,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -103,25 +75,29 @@ impl<'a> System<'a> for Sys {
|
||||
let ori2 = Vec2::from(look_dir);
|
||||
|
||||
// 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 scale = read_data.scales.get(attacker).map_or(1.0, |s| s.0);
|
||||
let scale_b = read_data.scales.get(target).map_or(1.0, |s| s.0);
|
||||
let rad = body.radius() * scale;
|
||||
let rad_b = body_b.radius() * scale_b;
|
||||
|
||||
// Check if entity is dodging
|
||||
let is_dodge = char_state_b_maybe.map_or(false, |c_s| c_s.is_melee_dodge());
|
||||
let is_dodge = read_data
|
||||
.char_states
|
||||
.get(target)
|
||||
.map_or(false, |c_s| c_s.is_melee_dodge());
|
||||
|
||||
// Check if it is a hit
|
||||
if entity != b
|
||||
if attacker != target
|
||||
&& !health_b.is_dead
|
||||
// Spherical wedge shaped attack field
|
||||
&& pos.0.distance_squared(pos_b.0) < (rad + rad_b + scale * attack.range).powi(2)
|
||||
&& ori2.angle_between(pos_b2 - pos2) < attack.max_angle + (rad_b / pos2.distance(pos_b2)).atan()
|
||||
&& pos.0.distance_squared(pos_b.0) < (rad + rad_b + scale * melee_attack.range).powi(2)
|
||||
&& ori2.angle_between(pos_b2 - pos2) < melee_attack.max_angle + (rad_b / pos2.distance(pos_b2)).atan()
|
||||
{
|
||||
// See if entities are in the same group
|
||||
let same_group = groups
|
||||
.get(entity)
|
||||
.map(|group_a| Some(group_a) == groups.get(b))
|
||||
let same_group = read_data
|
||||
.groups
|
||||
.get(attacker)
|
||||
.map(|group_a| Some(group_a) == read_data.groups.get(target))
|
||||
.unwrap_or(false);
|
||||
|
||||
let target_group = if same_group {
|
||||
@ -133,27 +109,27 @@ impl<'a> System<'a> for Sys {
|
||||
let dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(look_dir));
|
||||
|
||||
let attacker_info = Some(AttackerInfo {
|
||||
entity,
|
||||
entity: attacker,
|
||||
uid: *uid,
|
||||
energy: energies.get(entity),
|
||||
energy: read_data.energies.get(attacker),
|
||||
});
|
||||
|
||||
attack.attack.apply_attack(
|
||||
melee_attack.attack.apply_attack(
|
||||
target_group,
|
||||
attacker_info,
|
||||
b,
|
||||
inventory_b_maybe,
|
||||
target,
|
||||
read_data.inventories.get(target),
|
||||
dir,
|
||||
is_dodge,
|
||||
1.0,
|
||||
|e| server_emitter.emit(e),
|
||||
);
|
||||
|
||||
attack.hit_count += 1;
|
||||
melee_attack.hit_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
sys_metrics.melee_ns.store(
|
||||
read_data.metrics.melee_ns.store(
|
||||
start_time.elapsed().as_nanos() as u64,
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
|
@ -12,57 +12,45 @@ use common::{
|
||||
GroupTarget,
|
||||
};
|
||||
use specs::{
|
||||
saveload::MarkerAllocator, Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage,
|
||||
saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadExpect, ReadStorage,
|
||||
System, SystemData, World, WriteStorage,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
uid_allocator: Read<'a, UidAllocator>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
metrics: ReadExpect<'a, SysMetrics>,
|
||||
positions: ReadStorage<'a, Pos>,
|
||||
physics_states: ReadStorage<'a, PhysicsState>,
|
||||
velocities: ReadStorage<'a, Vel>,
|
||||
inventories: ReadStorage<'a, Inventory>,
|
||||
groups: ReadStorage<'a, Group>,
|
||||
energies: ReadStorage<'a, Energy>,
|
||||
}
|
||||
|
||||
/// This system is responsible for handling projectile effect triggers
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, DeltaTime>,
|
||||
Read<'a, UidAllocator>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
ReadExpect<'a, SysMetrics>,
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, PhysicsState>,
|
||||
ReadStorage<'a, Vel>,
|
||||
ReadData<'a>,
|
||||
WriteStorage<'a, Ori>,
|
||||
WriteStorage<'a, Projectile>,
|
||||
ReadStorage<'a, Inventory>,
|
||||
ReadStorage<'a, Group>,
|
||||
ReadStorage<'a, Energy>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(
|
||||
entities,
|
||||
dt,
|
||||
uid_allocator,
|
||||
server_bus,
|
||||
sys_metrics,
|
||||
positions,
|
||||
physics_states,
|
||||
velocities,
|
||||
mut orientations,
|
||||
mut projectiles,
|
||||
inventories,
|
||||
groups,
|
||||
energies,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
fn run(&mut self, (read_data, mut orientations, mut projectiles): Self::SystemData) {
|
||||
let start_time = std::time::Instant::now();
|
||||
span!(_guard, "run", "projectile::Sys::run");
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
|
||||
// Attacks
|
||||
'projectile_loop: for (entity, pos, physics, ori, mut projectile) in (
|
||||
&entities,
|
||||
&positions,
|
||||
&physics_states,
|
||||
&read_data.entities,
|
||||
&read_data.positions,
|
||||
&read_data.physics_states,
|
||||
&mut orientations,
|
||||
&mut projectiles,
|
||||
)
|
||||
@ -76,12 +64,12 @@ impl<'a> System<'a> for Sys {
|
||||
// Note: somewhat inefficient since we do the lookup for every touching
|
||||
// entity, but if we pull this out of the loop we would want to do it only
|
||||
// if there is at least one touching entity
|
||||
.and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into()))
|
||||
.and_then(|e| groups.get(e))
|
||||
.and_then(|uid| read_data.uid_allocator.retrieve_entity_internal(uid.into()))
|
||||
.and_then(|e| read_data.groups.get(e))
|
||||
.map_or(false, |owner_group|
|
||||
Some(owner_group) == uid_allocator
|
||||
Some(owner_group) == read_data.uid_allocator
|
||||
.retrieve_entity_internal(other.into())
|
||||
.and_then(|e| groups.get(e))
|
||||
.and_then(|e| read_data.groups.get(e))
|
||||
);
|
||||
|
||||
let target_group = if same_group {
|
||||
@ -105,19 +93,20 @@ impl<'a> System<'a> for Sys {
|
||||
for effect in projectile.hit_entity.drain(..) {
|
||||
match effect {
|
||||
projectile::Effect::Attack(attack) => {
|
||||
if let Some(target_entity) =
|
||||
uid_allocator.retrieve_entity_internal(other.into())
|
||||
if let Some(target_entity) = read_data
|
||||
.uid_allocator
|
||||
.retrieve_entity_internal(other.into())
|
||||
{
|
||||
let owner_entity = projectile
|
||||
.owner
|
||||
.and_then(|u| uid_allocator.retrieve_entity_internal(u.into()));
|
||||
let owner_entity = projectile.owner.and_then(|u| {
|
||||
read_data.uid_allocator.retrieve_entity_internal(u.into())
|
||||
});
|
||||
|
||||
let attacker_info =
|
||||
owner_entity.zip(projectile.owner).map(|(entity, uid)| {
|
||||
AttackerInfo {
|
||||
entity,
|
||||
uid,
|
||||
energy: energies.get(entity),
|
||||
energy: read_data.energies.get(entity),
|
||||
}
|
||||
});
|
||||
|
||||
@ -125,7 +114,7 @@ impl<'a> System<'a> for Sys {
|
||||
target_group,
|
||||
attacker_info,
|
||||
target_entity,
|
||||
inventories.get(target_entity),
|
||||
read_data.inventories.get(target_entity),
|
||||
ori.look_dir(),
|
||||
false,
|
||||
1.0,
|
||||
@ -191,7 +180,8 @@ impl<'a> System<'a> for Sys {
|
||||
if projectile_vanished {
|
||||
continue 'projectile_loop;
|
||||
}
|
||||
} else if let Some(dir) = velocities
|
||||
} else if let Some(dir) = read_data
|
||||
.velocities
|
||||
.get(entity)
|
||||
.and_then(|vel| Dir::from_unnormalized(vel.0))
|
||||
{
|
||||
@ -206,10 +196,10 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
projectile.time_left = projectile
|
||||
.time_left
|
||||
.checked_sub(Duration::from_secs_f32(dt.0))
|
||||
.checked_sub(Duration::from_secs_f32(read_data.dt.0))
|
||||
.unwrap_or_default();
|
||||
}
|
||||
sys_metrics.projectile_ns.store(
|
||||
read_data.metrics.projectile_ns.store(
|
||||
start_time.elapsed().as_nanos() as u64,
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
|
@ -1,80 +1,62 @@
|
||||
use common::{
|
||||
combat::AttackerInfo,
|
||||
comp::{
|
||||
group, Body, Energy, Health, HealthSource, Inventory, Last, Ori, PhysicsState, Pos, Scale,
|
||||
Body, Energy, Group, Health, HealthSource, Inventory, Last, Ori, PhysicsState, Pos, Scale,
|
||||
Shockwave, ShockwaveHitEntities,
|
||||
},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
event::{EventBus, ServerEvent},
|
||||
resources::{DeltaTime, Time},
|
||||
uid::{Uid, UidAllocator},
|
||||
util::Dir,
|
||||
GroupTarget,
|
||||
};
|
||||
use specs::{saveload::MarkerAllocator, Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||
use specs::{
|
||||
saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, System,
|
||||
SystemData, World, WriteStorage,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
time: Read<'a, Time>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
uid_allocator: Read<'a, UidAllocator>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
positions: ReadStorage<'a, Pos>,
|
||||
last_positions: ReadStorage<'a, Last<Pos>>,
|
||||
orientations: ReadStorage<'a, Ori>,
|
||||
scales: ReadStorage<'a, Scale>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
healths: ReadStorage<'a, Health>,
|
||||
inventories: ReadStorage<'a, Inventory>,
|
||||
groups: ReadStorage<'a, Group>,
|
||||
physics_states: ReadStorage<'a, PhysicsState>,
|
||||
energies: ReadStorage<'a, Energy>,
|
||||
}
|
||||
|
||||
/// This system is responsible for handling accepted inputs like moving or
|
||||
/// attacking
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
Read<'a, EventBus<LocalEvent>>,
|
||||
Read<'a, Time>,
|
||||
Read<'a, DeltaTime>,
|
||||
Read<'a, UidAllocator>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Last<Pos>>,
|
||||
ReadStorage<'a, Ori>,
|
||||
ReadStorage<'a, Scale>,
|
||||
ReadStorage<'a, Body>,
|
||||
ReadStorage<'a, Health>,
|
||||
ReadStorage<'a, Inventory>,
|
||||
ReadStorage<'a, group::Group>,
|
||||
ReadStorage<'a, PhysicsState>,
|
||||
ReadData<'a>,
|
||||
WriteStorage<'a, Shockwave>,
|
||||
WriteStorage<'a, ShockwaveHitEntities>,
|
||||
ReadStorage<'a, Energy>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(
|
||||
entities,
|
||||
server_bus,
|
||||
local_bus,
|
||||
time,
|
||||
dt,
|
||||
uid_allocator,
|
||||
uids,
|
||||
positions,
|
||||
last_positions,
|
||||
orientations,
|
||||
scales,
|
||||
bodies,
|
||||
healths,
|
||||
inventories,
|
||||
groups,
|
||||
physics_states,
|
||||
mut shockwaves,
|
||||
mut shockwave_hit_lists,
|
||||
energies,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let mut server_emitter = server_bus.emitter();
|
||||
let _local_emitter = local_bus.emitter();
|
||||
fn run(&mut self, (read_data, mut shockwaves, mut shockwave_hit_lists): Self::SystemData) {
|
||||
let mut server_emitter = read_data.server_bus.emitter();
|
||||
|
||||
let time = time.0;
|
||||
let dt = dt.0;
|
||||
let time = read_data.time.0;
|
||||
let dt = read_data.dt.0;
|
||||
|
||||
// Shockwaves
|
||||
for (entity, pos, ori, shockwave, shockwave_hit_list) in (
|
||||
&entities,
|
||||
&positions,
|
||||
&orientations,
|
||||
&read_data.entities,
|
||||
&read_data.positions,
|
||||
&read_data.orientations,
|
||||
&shockwaves,
|
||||
&mut shockwave_hit_lists,
|
||||
)
|
||||
@ -119,32 +101,20 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let shockwave_owner = shockwave
|
||||
.owner
|
||||
.and_then(|uid| uid_allocator.retrieve_entity_internal(uid.into()));
|
||||
.and_then(|uid| read_data.uid_allocator.retrieve_entity_internal(uid.into()));
|
||||
|
||||
// Group to ignore collisions with
|
||||
// Might make this more nuanced if shockwaves are used for non damage effects
|
||||
let group = shockwave_owner.and_then(|e| groups.get(e));
|
||||
let group = shockwave_owner.and_then(|e| read_data.groups.get(e));
|
||||
|
||||
// Go through all other effectable entities
|
||||
for (
|
||||
b,
|
||||
uid_b,
|
||||
pos_b,
|
||||
last_pos_b_maybe,
|
||||
scale_b_maybe,
|
||||
health_b,
|
||||
body_b,
|
||||
physics_state_b,
|
||||
) in (
|
||||
&entities,
|
||||
&uids,
|
||||
&positions,
|
||||
// TODO: make sure that these are maintained on the client and remove `.maybe()`
|
||||
last_positions.maybe(),
|
||||
scales.maybe(),
|
||||
&healths,
|
||||
&bodies,
|
||||
&physics_states,
|
||||
for (target, uid_b, pos_b, health_b, body_b, physics_state_b) in (
|
||||
&read_data.entities,
|
||||
&read_data.uids,
|
||||
&read_data.positions,
|
||||
&read_data.healths,
|
||||
&read_data.bodies,
|
||||
&read_data.physics_states,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -159,10 +129,10 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// 2D versions
|
||||
let pos_b2 = pos_b.0.xy();
|
||||
let last_pos_b2_maybe = last_pos_b_maybe.map(|p| (p.0).0.xy());
|
||||
let last_pos_b2_maybe = read_data.last_positions.get(target).map(|p| (p.0).0.xy());
|
||||
|
||||
// Scales
|
||||
let scale_b = scale_b_maybe.map_or(1.0, |s| s.0);
|
||||
let scale_b = read_data.scales.get(target).map_or(1.0, |s| s.0);
|
||||
let rad_b = body_b.radius() * scale_b;
|
||||
|
||||
// Angle checks
|
||||
@ -171,7 +141,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// See if entities are in the same group
|
||||
let same_group = group
|
||||
.map(|group_a| Some(group_a) == groups.get(b))
|
||||
.map(|group_a| Some(group_a) == read_data.groups.get(target))
|
||||
.unwrap_or(Some(*uid_b) == shockwave.owner);
|
||||
|
||||
let target_group = if same_group {
|
||||
@ -181,7 +151,7 @@ impl<'a> System<'a> for Sys {
|
||||
};
|
||||
|
||||
// Check if it is a hit
|
||||
let hit = entity != b
|
||||
let hit = entity != target
|
||||
&& !health_b.is_dead
|
||||
// Collision shapes
|
||||
&& {
|
||||
@ -203,14 +173,14 @@ impl<'a> System<'a> for Sys {
|
||||
.map(|(entity, uid)| AttackerInfo {
|
||||
entity,
|
||||
uid,
|
||||
energy: energies.get(entity),
|
||||
energy: read_data.energies.get(entity),
|
||||
});
|
||||
|
||||
shockwave.properties.attack.apply_attack(
|
||||
target_group,
|
||||
attacker_info,
|
||||
b,
|
||||
inventories.get(b),
|
||||
target,
|
||||
read_data.inventories.get(target),
|
||||
dir,
|
||||
false,
|
||||
1.0,
|
||||
|
@ -12,73 +12,75 @@ use common::{
|
||||
uid::Uid,
|
||||
};
|
||||
use hashbrown::HashSet;
|
||||
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage};
|
||||
use specs::{
|
||||
shred::ResourceId, Entities, Join, Read, ReadExpect, ReadStorage, System, SystemData, World,
|
||||
Write, WriteStorage,
|
||||
};
|
||||
use vek::Vec3;
|
||||
|
||||
const ENERGY_REGEN_ACCEL: f32 = 10.0;
|
||||
const POISE_REGEN_ACCEL: f32 = 2.0;
|
||||
|
||||
#[derive(SystemData)]
|
||||
pub struct ReadData<'a> {
|
||||
entities: Entities<'a>,
|
||||
dt: Read<'a, DeltaTime>,
|
||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||
metrics: ReadExpect<'a, SysMetrics>,
|
||||
positions: ReadStorage<'a, Pos>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
char_states: ReadStorage<'a, CharacterState>,
|
||||
}
|
||||
|
||||
/// This system kills players, levels them up, and regenerates energy.
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
#[allow(clippy::type_complexity)]
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
Read<'a, DeltaTime>,
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
ReadExpect<'a, SysMetrics>,
|
||||
WriteStorage<'a, CharacterState>,
|
||||
ReadData<'a>,
|
||||
WriteStorage<'a, Stats>,
|
||||
WriteStorage<'a, Health>,
|
||||
WriteStorage<'a, Poise>,
|
||||
WriteStorage<'a, Energy>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, Pos>,
|
||||
Write<'a, Vec<Outcome>>,
|
||||
ReadStorage<'a, Body>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(
|
||||
entities,
|
||||
dt,
|
||||
server_event_bus,
|
||||
sys_metrics,
|
||||
character_states,
|
||||
read_data,
|
||||
mut stats,
|
||||
mut healths,
|
||||
mut poises,
|
||||
mut energies,
|
||||
uids,
|
||||
positions,
|
||||
mut outcomes,
|
||||
bodies,
|
||||
): Self::SystemData,
|
||||
) {
|
||||
let start_time = std::time::Instant::now();
|
||||
span!(_guard, "run", "stats::Sys::run");
|
||||
let mut server_event_emitter = server_event_bus.emitter();
|
||||
let mut server_event_emitter = read_data.server_bus.emitter();
|
||||
let dt = read_data.dt.0;
|
||||
|
||||
// Increment last change timer
|
||||
healths.set_event_emission(false); // avoid unnecessary syncing
|
||||
poises.set_event_emission(false); // avoid unnecessary syncing
|
||||
for mut health in (&mut healths).join() {
|
||||
health.last_change.0 += f64::from(dt.0);
|
||||
health.last_change.0 += f64::from(dt);
|
||||
}
|
||||
for mut poise in (&mut poises).join() {
|
||||
poise.last_change.0 += f64::from(dt.0);
|
||||
poise.last_change.0 += f64::from(dt);
|
||||
}
|
||||
healths.set_event_emission(true);
|
||||
poises.set_event_emission(true);
|
||||
|
||||
// Update stats
|
||||
for (entity, uid, mut stats, mut health, pos) in (
|
||||
&entities,
|
||||
&uids,
|
||||
&read_data.entities,
|
||||
&read_data.uids,
|
||||
&mut stats.restrict_mut(),
|
||||
&mut healths.restrict_mut(),
|
||||
&positions,
|
||||
&read_data.positions,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -127,7 +129,7 @@ impl<'a> System<'a> for Sys {
|
||||
&mut stats.restrict_mut(),
|
||||
&mut healths.restrict_mut(),
|
||||
&mut energies.restrict_mut(),
|
||||
&bodies,
|
||||
&read_data.bodies,
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -159,7 +161,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Update energies and poises
|
||||
for (character_state, mut energy, mut poise) in (
|
||||
&character_states,
|
||||
&read_data.char_states,
|
||||
&mut energies.restrict_mut(),
|
||||
&mut poises.restrict_mut(),
|
||||
)
|
||||
@ -186,13 +188,12 @@ impl<'a> System<'a> for Sys {
|
||||
let energy = &mut *energy;
|
||||
// Have to account for Calc I differential equations due to acceleration
|
||||
energy.change_by(EnergyChange {
|
||||
amount: (energy.regen_rate * dt.0
|
||||
+ ENERGY_REGEN_ACCEL * dt.0.powi(2) / 2.0)
|
||||
amount: (energy.regen_rate * dt + ENERGY_REGEN_ACCEL * dt.powi(2) / 2.0)
|
||||
as i32,
|
||||
source: EnergySource::Regen,
|
||||
});
|
||||
energy.regen_rate =
|
||||
(energy.regen_rate + ENERGY_REGEN_ACCEL * dt.0).min(100.0);
|
||||
(energy.regen_rate + ENERGY_REGEN_ACCEL * dt).min(100.0);
|
||||
}
|
||||
|
||||
let res_poise = {
|
||||
@ -205,14 +206,14 @@ impl<'a> System<'a> for Sys {
|
||||
let poise = &mut *poise;
|
||||
poise.change_by(
|
||||
PoiseChange {
|
||||
amount: (poise.regen_rate * dt.0
|
||||
+ POISE_REGEN_ACCEL * dt.0.powi(2) / 2.0)
|
||||
amount: (poise.regen_rate * dt
|
||||
+ POISE_REGEN_ACCEL * dt.powi(2) / 2.0)
|
||||
as i32,
|
||||
source: PoiseSource::Regen,
|
||||
},
|
||||
Vec3::zero(),
|
||||
);
|
||||
poise.regen_rate = (poise.regen_rate + POISE_REGEN_ACCEL * dt.0).min(10.0);
|
||||
poise.regen_rate = (poise.regen_rate + POISE_REGEN_ACCEL * dt).min(10.0);
|
||||
}
|
||||
},
|
||||
// Ability and glider use does not regen and sets the rate back to zero.
|
||||
@ -255,7 +256,7 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
}
|
||||
|
||||
sys_metrics.stats_ns.store(
|
||||
read_data.metrics.stats_ns.store(
|
||||
start_time.elapsed().as_nanos() as u64,
|
||||
std::sync::atomic::Ordering::Relaxed,
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user