From 6ce422748cc617f1dd326cda517c0e77425abb3d Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 7 Jan 2021 14:47:29 -0500 Subject: [PATCH] Added SkillSetConfig to specify skill sets for npcs. --- common/src/comp/skills.rs | 30 ++++++++-------- common/src/generation.rs | 16 ++++++--- common/src/lib.rs | 2 ++ common/src/skillset_builder.rs | 59 ++++++++++++++++++++++++++++++++ server/src/sys/terrain.rs | 12 ++++--- voxygen/src/hud/mod.rs | 8 ++--- world/src/site/dungeon/mod.rs | 24 ++++++------- world/src/site/settlement/mod.rs | 4 +-- 8 files changed, 114 insertions(+), 41 deletions(-) create mode 100644 common/src/skillset_builder.rs diff --git a/common/src/comp/skills.rs b/common/src/comp/skills.rs index fe4a1c183e..37b71f8abf 100644 --- a/common/src/comp/skills.rs +++ b/common/src/comp/skills.rs @@ -332,7 +332,7 @@ impl SkillSet { /// assert_eq!(skillset.skills.len(), 1); /// ``` pub fn unlock_skill(&mut self, skill: Skill) { - if let Some(skill_group_type) = SkillSet::get_skill_group_type_for_skill(&skill) { + if let Some(skill_group_type) = skill.get_skill_group_type() { let next_level = if self.skills.contains_key(&skill) { self.skills.get(&skill).copied().flatten().map(|l| l + 1) } else { @@ -395,7 +395,7 @@ impl SkillSet { /// ``` pub fn refund_skill(&mut self, skill: Skill) { if self.skills.contains_key(&skill) { - if let Some(skill_group_type) = SkillSet::get_skill_group_type_for_skill(&skill) { + if let Some(skill_group_type) = skill.get_skill_group_type() { if let Some(mut skill_group) = self .skill_groups .iter_mut() @@ -423,18 +423,6 @@ impl SkillSet { } } - /// Returns the skill group type for a skill from the static skill group - /// definitions. - fn get_skill_group_type_for_skill(skill: &Skill) -> Option { - SKILL_GROUP_DEFS.iter().find_map(|(key, val)| { - if val.contains(&skill) { - Some(*key) - } else { - None - } - }) - } - /// Adds skill points to a skill group as long as the player has that skill /// group type. /// @@ -538,7 +526,7 @@ impl SkillSet { /// Checks if player has sufficient skill points to purchase a skill pub fn sufficient_skill_points(&self, skill: Skill) -> bool { - if let Some(skill_group_type) = SkillSet::get_skill_group_type_for_skill(&skill) { + if let Some(skill_group_type) = skill.get_skill_group_type() { if let Some(skill_group) = self .skill_groups .iter() @@ -589,6 +577,18 @@ impl Skill { /// Returns the maximum level a skill can reach, returns None if the skill /// doesn't level pub fn get_max_level(self) -> Option { SKILL_MAX_LEVEL.get(&self).copied().flatten() } + + /// Returns the skill group type for a skill from the static skill group + /// definitions. + pub fn get_skill_group_type(self) -> Option { + SKILL_GROUP_DEFS.iter().find_map(|(key, val)| { + if val.contains(&self) { + Some(*key) + } else { + None + } + }) + } } #[cfg(test)] diff --git a/common/src/generation.rs b/common/src/generation.rs index aa8992dfa9..b1568cefc6 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -1,6 +1,7 @@ use crate::{ comp::{self, humanoid, inventory::loadout_builder::LoadoutConfig, Alignment, Body, Item}, npc::{self, NPC_NAMES}, + skillset_builder::SkillSetConfig, }; use vek::*; @@ -23,7 +24,8 @@ pub struct EntityInfo { // TODO: Properly give NPCs skills pub level: Option, pub loot_drop: Option, - pub config: Option, + pub loadout_config: Option, + pub skillset_config: Option, pub pet: Option>, } @@ -42,7 +44,8 @@ impl EntityInfo { scale: 1.0, level: None, loot_drop: None, - config: None, + loadout_config: None, + skillset_config: None, pet: None, } } @@ -109,8 +112,13 @@ impl EntityInfo { self } - pub fn with_config(mut self, config: LoadoutConfig) -> Self { - self.config = Some(config); + pub fn with_loadout_config(mut self, config: LoadoutConfig) -> Self { + self.loadout_config = Some(config); + self + } + + pub fn with_skillset_config(mut self, config: SkillSetConfig) -> Self { + self.skillset_config = Some(config); self } diff --git a/common/src/lib.rs b/common/src/lib.rs index f762badc07..83e93fbccf 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -43,6 +43,7 @@ pub mod recipe; pub mod region; pub mod resources; pub mod rtsim; +pub mod skillset_builder; pub mod spiral; pub mod states; pub mod store; @@ -57,3 +58,4 @@ pub mod volumes; pub use combat::{Damage, DamageSource, GroupTarget, Knockback}; pub use comp::inventory::loadout_builder::LoadoutBuilder; pub use explosion::{Explosion, RadiusEffect}; +pub use skillset_builder::SkillSetBuilder; diff --git a/common/src/skillset_builder.rs b/common/src/skillset_builder.rs new file mode 100644 index 0000000000..d2abc8cbac --- /dev/null +++ b/common/src/skillset_builder.rs @@ -0,0 +1,59 @@ +use crate::comp::{ + item::tool::ToolKind, + skills::{Skill, SkillGroupType, SkillSet, SwordSkill}, +}; +use tracing::warn; + +#[derive(Copy, Clone)] +pub enum SkillSetConfig { + Guard, + Villager, + Outcast, + Highwayman, + Bandit, + CultistNovice, + CultistAcolyte, + Warlord, + Warlock, +} + +pub struct SkillSetBuilder(SkillSet); + +impl Default for SkillSetBuilder { + fn default() -> Self { Self(SkillSet::default()) } +} + +impl SkillSetBuilder { + pub fn build_skillset(config: SkillSetConfig) -> Self { + let mut skillset = Self::default(); + use SkillSetConfig::*; + match config { + Guard => { + skillset.with_skill_group(SkillGroupType::Weapon(ToolKind::Sword)); + skillset.with_skill(Skill::Sword(SwordSkill::SUnlockSpin)); + }, + _ => {}, + } + skillset + } + + pub fn with_skill(&mut self, skill: Skill) { + if let Some(skill_group) = skill.get_skill_group_type() { + self.0 + .add_skill_points(skill_group, self.0.skill_point_cost(skill)); + self.0.unlock_skill(skill); + if !self.0.skills.contains_key(&skill) { + warn!( + "Failed to add skill. Verify that it has the appropriate skill group \ + available." + ); + } + } + } + + pub fn with_skill_group(&mut self, skill_group: SkillGroupType) { + self.0.unlock_skill_group(skill_group); + } + + pub fn build(self) -> SkillSet { self.0 } +} diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 164f2e56b6..4310aad765 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -9,7 +9,7 @@ use common::{ npc::NPC_NAMES, span, terrain::TerrainGrid, - LoadoutBuilder, + LoadoutBuilder, SkillSetBuilder, }; use common_net::msg::ServerGeneral; use common_sys::state::TerrainChanges; @@ -148,9 +148,13 @@ impl<'a> System<'a> for Sys { scale = 2.0 + rand::random::(); } - let config = entity.config; + let loadout_config = entity.loadout_config; + let skillset_config = entity.skillset_config; - let loadout = LoadoutBuilder::build_loadout(body, main_tool, config).build(); + let loadout = LoadoutBuilder::build_loadout(body, main_tool, loadout_config).build(); + if let Some(config) = skillset_config { + stats.skill_set = SkillSetBuilder::build_skillset(config).build(); + } let health = comp::Health::new(stats.body_type, entity.level.unwrap_or(0)); @@ -184,7 +188,7 @@ impl<'a> System<'a> for Sys { can_speak, &body, matches!( - config, + loadout_config, Some(comp::inventory::loadout_builder::LoadoutConfig::Guard) ), )) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index be2e617d06..fa100c2087 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -853,7 +853,7 @@ impl Hud { let scales = ecs.read_storage::(); let bodies = ecs.read_storage::(); let items = ecs.read_storage::(); - let loadouts = ecs.read_storage::(); + let inventories = ecs.read_storage::(); let entities = ecs.entities(); let me = client.entity(); //self.input = client.read_storage::(); @@ -1276,7 +1276,7 @@ impl Hud { &bodies, &hp_floater_lists, &uids, - &loadouts, + &inventories, ) .join() .filter(|t| { @@ -1297,7 +1297,7 @@ impl Hud { body, hpfl, uid, - loadout, + inventory, )| { // Use interpolated position if available let pos = interpolated.map_or(pos.0, |i| i.pos); @@ -1330,7 +1330,7 @@ impl Hud { health, buffs, energy, - combat_rating: combat::combat_rating(loadout, health, &stats.body_type), + combat_rating: combat::combat_rating(inventory, health, &stats.body_type), }); let bubble = if dist_sqr < SPEECH_BUBBLE_RANGE.powi(2) { speech_bubbles.get(uid) diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 6ca04f7f43..694d2d35c9 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -599,7 +599,7 @@ impl Floor { //.do_if(is_giant, |e| e.into_giant()) .with_body(comp::Body::Humanoid(comp::humanoid::Body::random())) .with_alignment(comp::Alignment::Enemy) - .with_config(loadout_builder::LoadoutConfig::CultistAcolyte) + .with_loadout_config(loadout_builder::LoadoutConfig::CultistAcolyte) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_level(dynamic_rng.gen_range( (room.difficulty as f32).powf(1.25) + 3.0, @@ -608,7 +608,7 @@ impl Floor { let entity = match room.difficulty { 0 => entity .with_name("Outcast") - .with_config(loadout_builder::LoadoutConfig::Outcast) + .with_loadout_config(loadout_builder::LoadoutConfig::Outcast) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -622,7 +622,7 @@ impl Floor { )), 1 => entity .with_name("Highwayman") - .with_config(loadout_builder::LoadoutConfig::Highwayman) + .with_loadout_config(loadout_builder::LoadoutConfig::Highwayman) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -636,7 +636,7 @@ impl Floor { )), 2 => entity .with_name("Bandit") - .with_config(loadout_builder::LoadoutConfig::Bandit) + .with_loadout_config(loadout_builder::LoadoutConfig::Bandit) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -650,7 +650,7 @@ impl Floor { )), 3 => entity .with_name("Cultist Novice") - .with_config(loadout_builder::LoadoutConfig::CultistNovice) + .with_loadout_config(loadout_builder::LoadoutConfig::CultistNovice) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -664,7 +664,7 @@ impl Floor { )), 4 => entity .with_name("Cultist Acolyte") - .with_config(loadout_builder::LoadoutConfig::CultistAcolyte) + .with_loadout_config(loadout_builder::LoadoutConfig::CultistAcolyte) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -679,14 +679,14 @@ impl Floor { 5 => match dynamic_rng.gen_range(0, 6) { 0 => entity .with_name("Cultist Warlock") - .with_config(loadout_builder::LoadoutConfig::Warlock) + .with_loadout_config(loadout_builder::LoadoutConfig::Warlock) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( "common.items.npc_weapons.staff.cultist_staff", )), _ => entity .with_name("Cultist Warlord") - .with_config(loadout_builder::LoadoutConfig::Warlord) + .with_loadout_config(loadout_builder::LoadoutConfig::Warlord) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 5) { @@ -755,7 +755,7 @@ impl Floor { )) .with_name("Outcast Leader".to_string()) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) - .with_config(loadout_builder::LoadoutConfig::Outcast) + .with_loadout_config(loadout_builder::LoadoutConfig::Outcast) .with_scale(2.0) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -801,7 +801,7 @@ impl Floor { )) .with_name("Bandit Captain".to_string()) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) - .with_config(loadout_builder::LoadoutConfig::Bandit) + .with_loadout_config(loadout_builder::LoadoutConfig::Bandit) .with_scale(2.0) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -822,7 +822,7 @@ impl Floor { )) .with_name("Cultist Acolyte".to_string()) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) - .with_config(loadout_builder::LoadoutConfig::CultistAcolyte) + .with_loadout_config(loadout_builder::LoadoutConfig::CultistAcolyte) .with_scale(2.0) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { @@ -973,7 +973,7 @@ impl Floor { )) .with_name("Animal Trainer".to_string()) .with_loot_drop(comp::Item::new_from_asset_expect(chosen)) - .with_config(loadout_builder::LoadoutConfig::CultistAcolyte) + .with_loadout_config(loadout_builder::LoadoutConfig::CultistAcolyte) .with_scale(2.0) .with_main_tool(comp::Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 6) { diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index cd0a8906a4..6bb7c9e7fb 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -934,7 +934,7 @@ impl Settlement { )) .with_name("Guard") .with_level(dynamic_rng.gen_range(10, 15)) - .with_config(loadout_builder::LoadoutConfig::Guard), + .with_loadout_config(loadout_builder::LoadoutConfig::Guard), _ => entity .with_main_tool(Item::new_from_asset_expect( match dynamic_rng.gen_range(0, 7) { @@ -948,7 +948,7 @@ impl Settlement { //_ => "common.items.npc_weapons.bow.starter_bow", TODO: Re-Add this when we have a better way of distributing npc_weapons here }, )) - .with_config(loadout_builder::LoadoutConfig::Villager), + .with_loadout_config(loadout_builder::LoadoutConfig::Villager), } });