From fe45a158edb73ccdef587de89b8df560c7ecf69f Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 13 Oct 2021 10:44:28 -0400 Subject: [PATCH] Skill groups now only persist earned_exp instead of available_exp, earned_sp, and available_sp. --- common/src/comp/skills.rs | 59 ++++++++++++++----- common/src/outcome.rs | 2 +- common/systems/src/stats.rs | 9 ++- server/src/events/entity_manipulation.rs | 4 +- server/src/events/interaction.rs | 4 +- .../src/migrations/V45__skill_persistence.sql | 1 + server/src/persistence/character.rs | 24 +++----- .../src/persistence/character/conversions.rs | 16 ++--- server/src/persistence/models.rs | 4 +- voxygen/src/hud/diary.rs | 2 +- voxygen/src/hud/mod.rs | 2 +- 11 files changed, 76 insertions(+), 51 deletions(-) create mode 100644 server/src/migrations/V45__skill_persistence.sql diff --git a/common/src/comp/skills.rs b/common/src/comp/skills.rs index 5c9cf61a14..8b6e34268b 100644 --- a/common/src/comp/skills.rs +++ b/common/src/comp/skills.rs @@ -659,7 +659,7 @@ pub enum SkillGroupKind { impl SkillGroupKind { /// Gets the cost in experience of earning a skill point - pub fn skill_point_cost(self, level: u16) -> u16 { + pub fn skill_point_cost(self, level: u16) -> u32 { const EXP_INCREMENT: f32 = 10.0; const STARTING_EXP: f32 = 70.0; const EXP_CEILING: f32 = 1000.0; @@ -670,7 +670,7 @@ impl SkillGroupKind { / (1.0 + std::f32::consts::E.powf(-SCALING_FACTOR * level as f32) * (EXP_CEILING / STARTING_EXP - 1.0))) - .floor()) as u16 + .floor()) as u32 } /// Gets the total amount of skill points that can be spent in a particular @@ -694,7 +694,10 @@ impl SkillGroupKind { #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct SkillGroup { pub skill_group_kind: SkillGroupKind, - pub exp: u16, + // How much exp has been used for skill points + pub spent_exp: u32, + // How much exp has been earned in total + pub earned_exp: u32, pub available_sp: u16, pub earned_sp: u16, } @@ -703,11 +706,29 @@ impl SkillGroup { fn new(skill_group_kind: SkillGroupKind) -> SkillGroup { SkillGroup { skill_group_kind, - exp: 0, + spent_exp: 0, + earned_exp: 0, available_sp: 0, earned_sp: 0, } } + + /// Returns the available experience that could be used to earn another + /// skill point in a particular skill group. + pub fn available_experience(&self) -> u32 { self.earned_exp - self.spent_exp } + + /// Adds a skill point while subtracting the necessary amount of experience + pub fn earn_skill_point(&mut self) -> Result<(), SpRewardError> { + let sp_cost = self.skill_group_kind.skill_point_cost(self.earned_sp); + if self.available_experience() >= sp_cost { + self.spent_exp = self.spent_exp.saturating_add(sp_cost); + self.available_sp = self.available_sp.saturating_add(1); + self.earned_sp = self.earned_sp.saturating_add(1); + Ok(()) + } else { + Err(SpRewardError::InsufficientExp) + } + } } /// Contains all of a player's skill groups and skills. Provides methods for @@ -882,12 +903,14 @@ impl SkillSet { } /// Adds a skill point while subtracting the necessary amount of experience - pub fn earn_skill_point(&mut self, skill_group_kind: SkillGroupKind) { - let sp_cost = self.skill_point_cost(skill_group_kind); - if let Some(mut skill_group) = self.skill_group_mut(skill_group_kind) { - skill_group.exp = skill_group.exp.saturating_sub(sp_cost); - skill_group.available_sp = skill_group.available_sp.saturating_add(1); - skill_group.earned_sp = skill_group.earned_sp.saturating_add(1); + pub fn earn_skill_point( + &mut self, + skill_group_kind: SkillGroupKind, + ) -> Result<(), SpRewardError> { + if let Some(skill_group) = self.skill_group_mut(skill_group_kind) { + skill_group.earn_skill_point() + } else { + Err(SpRewardError::UnavailableSkillGroup) } } @@ -901,9 +924,9 @@ impl SkillSet { /// Adds/subtracts experience to the skill group within an entity's skill /// set - pub fn change_experience(&mut self, skill_group_kind: SkillGroupKind, amount: i32) { + pub fn add_experience(&mut self, skill_group_kind: SkillGroupKind, amount: u32) { if let Some(mut skill_group) = self.skill_group_mut(skill_group_kind) { - skill_group.exp = (skill_group.exp as i32 + amount) as u16; + skill_group.earned_exp = skill_group.earned_exp.saturating_add(amount); } else { warn!("Tried to add experience to a skill group that player does not have"); } @@ -943,8 +966,9 @@ impl SkillSet { } /// Gets the available experience for a particular skill group - pub fn experience(&self, skill_group: SkillGroupKind) -> u16 { - self.skill_group(skill_group).map_or(0, |s_g| s_g.exp) + pub fn available_experience(&self, skill_group: SkillGroupKind) -> u32 { + self.skill_group(skill_group) + .map_or(0, |s_g| s_g.available_experience()) } /// Gets skill point cost to purchase skill of next level @@ -980,7 +1004,7 @@ impl SkillSet { } /// Checks how much experience is needed for the next skill point in a tree - pub fn skill_point_cost(&self, skill_group: SkillGroupKind) -> u16 { + pub fn skill_point_cost(&self, skill_group: SkillGroupKind) -> u32 { if let Some(level) = self.skill_group(skill_group).map(|sg| sg.earned_sp) { skill_group.skill_point_cost(level) } else { @@ -1056,6 +1080,11 @@ impl Skill { } } +pub enum SpRewardError { + InsufficientExp, + UnavailableSkillGroup, +} + #[cfg(test)] mod tests { use super::*; diff --git a/common/src/outcome.rs b/common/src/outcome.rs index 51d87d54ce..d68204ca7c 100644 --- a/common/src/outcome.rs +++ b/common/src/outcome.rs @@ -36,7 +36,7 @@ pub enum Outcome { }, ExpChange { uid: Uid, - exp: i32, + exp: u32, xp_pools: HashSet, }, SkillPointGain { diff --git a/common/systems/src/stats.rs b/common/systems/src/stats.rs index 76536279bc..9b7a57cf81 100644 --- a/common/systems/src/stats.rs +++ b/common/systems/src/stats.rs @@ -16,6 +16,7 @@ use hashbrown::HashSet; use specs::{ shred::ResourceId, Entities, Join, Read, ReadStorage, SystemData, World, Write, WriteStorage, }; +use tracing::warn; use vek::Vec3; const ENERGY_REGEN_ACCEL: f32 = 1.0; @@ -132,14 +133,18 @@ impl<'a> System<'a> for Sys { .skill_groups .iter() .filter_map(|s_g| { - (s_g.exp >= skill_set.skill_point_cost(s_g.skill_group_kind)) + (s_g.available_experience() >= skill_set.skill_point_cost(s_g.skill_group_kind)) .then(|| s_g.skill_group_kind) }) .collect::>(); if !skills_to_level.is_empty() { for skill_group in skills_to_level { - skill_set.earn_skill_point(skill_group); + if skill_set.earn_skill_point(skill_group).is_err() { + warn!( + "Attempted to add skill point to group which is inelgible to earn one" + ); + } outcomes.push(Outcome::SkillPointGain { uid: *uid, skill_tree: skill_group, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index ec862c858e..c2e60efa8e 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1117,11 +1117,11 @@ fn handle_exp_gain( add_tool_from_slot(EquipSlot::InactiveOffhand); let num_pools = xp_pools.len() as f32; for pool in xp_pools.iter() { - skill_set.change_experience(*pool, (exp_reward / num_pools).ceil() as i32); + skill_set.add_experience(*pool, (exp_reward / num_pools).ceil() as u32); } outcomes.push(Outcome::ExpChange { uid: *uid, - exp: exp_reward as i32, + exp: exp_reward as u32, xp_pools, }); } diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index 5c41ad8b8a..4533275950 100644 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -301,7 +301,7 @@ fn within_mounting_range(player_position: Option<&Pos>, mount_position: Option<& } #[derive(Deserialize)] -struct ResourceExperienceManifest(HashMap); +struct ResourceExperienceManifest(HashMap); impl assets::Asset for ResourceExperienceManifest { type Loader = assets::RonLoader; @@ -339,7 +339,7 @@ pub fn handle_mine_block( .0 .get(item.item_definition_id()), ) { - skillset.change_experience(SkillGroupKind::Weapon(tool), *exp_reward); + skillset.add_experience(SkillGroupKind::Weapon(tool), *exp_reward); state .ecs() .write_resource::>() diff --git a/server/src/migrations/V45__skill_persistence.sql b/server/src/migrations/V45__skill_persistence.sql new file mode 100644 index 0000000000..ef23bea80b --- /dev/null +++ b/server/src/migrations/V45__skill_persistence.sql @@ -0,0 +1 @@ +-- Do skill group change of storing (available_exp, available_sp, and earned_sp) to earned_exp \ No newline at end of file diff --git a/server/src/persistence/character.rs b/server/src/persistence/character.rs index 7ce7c5e1bd..9771c93f9e 100644 --- a/server/src/persistence/character.rs +++ b/server/src/persistence/character.rs @@ -196,9 +196,7 @@ pub fn load_character_data( Ok(SkillGroup { entity_id: char_id, skill_group_kind: row.get(0)?, - exp: row.get(1)?, - available_sp: row.get(2)?, - earned_sp: row.get(3)?, + earned_exp: row.get(1)?, }) })? .filter_map(Result::ok) @@ -442,19 +440,15 @@ pub fn create_character( " INSERT INTO skill_group (entity_id, skill_group_kind, - exp, - available_sp, - earned_sp) - VALUES (?1, ?2, ?3, ?4, ?5)", + earned_exp) + VALUES (?1, ?2, ?3)", )?; for skill_group in db_skill_groups { stmt.execute(&[ &character_id as &dyn ToSql, &skill_group.skill_group_kind, - &skill_group.exp, - &skill_group.available_sp, - &skill_group.earned_sp, + &skill_group.earned_exp, ])?; } drop(stmt); @@ -1023,19 +1017,15 @@ pub fn update( REPLACE INTO skill_group (entity_id, skill_group_kind, - exp, - available_sp, - earned_sp) - VALUES (?1, ?2, ?3, ?4, ?5)", + earned_exp) + VALUES (?1, ?2, ?3)", )?; for skill_group in db_skill_groups { stmt.execute(&[ &skill_group.entity_id as &dyn ToSql, &skill_group.skill_group_kind, - &skill_group.exp, - &skill_group.available_sp, - &skill_group.earned_sp, + &skill_group.earned_exp, ])?; } diff --git a/server/src/persistence/character/conversions.rs b/server/src/persistence/character/conversions.rs index 4544c7da34..5567fc9bb4 100644 --- a/server/src/persistence/character/conversions.rs +++ b/server/src/persistence/character/conversions.rs @@ -525,12 +525,16 @@ fn convert_skill_groups_from_database(skill_groups: &[SkillGroup]) -> Vec Widget for Diary<'a> { } // Exp Bars and Rank Display - let current_exp = self.skill_set.experience(*sel_tab) as f64; + let current_exp = self.skill_set.available_experience(*sel_tab) as f64; let max_exp = self.skill_set.skill_point_cost(*sel_tab) as f64; let exp_percentage = current_exp / max_exp; let rank = self.skill_set.earned_sp(*sel_tab); diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index f2c42d0937..84ee7cb824 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -435,7 +435,7 @@ pub struct BuffInfo { pub struct ExpFloater { pub owner: Uid, - pub exp_change: i32, + pub exp_change: u32, pub timer: f32, pub rand_offset: (f32, f32), pub xp_pools: HashSet,