Load skillsets from assets

Done:
    support loading from assets in skillset_builder.rs
    entity_config field with skillset asset field
    move every SkillSet config to assets
    tests for skillset assets
    tests for assets in entity configs
This commit is contained in:
juliancoffee 2021-06-08 00:58:05 +03:00
parent 37af73b19e
commit 0c9f05b8d1
47 changed files with 585 additions and 840 deletions

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.adlet_bow")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-0.gnarling"),
loadout_asset: Some("common.loadout.dungeon.tier-0.gnarling"),
skillset_asset: Some("common.skillset.dungeon.tier-0.bow"),
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.wooden_spear")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-0.gnarling"),
loadout_asset: Some("common.loadout.dungeon.tier-0.gnarling"),
skillset_asset: None,
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.gnarling.gnoll_staff")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-0.gnarling"),
loadout_asset: Some("common.loadout.dungeon.tier-0.gnarling"),
skillset_asset: None,
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.adlet_bow")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-1.adlet_bow"),
loadout_asset: Some("common.loadout.dungeon.tier-1.adlet_bow"),
skillset_asset: Some("common.skillset.dungeon.tier-1.bow"),
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.wooden_spear")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-1.adlet_spear"),
loadout_asset: Some("common.loadout.dungeon.tier-1.adlet_spear"),
skillset_asset: None,
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.adlet.gnoll_staff")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-1.adlet_spear"),
loadout_asset: Some("common.loadout.dungeon.tier-1.adlet_spear"),
skillset_asset: None,
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.adlet_bow")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-2.sahagin"),
loadout_asset: Some("common.loadout.dungeon.tier-2.sahagin"),
skillset_asset: Some("common.skillset.dungeon.tier-2.bow"),
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.wooden_spear")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-2.sahagin"),
loadout_asset: Some("common.loadout.dungeon.tier-2.sahagin"),
skillset_asset: None,
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.sahagin.gnoll_staff")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-2.sahagin"),
loadout_asset: Some("common.loadout.dungeon.tier-2.sahagin"),
skillset_asset: None,
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.adlet_bow")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-3.haniwa"),
loadout_asset: Some("common.loadout.dungeon.tier-3.haniwa"),
skillset_asset: Some("common.skillset.dungeon.tier-3.bow"),
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.wooden_spear")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-3.haniwa"),
loadout_asset: Some("common.loadout.dungeon.tier-3.haniwa"),
skillset_asset: None,
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.haniwa.gnoll_staff")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-3.haniwa"),
loadout_asset: Some("common.loadout.dungeon.tier-3.haniwa"),
skillset_asset: None,
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.adlet_bow")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-4.myrmidon"),
loadout_asset: Some("common.loadout.dungeon.tier-4.myrmidon"),
skillset_asset: Some("common.skillset.dungeon.tier-4.bow"),
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.wooden_spear")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-4.myrmidon"),
loadout_asset: Some("common.loadout.dungeon.tier-4.myrmidon"),
skillset_asset: None,
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.npc_weapons.biped_small.myrmidon.gnoll_staff")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-4.myrmidon"),
loadout_asset: Some("common.loadout.dungeon.tier-4.myrmidon"),
skillset_asset: None,
)

View File

@ -8,5 +8,7 @@ EntityConfig (
])),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-5.beastmaster"),
loadout_asset: Some("common.loadout.dungeon.tier-5.beastmaster"),
// TODO: make skillset for him, I'm just too lazy
skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"),
)

View File

@ -0,0 +1,9 @@
EntityConfig (
name: Some("Mindflayer"),
main_tool: None,
second_tool: None,
loadout_asset: None,
skillset_asset: Some("common.skillset.dungeon.tier-5.mindflayer"),
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: None,
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-5.husk"),
loadout_asset: Some("common.loadout.dungeon.tier-5.husk"),
skillset_asset: None,
)

View File

@ -4,5 +4,6 @@ EntityConfig (
main_tool: Some(Item("common.items.weapons.staff.cultist_staff")),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-5.warlock"),
loadout_asset: Some("common.loadout.dungeon.tier-5.warlock"),
skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"),
)

View File

@ -10,5 +10,6 @@ EntityConfig (
])),
second_tool: None,
loadout_config: Some("common.loadout.dungeon.tier-5.warlord"),
loadout_asset: Some("common.loadout.dungeon.tier-5.warlord"),
skillset_asset: Some("common.skillset.dungeon.tier-5.enemy"),
)

View File

