diff --git a/client/src/lib.rs b/client/src/lib.rs index 466778ab1f..1331838ab5 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -939,16 +939,50 @@ impl Client { /* Ok(()) => self.send_msg(ClientGeneral::ChatMsg(message)), */ Ok(()) => { if message.starts_with('@') { - if message == "@stats" { - let stats = self - .state - .ecs() - .read_storage::() - .get(self.entity) - .cloned() - .unwrap(); + use comp::{item::tool::ToolKind::*, skills::*}; + match message.as_str() { + "@stats" => { + let stats = self + .state + .ecs() + .read_storage::() + .get(self.entity) + .cloned() + .unwrap(); - tracing::info!("{:?}", stats.skill_set); + tracing::info!("{:?}", stats.skill_set); + }, + "@unlock sword" => { + self.send_msg(ClientGeneral::UnlockSkill(Skill::UnlockGroup( + SkillGroupType::Weapon(Sword), + ))); + }, + "@unlock axe" => { + self.send_msg(ClientGeneral::UnlockSkill(Skill::UnlockGroup( + SkillGroupType::Weapon(Axe), + ))); + }, + "@unlock hammer" => { + self.send_msg(ClientGeneral::UnlockSkill(Skill::UnlockGroup( + SkillGroupType::Weapon(Hammer), + ))); + }, + "@unlock bow" => { + self.send_msg(ClientGeneral::UnlockSkill(Skill::UnlockGroup( + SkillGroupType::Weapon(Bow), + ))); + }, + "@unlock staff" => { + self.send_msg(ClientGeneral::UnlockSkill(Skill::UnlockGroup( + SkillGroupType::Weapon(Staff), + ))); + }, + "@unlock sceptre" => { + self.send_msg(ClientGeneral::UnlockSkill(Skill::UnlockGroup( + SkillGroupType::Weapon(Sceptre), + ))); + }, + _ => {}, } } else { self.send_msg(ClientGeneral::ChatMsg(message)) diff --git a/common/src/comp/skills.rs b/common/src/comp/skills.rs index 8261231763..720572b176 100644 --- a/common/src/comp/skills.rs +++ b/common/src/comp/skills.rs @@ -14,6 +14,13 @@ lazy_static! { defs.insert( SkillGroupType::General, [ Skill::General(GeneralSkill::HealthIncrease1), + Skill::General(GeneralSkill::HealthIncrease2), + Skill::UnlockGroup(SkillGroupType::Weapon(ToolKind::Sword)), + Skill::UnlockGroup(SkillGroupType::Weapon(ToolKind::Axe)), + Skill::UnlockGroup(SkillGroupType::Weapon(ToolKind::Hammer)), + Skill::UnlockGroup(SkillGroupType::Weapon(ToolKind::Bow)), + Skill::UnlockGroup(SkillGroupType::Weapon(ToolKind::Staff)), + Skill::UnlockGroup(SkillGroupType::Weapon(ToolKind::Sceptre)), ].iter().cloned().collect::>()); defs.insert( SkillGroupType::Weapon(ToolKind::Sword), [ @@ -56,6 +63,7 @@ pub enum Skill { Bow(BowSkill), Staff(StaffSkill), Sceptre(SceptreSkill), + UnlockGroup(SkillGroupType), } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] @@ -91,12 +99,7 @@ pub enum SceptreSkill { #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] pub enum GeneralSkill { HealthIncrease1, - UnlockSwordTree, - UnlockAxeTree, - UnlockHammerTree, - UnlockBowTree, - UnlockStaffTree, - UnlockSceptreTree, + HealthIncrease2, } #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] @@ -111,8 +114,8 @@ pub enum SkillGroupType { #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct SkillGroup { pub skill_group_type: SkillGroupType, - pub exp: u32, - pub available_sp: u8, + pub exp: u16, + pub available_sp: u16, } impl SkillGroup { @@ -141,11 +144,7 @@ impl Default for SkillSet { fn default() -> Self { // TODO: Default skill groups for new players? Self { - skill_groups: vec![ - SkillGroup::new(SkillGroupType::General), - SkillGroup::new(SkillGroupType::Weapon(ToolKind::Sword)), - SkillGroup::new(SkillGroupType::Weapon(ToolKind::Bow)), - ], + skill_groups: vec![SkillGroup::new(SkillGroupType::General)], skills: HashSet::new(), } } @@ -200,6 +199,9 @@ impl SkillSet { { if skill_group.available_sp > 0 { skill_group.available_sp -= 1; + if let Skill::UnlockGroup(group) = skill { + self.unlock_skill_group(group); + } self.skills.insert(skill); } else { warn!("Tried to unlock skill for skill group with no available SP"); @@ -284,7 +286,7 @@ impl SkillSet { pub fn add_skill_points( &mut self, skill_group_type: SkillGroupType, - number_of_skill_points: u8, + number_of_skill_points: u16, ) { if let Some(mut skill_group) = self .skill_groups @@ -305,14 +307,15 @@ impl SkillSet { .any(|x| x.skill_group_type == skill_group_type) } - /// Adds experience to the skill group within an entity's skill set - pub fn add_experience(&mut self, skill_group_type: SkillGroupType, amount: u32) { + /// Adds/subtracts experience to the skill group within an entity's skill + /// set + pub fn change_experience(&mut self, skill_group_type: SkillGroupType, amount: i32) { if let Some(mut skill_group) = self .skill_groups .iter_mut() .find(|x| x.skill_group_type == skill_group_type) { - skill_group.exp += amount; + skill_group.exp = (skill_group.exp as i32 + amount) as u16; } else { warn!("Tried to add experience to a skill group that player does not have"); } diff --git a/common/sys/src/stats.rs b/common/sys/src/stats.rs index 0ce26b9b80..5de01c0dbe 100644 --- a/common/sys/src/stats.rs +++ b/common/sys/src/stats.rs @@ -1,11 +1,14 @@ use common::{ - comp::{CharacterState, Energy, EnergyChange, EnergySource, Health, HealthSource, Stats}, + comp::{ + skills::SkillGroupType, CharacterState, Energy, EnergyChange, EnergySource, Health, Stats, + }, event::{EventBus, ServerEvent}, metrics::SysMetrics, resources::DeltaTime, span, }; use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage}; +use std::collections::HashSet; const ENERGY_REGEN_ACCEL: f32 = 10.0; @@ -56,13 +59,9 @@ impl<'a> System<'a> for Sys { ) .join() { - let (set_dead, level_up) = { - let stat = stats.get_unchecked(); + let set_dead = { let health = health.get_unchecked(); - ( - health.should_die() && !health.is_dead, - stat.exp.current() >= stat.exp.maximum(), - ) + health.should_die() && !health.is_dead }; if set_dead { @@ -75,20 +74,22 @@ impl<'a> System<'a> for Sys { health.is_dead = true; } - if level_up { - let mut stat = stats.get_mut_unchecked(); - let stat = &mut *stat; - while stat.exp.current() >= stat.exp.maximum() { - stat.exp.change_by(-(stat.exp.maximum() as i64)); - stat.level.change_by(1); - stat.exp.update_maximum(stat.level.level()); - server_event_emitter.emit(ServerEvent::LevelUp(entity, stat.level.level())); + let mut skills_to_level = HashSet::::new(); + let stat = stats.get_unchecked(); + { + for skill_group in stat.skill_set.skill_groups.iter() { + if skill_group.exp >= 300 { + skills_to_level.insert(skill_group.skill_group_type); + } } + } - let mut health = health.get_mut_unchecked(); - let health = &mut *health; - health.update_max_hp(Some(stat.body_type), stat.level.level()); - health.set_to(health.maximum(), HealthSource::LevelUp); + if !skills_to_level.is_empty() { + let mut stat = stats.get_mut_unchecked(); + for skill_group in skills_to_level.drain() { + stat.skill_set.change_experience(skill_group, -300); + stat.skill_set.add_skill_points(skill_group, 1); + } } } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 21d83483c5..ce0f57a066 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -255,7 +255,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc for pool in xp_pools.drain() { stats .skill_set - .add_experience(pool, (exp / num_pools).ceil() as u32); + .change_experience(pool, (exp / num_pools).ceil() as i32); } } }); @@ -293,7 +293,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc for pool in xp_pools.drain() { attacker_stats .skill_set - .add_experience(pool, (exp_reward / num_pools).ceil() as u32); + .change_experience(pool, (exp_reward / num_pools).ceil() as i32); } } })();