2020-12-01 00:28:00 +00:00
|
|
|
use common::{
|
2020-11-15 21:05:02 +00:00
|
|
|
comp::{
|
2021-01-16 17:01:57 +00:00
|
|
|
skills::{GeneralSkill, Skill},
|
2021-01-09 16:56:19 +00:00
|
|
|
CharacterState, Energy, EnergyChange, EnergySource, Health, Pos, Stats,
|
2020-11-15 21:05:02 +00:00
|
|
|
},
|
2020-01-11 04:08:33 +00:00
|
|
|
event::{EventBus, ServerEvent},
|
2020-09-14 12:56:05 +00:00
|
|
|
metrics::SysMetrics,
|
2021-01-04 19:29:15 +00:00
|
|
|
outcome::Outcome,
|
2020-12-01 00:28:00 +00:00
|
|
|
resources::DeltaTime,
|
2020-08-26 08:11:31 +00:00
|
|
|
span,
|
2021-01-04 19:29:15 +00:00
|
|
|
uid::Uid,
|
2019-05-17 20:47:58 +00:00
|
|
|
};
|
2020-12-13 17:21:51 +00:00
|
|
|
use hashbrown::HashSet;
|
2021-01-04 19:29:15 +00:00
|
|
|
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteStorage};
|
2019-05-17 20:47:58 +00:00
|
|
|
|
2020-02-24 19:57:33 +00:00
|
|
|
const ENERGY_REGEN_ACCEL: f32 = 10.0;
|
2019-05-17 20:47:58 +00:00
|
|
|
|
2019-11-22 00:53:28 +00:00
|
|
|
/// This system kills players, levels them up, and regenerates energy.
|
2019-05-17 20:47:58 +00:00
|
|
|
pub struct Sys;
|
|
|
|
impl<'a> System<'a> for Sys {
|
2020-06-10 19:47:36 +00:00
|
|
|
#[allow(clippy::type_complexity)]
|
2019-05-17 20:47:58 +00:00
|
|
|
type SystemData = (
|
|
|
|
Entities<'a>,
|
2019-05-19 20:14:18 +00:00
|
|
|
Read<'a, DeltaTime>,
|
2019-08-25 14:49:54 +00:00
|
|
|
Read<'a, EventBus<ServerEvent>>,
|
2020-09-14 12:56:05 +00:00
|
|
|
ReadExpect<'a, SysMetrics>,
|
2019-11-20 18:31:36 +00:00
|
|
|
ReadStorage<'a, CharacterState>,
|
2019-05-19 20:14:18 +00:00
|
|
|
WriteStorage<'a, Stats>,
|
2020-10-31 22:34:08 +00:00
|
|
|
WriteStorage<'a, Health>,
|
2019-11-20 18:31:36 +00:00
|
|
|
WriteStorage<'a, Energy>,
|
2021-01-04 19:29:15 +00:00
|
|
|
ReadStorage<'a, Uid>,
|
2021-01-09 16:56:19 +00:00
|
|
|
ReadStorage<'a, Pos>,
|
2021-01-04 19:29:15 +00:00
|
|
|
Write<'a, Vec<Outcome>>,
|
2019-05-17 20:47:58 +00:00
|
|
|
);
|
|
|
|
|
2019-11-20 18:31:36 +00:00
|
|
|
fn run(
|
|
|
|
&mut self,
|
2020-10-31 22:34:08 +00:00
|
|
|
(
|
|
|
|
entities,
|
|
|
|
dt,
|
|
|
|
server_event_bus,
|
|
|
|
sys_metrics,
|
|
|
|
character_states,
|
|
|
|
mut stats,
|
|
|
|
mut healths,
|
|
|
|
mut energies,
|
2021-01-04 19:29:15 +00:00
|
|
|
uids,
|
2021-01-09 16:56:19 +00:00
|
|
|
positions,
|
2021-01-04 19:29:15 +00:00
|
|
|
mut outcomes,
|
2020-10-31 22:34:08 +00:00
|
|
|
): Self::SystemData,
|
2019-11-20 18:31:36 +00:00
|
|
|
) {
|
2020-09-14 12:56:05 +00:00
|
|
|
let start_time = std::time::Instant::now();
|
2020-09-07 04:59:16 +00:00
|
|
|
span!(_guard, "run", "stats::Sys::run");
|
2019-11-23 08:26:39 +00:00
|
|
|
let mut server_event_emitter = server_event_bus.emitter();
|
2019-08-23 10:11:37 +00:00
|
|
|
|
2019-12-01 21:54:21 +00:00
|
|
|
// Increment last change timer
|
2020-10-31 22:34:08 +00:00
|
|
|
healths.set_event_emission(false); // avoid unnecessary syncing
|
2021-01-07 20:25:12 +00:00
|
|
|
for mut health in (&mut healths).join() {
|
2020-10-31 22:34:08 +00:00
|
|
|
health.last_change.0 += f64::from(dt.0);
|
2019-12-01 21:54:21 +00:00
|
|
|
}
|
2020-10-31 22:34:08 +00:00
|
|
|
healths.set_event_emission(true);
|
2019-12-01 21:54:21 +00:00
|
|
|
|
2020-07-05 12:39:28 +00:00
|
|
|
// Update stats
|
2021-01-09 16:56:19 +00:00
|
|
|
for (entity, uid, mut stats, mut health, pos) in (
|
2020-10-31 22:34:08 +00:00
|
|
|
&entities,
|
2021-01-04 19:29:15 +00:00
|
|
|
&uids,
|
2020-10-31 22:34:08 +00:00
|
|
|
&mut stats.restrict_mut(),
|
|
|
|
&mut healths.restrict_mut(),
|
2021-01-09 16:56:19 +00:00
|
|
|
&positions,
|
2020-10-31 22:34:08 +00:00
|
|
|
)
|
|
|
|
.join()
|
|
|
|
{
|
2020-11-15 21:05:02 +00:00
|
|
|
let set_dead = {
|
2020-10-31 22:34:08 +00:00
|
|
|
let health = health.get_unchecked();
|
2020-11-15 21:05:02 +00:00
|
|
|
health.should_die() && !health.is_dead
|
2019-12-01 21:54:21 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if set_dead {
|
2021-01-07 20:25:12 +00:00
|
|
|
let mut health = health.get_mut_unchecked();
|
2019-11-23 08:26:39 +00:00
|
|
|
server_event_emitter.emit(ServerEvent::Destroy {
|
2019-05-27 19:41:24 +00:00
|
|
|
entity,
|
2020-10-31 22:34:08 +00:00
|
|
|
cause: health.last_change.1.cause,
|
2019-08-23 10:11:37 +00:00
|
|
|
});
|
|
|
|
|
2020-10-31 22:34:08 +00:00
|
|
|
health.is_dead = true;
|
2019-05-17 20:47:58 +00:00
|
|
|
}
|
2019-08-23 10:11:37 +00:00
|
|
|
|
2020-11-15 21:05:02 +00:00
|
|
|
let stat = stats.get_unchecked();
|
2021-01-16 17:01:57 +00:00
|
|
|
let skills_to_level = stat
|
|
|
|
.skill_set
|
|
|
|
.skill_groups
|
|
|
|
.iter()
|
|
|
|
.filter_map(|s_g| {
|
2021-01-18 19:08:13 +00:00
|
|
|
(s_g.exp >= stat.skill_set.skill_point_cost(s_g.skill_group_kind))
|
|
|
|
.then(|| s_g.skill_group_kind)
|
2021-01-16 17:01:57 +00:00
|
|
|
})
|
|
|
|
.collect::<HashSet<_>>();
|
2019-11-23 08:26:39 +00:00
|
|
|
|
2020-11-15 21:05:02 +00:00
|
|
|
if !skills_to_level.is_empty() {
|
|
|
|
let mut stat = stats.get_mut_unchecked();
|
2021-01-16 17:01:57 +00:00
|
|
|
for skill_group in skills_to_level {
|
2021-01-08 20:53:52 +00:00
|
|
|
stat.skill_set.earn_skill_point(skill_group);
|
2021-01-04 19:29:15 +00:00
|
|
|
outcomes.push(Outcome::SkillPointGain {
|
|
|
|
uid: *uid,
|
|
|
|
skill_tree: skill_group,
|
2021-01-16 17:01:57 +00:00
|
|
|
total_points: stat.skill_set.earned_sp(skill_group),
|
2021-01-09 16:56:19 +00:00
|
|
|
pos: pos.0,
|
2021-01-04 19:29:15 +00:00
|
|
|
});
|
2020-11-15 21:05:02 +00:00
|
|
|
}
|
2019-08-03 19:30:01 +00:00
|
|
|
}
|
2020-07-05 12:39:28 +00:00
|
|
|
}
|
2019-11-20 18:31:36 +00:00
|
|
|
|
2020-12-31 18:37:25 +00:00
|
|
|
// Apply effects from leveling skills
|
|
|
|
for (mut stats, mut health, mut energy) in (
|
|
|
|
&mut stats.restrict_mut(),
|
|
|
|
&mut healths.restrict_mut(),
|
|
|
|
&mut energies.restrict_mut(),
|
|
|
|
)
|
|
|
|
.join()
|
|
|
|
{
|
|
|
|
let stat = stats.get_unchecked();
|
|
|
|
if stat.skill_set.modify_health {
|
|
|
|
let mut health = health.get_mut_unchecked();
|
|
|
|
let health_level = stat
|
|
|
|
.skill_set
|
2021-01-16 17:01:57 +00:00
|
|
|
.skill_level(Skill::General(GeneralSkill::HealthIncrease))
|
|
|
|
.unwrap_or(None)
|
2020-12-31 18:37:25 +00:00
|
|
|
.unwrap_or(0);
|
2021-01-04 17:16:42 +00:00
|
|
|
health.update_max_hp(Some(stat.body_type), health_level);
|
2020-12-31 18:37:25 +00:00
|
|
|
let mut stat = stats.get_mut_unchecked();
|
|
|
|
stat.skill_set.modify_health = false;
|
|
|
|
}
|
|
|
|
let stat = stats.get_unchecked();
|
|
|
|
if stat.skill_set.modify_energy {
|
|
|
|
let mut energy = energy.get_mut_unchecked();
|
|
|
|
let energy_level = stat
|
|
|
|
.skill_set
|
2021-01-16 17:01:57 +00:00
|
|
|
.skill_level(Skill::General(GeneralSkill::EnergyIncrease))
|
|
|
|
.unwrap_or(None)
|
2021-01-04 17:16:42 +00:00
|
|
|
.unwrap_or(0);
|
|
|
|
energy.update_max_energy(Some(stat.body_type), energy_level);
|
2020-12-31 18:37:25 +00:00
|
|
|
let mut stat = stats.get_mut_unchecked();
|
|
|
|
stat.skill_set.modify_energy = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-05 12:39:28 +00:00
|
|
|
// Update energies
|
2020-07-05 14:06:01 +00:00
|
|
|
for (character_state, mut energy) in
|
|
|
|
(&character_states, &mut energies.restrict_mut()).join()
|
2020-07-05 12:39:28 +00:00
|
|
|
{
|
2020-02-03 21:02:32 +00:00
|
|
|
match character_state {
|
2020-06-13 11:35:31 +00:00
|
|
|
// Accelerate recharging energy.
|
2020-06-13 03:45:03 +00:00
|
|
|
CharacterState::Idle { .. }
|
|
|
|
| CharacterState::Sit { .. }
|
|
|
|
| CharacterState::Dance { .. }
|
2020-08-02 05:09:11 +00:00
|
|
|
| CharacterState::Sneak { .. }
|
2020-06-16 21:32:39 +00:00
|
|
|
| CharacterState::GlideWield { .. }
|
2020-06-13 03:45:03 +00:00
|
|
|
| CharacterState::Wielding { .. }
|
|
|
|
| CharacterState::Equipping { .. }
|
|
|
|
| CharacterState::Boost { .. } => {
|
2020-06-08 18:37:41 +00:00
|
|
|
let res = {
|
2020-01-19 19:19:44 +00:00
|
|
|
let energy = energy.get_unchecked();
|
|
|
|
energy.current() < energy.maximum()
|
2020-06-08 18:37:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if res {
|
2020-01-19 19:19:44 +00:00
|
|
|
let mut energy = energy.get_mut_unchecked();
|
2021-01-07 20:25:12 +00:00
|
|
|
let energy = &mut *energy;
|
2020-01-19 19:44:44 +00:00
|
|
|
// Have to account for Calc I differential equations due to acceleration
|
2020-10-30 21:49:58 +00:00
|
|
|
energy.change_by(EnergyChange {
|
|
|
|
amount: (energy.regen_rate * dt.0
|
2020-11-25 00:28:24 +00:00
|
|
|
+ ENERGY_REGEN_ACCEL * dt.0.powi(2) / 2.0)
|
2020-01-19 21:39:01 +00:00
|
|
|
as i32,
|
2020-10-30 21:49:58 +00:00
|
|
|
source: EnergySource::Regen,
|
|
|
|
});
|
2020-02-24 19:57:33 +00:00
|
|
|
energy.regen_rate =
|
|
|
|
(energy.regen_rate + ENERGY_REGEN_ACCEL * dt.0).min(100.0);
|
2020-01-19 19:19:44 +00:00
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2020-12-13 19:49:11 +00:00
|
|
|
// Ability and glider use does not regen and sets the rate back to zero.
|
|
|
|
CharacterState::Glide { .. }
|
|
|
|
| CharacterState::BasicMelee { .. }
|
2020-06-13 03:45:03 +00:00
|
|
|
| CharacterState::DashMelee { .. }
|
2020-07-03 15:40:12 +00:00
|
|
|
| CharacterState::LeapMelee { .. }
|
2020-07-08 19:58:41 +00:00
|
|
|
| CharacterState::SpinMelee { .. }
|
2020-09-04 01:54:59 +00:00
|
|
|
| CharacterState::ComboMelee { .. }
|
2020-07-26 03:06:53 +00:00
|
|
|
| CharacterState::BasicRanged { .. }
|
2020-09-21 04:16:52 +00:00
|
|
|
| CharacterState::ChargedMelee { .. }
|
2020-08-08 20:53:55 +00:00
|
|
|
| CharacterState::ChargedRanged { .. }
|
2020-09-21 04:16:52 +00:00
|
|
|
| CharacterState::RepeaterRanged { .. }
|
2020-10-04 01:24:15 +00:00
|
|
|
| CharacterState::Shockwave { .. }
|
2020-08-27 00:08:29 +00:00
|
|
|
| CharacterState::BasicBeam { .. } => {
|
2020-01-19 19:19:44 +00:00
|
|
|
if energy.get_unchecked().regen_rate != 0.0 {
|
|
|
|
energy.get_mut_unchecked().regen_rate = 0.0
|
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2020-08-25 12:21:25 +00:00
|
|
|
// recover small amount of passive energy from blocking, and bonus energy from
|
2020-07-01 09:51:06 +00:00
|
|
|
// blocking attacks?
|
|
|
|
CharacterState::BasicBlock => {
|
|
|
|
let res = {
|
|
|
|
let energy = energy.get_unchecked();
|
|
|
|
energy.current() < energy.maximum()
|
|
|
|
};
|
|
|
|
|
|
|
|
if res {
|
2020-10-30 21:49:58 +00:00
|
|
|
energy.get_mut_unchecked().change_by(EnergyChange {
|
|
|
|
amount: -3,
|
|
|
|
source: EnergySource::Regen,
|
|
|
|
});
|
2020-07-01 09:51:06 +00:00
|
|
|
}
|
|
|
|
},
|
2020-06-13 11:35:31 +00:00
|
|
|
// Non-combat abilities that consume energy;
|
|
|
|
// temporarily stall energy gain, but preserve regen_rate.
|
|
|
|
CharacterState::Roll { .. } | CharacterState::Climb { .. } => {},
|
2019-08-03 19:30:01 +00:00
|
|
|
}
|
2019-05-17 20:47:58 +00:00
|
|
|
}
|
2020-09-14 12:56:05 +00:00
|
|
|
sys_metrics.stats_ns.store(
|
2020-12-15 23:51:07 +00:00
|
|
|
start_time.elapsed().as_nanos() as u64,
|
2020-09-14 12:56:05 +00:00
|
|
|
std::sync::atomic::Ordering::Relaxed,
|
|
|
|
);
|
2019-05-17 20:47:58 +00:00
|
|
|
}
|
|
|
|
}
|