@ -16,10 +16,10 @@ EntityConfig (
second_tool: None,
/// Loadout Config (with asset_specifier for loadout)
loadout_config: Some("common.loadout.village.merchant"),
loadout_asset: Some("common.loadout.village.merchant"),
/// Skillset Config as Option<SkillSet> (with asset_specifier for skillset)
// skillset_config: None,
/// Skillset Config (with asset_specifier for skillset)
skillset_asset: Some("common.skillset.village.merchant"),
/// Loot
/// Can be Item (with asset_specifier for item)

View File

@ -0,0 +1,9 @@
EntityConfig (
name: Some("Guard"),
main_tool: Some(Item("common.items.weapons.sword.iron-4")),
second_tool: None,
loadout_asset: None,
skillset_asset: Some("common.skillset.village.guard"),
)

View File

@ -0,0 +1,9 @@
EntityConfig (
name: Some("Merchant"),
main_tool: Some(Item("common.items.weapons.bow.eldwood-0")),
second_tool: None,
loadout_asset: None,
skillset_asset: Some("common.skillset.village.merchant"),
)

View File

@ -0,0 +1,16 @@
EntityConfig (
name: None,
main_tool: Some(Choice([
(1.0, Some(Item("common.items.weapons.tool.broom"))),
(1.0, Some(Item("common.items.weapons.tool.hoe"))),
(1.0, Some(Item("common.items.weapons.tool.pickaxe"))),
(1.0, Some(Item("common.items.weapons.tool.rake"))),
(1.0, Some(Item("common.items.weapons.tool.shovel-0"))),
(1.0, Some(Item("common.items.weapons.tool.shovel-1"))),
])),
second_tool: None,
loadout_asset: None,
skillset_asset: None,
)

View File

@ -9,7 +9,7 @@
Armor(Chest): Item("common.items.npc_armor.chest.leather_blue"),
Armor(Legs): Item("common.items.npc_armor.pants.leather_blue"),
Armor(Shoulders): Item("common.items.armor.swift.shoulder"),
Armor(Shoulders): Item("common.items.armor.hide.leather.shoulder"),
Armor(Back): Choice([
(1.0, Some(Item("common.items.armor.hide.rawhide.back"))),

View File

@ -0,0 +1,12 @@
([
Group(Weapon(Bow)),
// Charged
Skill((Bow(CDamage), Some(1))),
Skill((Bow(CKnockback), Some(1))),
Skill((Bow(CSpeed), Some(1))),
Skill((Bow(CMove), Some(1))),
// Repeater
Skill((Bow(RDamage), Some(1))),
])

View File

@ -0,0 +1,12 @@
([
Group(Weapon(Bow)),
// Charged
Skill((Bow(CDamage), Some(1))),
Skill((Bow(CKnockback), Some(1))),
Skill((Bow(CSpeed), Some(1))),
Skill((Bow(CMove), Some(1))),
// Repeater
Skill((Bow(RDamage), Some(1))),
])

View File

@ -0,0 +1,12 @@
([
Group(Weapon(Bow)),
// Charged
Skill((Bow(CDamage), Some(1))),
Skill((Bow(CKnockback), Some(1))),
Skill((Bow(CSpeed), Some(1))),
Skill((Bow(CMove), Some(1))),
// Repeater
Skill((Bow(RDamage), Some(1))),
])

View File

@ -0,0 +1,12 @@
([
Group(Weapon(Bow)),
// Charged
Skill((Bow(CDamage), Some(1))),
Skill((Bow(CKnockback), Some(1))),
Skill((Bow(CSpeed), Some(1))),
Skill((Bow(CMove), Some(1))),
// Repeater
Skill((Bow(RDamage), Some(1))),
])

View File

@ -0,0 +1,12 @@
([
Group(Weapon(Bow)),
// Charged
Skill((Bow(CDamage), Some(1))),
Skill((Bow(CKnockback), Some(1))),
Skill((Bow(CSpeed), Some(1))),
Skill((Bow(CMove), Some(1))),
// Repeater
Skill((Bow(RDamage), Some(1))),
])

View File

@ -0,0 +1,21 @@
([
Group(Weapon(Axe)),
// DoubleStrike
Skill((Axe(DsCombo), None)),
Skill((Axe(DsDamage), Some(1))),
Skill((Axe(DsSpeed), Some(1))),
Skill((Axe(DsRegen), Some(1))),
// Spin
Skill((Axe(SInfinite), None)),
Skill((Axe(SHelicopter), None)),
Skill((Axe(SDamage), Some(1))),
Skill((Axe(SSpeed), Some(1))),
// Leap
Skill((Axe(UnlockLeap), None)),
Skill((Axe(LDamage), Some(1))),
Skill((Axe(LKnockback), Some(1))),
Skill((Axe(LDistance), Some(1))),
])

View File

@ -0,0 +1,21 @@
([
Group(Weapon(Bow)),
// Charged
Skill((Bow(CDamage), Some(1))),
Skill((Bow(CRegen), Some(1))),
Skill((Bow(CKnockback), Some(1))),
Skill((Bow(CSpeed), Some(1))),
Skill((Bow(CMove), Some(1))),
// Repeater
Skill((Bow(RDamage), Some(1))),
Skill((Bow(RSpeed), Some(1))),
// Shotgun
Skill((Bow(UnlockShotgun), None)),
Skill((Bow(SDamage), Some(1))),
Skill((Bow(SSpread), Some(1))),
Skill((Bow(SArrows), Some(1))),
Skill((Bow(SCost), Some(1))),
])

View File

@ -0,0 +1,8 @@
([
// Just gather everything
Tree("common.skillset.dungeon.tier-5.sword"),
Tree("common.skillset.dungeon.tier-5.axe"),
Tree("common.skillset.dungeon.tier-5.hammer"),
Tree("common.skillset.dungeon.tier-5.bow"),
Tree("common.skillset.dungeon.tier-5.staff"),
])

View File

@ -0,0 +1,21 @@
([
Group(Weapon(Hammer)),
// Single Strike, as single as you are
Skill((Hammer(SsKnockback), Some(1))),
Skill((Hammer(SsDamage), Some(1))),
Skill((Hammer(SsSpeed), Some(1))),
Skill((Hammer(SsRegen), Some(1))),
// Charged
Skill((Hammer(CKnockback), Some(1))),
Skill((Hammer(CDamage), Some(1))),
Skill((Hammer(CDrain), Some(1))),
// Leap
Skill((Hammer(UnlockLeap), None)),
Skill((Hammer(LDamage), Some(1))),
Skill((Hammer(LDistance), Some(1))),
Skill((Hammer(LKnockback), Some(1))),
Skill((Hammer(LRange), Some(1))),
])

View File

@ -0,0 +1,21 @@
([
Group(Weapon(Staff)),
// Fireball
Skill((Staff(BDamage), Some(3))),
Skill((Staff(BRegen), Some(2))),
Skill((Staff(BRadius), Some(2))),
// Flamethrower
Skill((Staff(FDamage), Some(3))),
Skill((Staff(FRange), Some(2))),
Skill((Staff(FDrain), Some(2))),
Skill((Staff(FVelocity), Some(2))),
// Shockwave
Skill((Staff(UnlockShockwave), None)),
Skill((Staff(SDamage), Some(2))),
Skill((Staff(SKnockback), Some(2))),
Skill((Staff(SRange), Some(2))),
Skill((Staff(SCost), Some(2))),
])

View File

@ -0,0 +1,21 @@
([
Group(Weapon(Staff)),
// Fireball
Skill((Staff(BDamage), Some(1))),
Skill((Staff(BRegen), Some(1))),
Skill((Staff(BRadius), Some(1))),
// Flamethrower
Skill((Staff(FRange), Some(1))),
Skill((Staff(FDrain), Some(1))),
Skill((Staff(FDamage), Some(1))),
Skill((Staff(FVelocity), Some(1))),
// Shockwave
Skill((Staff(UnlockShockwave), None)),
Skill((Staff(SDamage), Some(1))),
Skill((Staff(SKnockback), Some(1))),
Skill((Staff(SRange), Some(1))),
Skill((Staff(SCost), Some(1))),
])

View File

@ -0,0 +1,19 @@
([
Group(Weapon(Sword)),
// TripleStrike
Skill((Sword(TsCombo), None)),
Skill((Sword(TsDamage), Some(1))),
Skill((Sword(TsRegen), Some(1))),
// Dash
Skill((Sword(DDamage), Some(1))),
Skill((Sword(DCost), Some(1))),
Skill((Sword(DDrain), Some(1))),
// Spin of death
Skill((Sword(UnlockSpin), None)),
Skill((Sword(SDamage), Some(1))),
Skill((Sword(SSpins), Some(2))),
Skill((Sword(SCost), Some(1))),
])

View File

@ -0,0 +1,24 @@
([
Group(Weapon(Sword)),
// TripleStrike
Skill((Sword(TsCombo), None)),
Skill((Sword(TsDamage), Some(1))),
Skill((Sword(TsRegen), Some(1))),
Skill((Sword(TsSpeed), Some(1))),
// Dash
Skill((Sword(DDamage), Some(1))),
Skill((Sword(DCost), Some(1))),
Skill((Sword(DDrain), Some(1))),
Skill((Sword(DScaling), Some(1))),
Skill((Sword(DSpeed), None)),
Skill((Sword(DInfinite), None)),
// Spin of death
Skill((Sword(UnlockSpin), None)),
Skill((Sword(SDamage), Some(1))),
Skill((Sword(SSpeed), Some(1))),
Skill((Sword(SSpins), Some(2))),
Skill((Sword(SCost), Some(1))),
])

View File

@ -0,0 +1,17 @@
([
Group(Weapon(Bow)),
// Charged
Skill((Bow(CDamage), Some(1))),
Skill((Bow(CRegen), Some(1))),
Skill((Bow(CKnockback), Some(1))),
Skill((Bow(CSpeed), Some(1))),
// Repeater
Skill((Bow(RDamage), Some(1))),
Skill((Bow(RCost), Some(1))),
// Shotgun
Skill((Bow(UnlockShotgun), None)),
Skill((Bow(SCost), Some(1))),
])

View File

@ -23,6 +23,27 @@ macro_rules! plot {
};
}
// Panic in debug or tests, warn in release
#[macro_export]
macro_rules! dev_panic {
($msg:expr) => {
if cfg!(any(debug_assertions, test)) {
panic!("{}", $msg);
} else {
tracing::warn!("{}", $msg);
}
};
($msg:expr, or return $result:expr) => {
if cfg!(any(debug_assertions, test)) {
panic!("{}", $msg);
} else {
tracing::warn!("{}", $msg);
return $result;
}
};
}
// https://discordapp.com/channels/676678179678715904/676685797524766720/723358438943621151
#[macro_export]
macro_rules! span {

View File

@ -45,6 +45,14 @@ pub enum Preset {
HuskSummon,
}
#[derive(Debug, Deserialize, Clone)]
pub struct LoadoutSpec(HashMap<EquipSlot, ItemSpec>);
impl assets::Asset for LoadoutSpec {
type Loader = assets::RonLoader;
const EXTENSION: &'static str = "ron";
}
#[derive(Debug, Deserialize, Clone)]
pub enum ItemSpec {
/// One specific item.
@ -112,35 +120,17 @@ fn choose<'a>(
WeightedError::NoItem | WeightedError::AllWeightsZero => &None,
WeightedError::InvalidWeight => {
let err = format!("Negative values of probability in {}.", asset_specifier);
if cfg!(any(debug_assertions, test)) {
panic!("{}", err);
} else {
warn!("{}", err);
&None
}
common_base::dev_panic!(err, or return &None)
},
WeightedError::TooMany => {
let err = format!("More than u32::MAX values in {}.", asset_specifier);
if cfg!(any(debug_assertions, test)) {
panic!("{}", err);
} else {
warn!("{}", err);
&None
}
common_base::dev_panic!(err, or return &None)
},
},
|(_p, itemspec)| itemspec,
)
}
#[derive(Debug, Deserialize, Clone)]
pub struct LoadoutSpec(HashMap<EquipSlot, ItemSpec>);
impl assets::Asset for LoadoutSpec {
type Loader = assets::RonLoader;
const EXTENSION: &'static str = "ron";
}
#[must_use]
pub fn make_potion_bag(quantity: u32) -> Item {
let mut bag = Item::new_from_asset_expect("common.items.armor.misc.bag.tiny_leather_pouch");

View File

@ -6,7 +6,6 @@ use crate::{
Alignment, Body, Item,
},
npc::{self, NPC_NAMES},
skillset_builder::SkillSetConfig,
trade,
trade::SiteInformation,
};
@ -18,7 +17,8 @@ struct EntityConfig {
name: Option<String>,
main_tool: Option<ItemSpec>,
second_tool: Option<ItemSpec>,
loadout_config: Option<String>,
loadout_asset: Option<String>,
skillset_asset: Option<String>,
}
impl assets::Asset for EntityConfig {
@ -43,10 +43,9 @@ pub struct EntityInfo {
// TODO: Properly give NPCs skills
pub level: Option<u16>,
pub loot_drop: Option<Item>,
pub loadout_config: Option<String>,
pub loadout_asset: Option<String>,
pub make_loadout: Option<fn(LoadoutBuilder, Option<&trade::SiteInformation>) -> LoadoutBuilder>,
pub skillset_config: Option<String>,
pub skillset_preset: Option<SkillSetConfig>,
pub skillset_asset: Option<String>,
pub pet: Option<Box<EntityInfo>>,
// we can't use DHashMap, do we want to move that into common?
pub trading_information: Option<trade::SiteInformation>,
@ -69,10 +68,9 @@ impl EntityInfo {
scale: 1.0,
level: None,
loot_drop: None,
loadout_config: None,
loadout_asset: None,
make_loadout: None,
skillset_config: None,
skillset_preset: None,
skillset_asset: None,
pet: None,
trading_information: None,
}
@ -90,7 +88,8 @@ impl EntityInfo {
name,
main_tool,
second_tool,
loadout_config,
loadout_asset,
skillset_asset,
} = config;
if let Some(name) = name {
@ -109,8 +108,12 @@ impl EntityInfo {
self = self.with_main_tool(second_tool);
}
if let Some(loadout_config) = loadout_config {
self = self.with_loadout_config(loadout_config);
if let Some(loadout_asset) = loadout_asset {
self = self.with_loadout_asset(loadout_asset);
}
if let Some(skillset_asset) = skillset_asset {
self = self.with_skillset_asset(skillset_asset);
}
self
@ -183,8 +186,8 @@ impl EntityInfo {
self
}
pub fn with_loadout_config(mut self, config: String) -> Self {
self.loadout_config = Some(config);
pub fn with_loadout_asset(mut self, asset: String) -> Self {
self.loadout_asset = Some(asset);
self
}
@ -196,15 +199,8 @@ impl EntityInfo {
self
}
pub fn with_skillset_preset(mut self, preset: SkillSetConfig) -> Self {
self.skillset_preset = Some(preset);
self
}
// FIXME: Doesn't work for now, because skills can't be loaded from assets for
// now
pub fn with_skillset_config(mut self, config: String) -> Self {
self.skillset_config = Some(config);
pub fn with_skillset_asset(mut self, asset: String) -> Self {
self.skillset_asset = Some(asset);
self
}
@ -269,6 +265,7 @@ pub fn get_npc_name<
#[cfg(test)]
mod tests {
use super::*;
use crate::{comp::inventory::slot::EquipSlot, SkillSetBuilder};
use assets::Error;
#[test]
@ -292,12 +289,35 @@ mod tests {
}
// It just load everything that could
// TODO: add some checks, e.g. that Armor(Head) key correspond
// to Item with ItemKind Head(_)
let entity_configs = EntityList::load_expect_cloned("common.entity.*").0;
for config in entity_configs {
let pos = Vec3::new(0.0, 0.0, 0.0);
std::mem::drop(EntityInfo::at(pos).with_entity_config(config, None));
let EntityConfig {
main_tool,
second_tool,
loadout_asset,
skillset_asset,
..
} = config;
if let Some(main_tool) = main_tool {
main_tool.validate(EquipSlot::ActiveMainhand);
}
if let Some(second_tool) = second_tool {
second_tool.validate(EquipSlot::ActiveOffhand);
}
if let Some(loadout_asset) = loadout_asset {
let rng = &mut rand::thread_rng();
let builder = LoadoutBuilder::default();
// we need to just load it check if it exists,
// because all loadouts are tested in LoadoutBuilder module
std::mem::drop(builder.with_asset_expect(&loadout_asset, rng));
}
if let Some(skillset_asset) = skillset_asset {
std::mem::drop(SkillSetBuilder::from_asset_expect(&skillset_asset));
}
}
}
}

View File

@ -1,30 +1,56 @@
use crate::comp::{
item::{tool::ToolKind, Item, ItemKind},
skills::{
AxeSkill, BowSkill, HammerSkill, Skill, SkillGroupKind, SkillSet, StaffSkill, SwordSkill,
},
};
#![warn(clippy::pedantic)]
//#![warn(clippy::nursery)]
use crate::comp::skills::{Skill, SkillGroupKind, SkillSet};
use crate::assets::{self, AssetExt};
use serde::{Deserialize, Serialize};
use tracing::warn;
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)]
pub enum SkillSetConfig {
Adlet,
Gnarling,
Sahagin,
Haniwa,
Myrmidon,
Guard,
Villager,
Merchant,
Outcast,
Highwayman,
Bandit,
CultistNovice,
CultistAcolyte,
Warlord,
Warlock,
Mindflayer,
pub enum Preset {
Empty,
}
#[derive(Debug, Deserialize, Clone)]
struct SkillSetTree(Vec<SkillNode>);
impl assets::Asset for SkillSetTree {
type Loader = assets::RonLoader;
const EXTENSION: &'static str = "ron";
}
#[derive(Debug, Deserialize, Clone)]
enum SkillNode {
Tree(String),
Skill((Skill, Option<u16>)),
Group(SkillGroupKind),
}
#[must_use]
fn skills_from_asset_expect(asset_specifier: &str) -> Vec<(Skill, Option<u16>)> {
let nodes = SkillSetTree::load_expect(asset_specifier).read().0.clone();
skills_from_nodes(nodes)
}
#[must_use]
fn skills_from_nodes(nodes: Vec<SkillNode>) -> Vec<(Skill, Option<u16>)> {
let mut skills = Vec::new();
for node in nodes {
match node {
SkillNode::Tree(asset) => {
skills.append(&mut skills_from_asset_expect(&asset));
},
SkillNode::Skill(req) => {
skills.push(req);
},
SkillNode::Group(group) => {
skills.push((Skill::UnlockGroup(group), None));
},
}
}
skills
}
pub struct SkillSetBuilder(SkillSet);
@ -34,652 +60,40 @@ impl Default for SkillSetBuilder {
}
impl SkillSetBuilder {
pub fn build_skillset(main_tool: &Option<Item>, config: Option<SkillSetConfig>) -> Self {
let active_item = main_tool.as_ref().and_then(|ic| {
if let ItemKind::Tool(tool) = &ic.kind() {
Some(tool.kind)
} else {
None
}
});
/// Creates `SkillSetBuilder` from `asset_specifier`
#[must_use]
pub fn from_asset_expect(asset_specifier: &str) -> Self {
let builder = Self::default();
use SkillSetConfig::*;
match config {
Some(Adlet) => {
match active_item {
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CKnockback), Some(1))
.with_skill(Skill::Bow(BowSkill::CSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CMove), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
},
_ => Self::default(),
}
},
Some(Gnarling) => {
match active_item {
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CKnockback), Some(1))
.with_skill(Skill::Bow(BowSkill::CSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CMove), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
},
_ => Self::default(),
}
},
Some(Sahagin) => {
match active_item {
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CKnockback), Some(1))
.with_skill(Skill::Bow(BowSkill::CSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CMove), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
},
_ => Self::default(),
}
},
Some(Haniwa) => {
match active_item {
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CKnockback), Some(1))
.with_skill(Skill::Bow(BowSkill::CSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CMove), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
},
_ => Self::default(),
}
},
Some(Myrmidon) => {
match active_item {
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CKnockback), Some(1))
.with_skill(Skill::Bow(BowSkill::CSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CMove), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
},
_ => Self::default(),
}
},
Some(Guard) => {
if let Some(ToolKind::Sword) = active_item {
// Sword
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword))
.with_skill(Skill::Sword(SwordSkill::TsCombo), None)
.with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::TsRegen), Some(1))
.with_skill(Skill::Sword(SwordSkill::TsSpeed), Some(1))
.with_skill(Skill::Sword(SwordSkill::DDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::DCost), Some(1))
.with_skill(Skill::Sword(SwordSkill::DDrain), Some(1))
.with_skill(Skill::Sword(SwordSkill::DScaling), Some(1))
.with_skill(Skill::Sword(SwordSkill::DSpeed), Some(1))
.with_skill(Skill::Sword(SwordSkill::DInfinite), None)
.with_skill(Skill::Sword(SwordSkill::UnlockSpin), None)
.with_skill(Skill::Sword(SwordSkill::SDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::SSpeed), Some(1))
.with_skill(Skill::Sword(SwordSkill::SSpins), Some(1))
.with_skill(Skill::Sword(SwordSkill::SCost), Some(1))
} else {
Self::default()
}
},
Some(Outcast) => {
match active_item {
Some(ToolKind::Sword) => {
// Sword
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword))
.with_skill(Skill::Sword(SwordSkill::TsCombo), None)
.with_skill(Skill::Sword(SwordSkill::DDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::DCost), Some(1))
},
Some(ToolKind::Axe) => {
// Axe
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe))
.with_skill(Skill::Axe(AxeSkill::DsCombo), None)
.with_skill(Skill::Axe(AxeSkill::SInfinite), Some(1))
.with_skill(Skill::Axe(AxeSkill::SSpeed), Some(1))
.with_skill(Skill::Axe(AxeSkill::SCost), Some(1))
},
Some(ToolKind::Hammer) => {
// Hammer
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer))
.with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsSpeed), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CSpeed), Some(1))
},
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CKnockback), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::RSpeed), Some(1))
},
Some(ToolKind::Staff) => {
// Staff
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff))
.with_skill(Skill::Staff(StaffSkill::FDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::FDrain), Some(1))
.with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1))
},
_ => Self::default(),
}
},
Some(Highwayman) => {
match active_item {
Some(ToolKind::Sword) => {
// Sword
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword))
.with_skill(Skill::Sword(SwordSkill::TsCombo), None)
.with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::DDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::UnlockSpin), None)
.with_skill(Skill::Sword(SwordSkill::SSpins), Some(1))
.with_skill(Skill::Sword(SwordSkill::SCost), Some(1))
},
Some(ToolKind::Axe) => {
// Axe
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe))
.with_skill(Skill::Axe(AxeSkill::DsCombo), None)
.with_skill(Skill::Axe(AxeSkill::DsDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::SInfinite), None)
.with_skill(Skill::Axe(AxeSkill::SDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::SSpeed), Some(1))
.with_skill(Skill::Axe(AxeSkill::SCost), Some(1))
.with_skill(Skill::Axe(AxeSkill::UnlockLeap), None)
},
Some(ToolKind::Hammer) => {
// Hammer
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer))
.with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsSpeed), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None)
.with_skill(Skill::Hammer(HammerSkill::LKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LRange), Some(1))
},
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CKnockback), Some(1))
.with_skill(Skill::Bow(BowSkill::CSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CMove), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::UnlockShotgun), None)
.with_skill(Skill::Bow(BowSkill::SArrows), Some(1))
},
Some(ToolKind::Staff) => {
// Staff
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff))
.with_skill(Skill::Staff(StaffSkill::BRegen), Some(1))
.with_skill(Skill::Staff(StaffSkill::BRadius), Some(1))
.with_skill(Skill::Staff(StaffSkill::FDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::FRange), Some(1))
.with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1))
.with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None)
},
_ => Self::default(),
}
},
Some(Bandit) | Some(Merchant) => {
match active_item {
Some(ToolKind::Sword) => {
// Sword
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword))
.with_skill(Skill::Sword(SwordSkill::TsCombo), None)
.with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::DDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::DCost), Some(1))
.with_skill(Skill::Sword(SwordSkill::UnlockSpin), None)
.with_skill(Skill::Sword(SwordSkill::SDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::SSpins), Some(1))
.with_skill(Skill::Sword(SwordSkill::SCost), Some(1))
},
Some(ToolKind::Axe) => {
// Axe
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe))
.with_skill(Skill::Axe(AxeSkill::DsCombo), None)
.with_skill(Skill::Axe(AxeSkill::DsSpeed), Some(1))
.with_skill(Skill::Axe(AxeSkill::DsRegen), Some(1))
.with_skill(Skill::Axe(AxeSkill::SInfinite), None)
.with_skill(Skill::Axe(AxeSkill::SDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::UnlockLeap), None)
.with_skill(Skill::Axe(AxeSkill::LKnockback), Some(1))
.with_skill(Skill::Axe(AxeSkill::LCost), Some(1))
.with_skill(Skill::Axe(AxeSkill::LDistance), Some(1))
},
Some(ToolKind::Hammer) => {
// Hammer
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer))
.with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsRegen), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None)
.with_skill(Skill::Hammer(HammerSkill::LDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LCost), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LDistance), Some(1))
},
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CRegen), Some(1))
.with_skill(Skill::Bow(BowSkill::CSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::RCost), Some(1))
.with_skill(Skill::Bow(BowSkill::UnlockShotgun), None)
.with_skill(Skill::Bow(BowSkill::SCost), Some(1))
.with_skill(Skill::Bow(BowSkill::RCost), Some(1))
},
Some(ToolKind::Staff) => {
// Staff
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff))
.with_skill(Skill::Staff(StaffSkill::FDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::FRange), Some(1))
.with_skill(Skill::Staff(StaffSkill::FDrain), Some(1))
.with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None)
.with_skill(Skill::Staff(StaffSkill::SDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::SRange), Some(1))
},
_ => Self::default(),
}
},
Some(CultistNovice) => {
match active_item {
Some(ToolKind::Sword) => {
// Sword
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword))
.with_skill(Skill::Sword(SwordSkill::TsCombo), None)
.with_skill(Skill::Sword(SwordSkill::TsRegen), Some(1))
.with_skill(Skill::Sword(SwordSkill::TsSpeed), Some(1))
.with_skill(Skill::Sword(SwordSkill::DDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::DCost), Some(1))
.with_skill(Skill::Sword(SwordSkill::DDrain), Some(1))
.with_skill(Skill::Sword(SwordSkill::DScaling), Some(1))
.with_skill(Skill::Sword(SwordSkill::UnlockSpin), None)
.with_skill(Skill::Sword(SwordSkill::SSpeed), Some(1))
.with_skill(Skill::Sword(SwordSkill::SSpins), Some(1))
.with_skill(Skill::Sword(SwordSkill::SCost), Some(1))
},
Some(ToolKind::Axe) => {
// Axe
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe))
.with_skill(Skill::Axe(AxeSkill::DsCombo), None)
.with_skill(Skill::Axe(AxeSkill::SInfinite), None)
.with_skill(Skill::Axe(AxeSkill::SHelicopter), None)
.with_skill(Skill::Axe(AxeSkill::SDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::UnlockLeap), None)
.with_skill(Skill::Axe(AxeSkill::LKnockback), Some(1))
.with_skill(Skill::Axe(AxeSkill::LDistance), Some(1))
},
Some(ToolKind::Hammer) => {
// Hammer
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer))
.with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsSpeed), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsRegen), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CDrain), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CSpeed), Some(1))
.with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None)
.with_skill(Skill::Hammer(HammerSkill::LDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LKnockback), Some(1))
},
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CKnockback), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::RSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::RCost), Some(1))
.with_skill(Skill::Bow(BowSkill::UnlockShotgun), None)
.with_skill(Skill::Bow(BowSkill::SDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::SArrows), Some(1))
.with_skill(Skill::Bow(BowSkill::SSpread), Some(1))
},
Some(ToolKind::Staff) => {
// Staff
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff))
.with_skill(Skill::Staff(StaffSkill::BDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::BRadius), Some(1))
.with_skill(Skill::Staff(StaffSkill::FDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::FRange), Some(1))
.with_skill(Skill::Staff(StaffSkill::FDrain), Some(1))
.with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1))
.with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None)
.with_skill(Skill::Staff(StaffSkill::SDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::SRange), Some(1))
},
_ => Self::default(),
}
},
Some(CultistAcolyte) => {
match active_item {
Some(ToolKind::Sword) => {
// Sword
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword))
.with_skill(Skill::Sword(SwordSkill::TsCombo), None)
.with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::TsSpeed), Some(1))
.with_skill(Skill::Sword(SwordSkill::DDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::DScaling), Some(1))
.with_skill(Skill::Sword(SwordSkill::UnlockSpin), None)
.with_skill(Skill::Sword(SwordSkill::SDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::SSpeed), Some(1))
.with_skill(Skill::Sword(SwordSkill::SSpins), Some(1))
},
Some(ToolKind::Axe) => {
// Axe
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe))
.with_skill(Skill::Axe(AxeSkill::DsCombo), None)
.with_skill(Skill::Axe(AxeSkill::DsDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::SInfinite), Some(1))
.with_skill(Skill::Axe(AxeSkill::SDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::SCost), Some(1))
.with_skill(Skill::Axe(AxeSkill::UnlockLeap), None)
.with_skill(Skill::Axe(AxeSkill::LDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::LKnockback), Some(1))
.with_skill(Skill::Axe(AxeSkill::LCost), Some(1))
.with_skill(Skill::Axe(AxeSkill::LDistance), Some(1))
},
Some(ToolKind::Hammer) => {
// Hammer
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer))
.with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsRegen), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CDrain), Some(1))
.with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None)
.with_skill(Skill::Hammer(HammerSkill::LDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LRange), Some(1))
},
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CKnockback), Some(1))
.with_skill(Skill::Bow(BowSkill::CSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::RCost), Some(1))
.with_skill(Skill::Bow(BowSkill::UnlockShotgun), None)
.with_skill(Skill::Bow(BowSkill::SDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::SSpread), Some(1))
.with_skill(Skill::Bow(BowSkill::SArrows), Some(1))
.with_skill(Skill::Bow(BowSkill::SCost), Some(1))
},
Some(ToolKind::Staff) => {
// Staff
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff))
.with_skill(Skill::Staff(StaffSkill::BDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::BRadius), Some(1))
.with_skill(Skill::Staff(StaffSkill::FDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::FRange), Some(1))
.with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1))
.with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None)
.with_skill(Skill::Staff(StaffSkill::SDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::SKnockback), Some(1))
.with_skill(Skill::Staff(StaffSkill::SRange), Some(1))
},
_ => Self::default(),
}
},
Some(Warlord) => {
match active_item {
Some(ToolKind::Sword) => {
// Sword
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword))
.with_skill(Skill::Sword(SwordSkill::TsCombo), None)
.with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::TsRegen), Some(1))
.with_skill(Skill::Sword(SwordSkill::DDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::DCost), Some(1))
.with_skill(Skill::Sword(SwordSkill::DDrain), Some(1))
.with_skill(Skill::Sword(SwordSkill::UnlockSpin), None)
.with_skill(Skill::Sword(SwordSkill::SDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::SSpins), Some(2))
.with_skill(Skill::Sword(SwordSkill::SCost), Some(1))
},
Some(ToolKind::Axe) => {
// Axe
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe))
.with_skill(Skill::Axe(AxeSkill::DsCombo), None)
.with_skill(Skill::Axe(AxeSkill::DsDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::DsSpeed), Some(1))
.with_skill(Skill::Axe(AxeSkill::DsRegen), Some(1))
.with_skill(Skill::Axe(AxeSkill::SInfinite), None)
.with_skill(Skill::Axe(AxeSkill::SHelicopter), None)
.with_skill(Skill::Axe(AxeSkill::SDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::SSpeed), Some(1))
.with_skill(Skill::Axe(AxeSkill::UnlockLeap), None)
.with_skill(Skill::Axe(AxeSkill::LDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::LKnockback), Some(1))
.with_skill(Skill::Axe(AxeSkill::LDistance), Some(1))
},
Some(ToolKind::Hammer) => {
// Hammer
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer))
.with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsSpeed), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsRegen), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CDrain), Some(1))
.with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None)
.with_skill(Skill::Hammer(HammerSkill::LDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LDistance), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LRange), Some(1))
},
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CRegen), Some(1))
.with_skill(Skill::Bow(BowSkill::CKnockback), Some(1))
.with_skill(Skill::Bow(BowSkill::CSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CMove), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::RSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::UnlockShotgun), None)
.with_skill(Skill::Bow(BowSkill::SDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::SSpread), Some(1))
.with_skill(Skill::Bow(BowSkill::SArrows), Some(1))
.with_skill(Skill::Bow(BowSkill::SCost), Some(1))
},
Some(ToolKind::Staff) => {
// Staff
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff))
.with_skill(Skill::Staff(StaffSkill::BDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::BRegen), Some(1))
.with_skill(Skill::Staff(StaffSkill::BRadius), Some(1))
.with_skill(Skill::Staff(StaffSkill::FDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::FDrain), Some(1))
.with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1))
.with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None)
.with_skill(Skill::Staff(StaffSkill::SDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::SKnockback), Some(1))
.with_skill(Skill::Staff(StaffSkill::SCost), Some(1))
},
_ => Self::default(),
}
},
Some(Warlock) => {
match active_item {
Some(ToolKind::Sword) => {
// Sword
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Sword))
.with_skill(Skill::Sword(SwordSkill::TsCombo), None)
.with_skill(Skill::Sword(SwordSkill::TsDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::TsRegen), Some(1))
.with_skill(Skill::Sword(SwordSkill::TsSpeed), Some(1))
.with_skill(Skill::Sword(SwordSkill::DDamage), Some(2))
.with_skill(Skill::Sword(SwordSkill::DCost), Some(1))
.with_skill(Skill::Sword(SwordSkill::DDrain), Some(1))
.with_skill(Skill::Sword(SwordSkill::DScaling), Some(1))
.with_skill(Skill::Sword(SwordSkill::UnlockSpin), None)
.with_skill(Skill::Sword(SwordSkill::SDamage), Some(1))
.with_skill(Skill::Sword(SwordSkill::SSpeed), Some(1))
.with_skill(Skill::Sword(SwordSkill::SSpins), Some(2))
.with_skill(Skill::Sword(SwordSkill::SCost), Some(1))
},
Some(ToolKind::Axe) => {
// Axe
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Axe))
.with_skill(Skill::Axe(AxeSkill::DsCombo), None)
.with_skill(Skill::Axe(AxeSkill::DsDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::DsSpeed), Some(1))
.with_skill(Skill::Axe(AxeSkill::DsRegen), Some(1))
.with_skill(Skill::Axe(AxeSkill::SInfinite), None)
.with_skill(Skill::Axe(AxeSkill::SHelicopter), None)
.with_skill(Skill::Axe(AxeSkill::SDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::SSpeed), Some(1))
.with_skill(Skill::Axe(AxeSkill::SCost), Some(1))
.with_skill(Skill::Axe(AxeSkill::UnlockLeap), None)
.with_skill(Skill::Axe(AxeSkill::LDamage), Some(1))
.with_skill(Skill::Axe(AxeSkill::LKnockback), Some(1))
.with_skill(Skill::Axe(AxeSkill::LCost), Some(1))
.with_skill(Skill::Axe(AxeSkill::LDistance), Some(1))
},
Some(ToolKind::Hammer) => {
// Hammer
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Hammer))
.with_skill(Skill::Hammer(HammerSkill::SsKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsSpeed), Some(1))
.with_skill(Skill::Hammer(HammerSkill::SsRegen), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CDrain), Some(1))
.with_skill(Skill::Hammer(HammerSkill::CSpeed), Some(1))
.with_skill(Skill::Hammer(HammerSkill::UnlockLeap), None)
.with_skill(Skill::Hammer(HammerSkill::LDamage), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LCost), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LDistance), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LKnockback), Some(1))
.with_skill(Skill::Hammer(HammerSkill::LRange), Some(1))
},
Some(ToolKind::Bow) => {
// Bow
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Bow))
.with_skill(Skill::Bow(BowSkill::ProjSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::CRegen), Some(1))
.with_skill(Skill::Bow(BowSkill::CKnockback), Some(1))
.with_skill(Skill::Bow(BowSkill::CSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::CMove), Some(1))
.with_skill(Skill::Bow(BowSkill::RDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::RSpeed), Some(1))
.with_skill(Skill::Bow(BowSkill::RCost), Some(1))
.with_skill(Skill::Bow(BowSkill::UnlockShotgun), None)
.with_skill(Skill::Bow(BowSkill::SDamage), Some(1))
.with_skill(Skill::Bow(BowSkill::SSpread), Some(1))
.with_skill(Skill::Bow(BowSkill::SArrows), Some(1))
.with_skill(Skill::Bow(BowSkill::SCost), Some(1))
},
Some(ToolKind::Staff) => {
// Staff
Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff))
.with_skill(Skill::Staff(StaffSkill::BDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::BRegen), Some(1))
.with_skill(Skill::Staff(StaffSkill::BRadius), Some(1))
.with_skill(Skill::Staff(StaffSkill::FDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::FRange), Some(1))
.with_skill(Skill::Staff(StaffSkill::FDrain), Some(1))
.with_skill(Skill::Staff(StaffSkill::FVelocity), Some(1))
.with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None)
.with_skill(Skill::Staff(StaffSkill::SDamage), Some(1))
.with_skill(Skill::Staff(StaffSkill::SKnockback), Some(1))
.with_skill(Skill::Staff(StaffSkill::SRange), Some(1))
.with_skill(Skill::Staff(StaffSkill::SCost), Some(1))
},
_ => Self::default(),
}
},
Some(Mindflayer) => Self::default()
.with_skill_group(SkillGroupKind::Weapon(ToolKind::Staff))
.with_skill(Skill::Staff(StaffSkill::BDamage), Some(3))
.with_skill(Skill::Staff(StaffSkill::BRegen), Some(2))
.with_skill(Skill::Staff(StaffSkill::BRadius), Some(2))
.with_skill(Skill::Staff(StaffSkill::FDamage), Some(3))
.with_skill(Skill::Staff(StaffSkill::FRange), Some(2))
.with_skill(Skill::Staff(StaffSkill::FDrain), Some(2))
.with_skill(Skill::Staff(StaffSkill::FVelocity), Some(2))
.with_skill(Skill::Staff(StaffSkill::UnlockShockwave), None)
.with_skill(Skill::Staff(StaffSkill::SDamage), Some(2))
.with_skill(Skill::Staff(StaffSkill::SKnockback), Some(2))
.with_skill(Skill::Staff(StaffSkill::SRange), Some(2))
.with_skill(Skill::Staff(StaffSkill::SCost), Some(2)),
Some(Villager) | None => Self::default(),
builder.with_asset_expect(asset_specifier)
}
/// Applies `asset_specifier` with needed skill tree
#[must_use]
pub fn with_asset_expect(mut self, asset_specifier: &str) -> Self {
let tree = skills_from_asset_expect(asset_specifier);
for (skill, level) in tree {
self = self.with_skill(skill, level);
}
self
}
/// Creates `SkillSetBuilder` for given preset
#[must_use]
pub fn from_preset(preset: Preset) -> Self {
let builder = Self::default();
builder.with_preset(preset)
}
/// Applies preset
#[must_use]
pub const fn with_preset(self, preset: Preset) -> Self {
match preset {
Preset::Empty => {},
}
self
}
#[must_use]
@ -689,7 +103,6 @@ impl SkillSetBuilder {
/// 2) If added skill already applied
/// 3) If added skill wasn't applied at the end
pub fn with_skill(mut self, skill: Skill, level: Option<u16>) -> Self {
#![warn(clippy::pedantic)]
let group = if let Some(skill_group) = skill.skill_group_kind() {
skill_group
} else {
@ -697,12 +110,7 @@ impl SkillSetBuilder {
"Tried to add skill: {:?} which does not have an associated skill group.",
skill
);
if cfg!(test) {
panic!("{}", err);
} else {
warn!("{}", err);
}
return self;
common_base::dev_panic!(err, or return self);
};
let SkillSetBuilder(ref mut skill_set) = self;
@ -711,12 +119,7 @@ impl SkillSetBuilder {
"Tried to add skill: {:?} with level {:?} which is already applied",
skill, level,
);
if cfg!(test) {
panic!("{}", err);
} else {
warn!("{}", err);
}
return self;
common_base::dev_panic!(err, or return self);
}
for _ in 0..level.unwrap_or(1) {
skill_set.add_skill_points(group, skill_set.skill_cost(skill));
@ -728,22 +131,16 @@ impl SkillSetBuilder {
available and meets all prerequisite skills.",
skill
);
if cfg!(test) {
panic!("{}", err);
} else {
warn!("{}", err);
}
common_base::dev_panic!(err);
}
self
}
pub fn with_skill_group(self, skill_group: SkillGroupKind) -> Self {
self.with_skill(Skill::UnlockGroup(skill_group), None)
}
#[must_use]
pub fn build(self) -> SkillSet { self.0 }
}
#[must_use]
fn skill_is_applied(skill_set: &SkillSet, skill: Skill, level: Option<u16>) -> bool {
if let Ok(applied_level) = skill_set.skill_level(skill) {
applied_level == level
@ -751,3 +148,45 @@ fn skill_is_applied(skill_set: &SkillSet, skill: Skill, level: Option<u16>) -> b
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use assets::Error;
#[test]
fn test_all_skillset_assets() {
#[derive(Clone)]
struct SkillSetList(Vec<SkillSetTree>);
impl assets::Compound for SkillSetList {
fn load<S: assets::source::Source>(
cache: &assets::AssetCache<S>,
specifier: &str,
) -> Result<Self, Error> {
let list = cache
.load::<assets::Directory>(specifier)?
.read()
.iter()
.map(|spec| SkillSetTree::load_cloned(spec))
.collect::<Result<_, Error>>()?;
Ok(Self(list))
}
}
let skillsets = SkillSetList::load_expect_cloned("common.skillset.*").0;
for skillset in skillsets {
std::mem::drop({
let mut skillset_builder = SkillSetBuilder::default();
let nodes = skillset.0;
let tree = skills_from_nodes(nodes);
for (skill, level) in tree {
skillset_builder = skillset_builder.with_skill(skill, level);
}
skillset_builder
});
}
}
}

View File

@ -6,7 +6,7 @@ use crate::{
},
event::{LocalEvent, ServerEvent},
outcome::Outcome,
skillset_builder::{SkillSetBuilder, SkillSetConfig},
skillset_builder::{self, SkillSetBuilder},
states::{
behavior::{CharacterBehavior, JoinData},
utils::*,
@ -74,24 +74,33 @@ impl CharacterBehavior for Data {
> self.static_data.cast_duration * self.summon_count
/ self.static_data.summon_amount
{
let body = self.static_data.summon_info.body;
let SummonInfo {
body,
loadout_config,
skillset_config,
..
} = self.static_data.summon_info;
let mut loadout_builder =
LoadoutBuilder::new().with_default_maintool(&body);
let loadout = {
let loadout_builder =
LoadoutBuilder::new().with_default_maintool(&body);
if let Some(preset) = loadout_config {
loadout_builder.with_preset(preset).build()
} else {
loadout_builder.build()
}
};
if let Some(preset) = self.static_data.summon_info.loadout_config {
loadout_builder = loadout_builder.with_preset(preset);
}
let loadout = loadout_builder.build();
let skill_set = {
let skillset_builder = SkillSetBuilder::default();
if let Some(preset) = skillset_config {
skillset_builder.with_preset(preset).build()
} else {
skillset_builder.build()
}
};
let stats = comp::Stats::new("Summon".to_string());
let skill_set = SkillSetBuilder::build_skillset(
&None,
self.static_data.summon_info.skillset_config,
)
.build();
// Send server event to create npc
update.server_events.push_front(ServerEvent::CreateNpc {
pos: *data.pos,
@ -179,5 +188,5 @@ pub struct SummonInfo {
health_scaling: u16,
// TODO: use assets for specifying skills and loadout?
loadout_config: Option<loadout_builder::Preset>,
skillset_config: Option<SkillSetConfig>,
skillset_config: Option<skillset_builder::Preset>,
}

View File

@ -195,44 +195,49 @@ impl<'a> System<'a> for Sys {
}
let EntityInfo {
skillset_preset,
skillset_asset,
main_tool,
loadout_config,
loadout_asset,
make_loadout,
trading_information: economy,
..
} = entity;
let skill_set =
SkillSetBuilder::build_skillset(&main_tool, skillset_preset).build();
let skill_set = {
let skillset_builder = SkillSetBuilder::default();
if let Some(skillset_asset) = skillset_asset {
skillset_builder.with_asset_expect(&skillset_asset).build()
} else {
skillset_builder.build()
}
};
let mut loadout_builder = LoadoutBuilder::new();
let rng = &mut rand::thread_rng();
let loadout = {
let mut loadout_builder = LoadoutBuilder::new();
let rng = &mut rand::thread_rng();
// If main tool is passed, use it. Otherwise fallback to default tool
if let Some(main_tool) = main_tool {
loadout_builder = loadout_builder.active_mainhand(Some(main_tool));
} else {
loadout_builder = loadout_builder.with_default_maintool(&body);
}
// If main tool is passed, use it. Otherwise fallback to default tool
if let Some(main_tool) = main_tool {
loadout_builder = loadout_builder.active_mainhand(Some(main_tool));
} else {
loadout_builder = loadout_builder.with_default_maintool(&body);
}
// If there is config, apply it.
// If not, use default equipement for this body.
match loadout_config {
Some(asset) => {
// If there is config, apply it.
// If not, use default equipement for this body.
if let Some(asset) = loadout_asset {
loadout_builder = loadout_builder.with_asset_expect(&asset, rng);
},
None => {
} else {
loadout_builder = loadout_builder.with_default_equipment(&body);
},
}
}
// Evaluate lazy function for loadout creation
if let Some(make_loadout) = make_loadout {
loadout_builder = loadout_builder.with_creator(make_loadout, economy.as_ref());
}
let loadout = loadout_builder.build();
// Evaluate lazy function for loadout creation
if let Some(make_loadout) = make_loadout {
loadout_builder =
loadout_builder.with_creator(make_loadout, economy.as_ref());
}
loadout_builder.build()
};
let health = comp::Health::new(body, entity.level.unwrap_or(0));
let poise = comp::Poise::new(body);

View File

@ -932,8 +932,7 @@ fn enemy_0(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
&comp::biped_small::Species::Gnarling,
),
))
.with_loot_drop(chosen.read().choose().to_item())
.with_skillset_preset(common::skillset_builder::SkillSetConfig::Gnarling);
.with_loot_drop(chosen.read().choose().to_item());
match dynamic_rng.gen_range(0..5) {
0 => gnarling.with_asset_expect("common.entity.dungeon.tier-0.bow"),
@ -949,7 +948,6 @@ fn enemy_1(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
.with_body(comp::Body::BipedSmall(
comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Adlet),
))
.with_skillset_preset(common::skillset_builder::SkillSetConfig::Adlet)
.with_loot_drop(chosen.read().choose().to_item());
match dynamic_rng.gen_range(0..5) {
@ -966,7 +964,6 @@ fn enemy_2(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
.with_body(comp::Body::BipedSmall(
comp::biped_small::Body::random_with(dynamic_rng, &comp::biped_small::Species::Sahagin),
))
.with_skillset_preset(common::skillset_builder::SkillSetConfig::Sahagin)
.with_loot_drop(chosen.read().choose().to_item());
match dynamic_rng.gen_range(0..5) {
@ -994,7 +991,6 @@ fn enemy_3(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
&comp::biped_small::Species::Haniwa,
),
))
.with_skillset_preset(common::skillset_builder::SkillSetConfig::Haniwa)
.with_loot_drop(chosen.read().choose().to_item());
match dynamic_rng.gen_range(0..5) {
@ -1015,7 +1011,6 @@ fn enemy_4(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
&comp::biped_small::Species::Myrmidon,
),
))
.with_skillset_preset(common::skillset_builder::SkillSetConfig::Myrmidon)
.with_loot_drop(chosen.read().choose().to_item());
match dynamic_rng.gen_range(0..5) {
@ -1037,12 +1032,10 @@ fn enemy_5(dynamic_rng: &mut impl Rng, entity: EntityInfo) -> EntityInfo {
)),
1 => entity
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
.with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlock)
.with_loot_drop(chosen.read().choose().to_item())
.with_asset_expect("common.entity.dungeon.tier-5.warlock"),
_ => entity
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
.with_skillset_preset(common::skillset_builder::SkillSetConfig::Warlord)
.with_loot_drop(chosen.read().choose().to_item())
.with_asset_expect("common.entity.dungeon.tier-5.warlord"),
}
@ -1147,9 +1140,8 @@ fn boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<EntityInfo
&comp::biped_large::Species::Mindflayer,
),
))
.with_name("Mindflayer".to_string())
.with_loot_drop(chosen.read().choose().to_item())
.with_skillset_preset(common::skillset_builder::SkillSetConfig::Mindflayer),
.with_asset_expect("common.entity.dungeon.tier-5.boss"),
]
}
@ -1258,7 +1250,6 @@ fn mini_boss_5(dynamic_rng: &mut impl Rng, tile_wcenter: Vec3<i32>) -> Vec<Entit
EntityInfo::at(tile_wcenter.map(|e| e as f32))
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
.with_loot_drop(trainer_loot.read().choose().to_item())
.with_skillset_preset(common::skillset_builder::SkillSetConfig::CultistAcolyte)
.with_asset_expect("common.entity.dungeon.tier-5.beastmaster"),
);
entities.resize_with(entities.len() + 2, || {

View File

@ -952,45 +952,19 @@ impl Settlement {
.do_if(is_human && dynamic_rng.gen(), |entity| {
match dynamic_rng.gen_range(0..6) {
0 => entity
.with_main_tool(Item::new_from_asset_expect(
"common.items.weapons.sword.iron-4",
))
.with_name("Guard")
.with_agent_mark(agent::Mark::Guard)
.with_lazy_loadout(guard_loadout)
.with_level(dynamic_rng.gen_range(10..15))
.with_skillset_preset(
common::skillset_builder::SkillSetConfig::Guard,
),
.with_asset_expect("common.entity.village.guard"),
1 | 2 => entity
.with_main_tool(Item::new_from_asset_expect(
"common.items.weapons.bow.eldwood-0",
))
.with_name("Merchant")
.with_agent_mark(agent::Mark::Merchant)
.with_economy(&economy)
.with_lazy_loadout(merchant_loadout)
.with_level(dynamic_rng.gen_range(10..15))
.with_skillset_preset(
common::skillset_builder::SkillSetConfig::Merchant,
),
.with_asset_expect("common.entity.village.merchant"),
_ => entity
.with_main_tool(Item::new_from_asset_expect(
match dynamic_rng.gen_range(0..7) {
0 => "common.items.weapons.tool.broom",
1 => "common.items.weapons.tool.hoe",
2 => "common.items.weapons.tool.pickaxe",
3 => "common.items.weapons.tool.pitchfork",
4 => "common.items.weapons.tool.rake",
5 => "common.items.weapons.tool.shovel-0",
_ => "common.items.weapons.tool.shovel-1",
//_ => "common.items.weapons.bow.starter", TODO: Re-Add this when we have a better way of distributing npc_weapons here
},
))
.with_lazy_loadout(villager_loadout)
.with_skillset_preset(
common::skillset_builder::SkillSetConfig::Villager,
),
.with_asset_expect("common.entity.village.villager"),
}
});