2020-12-01 00:28:00 +00:00
|
|
|
use common::{
|
2021-06-06 23:48:47 +00:00
|
|
|
combat,
|
2020-11-15 21:05:02 +00:00
|
|
|
comp::{
|
2021-03-04 20:43:58 +00:00
|
|
|
self,
|
2021-01-16 17:01:57 +00:00
|
|
|
skills::{GeneralSkill, Skill},
|
2021-09-25 18:07:47 +00:00
|
|
|
Body, CharacterState, Combo, Energy, Health, Inventory, Poise, Pos, SkillSet, Stats,
|
|
|
|
StatsModifier,
|
2020-11-15 21:05:02 +00:00
|
|
|
},
|
2020-01-11 04:08:33 +00:00
|
|
|
event::{EventBus, ServerEvent},
|
2021-01-04 19:29:15 +00:00
|
|
|
outcome::Outcome,
|
2021-05-05 13:54:24 +00:00
|
|
|
resources::{DeltaTime, EntitiesDiedLastTick, Time},
|
2021-01-04 19:29:15 +00:00
|
|
|
uid::Uid,
|
2019-05-17 20:47:58 +00:00
|
|
|
};
|
2021-03-08 22:40:02 +00:00
|
|
|
use common_ecs::{Job, Origin, Phase, System};
|
2020-12-13 17:21:51 +00:00
|
|
|
use hashbrown::HashSet;
|
2021-02-22 19:27:34 +00:00
|
|
|
use specs::{
|
2021-03-08 08:36:53 +00:00
|
|
|
shred::ResourceId, Entities, Join, Read, ReadStorage, SystemData, World, Write, WriteStorage,
|
2021-02-22 19:27:34 +00:00
|
|
|
};
|
2021-10-13 14:44:28 +00:00
|
|
|
use tracing::warn;
|
2020-12-16 23:30:33 +00:00
|
|
|
use vek::Vec3;
|
2019-05-17 20:47:58 +00:00
|
|
|
|
2021-09-14 02:16:01 +00:00
|
|
|
const ENERGY_REGEN_ACCEL: f32 = 1.0;
|
2020-12-16 23:30:33 +00:00
|
|
|
const POISE_REGEN_ACCEL: f32 = 2.0;
|
2019-05-17 20:47:58 +00:00
|
|
|
|
2021-02-22 19:27:34 +00:00
|
|
|
#[derive(SystemData)]
|
2021-02-22 21:02:37 +00:00
|
|
|
pub struct ReadData<'a> {
|
2021-02-22 19:27:34 +00:00
|
|
|
entities: Entities<'a>,
|
|
|
|
dt: Read<'a, DeltaTime>,
|
2021-02-27 19:55:06 +00:00
|
|
|
time: Read<'a, Time>,
|
2021-02-22 19:27:34 +00:00
|
|
|
server_bus: Read<'a, EventBus<ServerEvent>>,
|
|
|
|
positions: ReadStorage<'a, Pos>,
|
|
|
|
uids: ReadStorage<'a, Uid>,
|
|
|
|
bodies: ReadStorage<'a, Body>,
|
|
|
|
char_states: ReadStorage<'a, CharacterState>,
|
2021-05-21 00:52:29 +00:00
|
|
|
inventories: ReadStorage<'a, Inventory>,
|
2021-02-22 19:27:34 +00:00
|
|
|
}
|
|
|
|
|
2019-11-22 00:53:28 +00:00
|
|
|
/// This system kills players, levels them up, and regenerates energy.
|
2021-03-04 14:00:16 +00:00
|
|
|
#[derive(Default)]
|
2019-05-17 20:47:58 +00:00
|
|
|
pub struct Sys;
|
2021-03-08 11:13:59 +00:00
|
|
|
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 = (
|
2021-02-22 21:02:37 +00:00
|
|
|
ReadData<'a>,
|
2019-05-19 20:14:18 +00:00
|
|
|
WriteStorage<'a, Stats>,
|
2021-04-14 15:35:34 +00:00
|
|
|
WriteStorage<'a, SkillSet>,
|
2020-10-31 22:34:08 +00:00
|
|
|
WriteStorage<'a, Health>,
|
2020-12-10 00:32:24 +00:00
|
|
|
WriteStorage<'a, Poise>,
|
2019-11-20 18:31:36 +00:00
|
|
|
WriteStorage<'a, Energy>,
|
2021-02-27 19:55:06 +00:00
|
|
|
WriteStorage<'a, Combo>,
|
2021-05-05 13:54:24 +00:00
|
|
|
Write<'a, EntitiesDiedLastTick>,
|
2021-01-04 19:29:15 +00:00
|
|
|
Write<'a, Vec<Outcome>>,
|
2019-05-17 20:47:58 +00:00
|
|
|
);
|
|
|
|
|
2021-03-04 14:00:16 +00:00
|
|
|
const NAME: &'static str = "stats";
|
|
|
|
const ORIGIN: Origin = Origin::Common;
|
|
|
|
const PHASE: Phase = Phase::Create;
|
|
|
|
|
2019-11-20 18:31:36 +00:00
|
|
|
fn run(
|
2021-03-08 11:13:59 +00:00
|
|
|
_job: &mut Job<Self>,
|
2020-10-31 22:34:08 +00:00
|
|
|
(
|
2021-02-22 21:02:37 +00:00
|
|
|
read_data,
|
2021-04-14 15:35:34 +00:00
|
|
|
stats,
|
|
|
|
mut skill_sets,
|
2020-10-31 22:34:08 +00:00
|
|
|
mut healths,
|
2020-12-10 00:32:24 +00:00
|
|
|
mut poises,
|
2020-10-31 22:34:08 +00:00
|
|
|
mut energies,
|
2021-02-27 19:55:06 +00:00
|
|
|
mut combos,
|
2021-05-05 13:54:24 +00:00
|
|
|
mut entities_died_last_tick,
|
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
|
|
|
) {
|
2021-05-05 13:54:24 +00:00
|
|
|
entities_died_last_tick.0.clear();
|
2021-02-22 21:02:37 +00:00
|
|
|
let mut server_event_emitter = read_data.server_bus.emitter();
|
|
|
|
let dt = read_data.dt.0;
|
2019-08-23 10:11:37 +00:00
|
|
|
|
2020-07-05 12:39:28 +00:00
|
|
|
// Update stats
|
2021-05-21 00:52:29 +00:00
|
|
|
for (entity, uid, stats, mut skill_set, mut health, pos, mut energy, inventory) in (
|
2021-02-22 21:02:37 +00:00
|
|
|
&read_data.entities,
|
|
|
|
&read_data.uids,
|
2021-04-14 15:35:34 +00:00
|
|
|
&stats,
|
2021-07-27 20:22:39 +00:00
|
|
|
&mut skill_sets,
|
|
|
|
&mut healths,
|
2021-02-22 21:02:37 +00:00
|
|
|
&read_data.positions,
|
2021-07-27 20:22:39 +00:00
|
|
|
&mut energies,
|
2021-05-21 00:52:29 +00:00
|
|
|
read_data.inventories.maybe(),
|
2020-10-31 22:34:08 +00:00
|
|
|
)
|
|
|
|
.join()
|
|
|
|
{
|
2021-07-27 20:22:39 +00:00
|
|
|
let set_dead = { health.should_die() && !health.is_dead };
|
2019-12-01 21:54:21 +00:00
|
|
|
|
|
|
|
if set_dead {
|
2021-05-05 13:54:24 +00:00
|
|
|
let cloned_entity = (entity, *pos);
|
|
|
|
entities_died_last_tick.0.push(cloned_entity);
|
2019-11-23 08:26:39 +00:00
|
|
|
server_event_emitter.emit(ServerEvent::Destroy {
|
2019-05-27 19:41:24 +00:00
|
|
|
entity,
|
2021-11-13 20:46:45 +00:00
|
|
|
cause: health.last_change,
|
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
|
|
|
}
|
2021-04-14 15:35:34 +00:00
|
|
|
let stat = stats;
|
2021-03-20 17:29:57 +00:00
|
|
|
|
|
|
|
let update_max_hp = {
|
2021-09-09 04:07:17 +00:00
|
|
|
stat.max_health_modifiers.update_maximum()
|
2021-09-10 19:20:14 +00:00
|
|
|
|| (health.base_max() - health.maximum()).abs() > Health::HEALTH_EPSILON
|
2021-03-20 17:29:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
if update_max_hp {
|
2021-09-09 04:07:17 +00:00
|
|
|
health.update_maximum(stat.max_health_modifiers);
|
2021-03-20 17:29:57 +00:00
|
|
|
}
|
|
|
|
|
2021-09-14 02:16:01 +00:00
|
|
|
let (change_energy, energy_mods) = {
|
2021-06-06 23:48:47 +00:00
|
|
|
// Calculates energy scaling from stats and inventory
|
2021-09-14 02:16:01 +00:00
|
|
|
let energy_mods = StatsModifier {
|
|
|
|
add_mod: stat.max_energy_modifiers.add_mod
|
|
|
|
+ combat::compute_max_energy_mod(inventory),
|
|
|
|
mult_mod: stat.max_energy_modifiers.mult_mod,
|
|
|
|
};
|
2021-06-06 23:48:47 +00:00
|
|
|
(
|
2021-09-14 02:16:01 +00:00
|
|
|
energy_mods.update_maximum()
|
|
|
|
|| (energy.base_max() - energy.maximum()).abs() > Energy::ENERGY_EPSILON,
|
|
|
|
energy_mods,
|
2021-06-06 23:48:47 +00:00
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
// If modifier sufficiently different, mutably access energy
|
|
|
|
if change_energy {
|
2021-09-14 02:16:01 +00:00
|
|
|
energy.update_maximum(energy_mods);
|
2021-06-06 23:48:47 +00:00
|
|
|
}
|
2021-05-21 00:52:29 +00:00
|
|
|
|
2021-07-27 20:22:39 +00:00
|
|
|
let skills_to_level = skill_set
|
2021-11-15 01:11:58 +00:00
|
|
|
.skill_groups()
|
2021-01-16 17:01:57 +00:00
|
|
|
.filter_map(|s_g| {
|
2021-12-02 04:38:33 +00:00
|
|
|
(s_g.available_exp >= skill_set.skill_point_cost(s_g.skill_group_kind))
|
2021-01-18 19:08:13 +00:00
|
|
|
.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() {
|
2021-01-16 17:01:57 +00:00
|
|
|
for skill_group in skills_to_level {
|
2021-12-01 04:12:02 +00:00
|
|
|
match skill_set.earn_skill_point(skill_group) {
|
|
|
|
Ok(_) => outcomes.push(Outcome::SkillPointGain {
|
|
|
|
uid: *uid,
|
|
|
|
skill_tree: skill_group,
|
|
|
|
total_points: skill_set.earned_sp(skill_group),
|
|
|
|
pos: pos.0,
|
|
|
|
}),
|
|
|
|
Err(_) => warn!(
|
2021-10-13 14:44:28 +00:00
|
|
|
"Attempted to add skill point to group which is inelgible to earn one"
|
2021-12-01 04:12:02 +00:00
|
|
|
),
|
2021-10-13 14:44:28 +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
|
2021-04-14 15:35:34 +00:00
|
|
|
for (mut skill_set, mut health, mut energy, body) in (
|
2021-07-27 20:22:39 +00:00
|
|
|
&mut skill_sets,
|
|
|
|
&mut healths,
|
|
|
|
&mut energies,
|
2021-02-22 21:02:37 +00:00
|
|
|
&read_data.bodies,
|
2020-12-31 18:37:25 +00:00
|
|
|
)
|
|
|
|
.join()
|
|
|
|
{
|
2021-07-27 20:22:39 +00:00
|
|
|
if skill_set.modify_health {
|
|
|
|
let health_level = skill_set
|
2021-01-16 17:01:57 +00:00
|
|
|
.skill_level(Skill::General(GeneralSkill::HealthIncrease))
|
2020-12-31 18:37:25 +00:00
|
|
|
.unwrap_or(0);
|
2021-09-09 04:07:17 +00:00
|
|
|
health.update_max_hp(*body, health_level);
|
2021-07-27 20:22:39 +00:00
|
|
|
skill_set.modify_health = false;
|
2020-12-31 18:37:25 +00:00
|
|
|
}
|
2021-07-27 20:22:39 +00:00
|
|
|
if skill_set.modify_energy {
|
|
|
|
let energy_level = skill_set
|
2021-01-16 17:01:57 +00:00
|
|
|
.skill_level(Skill::General(GeneralSkill::EnergyIncrease))
|
2021-01-04 17:16:42 +00:00
|
|
|
.unwrap_or(0);
|
2021-09-14 02:16:01 +00:00
|
|
|
energy.update_max_energy(*body, energy_level);
|
2021-04-14 15:35:34 +00:00
|
|
|
skill_set.modify_energy = false;
|
2020-12-31 18:37:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-10 00:32:24 +00:00
|
|
|
// Update energies and poises
|
2021-07-27 20:22:39 +00:00
|
|
|
for (character_state, mut energy, mut poise) in
|
|
|
|
(&read_data.char_states, &mut energies, &mut poises).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 { .. }
|
2021-01-31 20:29:50 +00:00
|
|
|
| CharacterState::Talk { .. }
|
2020-06-13 03:45:03 +00:00
|
|
|
| CharacterState::Sit { .. }
|
|
|
|
| CharacterState::Dance { .. }
|
2021-04-27 14:41:48 +00:00
|
|
|
| CharacterState::Glide { .. }
|
2020-06-16 21:32:39 +00:00
|
|
|
| CharacterState::GlideWield { .. }
|
2020-06-13 03:45:03 +00:00
|
|
|
| CharacterState::Wielding { .. }
|
|
|
|
| CharacterState::Equipping { .. }
|
|
|
|
| CharacterState::Boost { .. } => {
|
2021-07-27 20:22:39 +00:00
|
|
|
let res = { energy.current() < energy.maximum() };
|
2020-06-08 18:37:41 +00:00
|
|
|
|
|
|
|
if res {
|
2021-01-07 20:25:12 +00:00
|
|
|
let energy = &mut *energy;
|
2021-09-14 02:16:01 +00:00
|
|
|
energy.change_by(energy.regen_rate * dt);
|
|
|
|
energy.regen_rate = (energy.regen_rate + ENERGY_REGEN_ACCEL * dt).min(10.0);
|
2020-01-19 19:19:44 +00:00
|
|
|
}
|
2020-12-10 00:32:24 +00:00
|
|
|
|
2021-07-27 20:22:39 +00:00
|
|
|
let res_poise = { poise.current() < poise.maximum() };
|
2020-12-10 00:32:24 +00:00
|
|
|
|
2020-12-16 23:30:33 +00:00
|
|
|
if res_poise {
|
2021-01-27 03:05:13 +00:00
|
|
|
let poise = &mut *poise;
|
2021-09-25 18:07:47 +00:00
|
|
|
poise.change_by(poise.regen_rate * dt, Vec3::zero());
|
2021-02-22 19:27:34 +00:00
|
|
|
poise.regen_rate = (poise.regen_rate + POISE_REGEN_ACCEL * dt).min(10.0);
|
2020-12-16 23:30:33 +00:00
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2021-04-27 14:41:48 +00:00
|
|
|
// Ability use does not regen and sets the rate back to zero.
|
|
|
|
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 { .. }
|
2021-03-01 20:44:29 +00:00
|
|
|
| CharacterState::BasicBeam { .. }
|
2021-03-06 21:29:00 +00:00
|
|
|
| CharacterState::BasicAura { .. }
|
2021-03-21 05:53:39 +00:00
|
|
|
| CharacterState::Blink { .. }
|
2021-04-24 19:01:36 +00:00
|
|
|
| CharacterState::BasicSummon { .. }
|
2021-06-19 18:53:23 +00:00
|
|
|
| CharacterState::SelfBuff { .. }
|
|
|
|
| CharacterState::SpriteSummon { .. } => {
|
2021-07-27 20:22:39 +00:00
|
|
|
if energy.regen_rate != 0.0 {
|
|
|
|
energy.regen_rate = 0.0
|
2020-01-19 19:19:44 +00:00
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2021-04-10 03:40:20 +00:00
|
|
|
// Abilities that temporarily stall energy gain, but preserve regen_rate.
|
2020-12-05 18:23:45 +00:00
|
|
|
CharacterState::Roll { .. }
|
|
|
|
| CharacterState::Climb { .. }
|
2021-04-10 03:40:20 +00:00
|
|
|
| CharacterState::Stunned { .. }
|
2021-06-25 01:38:11 +00:00
|
|
|
| CharacterState::BasicBlock { .. }
|
2021-08-20 04:23:39 +00:00
|
|
|
| CharacterState::UseItem { .. }
|
|
|
|
| CharacterState::SpriteInteract { .. } => {},
|
2019-08-03 19:30:01 +00:00
|
|
|
}
|
2019-05-17 20:47:58 +00:00
|
|
|
}
|
2021-01-15 03:32:12 +00:00
|
|
|
|
2021-02-27 19:55:06 +00:00
|
|
|
// Decay combo
|
|
|
|
for (_, mut combo) in (&read_data.entities, &mut combos).join() {
|
2021-03-04 20:43:58 +00:00
|
|
|
if combo.counter() > 0
|
|
|
|
&& read_data.time.0 - combo.last_increase() > comp::combo::COMBO_DECAY_START
|
|
|
|
{
|
2021-02-27 19:55:06 +00:00
|
|
|
combo.reset();
|
|
|
|
}
|
|
|
|
}
|
2019-05-17 20:47:58 +00:00
|
|
|
}
|
|
|
|
}
|