Merge branch 'sam/ecs-sys-struct' into 'master'

Sam/ecs sys struct

See merge request veloren/veloren!1813
This commit is contained in:
Marcel 2021-02-23 00:08:09 +00:00
commit 6081ebbe15
12 changed files with 445 additions and 529 deletions

View File

@ -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: [],

View File

@ -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,

View File

@ -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,
}

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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,
);

View File

@ -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,
);

View File

@ -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,
);

View File

@ -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,
);

View File

@ -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,

View File

@ -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,
);